aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/base.php15
-rw-r--r--lib/composer/composer/LICENSE2
-rw-r--r--lib/composer/composer/autoload_classmap.php6
-rw-r--r--lib/composer/composer/autoload_static.php6
-rw-r--r--lib/composer/composer/installed.json69
-rw-r--r--lib/composer/composer/installed.php15
-rw-r--r--lib/l10n/pt_BR.js21
-rw-r--r--lib/l10n/pt_BR.json21
-rw-r--r--lib/private/App/AppManager.php16
-rw-r--r--lib/private/AppConfig.php48
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php30
-rw-r--r--lib/private/Collaboration/Reference/File/FileReferenceProvider.php8
-rw-r--r--lib/private/DirectEditing/Manager.php6
-rw-r--r--lib/private/Files/Config/CachedMountInfo.php7
-rw-r--r--lib/private/Files/Config/UserMountCache.php41
-rw-r--r--lib/private/Files/Node/Folder.php6
-rw-r--r--lib/private/Files/Node/LazyFolder.php6
-rw-r--r--lib/private/Files/Node/LazyRoot.php5
-rw-r--r--lib/private/Files/Node/LazyUserFolder.php12
-rw-r--r--lib/private/Files/Node/NonExistingFolder.php4
-rw-r--r--lib/private/Files/Node/Root.php32
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php3
-rw-r--r--lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php53
-rw-r--r--lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php4
-rw-r--r--lib/private/FilesMetadata/Job/UpdateSingleMetadata.php7
-rw-r--r--lib/private/Http/Client/Client.php37
-rw-r--r--lib/private/NavigationManager.php12
-rw-r--r--lib/private/Server.php43
-rw-r--r--lib/private/Share/Share.php309
-rw-r--r--lib/private/Share20/DefaultShareProvider.php11
-rw-r--r--lib/private/Share20/Manager.php9
-rw-r--r--lib/private/Share20/Share.php14
-rw-r--r--lib/private/SpeechToText/TranscriptionJob.php2
-rw-r--r--lib/private/Teams/TeamManager.php119
-rw-r--r--lib/private/Template/JSResourceLocator.php62
-rw-r--r--lib/private/legacy/OC_API.php2
-rw-r--r--lib/private/legacy/OC_EventSource.php4
-rw-r--r--lib/private/legacy/OC_Helper.php5
-rw-r--r--lib/private/legacy/OC_JSON.php14
-rw-r--r--lib/public/App/IAppManager.php9
-rw-r--r--lib/public/AppFramework/Bootstrap/IRegistrationContext.php8
-rw-r--r--lib/public/Files/Folder.php22
-rw-r--r--lib/public/Files/IRootFolder.php18
-rw-r--r--lib/public/Http/Client/IClient.php31
-rw-r--r--lib/public/IURLGenerator.php4
-rw-r--r--lib/public/Share.php94
-rw-r--r--lib/public/Share/IShare.php2
-rw-r--r--lib/public/Teams/ITeamManager.php58
-rw-r--r--lib/public/Teams/ITeamResourceProvider.php76
-rw-r--r--lib/public/Teams/Team.php73
-rw-r--r--lib/public/Teams/TeamResource.php129
51 files changed, 977 insertions, 633 deletions
diff --git a/lib/base.php b/lib/base.php
index 72afa0753ba..891161ff0c6 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -1018,21 +1018,6 @@ class OC {
}
}
- // emergency app disabling
- if ($requestPath === '/disableapp'
- && $request->getMethod() === 'POST'
- ) {
- \OC_JSON::callCheck();
- \OC_JSON::checkAdminUser();
- $appIds = (array)$request->getParam('appid');
- foreach ($appIds as $appId) {
- $appId = \OC_App::cleanAppId($appId);
- Server::get(\OCP\App\IAppManager::class)->disableApp($appId);
- }
- \OC_JSON::success();
- exit();
- }
-
// Always load authentication apps
OC_App::loadApps(['authentication']);
OC_App::loadApps(['extended_authentication']);
diff --git a/lib/composer/composer/LICENSE b/lib/composer/composer/LICENSE
index 62ecfd8d004..f27399a042d 100644
--- a/lib/composer/composer/LICENSE
+++ b/lib/composer/composer/LICENSE
@@ -1,3 +1,4 @@
+
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -17,3 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index d5c89d0237f..e32e509e3ee 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -691,6 +691,10 @@ return array(
'OCP\\Talk\\IConversation' => $baseDir . '/lib/public/Talk/IConversation.php',
'OCP\\Talk\\IConversationOptions' => $baseDir . '/lib/public/Talk/IConversationOptions.php',
'OCP\\Talk\\ITalkBackend' => $baseDir . '/lib/public/Talk/ITalkBackend.php',
+ 'OCP\\Teams\\ITeamManager' => $baseDir . '/lib/public/Teams/ITeamManager.php',
+ 'OCP\\Teams\\ITeamResourceProvider' => $baseDir . '/lib/public/Teams/ITeamResourceProvider.php',
+ 'OCP\\Teams\\Team' => $baseDir . '/lib/public/Teams/Team.php',
+ 'OCP\\Teams\\TeamResource' => $baseDir . '/lib/public/Teams/TeamResource.php',
'OCP\\Template' => $baseDir . '/lib/public/Template.php',
'OCP\\TextProcessing\\Events\\AbstractTextProcessingEvent' => $baseDir . '/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php',
'OCP\\TextProcessing\\Events\\TaskFailedEvent' => $baseDir . '/lib/public/TextProcessing/Events/TaskFailedEvent.php',
@@ -1158,6 +1162,7 @@ return array(
'OC\\Core\\Controller\\ReferenceController' => $baseDir . '/core/Controller/ReferenceController.php',
'OC\\Core\\Controller\\SearchController' => $baseDir . '/core/Controller/SearchController.php',
'OC\\Core\\Controller\\SetupController' => $baseDir . '/core/Controller/SetupController.php',
+ 'OC\\Core\\Controller\\TeamsApiController' => $baseDir . '/core/Controller/TeamsApiController.php',
'OC\\Core\\Controller\\TextProcessingApiController' => $baseDir . '/core/Controller/TextProcessingApiController.php',
'OC\\Core\\Controller\\TextToImageApiController' => $baseDir . '/core/Controller/TextToImageApiController.php',
'OC\\Core\\Controller\\TranslationApiController' => $baseDir . '/core/Controller/TranslationApiController.php',
@@ -1794,6 +1799,7 @@ return array(
'OC\\Tags' => $baseDir . '/lib/private/Tags.php',
'OC\\Talk\\Broker' => $baseDir . '/lib/private/Talk/Broker.php',
'OC\\Talk\\ConversationOptions' => $baseDir . '/lib/private/Talk/ConversationOptions.php',
+ 'OC\\Teams\\TeamManager' => $baseDir . '/lib/private/Teams/TeamManager.php',
'OC\\TempManager' => $baseDir . '/lib/private/TempManager.php',
'OC\\TemplateLayout' => $baseDir . '/lib/private/TemplateLayout.php',
'OC\\Template\\Base' => $baseDir . '/lib/private/Template/Base.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 906a5ac1996..2e8a3c71c0a 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -724,6 +724,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Talk\\IConversation' => __DIR__ . '/../../..' . '/lib/public/Talk/IConversation.php',
'OCP\\Talk\\IConversationOptions' => __DIR__ . '/../../..' . '/lib/public/Talk/IConversationOptions.php',
'OCP\\Talk\\ITalkBackend' => __DIR__ . '/../../..' . '/lib/public/Talk/ITalkBackend.php',
+ 'OCP\\Teams\\ITeamManager' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamManager.php',
+ 'OCP\\Teams\\ITeamResourceProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamResourceProvider.php',
+ 'OCP\\Teams\\Team' => __DIR__ . '/../../..' . '/lib/public/Teams/Team.php',
+ 'OCP\\Teams\\TeamResource' => __DIR__ . '/../../..' . '/lib/public/Teams/TeamResource.php',
'OCP\\Template' => __DIR__ . '/../../..' . '/lib/public/Template.php',
'OCP\\TextProcessing\\Events\\AbstractTextProcessingEvent' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/Events/AbstractTextProcessingEvent.php',
'OCP\\TextProcessing\\Events\\TaskFailedEvent' => __DIR__ . '/../../..' . '/lib/public/TextProcessing/Events/TaskFailedEvent.php',
@@ -1191,6 +1195,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Controller\\ReferenceController' => __DIR__ . '/../../..' . '/core/Controller/ReferenceController.php',
'OC\\Core\\Controller\\SearchController' => __DIR__ . '/../../..' . '/core/Controller/SearchController.php',
'OC\\Core\\Controller\\SetupController' => __DIR__ . '/../../..' . '/core/Controller/SetupController.php',
+ 'OC\\Core\\Controller\\TeamsApiController' => __DIR__ . '/../../..' . '/core/Controller/TeamsApiController.php',
'OC\\Core\\Controller\\TextProcessingApiController' => __DIR__ . '/../../..' . '/core/Controller/TextProcessingApiController.php',
'OC\\Core\\Controller\\TextToImageApiController' => __DIR__ . '/../../..' . '/core/Controller/TextToImageApiController.php',
'OC\\Core\\Controller\\TranslationApiController' => __DIR__ . '/../../..' . '/core/Controller/TranslationApiController.php',
@@ -1827,6 +1832,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Tags' => __DIR__ . '/../../..' . '/lib/private/Tags.php',
'OC\\Talk\\Broker' => __DIR__ . '/../../..' . '/lib/private/Talk/Broker.php',
'OC\\Talk\\ConversationOptions' => __DIR__ . '/../../..' . '/lib/private/Talk/ConversationOptions.php',
+ 'OC\\Teams\\TeamManager' => __DIR__ . '/../../..' . '/lib/private/Teams/TeamManager.php',
'OC\\TempManager' => __DIR__ . '/../../..' . '/lib/private/TempManager.php',
'OC\\TemplateLayout' => __DIR__ . '/../../..' . '/lib/private/TemplateLayout.php',
'OC\\Template\\Base' => __DIR__ . '/../../..' . '/lib/private/Template/Base.php',
diff --git a/lib/composer/composer/installed.json b/lib/composer/composer/installed.json
index f20a6c47c6d..13ea12dca2a 100644
--- a/lib/composer/composer/installed.json
+++ b/lib/composer/composer/installed.json
@@ -1,5 +1,68 @@
{
- "packages": [],
- "dev": false,
- "dev-package-names": []
+ "packages": [
+ {
+ "name": "bamarni/composer-bin-plugin",
+ "version": "1.8.2",
+ "version_normalized": "1.8.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bamarni/composer-bin-plugin.git",
+ "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
+ "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "ext-json": "*",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
+ "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
+ "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
+ },
+ "time": "2022-10-31T08:38:03+00:00",
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Bamarni\\Composer\\Bin\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "No conflicts for your bin dependencies",
+ "keywords": [
+ "composer",
+ "conflict",
+ "dependency",
+ "executable",
+ "isolation",
+ "tool"
+ ],
+ "support": {
+ "issues": "https://github.com/bamarni/composer-bin-plugin/issues",
+ "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
+ },
+ "install-path": "../bamarni/composer-bin-plugin"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": [
+ "bamarni/composer-bin-plugin"
+ ]
}
diff --git a/lib/composer/composer/installed.php b/lib/composer/composer/installed.php
index ada5df14e4f..88d9c302532 100644
--- a/lib/composer/composer/installed.php
+++ b/lib/composer/composer/installed.php
@@ -3,21 +3,30 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '559a758533026559cf632ed1b3d74f6b1ebfb481',
+ 'reference' => 'b6abfc4cba2d1ef4fdd8f2c22bbff46796b9485e',
'type' => 'library',
'install_path' => __DIR__ . '/../../../',
'aliases' => array(),
- 'dev' => false,
+ 'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '559a758533026559cf632ed1b3d74f6b1ebfb481',
+ 'reference' => 'b6abfc4cba2d1ef4fdd8f2c22bbff46796b9485e',
'type' => 'library',
'install_path' => __DIR__ . '/../../../',
'aliases' => array(),
'dev_requirement' => false,
),
+ 'bamarni/composer-bin-plugin' => array(
+ 'pretty_version' => '1.8.2',
+ 'version' => '1.8.2.0',
+ 'reference' => '92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880',
+ 'type' => 'composer-plugin',
+ 'install_path' => __DIR__ . '/../bamarni/composer-bin-plugin',
+ 'aliases' => array(),
+ 'dev_requirement' => true,
+ ),
),
);
diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js
index ec60dde37a3..af812096080 100644
--- a/lib/l10n/pt_BR.js
+++ b/lib/l10n/pt_BR.js
@@ -36,6 +36,9 @@ OC.L10N.register(
"The following platforms are supported: %s" : "As seguintes plataformas são suportadas: %s",
"Server version %s or higher is required." : "É requerido um servidor da versão %s ou superior.",
"Server version %s or lower is required." : "É requerido um servidor da versão %s ou abaixo.",
+ "Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "A conta conectada deve ser um administrador, um subadministrador ou ter direito especial para acessar esta configuração",
+ "Logged in account must be an admin or sub admin" : "A conta conectada deve ser de um administrador ou subadministrador",
+ "Logged in account must be an admin" : "A conta logada deve ser de um administrador",
"Wiping of device %s has started" : "Limpeza do dispositivo %s iniciou",
"Wiping of device »%s« has started" : "A limpeza do dispositivo »%s« terminou",
"»%s« started remote wipe" : "»%s« iniciou a limpeza remota",
@@ -114,15 +117,22 @@ OC.L10N.register(
"Headline" : "Título ",
"Organisation" : "Organização",
"Role" : "Função",
+ "Unknown account" : "Conta desconhecida",
"Additional settings" : "Configurações adicionais",
+ "Enter the database Login and name for %s" : "Insira o login e o nome do banco de dados para %s",
+ "Enter the database Login for %s" : "Insira o login do banco de dados para %s",
"Enter the database name for %s" : "Digite o nome do banco de dados para %s",
"You cannot use dots in the database name %s" : "Você não pode usar pontos no nome do banco de dados %s",
+ "MySQL Login and/or password not valid" : "Login e/ou senha do MySQL inválidos",
"You need to enter details of an existing account." : "Você necessita entrar detalhes de uma conta existente.",
"Oracle connection could not be established" : "Conexão Oracle não pôde ser estabelecida",
+ "Oracle Login and/or password not valid" : "Login e/ou senha Oracle inválidos",
+ "PostgreSQL Login and/or password not valid" : "Login e/ou senha do PostgreSQL inválidos",
"Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X não é suportado e %s não funcionará corretamente nesta plataforma. Use-o por sua conta e risco!",
"For the best results, please consider using a GNU/Linux server instead." : "Para obter melhores resultados, por favor considere o uso de um servidor GNU/Linux em seu lugar.",
"It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Aparentemente a instância %s está rodando em um ambiente PHP de 32 bits e o open_basedir foi configurado no php.ini. Isto pode gerar problemas com arquivos maiores que 4GB e é altamente não recomendável.",
"Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, remova a configuração de open_basedir de seu php.ini ou mude o PHP para 64bit.",
+ "Set an admin Login." : "Defina um login de administrador.",
"Set an admin password." : "Defina uma senha para o administrador.",
"Cannot create or write into the data directory %s" : "Não foi possível criar ou escrever no diretório de dados %s",
"Sharing backend %s must implement the interface OCP\\Share_Backend" : "A plataforma de compartilhamento %s deve implementar a interface OCP\\Share_Backend",
@@ -140,6 +150,7 @@ OC.L10N.register(
"Expiration date is in the past" : "Data de expiração está no passado",
"_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro"],
"Sharing is only allowed with group members" : "O compartilhamento só é permitido com membros do grupo ",
+ "Sharing %s failed, because this item is already shared with the account %s" : "Falha no compartilhamento %s porque este item já está compartilhado com a conta %s",
"%1$s shared »%2$s« with you" : "%1$s compartilhou »%2$s« com você",
"%1$s shared »%2$s« with you." : "%1$s compartilhou »%2$s« com você.",
"Click the button below to open it." : "Clique no botão abaixo para abri-lo.",
@@ -193,6 +204,14 @@ OC.L10N.register(
"Nov." : "Nov.",
"Dec." : "Dez.",
"A valid password must be provided" : "Uma senha válida deve ser fornecida",
+ "The Login is already being used" : "O Login já está sendo usado",
+ "Could not create account" : "Não foi possível criar a conta",
+ "Only the following characters are allowed in an Login: \"a-z\", \"A-Z\", \"0-9\", spaces and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um Login: \"a-z\", \"A-Z\", \"0-9\", espaços e \"_.@-'\"",
+ "A valid Login must be provided" : "Um login válido deve ser fornecido",
+ "Login contains whitespace at the beginning or at the end" : "O login contém espaços em branco no início ou no final",
+ "Login must not consist of dots only" : "O login não deve consistir apenas em pontos",
+ "Login is invalid because files already exist for this user" : "O login é inválido porque já existem arquivos para este usuário",
+ "Account disabled" : "Conta desativada",
"Login canceled by app" : "Login cancelado pelo aplicativo",
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "O aplicativo \"%1$s\" não pode ser instalado devido à estas dependências: %2$s",
"a safe home for all your data" : "Um lar seguro para todos os seus dados",
@@ -225,6 +244,8 @@ OC.L10N.register(
"Please ask your server administrator to restart the web server." : "Por favor peça ao administrador do servidor para reiniciar o servidor web.",
"The required %s config variable is not configured in the config.php file." : "A variável %s de configuração necessária não está configurada no arquivo config.php.",
"Please ask your server administrator to check the Nextcloud configuration." : "Peça ao administrador do servidor para verificar a configuração do Nextcloud.",
+ "Your data directory is readable by other people." : "Seu diretório de dados pode ser lido por outras pessoas.",
+ "Please change the permissions to 0770 so that the directory cannot be listed by other people." : "Altere as permissões para 0770 para que o diretório não possa ser listado por outras pessoas.",
"Your data directory must be an absolute path." : "Seu diretório de dados deve ser um caminho absoluto.",
"Check the value of \"datadirectory\" in your configuration." : "Verifique o valor de \"datadirectory\" em sua configuração.",
"Your data directory is invalid." : "Seu diretório de dados é inválido.",
diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json
index 3a80558b675..100cfc51ce4 100644
--- a/lib/l10n/pt_BR.json
+++ b/lib/l10n/pt_BR.json
@@ -34,6 +34,9 @@
"The following platforms are supported: %s" : "As seguintes plataformas são suportadas: %s",
"Server version %s or higher is required." : "É requerido um servidor da versão %s ou superior.",
"Server version %s or lower is required." : "É requerido um servidor da versão %s ou abaixo.",
+ "Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "A conta conectada deve ser um administrador, um subadministrador ou ter direito especial para acessar esta configuração",
+ "Logged in account must be an admin or sub admin" : "A conta conectada deve ser de um administrador ou subadministrador",
+ "Logged in account must be an admin" : "A conta logada deve ser de um administrador",
"Wiping of device %s has started" : "Limpeza do dispositivo %s iniciou",
"Wiping of device »%s« has started" : "A limpeza do dispositivo »%s« terminou",
"»%s« started remote wipe" : "»%s« iniciou a limpeza remota",
@@ -112,15 +115,22 @@
"Headline" : "Título ",
"Organisation" : "Organização",
"Role" : "Função",
+ "Unknown account" : "Conta desconhecida",
"Additional settings" : "Configurações adicionais",
+ "Enter the database Login and name for %s" : "Insira o login e o nome do banco de dados para %s",
+ "Enter the database Login for %s" : "Insira o login do banco de dados para %s",
"Enter the database name for %s" : "Digite o nome do banco de dados para %s",
"You cannot use dots in the database name %s" : "Você não pode usar pontos no nome do banco de dados %s",
+ "MySQL Login and/or password not valid" : "Login e/ou senha do MySQL inválidos",
"You need to enter details of an existing account." : "Você necessita entrar detalhes de uma conta existente.",
"Oracle connection could not be established" : "Conexão Oracle não pôde ser estabelecida",
+ "Oracle Login and/or password not valid" : "Login e/ou senha Oracle inválidos",
+ "PostgreSQL Login and/or password not valid" : "Login e/ou senha do PostgreSQL inválidos",
"Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X não é suportado e %s não funcionará corretamente nesta plataforma. Use-o por sua conta e risco!",
"For the best results, please consider using a GNU/Linux server instead." : "Para obter melhores resultados, por favor considere o uso de um servidor GNU/Linux em seu lugar.",
"It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Aparentemente a instância %s está rodando em um ambiente PHP de 32 bits e o open_basedir foi configurado no php.ini. Isto pode gerar problemas com arquivos maiores que 4GB e é altamente não recomendável.",
"Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Por favor, remova a configuração de open_basedir de seu php.ini ou mude o PHP para 64bit.",
+ "Set an admin Login." : "Defina um login de administrador.",
"Set an admin password." : "Defina uma senha para o administrador.",
"Cannot create or write into the data directory %s" : "Não foi possível criar ou escrever no diretório de dados %s",
"Sharing backend %s must implement the interface OCP\\Share_Backend" : "A plataforma de compartilhamento %s deve implementar a interface OCP\\Share_Backend",
@@ -138,6 +148,7 @@
"Expiration date is in the past" : "Data de expiração está no passado",
"_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro"],
"Sharing is only allowed with group members" : "O compartilhamento só é permitido com membros do grupo ",
+ "Sharing %s failed, because this item is already shared with the account %s" : "Falha no compartilhamento %s porque este item já está compartilhado com a conta %s",
"%1$s shared »%2$s« with you" : "%1$s compartilhou »%2$s« com você",
"%1$s shared »%2$s« with you." : "%1$s compartilhou »%2$s« com você.",
"Click the button below to open it." : "Clique no botão abaixo para abri-lo.",
@@ -191,6 +202,14 @@
"Nov." : "Nov.",
"Dec." : "Dez.",
"A valid password must be provided" : "Uma senha válida deve ser fornecida",
+ "The Login is already being used" : "O Login já está sendo usado",
+ "Could not create account" : "Não foi possível criar a conta",
+ "Only the following characters are allowed in an Login: \"a-z\", \"A-Z\", \"0-9\", spaces and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um Login: \"a-z\", \"A-Z\", \"0-9\", espaços e \"_.@-'\"",
+ "A valid Login must be provided" : "Um login válido deve ser fornecido",
+ "Login contains whitespace at the beginning or at the end" : "O login contém espaços em branco no início ou no final",
+ "Login must not consist of dots only" : "O login não deve consistir apenas em pontos",
+ "Login is invalid because files already exist for this user" : "O login é inválido porque já existem arquivos para este usuário",
+ "Account disabled" : "Conta desativada",
"Login canceled by app" : "Login cancelado pelo aplicativo",
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "O aplicativo \"%1$s\" não pode ser instalado devido à estas dependências: %2$s",
"a safe home for all your data" : "Um lar seguro para todos os seus dados",
@@ -223,6 +242,8 @@
"Please ask your server administrator to restart the web server." : "Por favor peça ao administrador do servidor para reiniciar o servidor web.",
"The required %s config variable is not configured in the config.php file." : "A variável %s de configuração necessária não está configurada no arquivo config.php.",
"Please ask your server administrator to check the Nextcloud configuration." : "Peça ao administrador do servidor para verificar a configuração do Nextcloud.",
+ "Your data directory is readable by other people." : "Seu diretório de dados pode ser lido por outras pessoas.",
+ "Please change the permissions to 0770 so that the directory cannot be listed by other people." : "Altere as permissões para 0770 para que o diretório não possa ser listado por outras pessoas.",
"Your data directory must be an absolute path." : "Seu diretório de dados deve ser um caminho absoluto.",
"Check the value of \"datadirectory\" in your configuration." : "Verifique o valor de \"datadirectory\" em sua configuração.",
"Your data directory is invalid." : "Seu diretório de dados é inválido.",
diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php
index 5eeb7ef1fbd..5dec4e7ccde 100644
--- a/lib/private/App/AppManager.php
+++ b/lib/private/App/AppManager.php
@@ -56,6 +56,7 @@ use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
+use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Settings\IManager as ISettingsManager;
@@ -104,9 +105,24 @@ class AppManager implements IAppManager {
private ICacheFactory $memCacheFactory,
private IEventDispatcher $dispatcher,
private LoggerInterface $logger,
+ private IURLGenerator $urlGenerator,
) {
}
+ public function getAppIcon(string $appId): ?string {
+ $possibleIcons = [$appId . '.svg', 'app.svg', $appId . '-dark.svg', 'app-dark.svg'];
+ $icon = null;
+ foreach ($possibleIcons as $iconName) {
+ try {
+ $icon = $this->urlGenerator->imagePath($appId, $iconName);
+ break;
+ } catch (\RuntimeException $e) {
+ // ignore
+ }
+ }
+ return $icon;
+ }
+
/**
* @return string[] $appId => $enabled
*/
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index 00e552dd24f..debd928f952 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -46,6 +46,7 @@ use OCP\Exceptions\AppConfigUnknownKeyException;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
+use OCP\Security\ICrypto;
use Psr\Log\LoggerInterface;
/**
@@ -70,6 +71,8 @@ use Psr\Log\LoggerInterface;
class AppConfig implements IAppConfig {
private const APP_MAX_LENGTH = 32;
private const KEY_MAX_LENGTH = 64;
+ private const ENCRYPTION_PREFIX = '$AppConfigEncryption$';
+ private const ENCRYPTION_PREFIX_LENGTH = 21; // strlen(self::ENCRYPTION_PREFIX)
/** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */
private array $fastCache = []; // cache for normal config keys
@@ -92,7 +95,8 @@ class AppConfig implements IAppConfig {
public function __construct(
protected IDBConnection $connection,
- private LoggerInterface $logger,
+ protected LoggerInterface $logger,
+ protected ICrypto $crypto,
) {
}
@@ -469,12 +473,26 @@ class AppConfig implements IAppConfig {
/**
* - the pair $app/$key cannot exist in both array,
- * - we should still returns an existing non-lazy value even if current method
+ * - we should still return an existing non-lazy value even if current method
* is called with $lazy is true
*
* This way, lazyCache will be empty until the load for lazy config value is requested.
*/
- return $this->lazyCache[$app][$key] ?? $this->fastCache[$app][$key] ?? $default;
+ if (isset($this->lazyCache[$app][$key])) {
+ $value = $this->lazyCache[$app][$key];
+ } elseif (isset($this->fastCache[$app][$key])) {
+ $value = $this->fastCache[$app][$key];
+ } else {
+ return $default;
+ }
+
+ $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $knownType);
+ if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) {
+ // Only decrypt values that are stored encrypted
+ $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
+ }
+
+ return $value;
}
/**
@@ -742,6 +760,10 @@ class AppConfig implements IAppConfig {
return false;
}
+ if ($sensitive || ($this->hasKey($app, $key, $lazy) && $this->isSensitive($app, $key, $lazy))) {
+ $value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value);
+ }
+
$refreshCache = false;
$insert = $this->connection->getQueryBuilder();
$insert->insert('appconfig')
@@ -789,7 +811,7 @@ class AppConfig implements IAppConfig {
// we fix $type if the stored value, or the new value as it might be changed, is set as sensitive
if ($sensitive || $this->isTyped(self::VALUE_SENSITIVE, $currType)) {
- $type = $type | self::VALUE_SENSITIVE;
+ $type |= self::VALUE_SENSITIVE;
}
if ($lazy !== $this->isLazy($app, $key)) {
@@ -893,18 +915,34 @@ class AppConfig implements IAppConfig {
return false;
}
+ $lazy = $this->isLazy($app, $key);
+ if ($lazy) {
+ $cache = $this->lazyCache;
+ } else {
+ $cache = $this->fastCache;
+ }
+
+ if (!isset($cache[$app][$key])) {
+ throw new AppConfigUnknownKeyException('unknown config key');
+ }
+
/**
* type returned by getValueType() is already cleaned from sensitive flag
* we just need to update it based on $sensitive and store it in database
*/
$type = $this->getValueType($app, $key);
+ $value = $cache[$app][$key];
if ($sensitive) {
- $type = $type | self::VALUE_SENSITIVE;
+ $type |= self::VALUE_SENSITIVE;
+ $value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value);
+ } else {
+ $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
}
$update = $this->connection->getQueryBuilder();
$update->update('appconfig')
->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
+ ->set('configvalue', $update->createNamedParameter($value))
->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
$update->executeStatement();
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index 120ee7ea9fa..6c51aafff9b 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -54,6 +54,7 @@ use OCP\Share\IPublicShareTemplateProvider;
use OCP\SpeechToText\ISpeechToTextProvider;
use OCP\Support\CrashReport\IReporter;
use OCP\Talk\ITalkBackend;
+use OCP\Teams\ITeamResourceProvider;
use OCP\TextProcessing\IProvider as ITextProcessingProvider;
use OCP\Translation\ITranslationProvider;
use OCP\UserMigration\IMigrator as IUserMigrator;
@@ -158,6 +159,9 @@ class RegistrationContext {
/** @var PreviewProviderRegistration[] */
private array $previewProviders = [];
+ /** @var ServiceRegistration<ITeamResourceProvider>[] */
+ private array $teamResourceProviders = [];
+
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
@@ -357,6 +361,13 @@ class RegistrationContext {
);
}
+ public function registerTeamResourceProvider(string $class) : void {
+ $this->context->registerTeamResourceProvider(
+ $this->appId,
+ $class
+ );
+ }
+
public function registerCalendarRoomBackend(string $class): void {
$this->context->registerCalendarRoomBackend(
$this->appId,
@@ -531,6 +542,17 @@ class RegistrationContext {
);
}
+
+ /**
+ * @psalm-param class-string<ITeamResourceProvider> $class
+ */
+ public function registerTeamResourceProvider(string $appId, string $class) {
+ $this->teamResourceProviders[] = new ServiceRegistration(
+ $appId,
+ $class
+ );
+ }
+
/**
* @psalm-param class-string<IUserMigrator> $migratorClass
*/
@@ -870,4 +892,12 @@ class RegistrationContext {
public function getSetupChecks(): array {
return $this->setupChecks;
}
+
+
+ /**
+ * @return ServiceRegistration<ITeamResourceProvider>[]
+ */
+ public function getTeamResourceProviders(): array {
+ return $this->teamResourceProviders;
+ }
}
diff --git a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
index 5f384213976..125649246df 100644
--- a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
+++ b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
@@ -31,7 +31,6 @@ use OCP\Collaboration\Reference\Reference;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
-use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IL10N;
@@ -121,15 +120,12 @@ class FileReferenceProvider extends ADiscoverableReferenceProvider {
try {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
- $files = $userFolder->getById($fileId);
+ $file = $userFolder->getFirstNodeById($fileId);
- if (empty($files)) {
+ if (!$file) {
throw new NotFoundException();
}
- /** @var Node $file */
- $file = array_shift($files);
-
$reference->setTitle($file->getName());
$reference->setDescription($file->getMimetype());
$reference->setUrl($this->urlGenerator->getAbsoluteURL('/index.php/f/' . $fileId));
diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php
index d1be1f50330..da4811589da 100644
--- a/lib/private/DirectEditing/Manager.php
+++ b/lib/private/DirectEditing/Manager.php
@@ -310,11 +310,11 @@ class Manager implements IManager {
if ($filePath !== null) {
return $userFolder->get($filePath);
}
- $files = $userFolder->getById($fileId);
- if (count($files) === 0) {
+ $file = $userFolder->getFirstNodeById($fileId);
+ if (!$file) {
throw new NotFoundException('File nound found by id ' . $fileId);
}
- return $files[0];
+ return $file;
}
public function isEnabled(): bool {
diff --git a/lib/private/Files/Config/CachedMountInfo.php b/lib/private/Files/Config/CachedMountInfo.php
index 7c97135a565..19fa87aa090 100644
--- a/lib/private/Files/Config/CachedMountInfo.php
+++ b/lib/private/Files/Config/CachedMountInfo.php
@@ -97,12 +97,7 @@ class CachedMountInfo implements ICachedMountInfo {
// TODO injection etc
Filesystem::initMountPoints($this->getUser()->getUID());
$userNode = \OC::$server->getUserFolder($this->getUser()->getUID());
- $nodes = $userNode->getParent()->getById($this->getRootId());
- if (count($nodes) > 0) {
- return $nodes[0];
- } else {
- return null;
- }
+ return $userNode->getParent()->getFirstNodeById($this->getRootId());
}
/**
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index 27d84e93838..8275eee7b9f 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -28,6 +28,7 @@
*/
namespace OC\Files\Config;
+use OC\User\LazyUser;
use OCP\Cache\CappedMemoryCache;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
@@ -213,13 +214,10 @@ class UserMountCache implements IUserMountCache {
/**
* @param array $row
* @param (callable(CachedMountInfo): string)|null $pathCallback
- * @return CachedMountInfo|null
+ * @return CachedMountInfo
*/
- private function dbRowToMountInfo(array $row, ?callable $pathCallback = null): ?ICachedMountInfo {
- $user = $this->userManager->get($row['user_id']);
- if (is_null($user)) {
- return null;
- }
+ private function dbRowToMountInfo(array $row, ?callable $pathCallback = null): ICachedMountInfo {
+ $user = new LazyUser($row['user_id'], $this->userManager);
$mount_id = $row['mount_id'];
if (!is_null($mount_id)) {
$mount_id = (int)$mount_id;
@@ -253,6 +251,9 @@ class UserMountCache implements IUserMountCache {
*/
public function getMountsForUser(IUser $user) {
$userUID = $user->getUID();
+ if (!$this->userManager->userExists($userUID)) {
+ return [];
+ }
if (!isset($this->mountsForUsers[$userUID])) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'mount_provider_class')
@@ -370,30 +371,22 @@ class UserMountCache implements IUserMountCache {
} catch (NotFoundException $e) {
return [];
}
- $builder = $this->connection->getQueryBuilder();
- $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class')
- ->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
- ->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($storageId, IQueryBuilder::PARAM_INT)));
-
- if ($user) {
- $query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user)));
- }
+ $mountsForStorage = $this->getMountsForStorageId($storageId, $user);
- $result = $query->execute();
- $rows = $result->fetchAll();
- $result->closeCursor();
- // filter mounts that are from the same storage but a different directory
- $filteredMounts = array_filter($rows, function (array $row) use ($internalPath, $fileId) {
- if ($fileId === (int)$row['root_id']) {
+ // filter mounts that are from the same storage but not a parent of the file we care about
+ $filteredMounts = array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
+ if ($fileId === $mount->getRootId()) {
return true;
}
- $internalMountPath = $row['path'] ?? '';
+ $internalMountPath = $mount->getRootInternalPath();
+
+ return $internalMountPath === '' || str_starts_with($internalPath, $internalMountPath . '/');
+ });
- return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
+ $filteredMounts = array_filter($filteredMounts, function (ICachedMountInfo $mount) {
+ return $this->userManager->userExists($mount->getUser()->getUID());
});
- $filteredMounts = array_filter(array_map([$this, 'dbRowToMountInfo'], $filteredMounts));
return array_map(function (ICachedMountInfo $mount) use ($internalPath) {
return new CachedMountFileInfo(
$mount->getUser(),
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index c7462572fed..014b66fdf63 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -307,12 +307,16 @@ class Folder extends Node implements \OCP\Files\Folder {
/**
* @param int $id
- * @return \OC\Files\Node\Node[]
+ * @return \OCP\Files\Node[]
*/
public function getById($id) {
return $this->root->getByIdInPath((int)$id, $this->getPath());
}
+ public function getFirstNodeById(int $id): ?\OCP\Files\Node {
+ return current($this->getById($id)) ?: null;
+ }
+
protected function getAppDataDirectoryName(): string {
$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
return 'appdata_' . $instanceId;
diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php
index e30cfea693e..4aae5cf9804 100644
--- a/lib/private/Files/Node/LazyFolder.php
+++ b/lib/private/Files/Node/LazyFolder.php
@@ -492,7 +492,11 @@ class LazyFolder implements Folder {
* @inheritDoc
*/
public function getById($id) {
- return $this->__call(__FUNCTION__, func_get_args());
+ return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath());
+ }
+
+ public function getFirstNodeById(int $id): ?\OCP\Files\Node {
+ return $this->getRootFolder()->getFirstNodeByIdInPath($id, $this->getPath());
}
/**
diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php
index 680e80cb45e..dd1596319fa 100644
--- a/lib/private/Files/Node/LazyRoot.php
+++ b/lib/private/Files/Node/LazyRoot.php
@@ -25,6 +25,7 @@ namespace OC\Files\Node;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
+use OCP\Files\Node;
use OCP\Files\Node as INode;
/**
@@ -56,6 +57,10 @@ class LazyRoot extends LazyFolder implements IRootFolder {
return $this->__call(__FUNCTION__, func_get_args());
}
+ public function getFirstNodeByIdInPath(int $id, string $path): ?Node {
+ return $this->__call(__FUNCTION__, func_get_args());
+ }
+
public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode {
return $this->getRootFolder()->getNodeFromCacheEntryAndMount($cacheEntry, $mountPoint);
}
diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php
index 917ab80f366..3cb840ccb6d 100644
--- a/lib/private/Files/Node/LazyUserFolder.php
+++ b/lib/private/Files/Node/LazyUserFolder.php
@@ -68,18 +68,6 @@ class LazyUserFolder extends LazyFolder {
]);
}
- public function get($path) {
- return $this->getRootFolder()->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/'));
- }
-
- /**
- * @param int $id
- * @return \OCP\Files\Node[]
- */
- public function getById($id) {
- return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath());
- }
-
public function getMountPoint() {
if ($this->folder !== null) {
return $this->folder->getMountPoint();
diff --git a/lib/private/Files/Node/NonExistingFolder.php b/lib/private/Files/Node/NonExistingFolder.php
index 34621b18f19..af0ad002f24 100644
--- a/lib/private/Files/Node/NonExistingFolder.php
+++ b/lib/private/Files/Node/NonExistingFolder.php
@@ -162,6 +162,10 @@ class NonExistingFolder extends Folder {
throw new NotFoundException();
}
+ public function getFirstNodeById(int $id): ?\OCP\Files\Node {
+ throw new NotFoundException();
+ }
+
public function getFreeSpace() {
throw new NotFoundException();
}
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index 9bfb610199b..71bc7a6da6b 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -49,6 +49,8 @@ use OCP\Files\Mount\IMountPoint;
use OCP\Files\Node as INode;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
+use OCP\ICache;
+use OCP\ICacheFactory;
use OCP\IUser;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
@@ -81,6 +83,7 @@ class Root extends Folder implements IRootFolder {
private LoggerInterface $logger;
private IUserManager $userManager;
private IEventDispatcher $eventDispatcher;
+ private ICache $pathByIdCache;
/**
* @param Manager $manager
@@ -94,7 +97,8 @@ class Root extends Folder implements IRootFolder {
IUserMountCache $userMountCache,
LoggerInterface $logger,
IUserManager $userManager,
- IEventDispatcher $eventDispatcher
+ IEventDispatcher $eventDispatcher,
+ ICacheFactory $cacheFactory,
) {
parent::__construct($this, $view, '');
$this->mountManager = $manager;
@@ -107,6 +111,7 @@ class Root extends Folder implements IRootFolder {
$eventDispatcher->addListener(FilesystemTornDownEvent::class, function () {
$this->userFolderCache = new CappedMemoryCache();
});
+ $this->pathByIdCache = $cacheFactory->createLocal('path-by-id');
}
/**
@@ -405,6 +410,31 @@ class Root extends Folder implements IRootFolder {
return $this->userMountCache;
}
+ public function getFirstNodeByIdInPath(int $id, string $path): ?INode {
+ // scope the cache by user, so we don't return nodes for different users
+ if ($this->user) {
+ $cachedPath = $this->pathByIdCache->get($this->user->getUID() . '::' . $id);
+ if ($cachedPath && str_starts_with($path, $cachedPath)) {
+ // getting the node by path is significantly cheaper than finding it by id
+ $node = $this->get($cachedPath);
+ // by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path
+ // if the cached path is invalid or a different file now we fall back to the uncached logic
+ if ($node && $node->getId() === $id) {
+ return $node;
+ }
+ }
+ }
+ $node = current($this->getByIdInPath($id, $path));
+ if (!$node) {
+ return null;
+ }
+
+ if ($this->user) {
+ $this->pathByIdCache->set($this->user->getUID() . '::' . $id, $node->getPath());
+ }
+ return $node;
+ }
+
/**
* @param int $id
* @return Node[]
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index 2ef9614ac85..623c4d08c74 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -212,7 +212,8 @@ trait S3ObjectTrait {
$copy->copy();
} else {
$this->getConnection()->copy($this->getBucket(), $from, $this->getBucket(), $to, 'private', array_merge([
- 'params' => $this->getSSECParameters() + $this->getSSECParameters(true)
+ 'params' => $this->getSSECParameters() + $this->getSSECParameters(true),
+ 'mup_threshold' => PHP_INT_MAX,
], $options));
}
}
diff --git a/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php b/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php
index 922b0ccb17c..7986df82adc 100644
--- a/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php
+++ b/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php
@@ -18,21 +18,19 @@ use OCP\Files\Search\ISearchOperator;
*/
class MergeDistributiveOperations extends ReplacingOptimizerStep {
public function processOperator(ISearchOperator &$operator): bool {
- if (
- $operator instanceof SearchBinaryOperator &&
- $this->isAllSameBinaryOperation($operator->getArguments())
- ) {
+ if ($operator instanceof SearchBinaryOperator) {
// either 'AND' or 'OR'
$topLevelType = $operator->getType();
// split the arguments into groups that share a first argument
- // (we already know that all arguments are binary operators with at least 1 child)
$groups = $this->groupBinaryOperatorsByChild($operator->getArguments(), 0);
$outerOperations = array_map(function (array $operators) use ($topLevelType) {
// no common operations, no need to change anything
if (count($operators) === 1) {
return $operators[0];
}
+
+ // for groups with size >1 we know they are binary operators with at least 1 child
/** @var ISearchBinaryOperator $firstArgument */
$firstArgument = $operators[0];
@@ -73,45 +71,24 @@ class MergeDistributiveOperations extends ReplacingOptimizerStep {
}
/**
- * Check that a list of operators is all the same type of (non-empty) binary operators
- *
- * @param ISearchOperator[] $operators
- * @return bool
- * @psalm-assert-if-true SearchBinaryOperator[] $operators
- */
- private function isAllSameBinaryOperation(array $operators): bool {
- $operation = null;
- foreach ($operators as $operator) {
- if (!$operator instanceof SearchBinaryOperator) {
- return false;
- }
- if (!$operator->getArguments()) {
- return false;
- }
- if ($operation === null) {
- $operation = $operator->getType();
- } else {
- if ($operation !== $operator->getType()) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
* Group a list of binary search operators that have a common argument
*
- * @param SearchBinaryOperator[] $operators
- * @return SearchBinaryOperator[][]
+ * Non-binary operators, or empty binary operators will each get their own 1-sized group
+ *
+ * @param ISearchOperator[] $operators
+ * @return ISearchOperator[][]
*/
private function groupBinaryOperatorsByChild(array $operators, int $index = 0): array {
$result = [];
foreach ($operators as $operator) {
- /** @var SearchBinaryOperator|SearchComparison $child */
- $child = $operator->getArguments()[$index];
- $childKey = (string) $child;
- $result[$childKey][] = $operator;
+ if ($operator instanceof ISearchBinaryOperator && count($operator->getArguments()) > 0) {
+ /** @var SearchBinaryOperator|SearchComparison $child */
+ $child = $operator->getArguments()[$index];
+ $childKey = (string)$child;
+ $result[$childKey][] = $operator;
+ } else {
+ $result[] = [$operator];
+ }
}
return array_values($result);
}
diff --git a/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php b/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php
index 546061522bc..473f8a87151 100644
--- a/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php
+++ b/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php
@@ -20,7 +20,9 @@ class ReplacingOptimizerStep extends QueryOptimizerStep {
$modified = false;
$arguments = $operator->getArguments();
foreach ($arguments as &$argument) {
- $modified = $modified || $this->processOperator($argument);
+ if ($this->processOperator($argument)) {
+ $modified = true;
+ }
}
if ($modified) {
$operator->setArguments($arguments);
diff --git a/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php b/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php
index d18c8aa3680..3a3b35ce205 100644
--- a/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php
+++ b/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php
@@ -55,10 +55,9 @@ class UpdateSingleMetadata extends QueuedJob {
[$userId, $fileId] = $argument;
try {
- $node = $this->rootFolder->getUserFolder($userId)->getById($fileId);
- if (count($node) > 0) {
- $file = array_shift($node);
- $this->filesMetadataManager->refreshMetadata($file, IFilesMetadataManager::PROCESS_BACKGROUND);
+ $node = $this->rootFolder->getUserFolder($userId)->getFirstNodeById($fileId);
+ if ($node) {
+ $this->filesMetadataManager->refreshMetadata($node, IFilesMetadataManager::PROCESS_BACKGROUND);
}
} catch (\Exception $e) {
$this->logger->warning('issue while running UpdateSingleMetadata', ['exception' => $e, 'userId' => $userId, 'fileId' => $fileId]);
diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php
index b43583ba09a..65784e6cb58 100644
--- a/lib/private/Http/Client/Client.php
+++ b/lib/private/Http/Client/Client.php
@@ -424,6 +424,43 @@ class Client implements IClient {
throw $e;
}
+ /**
+ * Sends a HTTP request
+ *
+ * @param string $method The HTTP method to use
+ * @param string $uri
+ * @param array $options Array such as
+ * 'query' => [
+ * 'field' => 'abc',
+ * 'other_field' => '123',
+ * 'file_name' => fopen('/path/to/file', 'r'),
+ * ],
+ * 'headers' => [
+ * 'foo' => 'bar',
+ * ],
+ * 'cookies' => [
+ * 'foo' => 'bar',
+ * ],
+ * 'allow_redirects' => [
+ * 'max' => 10, // allow at most 10 redirects.
+ * 'strict' => true, // use "strict" RFC compliant redirects.
+ * 'referer' => true, // add a Referer header
+ * 'protocols' => ['https'] // only allow https URLs
+ * ],
+ * 'sink' => '/path/to/file', // save to a file or a stream
+ * 'verify' => true, // bool or string to CA file
+ * 'debug' => true,
+ * 'timeout' => 5,
+ * @return IResponse
+ * @throws \Exception If the request could not get completed
+ */
+ public function request(string $method, string $uri, array $options = []): IResponse {
+ $this->preventLocalAddress($uri, $options);
+ $response = $this->client->request($method, $uri, $this->buildRequestOptions($options));
+ $isStream = isset($options['stream']) && $options['stream'];
+ return new Response($response, $isStream);
+ }
+
protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise {
return new GuzzlePromiseAdapter(
$promise,
diff --git a/lib/private/NavigationManager.php b/lib/private/NavigationManager.php
index 17573d9db86..cac52ab5c9f 100644
--- a/lib/private/NavigationManager.php
+++ b/lib/private/NavigationManager.php
@@ -372,16 +372,18 @@ class NavigationManager implements INavigationManager {
$order = $nav['order'] ?? 100;
$type = $nav['type'];
$route = !empty($nav['route']) ? $this->urlGenerator->linkToRoute($nav['route']) : '';
- $icon = $nav['icon'] ?? 'app.svg';
- foreach ([$icon, "$app.svg"] as $i) {
+ $icon = $nav['icon'] ?? null;
+ if ($icon !== null) {
try {
- $icon = $this->urlGenerator->imagePath($app, $i);
- break;
+ $icon = $this->urlGenerator->imagePath($app, $icon);
} catch (\RuntimeException $ex) {
- // no icon? - ignore it then
+ // ignore
}
}
if ($icon === null) {
+ $icon = $this->appManager->getAppIcon($app);
+ }
+ if ($icon === null) {
$icon = $this->urlGenerator->imagePath('core', 'default-app-icon');
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 1aedd7d06ac..2732b6486f2 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -152,12 +152,12 @@ use OC\Security\VerificationToken\VerificationToken;
use OC\Session\CryptoWrapper;
use OC\SetupCheck\SetupCheckManager;
use OC\Share20\ProviderFactory;
-use OC\Share20\ShareDisableChecker;
use OC\Share20\ShareHelper;
use OC\SpeechToText\SpeechToTextManager;
use OC\SystemTag\ManagerFactory as SystemTagManagerFactory;
use OC\Tagging\TagMapper;
use OC\Talk\Broker;
+use OC\Teams\TeamManager;
use OC\Template\JSCombiner;
use OC\Translation\TranslationManager;
use OC\User\AvailabilityCoordinator;
@@ -260,11 +260,13 @@ use OCP\Security\ITrustedDomainHelper;
use OCP\Security\RateLimiting\ILimiter;
use OCP\Security\VerificationToken\IVerificationToken;
use OCP\SetupCheck\ISetupCheckManager;
+use OCP\Share\IProviderFactory;
use OCP\Share\IShareHelper;
use OCP\SpeechToText\ISpeechToTextManager;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\Talk\IBroker;
+use OCP\Teams\ITeamManager;
use OCP\Translation\ITranslationManager;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCP\User\Events\BeforeUserLoggedInEvent;
@@ -448,14 +450,17 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService('RootFolder', function (ContainerInterface $c) {
$manager = \OC\Files\Filesystem::getMountManager();
$view = new View();
+ /** @var IUserSession $userSession */
+ $userSession = $c->get(IUserSession::class);
$root = new Root(
$manager,
$view,
- null,
+ $userSession->getUser(),
$c->get(IUserMountCache::class),
$this->get(LoggerInterface::class),
$this->get(IUserManager::class),
$this->get(IEventDispatcher::class),
+ $this->get(ICacheFactory::class),
);
$previewConnector = new \OC\Preview\WatcherConnector(
@@ -888,7 +893,8 @@ class Server extends ServerContainer implements IServerContainer {
$c->get(IGroupManager::class),
$c->get(ICacheFactory::class),
$c->get(IEventDispatcher::class),
- $c->get(LoggerInterface::class)
+ $c->get(LoggerInterface::class),
+ $c->get(IURLGenerator::class),
);
});
/** @deprecated 19.0.0 */
@@ -1242,36 +1248,14 @@ class Server extends ServerContainer implements IServerContainer {
/** @deprecated 19.0.0 */
$this->registerDeprecatedAlias('ContentSecurityPolicyManager', ContentSecurityPolicyManager::class);
- $this->registerService(\OCP\Share\IManager::class, function (IServerContainer $c) {
+ $this->registerService(IProviderFactory::class, function (ContainerInterface $c) {
$config = $c->get(\OCP\IConfig::class);
$factoryClass = $config->getSystemValue('sharing.managerFactory', ProviderFactory::class);
/** @var \OCP\Share\IProviderFactory $factory */
- $factory = new $factoryClass($this);
-
- $manager = new \OC\Share20\Manager(
- $c->get(LoggerInterface::class),
- $c->get(\OCP\IConfig::class),
- $c->get(ISecureRandom::class),
- $c->get(IHasher::class),
- $c->get(IMountManager::class),
- $c->get(IGroupManager::class),
- $c->getL10N('lib'),
- $c->get(IFactory::class),
- $factory,
- $c->get(IUserManager::class),
- $c->get(IRootFolder::class),
- $c->get(IMailer::class),
- $c->get(IURLGenerator::class),
- $c->get('ThemingDefaults'),
- $c->get(IEventDispatcher::class),
- $c->get(IUserSession::class),
- $c->get(KnownUserService::class),
- $c->get(ShareDisableChecker::class),
- $c->get(IDateTimeZone::class),
- );
-
- return $manager;
+ return new $factoryClass($this);
});
+
+ $this->registerAlias(\OCP\Share\IManager::class, \OC\Share20\Manager::class);
/** @deprecated 19.0.0 */
$this->registerDeprecatedAlias('ShareManager', \OCP\Share\IManager::class);
@@ -1297,6 +1281,7 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(\OCP\Collaboration\Resources\IManager::class, \OC\Collaboration\Resources\Manager::class);
$this->registerAlias(IReferenceManager::class, ReferenceManager::class);
+ $this->registerAlias(ITeamManager::class, TeamManager::class);
$this->registerDeprecatedAlias('SettingsManager', \OC\Settings\Manager::class);
$this->registerAlias(\OCP\Settings\IManager::class, \OC\Settings\Manager::class);
diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php
index fb1cd50d94a..54e0eb72662 100644
--- a/lib/private/Share/Share.php
+++ b/lib/private/Share/Share.php
@@ -36,10 +36,6 @@
namespace OC\Share;
use OCA\Files_Sharing\ShareBackend\File;
-use OCP\DB\Exception;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-use OCP\Share\IShare;
use Psr\Log\LoggerInterface;
/**
@@ -94,107 +90,6 @@ class Share extends Constants {
}
/**
- * Get the item of item type shared with a given user by source
- *
- * @param string $itemType
- * @param string $itemSource
- * @param ?string $user User to whom the item was shared
- * @param ?string $owner Owner of the share
- * @param ?int $shareType only look for a specific share type
- * @return array Return list of items with file_target, permissions and expiration
- * @throws Exception
- */
- public static function getItemSharedWithUser(string $itemType, string $itemSource, ?string $user = null, ?string $owner = null, ?int $shareType = null) {
- $shares = [];
- $fileDependent = $itemType === 'file' || $itemType === 'folder';
- $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent);
- $qb->from('share', 's');
- if ($fileDependent) {
- $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid'));
- $qb->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage'));
- $column = 'file_source';
- } else {
- $column = 'item_source';
- }
-
- $qb->where($qb->expr()->eq($column, $qb->createNamedParameter($itemSource)))
- ->andWhere($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType)));
-
- // for link shares $user === null
- if ($user !== null) {
- $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($user)));
- }
-
- if ($shareType !== null) {
- $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType, IQueryBuilder::PARAM_INT)));
- }
-
- if ($owner !== null) {
- $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($owner)));
- }
-
- $result = $qb->executeQuery();
- while ($row = $result->fetch()) {
- if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
- continue;
- }
- if ($fileDependent && (int)$row['file_parent'] === -1) {
- // if it is a mount point we need to get the path from the mount manager
- $mountManager = \OC\Files\Filesystem::getMountManager();
- $mountPoint = $mountManager->findByStorageId($row['storage_id']);
- if (!empty($mountPoint)) {
- $path = $mountPoint[0]->getMountPoint();
- $path = trim($path, '/');
- $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
- $row['path'] = $path;
- } else {
- \OC::$server->get(LoggerInterface::class)->warning(
- 'Could not resolve mount point for ' . $row['storage_id'],
- ['app' => 'OCP\Share']
- );
- }
- }
- $shares[] = $row;
- }
- $result->closeCursor();
-
- // if we didn't found a result then let's look for a group share.
- if (empty($shares) && $user !== null) {
- $userObject = \OC::$server->getUserManager()->get($user);
- $groups = [];
- if ($userObject) {
- $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
- }
-
- if (!empty($groups)) {
- $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent);
- $qb->from('share', 's');
-
- if ($fileDependent) {
- $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid'))
- ->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage'));
- }
-
- $qb->where($qb->expr()->eq($column, $qb->createNamedParameter($itemSource)))
- ->andWhere($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType, IQueryBuilder::PARAM_STR)))
- ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
-
- if ($owner !== null) {
- $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($owner)));
- }
- $result = $qb->executeQuery();
-
- while ($row = $result->fetch()) {
- $shares[] = $row;
- }
- $result->closeCursor();
- }
- }
-
- return $shares;
- }
-
- /**
* Get the backend class for the specified item type
*
* @param string $itemType
@@ -288,185 +183,6 @@ class Share extends Constants {
}
/**
- * Construct select statement
- *
- * @param bool $fileDependent ist it a file/folder share or a general share
- */
- private static function getSelectStatement(int $format, bool $fileDependent, ?string $uidOwner = null): IQueryBuilder {
- /** @var IDBConnection $connection */
- $connection = \OC::$server->get(IDBConnection::class);
- $qb = $connection->getQueryBuilder();
- if ($format == self::FORMAT_STATUSES) {
- if ($fileDependent) {
- return $qb->select(
- 's.id',
- 's.parent',
- 'share_type',
- 'path',
- 'storage',
- 'share_with',
- 'uid_owner',
- 'file_source',
- 'stime',
- 's.permissions',
- 'uid_initiator'
- )->selectAlias('st.id', 'storage_id')
- ->selectAlias('f.parent', 'file_parent');
- }
- return $qb->select('id', 'parent', 'share_type', 'share_with', 'uid_owner', 'item_source', 'stime', 's.permissions');
- }
-
- if (isset($uidOwner)) {
- if ($fileDependent) {
- return $qb->select(
- 's.id',
- 'item_type',
- 'item_source',
- 's.parent',
- 'share_type',
- 'share_with',
- 'file_source',
- 'file_target',
- 'path',
- 's.permissions',
- 'stime',
- 'expiration',
- 'token',
- 'storage',
- 'mail_send',
- 'uid_owner',
- 'uid_initiator'
- )->selectAlias('st.id', 'storage_id')
- ->selectAlias('f.parent', 'file_parent');
- }
- return $qb->select('id', 'item_type', 'item_source', 'parent', 'share_type',
- 'share_with', 'uid_owner', 'file_source', 'stime', 's.permissions',
- 'expiration', 'token', 'mail_send');
- }
-
- if ($fileDependent) {
- if ($format == File::FORMAT_GET_FOLDER_CONTENTS || $format == File::FORMAT_FILE_APP_ROOT) {
- return $qb->select(
- 's.id',
- 'item_type',
- 'item_source',
- 's.parent',
- 'uid_owner',
- 'share_type',
- 'share_with',
- 'file_source',
- 'path',
- 'file_target',
- 's.permissions',
- 'stime',
- 'expiration',
- 'storage',
- 'name',
- 'mtime',
- 'mimepart',
- 'size',
- 'encrypted',
- 'etag',
- 'mail_send'
- )->selectAlias('f.parent', 'file_parent');
- }
- return $qb->select(
- 's.id',
- 'item_type',
- 'item_source',
- 'item_target',
- 's.parent',
- 'share_type',
- 'share_with',
- 'uid_owner',
- 'file_source',
- 'path',
- 'file_target',
- 's.permissions',
- 'stime',
- 'expiration',
- 'token',
- 'storage',
- 'mail_send',
- )->selectAlias('f.parent', 'file_parent')
- ->selectAlias('st.id', 'storage_id');
- }
- return $qb->select('*');
- }
-
-
- /**
- * transform db results
- *
- * @param array $row result
- */
- private static function transformDBResults(&$row) {
- if (isset($row['id'])) {
- $row['id'] = (int)$row['id'];
- }
- if (isset($row['share_type'])) {
- $row['share_type'] = (int)$row['share_type'];
- }
- if (isset($row['parent'])) {
- $row['parent'] = (int)$row['parent'];
- }
- if (isset($row['file_parent'])) {
- $row['file_parent'] = (int)$row['file_parent'];
- }
- if (isset($row['file_source'])) {
- $row['file_source'] = (int)$row['file_source'];
- }
- if (isset($row['permissions'])) {
- $row['permissions'] = (int)$row['permissions'];
- }
- if (isset($row['storage'])) {
- $row['storage'] = (int)$row['storage'];
- }
- if (isset($row['stime'])) {
- $row['stime'] = (int)$row['stime'];
- }
- if (isset($row['expiration']) && $row['share_type'] !== IShare::TYPE_LINK) {
- // discard expiration date for non-link shares, which might have been
- // set by ancient bugs
- $row['expiration'] = null;
- }
- }
-
- /**
- * format result
- *
- * @param array $items result
- * @param string $column is it a file share or a general share ('file_target' or 'item_target')
- * @param \OCP\Share_Backend $backend sharing backend
- * @param int $format
- * @param array $parameters additional format parameters
- * @return array format result
- */
- private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE, $parameters = null) {
- if ($format === self::FORMAT_NONE) {
- return $items;
- } elseif ($format === self::FORMAT_STATUSES) {
- $statuses = [];
- foreach ($items as $item) {
- if ($item['share_type'] === IShare::TYPE_LINK) {
- if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
- continue;
- }
- $statuses[$item[$column]]['link'] = true;
- } elseif (!isset($statuses[$item[$column]])) {
- $statuses[$item[$column]]['link'] = false;
- }
- if (!empty($item['file_target'])) {
- $statuses[$item[$column]]['path'] = $item['path'];
- }
- }
- return $statuses;
- } else {
- return $backend->formatItems($items, $format, $parameters);
- }
- }
-
- /**
* remove protocol from URL
*
* @param string $url
@@ -489,29 +205,4 @@ class Share extends Constants {
public static function getExpireInterval() {
return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
}
-
- /**
- * Checks whether the given path is reachable for the given owner
- *
- * @param string $path path relative to files
- * @param string $ownerStorageId storage id of the owner
- *
- * @return boolean true if file is reachable, false otherwise
- */
- private static function isFileReachable($path, $ownerStorageId) {
- // if outside the home storage, file is always considered reachable
- if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
- substr($ownerStorageId, 0, 13) === 'object::user:'
- )) {
- return true;
- }
-
- // if inside the home storage, the file has to be under "/files/"
- $path = ltrim($path, '/');
- if (substr($path, 0, 6) === 'files/') {
- return true;
- }
-
- return false;
- }
}
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index b4ec16936f3..50196402b42 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -692,17 +692,24 @@ class DefaultShareProvider implements IShareProvider {
}, $childMountNodes);
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
+ $storageFilter = $qb->expr()->eq('f.storage', $qb->createNamedParameter($node->getMountPoint()->getNumericStorageId(), IQueryBuilder::PARAM_INT));
if ($shallow) {
$qb->andWhere(
$qb->expr()->orX(
- $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
+ $qb->expr()->andX(
+ $storageFilter,
+ $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
+ ),
$qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
)
);
} else {
$qb->andWhere(
$qb->expr()->orX(
- $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')),
+ $qb->expr()->andX(
+ $storageFilter,
+ $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')),
+ ),
$qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
)
);
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index 53f88cf14ce..9b54592dd1e 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -130,7 +130,6 @@ class Manager implements IManager {
IHasher $hasher,
IMountManager $mountManager,
IGroupManager $groupManager,
- IL10N $l,
IFactory $l10nFactory,
IProviderFactory $factory,
IUserManager $userManager,
@@ -150,7 +149,7 @@ class Manager implements IManager {
$this->hasher = $hasher;
$this->mountManager = $mountManager;
$this->groupManager = $groupManager;
- $this->l = $l;
+ $this->l = $l10nFactory->get('lib');
$this->l10nFactory = $l10nFactory;
$this->factory = $factory;
$this->userManager = $userManager;
@@ -311,8 +310,7 @@ class Manager implements IManager {
$mount = $userMount->getMountPoint();
// When it's a reshare use the parent share permissions as maximum
$userMountPointId = $mount->getStorageRootId();
- $userMountPoints = $userFolder->getById($userMountPointId);
- $userMountPoint = array_shift($userMountPoints);
+ $userMountPoint = $userFolder->getFirstNodeById($userMountPointId);
if ($userMountPoint === null) {
throw new GenericShareException('Could not get proper user mount for ' . $userMountPointId . '. Failing since else the next calls are called with null');
@@ -1723,8 +1721,7 @@ class Manager implements IManager {
//Get node for the owner and correct the owner in case of external storage
$userFolder = $this->rootFolder->getUserFolder($owner);
if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
- $nodes = $userFolder->getById($path->getId());
- $path = array_shift($nodes);
+ $path = $userFolder->getFirstNodeById($path->getId());
if ($path === null || $path->getOwner() === null) {
return [];
}
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index c80d332e9db..19b36cb60e8 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -188,12 +188,12 @@ class Share implements IShare {
$userFolder = $this->rootFolder->getUserFolder($this->sharedBy);
}
- $nodes = $userFolder->getById($this->fileId);
- if (empty($nodes)) {
+ $node = $userFolder->getFirstNodeById($this->fileId);
+ if (!$node) {
throw new NotFoundException('Node for share not found, fileid: ' . $this->fileId);
}
- $this->node = $nodes[0];
+ $this->node = $node;
}
return $this->node;
@@ -211,12 +211,16 @@ class Share implements IShare {
/**
* @inheritdoc
*/
- public function getNodeId() {
+ public function getNodeId(): int {
if ($this->fileId === null) {
$this->fileId = $this->getNode()->getId();
}
- return $this->fileId;
+ if ($this->fileId === null) {
+ throw new NotFoundException("Share source not found");
+ } else {
+ return $this->fileId;
+ }
}
/**
diff --git a/lib/private/SpeechToText/TranscriptionJob.php b/lib/private/SpeechToText/TranscriptionJob.php
index 8921d52ecd1..083cd129657 100644
--- a/lib/private/SpeechToText/TranscriptionJob.php
+++ b/lib/private/SpeechToText/TranscriptionJob.php
@@ -65,7 +65,7 @@ class TranscriptionJob extends QueuedJob {
try {
\OC_Util::setupFS($owner);
$userFolder = $this->rootFolder->getUserFolder($owner);
- $file = current($userFolder->getById($fileId));
+ $file = $userFolder->getFirstNodeById($fileId);
if (!($file instanceof File)) {
$this->logger->warning('Transcription of file ' . $fileId . ' failed. The file could not be found');
$this->eventDispatcher->dispatchTyped(
diff --git a/lib/private/Teams/TeamManager.php b/lib/private/Teams/TeamManager.php
new file mode 100644
index 00000000000..6651d3ce676
--- /dev/null
+++ b/lib/private/Teams/TeamManager.php
@@ -0,0 +1,119 @@
+<?php
+/**
+ * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Teams;
+
+use OC\AppFramework\Bootstrap\Coordinator;
+use OCA\Circles\CirclesManager;
+use OCA\Circles\Exceptions\CircleNotFoundException;
+use OCA\Circles\Model\Circle;
+use OCA\Circles\Model\Member;
+use OCP\IURLGenerator;
+use OCP\Server;
+use OCP\Teams\ITeamManager;
+use OCP\Teams\ITeamResourceProvider;
+use OCP\Teams\Team;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\NotFoundExceptionInterface;
+
+class TeamManager implements ITeamManager {
+
+ /** @var ?ITeamResourceProvider[] */
+ private ?array $providers = null;
+
+ public function __construct(
+ private Coordinator $bootContext,
+ private IURLGenerator $urlGenerator,
+ private ?CirclesManager $circlesManager,
+ ) {
+ }
+
+ public function hasTeamSupport(): bool {
+ return $this->circlesManager !== null;
+ }
+
+ public function getProviders(): array {
+ if ($this->providers !== null) {
+ return $this->providers;
+ }
+
+ $this->providers = [];
+ foreach ($this->bootContext->getRegistrationContext()->getTeamResourceProviders() as $providerRegistration) {
+ try {
+ /** @var ITeamResourceProvider $provider */
+ $provider = Server::get($providerRegistration->getService());
+ $this->providers[$provider->getId()] = $provider;
+ } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
+ }
+ }
+ return $this->providers;
+ }
+
+ public function getProvider(string $providerId): ITeamResourceProvider {
+ $providers = $this->getProviders();
+ if (isset($providers[$providerId])) {
+ return $providers[$providerId];
+ }
+
+ throw new \RuntimeException('No provider found for id ' .$providerId);
+ }
+
+ public function getSharedWith(string $teamId, string $userId): array {
+ if ($this->getTeam($teamId, $userId) === null) {
+ return [];
+ }
+
+ $resources = [];
+
+ foreach ($this->getProviders() as $provider) {
+ array_push($resources, ...$provider->getSharedWith($teamId));
+ }
+
+ return $resources;
+ }
+
+ public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array {
+ $provider = $this->getProvider($providerId);
+ return array_values(array_filter(array_map(function ($teamId) use ($userId) {
+ $team = $this->getTeam($teamId, $userId);
+ if ($team === null) {
+ return null;
+ }
+
+ return new Team(
+ $teamId,
+ $team->getDisplayName(),
+ $this->urlGenerator->linkToRouteAbsolute('contacts.contacts.directcircle', ['singleId' => $teamId]),
+ );
+ }, $provider->getTeamsForResource($resourceId))));
+ }
+
+ private function getTeam(string $teamId, string $userId): ?Circle {
+ try {
+ $federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER);
+ $this->circlesManager->startSession($federatedUser);
+ return $this->circlesManager->getCircle($teamId);
+ } catch (CircleNotFoundException) {
+ return null;
+ }
+ }
+}
diff --git a/lib/private/Template/JSResourceLocator.php b/lib/private/Template/JSResourceLocator.php
index 68a83fa4b73..c73b3efe997 100644
--- a/lib/private/Template/JSResourceLocator.php
+++ b/lib/private/Template/JSResourceLocator.php
@@ -51,6 +51,22 @@ class JSResourceLocator extends ResourceLocator {
// Extracting the appId and the script file name
$app = substr($script, 0, strpos($script, '/'));
$scriptName = basename($script);
+ // Get the app root path
+ $appRoot = $this->serverroot . 'apps/';
+ $appWebRoot = null;
+ try {
+ // We need the dir name as getAppPath appends the appid
+ $appRoot = dirname($this->appManager->getAppPath($app));
+ // Only do this if $app_path is set, because an empty argument to realpath gets turned into cwd.
+ if ($appRoot) {
+ // Handle symlinks
+ $appRoot = realpath($appRoot);
+ }
+ // Get the app webroot
+ $appWebRoot = dirname($this->appManager->getAppWebPath($app));
+ } catch (AppPathNotFoundException $e) {
+ // ignore
+ }
if (str_contains($script, '/l10n/')) {
// For language files we try to load them all, so themes can overwrite
@@ -60,13 +76,8 @@ class JSResourceLocator extends ResourceLocator {
$found += $this->appendScriptIfExist($this->serverroot, $theme_dir.'core/'.$script);
$found += $this->appendScriptIfExist($this->serverroot, $script);
$found += $this->appendScriptIfExist($this->serverroot, $theme_dir.$script);
-
- foreach (\OC::$APPSROOTS as $appRoot) {
- $dirName = basename($appRoot['path']);
- $rootPath = dirname($appRoot['path']);
- $found += $this->appendScriptIfExist($rootPath, $dirName.'/'.$script);
- $found += $this->appendScriptIfExist($this->serverroot, $theme_dir.$dirName.'/'.$script);
- }
+ $found += $this->appendScriptIfExist($appRoot, $script, $appWebRoot);
+ $found += $this->appendScriptIfExist($this->serverroot, $theme_dir.'apps/'.$script);
if ($found) {
return;
@@ -76,8 +87,9 @@ class JSResourceLocator extends ResourceLocator {
|| $this->appendScriptIfExist($this->serverroot, $script)
|| $this->appendScriptIfExist($this->serverroot, $theme_dir."dist/$app-$scriptName")
|| $this->appendScriptIfExist($this->serverroot, "dist/$app-$scriptName")
- || $this->appendScriptIfExist($this->serverroot, 'apps/'.$script)
+ || $this->appendScriptIfExist($appRoot, $script, $appWebRoot)
|| $this->cacheAndAppendCombineJsonIfExist($this->serverroot, $script.'.json')
+ || $this->cacheAndAppendCombineJsonIfExist($appRoot, $script.'.json', $appWebRoot)
|| $this->appendScriptIfExist($this->serverroot, $theme_dir.'core/'.$script)
|| $this->appendScriptIfExist($this->serverroot, 'core/'.$script)
|| (strpos($scriptName, '/') === -1 && ($this->appendScriptIfExist($this->serverroot, $theme_dir."dist/core-$scriptName")
@@ -87,43 +99,13 @@ class JSResourceLocator extends ResourceLocator {
return;
}
- $script = substr($script, strpos($script, '/') + 1);
- $app_url = null;
-
- try {
- $app_url = $this->appManager->getAppWebPath($app);
- } catch (AppPathNotFoundException) {
- // pass
- }
-
- try {
- $app_path = $this->appManager->getAppPath($app);
-
- // Account for the possibility of having symlinks in app path. Only
- // do this if $app_path is set, because an empty argument to realpath
- // gets turned into cwd.
- $app_path = realpath($app_path);
-
- // check combined files
- if (!str_starts_with($script, 'l10n/') && $this->cacheAndAppendCombineJsonIfExist($app_path, $script.'.json', $app)) {
- return;
- }
-
- // fallback to plain file location
- if ($this->appendScriptIfExist($app_path, $script, $app_url)) {
- return;
- }
- } catch (AppPathNotFoundException) {
- // pass (general error handling happens below)
- }
-
// missing translations files will be ignored
- if (str_starts_with($script, 'l10n/')) {
+ if (str_contains($script, '/l10n/')) {
return;
}
$this->logger->error('Could not find resource {resource} to load', [
- 'resource' => $app . '/' . $script . '.js',
+ 'resource' => $script . '.js',
'app' => 'jsresourceloader',
]);
}
diff --git a/lib/private/legacy/OC_API.php b/lib/private/legacy/OC_API.php
index 862e73e6edd..facd08c1d2f 100644
--- a/lib/private/legacy/OC_API.php
+++ b/lib/private/legacy/OC_API.php
@@ -171,7 +171,7 @@ class OC_API {
],
];
if ($format == 'json') {
- return OC_JSON::encode($response);
+ return json_encode($response, JSON_HEX_TAG);
}
$writer = new XMLWriter();
diff --git a/lib/private/legacy/OC_EventSource.php b/lib/private/legacy/OC_EventSource.php
index cd72ba1f2d5..49fde4a214f 100644
--- a/lib/private/legacy/OC_EventSource.php
+++ b/lib/private/legacy/OC_EventSource.php
@@ -113,13 +113,13 @@ class OC_EventSource implements \OCP\IEventSource {
}
if ($this->fallback) {
$response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
- . $this->fallBackId . ',"' . $type . '",' . OC_JSON::encode($data) . ')</script>' . PHP_EOL;
+ . $this->fallBackId . ',"' . ($type ?? '') . '",' . json_encode($data, JSON_HEX_TAG) . ')</script>' . PHP_EOL;
echo $response;
} else {
if ($type) {
echo 'event: ' . $type . PHP_EOL;
}
- echo 'data: ' . OC_JSON::encode($data) . PHP_EOL;
+ echo 'data: ' . json_encode($data, JSON_HEX_TAG) . PHP_EOL;
}
echo PHP_EOL;
flush();
diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php
index 37fbf7f5f8f..9a12bd50e0e 100644
--- a/lib/private/legacy/OC_Helper.php
+++ b/lib/private/legacy/OC_Helper.php
@@ -596,6 +596,11 @@ class OC_Helper {
'mountPoint' => trim($mountPoint, '/'),
];
+ if ($ownerId && $path === '/') {
+ // If path is root, store this as last known quota usage for this user
+ \OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative);
+ }
+
$memcache->set($cacheKey, $info, 5 * 60);
return $info;
diff --git a/lib/private/legacy/OC_JSON.php b/lib/private/legacy/OC_JSON.php
index b9cfb8210e0..b6791fe4b85 100644
--- a/lib/private/legacy/OC_JSON.php
+++ b/lib/private/legacy/OC_JSON.php
@@ -114,22 +114,10 @@ class OC_JSON {
}
/**
- * Convert OC_L10N_String to string, for use in json encodings
- */
- protected static function to_string(&$value) {
- if ($value instanceof \OC\L10N\L10NString) {
- $value = (string)$value;
- }
- }
-
- /**
* Encode JSON
* @deprecated Use a AppFramework JSONResponse instead
*/
- public static function encode($data) {
- if (is_array($data)) {
- array_walk_recursive($data, ['OC_JSON', 'to_string']);
- }
+ private static function encode($data) {
return json_encode($data, JSON_HEX_TAG);
}
}
diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php
index d15cfdbea96..968771388dc 100644
--- a/lib/public/App/IAppManager.php
+++ b/lib/public/App/IAppManager.php
@@ -62,6 +62,15 @@ interface IAppManager {
public function getAppVersion(string $appId, bool $useCache = true): string;
/**
+ * Returns the app icon or null if none is found
+ *
+ * @param string $appId
+ * @return string|null
+ * @since 29.0.0
+ */
+ public function getAppIcon(string $appId): string|null;
+
+ /**
* Check if an app is enabled for user
*
* @param string $appId
diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
index b39c8591a7e..f515180cef5 100644
--- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
+++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
@@ -352,6 +352,14 @@ interface IRegistrationContext {
public function registerCalendarRoomBackend(string $class): void;
/**
+ * @param string $class
+ * @psalm-param class-string<\OCP\Calendar\Room\IBackend> $actionClass
+ * @return void
+ * @since 29.0.0
+ */
+ public function registerTeamResourceProvider(string $class): void;
+
+ /**
* Register an implementation of \OCP\UserMigration\IMigrator that
* will handle the implementation of a migrator
*
diff --git a/lib/public/Files/Folder.php b/lib/public/Files/Folder.php
index eb81a2098ec..945df48a13e 100644
--- a/lib/public/Files/Folder.php
+++ b/lib/public/Files/Folder.php
@@ -152,11 +152,13 @@ interface Folder extends Node {
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0);
/**
- * get a file or folder inside the folder by it's internal id
+ * get a file or folder inside the folder by its internal id
*
* This method could return multiple entries. For example once the file/folder
* is shared or mounted (files_external) to the user multiple times.
*
+ * Note that the different entries can have different permissions.
+ *
* @param int $id
* @return \OCP\Files\Node[]
* @since 6.0.0
@@ -164,6 +166,24 @@ interface Folder extends Node {
public function getById($id);
/**
+ * get a file or folder inside the folder by its internal id
+ *
+ * Unlike getById, this method only returns a single node even if the user has
+ * access to the file with the requested id multiple times.
+ *
+ * This method provides no guarantee about which of the nodes in returned and the
+ * returned node might, for example, have less permissions than other nodes for the same file
+ *
+ * Apps that require accurate information about the users access to the file should use getById
+ * instead of pick the correct node out of the result.
+ *
+ * @param int $id
+ * @return Node|null
+ * @since 29.0.0
+ */
+ public function getFirstNodeById(int $id): ?Node;
+
+ /**
* Get the amount of free space inside the folder
*
* @return int
diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php
index 44f0ba5f2e1..c1c0e6e8c72 100644
--- a/lib/public/Files/IRootFolder.php
+++ b/lib/public/Files/IRootFolder.php
@@ -60,6 +60,24 @@ interface IRootFolder extends Folder, Emitter {
public function getByIdInPath(int $id, string $path);
/**
+ * get a file or folder inside the folder by its internal id
+ *
+ * Unlike getByIdInPath, this method only returns a single node even if the user has
+ * access to the file with the requested id multiple times.
+ *
+ * This method provides no guarantee about which of the nodes in returned and the
+ * returned node might, for example, have less permissions than other nodes for the same file
+ *
+ * Apps that require accurate information about the users access to the file should use getByIdInPath
+ * instead of pick the correct node out of the result.
+ *
+ * @param int $id
+ * @return Node|null
+ * @since 29.0.0
+ */
+ public function getFirstNodeByIdInPath(int $id, string $path): ?Node;
+
+ /**
* @return IMountPoint[]
*
* @since 28.0.0
diff --git a/lib/public/Http/Client/IClient.php b/lib/public/Http/Client/IClient.php
index 9268eb62920..c6b7cafe73f 100644
--- a/lib/public/Http/Client/IClient.php
+++ b/lib/public/Http/Client/IClient.php
@@ -218,6 +218,37 @@ interface IClient {
public function getResponseFromThrowable(\Throwable $e): IResponse;
/**
+ * Sends a HTTP request
+ * @param string $method The HTTP method to use
+ * @param string $uri
+ * @param array $options Array such as
+ * 'query' => [
+ * 'field' => 'abc',
+ * 'other_field' => '123',
+ * 'file_name' => fopen('/path/to/file', 'r'),
+ * ],
+ * 'headers' => [
+ * 'foo' => 'bar',
+ * ],
+ * 'cookies' => [
+ * 'foo' => 'bar',
+ * ],
+ * 'allow_redirects' => [
+ * 'max' => 10, // allow at most 10 redirects.
+ * 'strict' => true, // use "strict" RFC compliant redirects.
+ * 'referer' => true, // add a Referer header
+ * 'protocols' => ['https'] // only allow https URLs
+ * ],
+ * 'sink' => '/path/to/file', // save to a file or a stream
+ * 'verify' => true, // bool or string to CA file
+ * 'debug' => true,
+ * @return IResponse
+ * @throws \Exception If the request could not get completed
+ * @since 29.0.0
+ */
+ public function request(string $method, string $uri, array $options = []): IResponse;
+
+ /**
* Sends an asynchronous GET request
* @param string $uri
* @param array $options Array such as
diff --git a/lib/public/IURLGenerator.php b/lib/public/IURLGenerator.php
index f62503f14ec..396867e22b3 100644
--- a/lib/public/IURLGenerator.php
+++ b/lib/public/IURLGenerator.php
@@ -41,6 +41,7 @@ interface IURLGenerator {
* This is a copy of the frontend regex in core/src/OCP/comments.js, make sure to adjust both when changing
*
* @since 25.0.0
+ * @since 29.0.0 changed to match localhost and hostnames with ports
*/
public const URL_REGEX = '/' . self::URL_REGEX_NO_MODIFIERS . '/mi';
@@ -50,8 +51,9 @@ interface IURLGenerator {
* This is a copy of the frontend regex in core/src/OCP/comments.js, make sure to adjust both when changing
*
* @since 25.0.0
+ * @since 29.0.0 changed to match localhost and hostnames with ports
*/
- public const URL_REGEX_NO_MODIFIERS = '(\s|\n|^)(https?:\/\/)((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|\n|$)';
+ public const URL_REGEX_NO_MODIFIERS = '(\s|\n|^)(https?:\/\/)([-A-Z0-9+_.]+(?::[0-9]+)?(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|\n|$)';
/**
* Returns the URL for a route
diff --git a/lib/public/Share.php b/lib/public/Share.php
index 9e0ef96a158..2004f77cf0a 100644
--- a/lib/public/Share.php
+++ b/lib/public/Share.php
@@ -33,8 +33,7 @@
namespace OCP;
/**
- * This class provides the ability for apps to share their content between users.
- * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
+ * This class remains only for use with the ::class namespace used for various hooks
*
* It provides the following hooks:
* - post_shared
@@ -42,95 +41,4 @@ namespace OCP;
* @deprecated 17.0.0
*/
class Share extends \OC\Share\Constants {
- /**
- * Get the item of item type shared with a given user by source
- * @param string $itemType
- * @param string $itemSource
- * @param string $user User to whom the item was shared
- * @param string $owner Owner of the share
- * @return array Return list of items with file_target, permissions and expiration
- * @since 6.0.0 - parameter $owner was added in 8.0.0
- * @deprecated 17.0.0
- */
- public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null) {
- return \OC\Share\Share::getItemSharedWithUser($itemType, $itemSource, $user, $owner);
- }
-
- /**
- * Get the item of item type shared with the current user by source
- * @param string $itemType
- * @param string $itemSource
- * @param int $format (optional) Format type must be defined by the backend
- * @param mixed $parameters
- * @param bool $includeCollections
- * @return void
- * @since 5.0.0
- * @deprecated 17.0.0
- */
- public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
- $parameters = null, $includeCollections = false) {
- // not used by any app - only here to not break apps syntax
- }
-
- /**
- * Based on the given token the share information will be returned - password protected shares will be verified
- * @param string $token
- * @param bool $checkPasswordProtection
- * @return void
- * @since 5.0.0 - parameter $checkPasswordProtection was added in 7.0.0
- * @deprecated 17.0.0
- */
- public static function getShareByToken($token, $checkPasswordProtection = true) {
- // not used by any app - only here to not break apps syntax
- }
-
-
- /**
- * Get the shared items of item type owned by the current user
- * @param string $itemType
- * @param int $format (optional) Format type must be defined by the backend
- * @param mixed $parameters
- * @param int $limit Number of items to return (optional) Returns all by default
- * @param bool $includeCollections
- * @return void
- * @since 5.0.0
- * @deprecated 17.0.0
- */
- public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
- $limit = -1, $includeCollections = false) {
- // only used by AppVNCSafe app (https://github.com/vnc-biz/nextcloud-appvncsafe/issues/2) - only here to not break apps syntax
- }
-
- /**
- * Get the shared item of item type owned by the current user
- * @param string $itemType
- * @param string $itemSource
- * @param int $format (optional) Format type must be defined by the backend
- * @param mixed $parameters
- * @param bool $includeCollections
- * @return void
- * @since 5.0.0
- * @deprecated 17.0.0
- *
- * Refactoring notes:
- * * defacto $parameters and $format is always the default and therefore is removed in the subsequent call
- */
- public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
- $parameters = null, $includeCollections = false) {
- // not used by any app - only here to not break apps syntax
- }
-
- /**
- * sent status if users got informed by mail about share
- * @param string $itemType
- * @param string $itemSource
- * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
- * @param string $recipient with whom was the item shared
- * @param bool $status
- * @since 6.0.0 - parameter $originIsSource was added in 8.0.0
- * @deprecated 17.0.0
- */
- public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
- // not used by any app - only here to not break apps syntax
- }
}
diff --git a/lib/public/Share/IShare.php b/lib/public/Share/IShare.php
index 74d404101cd..a059696a75e 100644
--- a/lib/public/Share/IShare.php
+++ b/lib/public/Share/IShare.php
@@ -213,7 +213,7 @@ interface IShare {
* @since 9.0.0
* @throws NotFoundException
*/
- public function getNodeId();
+ public function getNodeId(): int;
/**
* Set the type of node (file/folder)
diff --git a/lib/public/Teams/ITeamManager.php b/lib/public/Teams/ITeamManager.php
new file mode 100644
index 00000000000..51d8a1feb5a
--- /dev/null
+++ b/lib/public/Teams/ITeamManager.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Teams;
+
+/**
+ * @since 29.0.0
+ */
+interface ITeamManager {
+ /**
+ * Get all providers that have registered as a team resource provider
+ *
+ * @return ITeamResourceProvider[]
+ * @since 29.0.0
+ */
+ public function getProviders(): array;
+
+ /**
+ * Get a specific team resource provider by its id
+ *
+ * @since 29.0.0
+ */
+ public function getProvider(string $providerId): ITeamResourceProvider;
+
+ /**
+ * Returns all team resources for a given team and user
+ *
+ * @return TeamResource[]
+ * @since 29.0.0
+ */
+ public function getSharedWith(string $teamId, string $userId): array;
+
+ /**
+ * Returns all teams for a given resource and user
+ *
+ * @since 29.0.0
+ */
+ public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array;
+}
diff --git a/lib/public/Teams/ITeamResourceProvider.php b/lib/public/Teams/ITeamResourceProvider.php
new file mode 100644
index 00000000000..722c877555e
--- /dev/null
+++ b/lib/public/Teams/ITeamResourceProvider.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Teams;
+
+/**
+ * Implement a provider of resources that are shared or owned by a team
+ *
+ * @since 29.0.0
+ */
+interface ITeamResourceProvider {
+
+ /**
+ * Unique identifier used to identify the provider (app id)
+ *
+ * @since 29.0.0
+ */
+ public function getId(): string;
+
+ /**
+ * User visible name of the provider (app name)
+ *
+ * @since 29.0.0
+ */
+ public function getName(): string;
+
+ /**
+ * Svg icon to show next to the provider (app icon)
+ *
+ * @since 29.0.0
+ */
+ public function getIconSvg(): string;
+
+ /**
+ * Return all resources that are shared to the given team id for the current provider
+ *
+ * @param string $teamId
+ * @return TeamResource[]
+ * @since 29.0.0
+ */
+ public function getSharedWith(string $teamId): array;
+
+ /**
+ * Check if a resource is shared with the given team
+ *
+ * @since 29.0.0
+ */
+ public function isSharedWithTeam(string $teamId, string $resourceId): bool;
+
+ /**
+ * Return team ids that a resource is shared with or owned by
+ *
+ * @return string[]
+ * @since 29.0.0
+ */
+ public function getTeamsForResource(string $resourceId): array;
+}
diff --git a/lib/public/Teams/Team.php b/lib/public/Teams/Team.php
new file mode 100644
index 00000000000..d3d6c2d143d
--- /dev/null
+++ b/lib/public/Teams/Team.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Teams;
+
+/**
+ * Simple abstraction to represent a team in the public API
+ *
+ * In the backend a team is a circle identified by the circles singleId
+ *
+ * @since 29.0.0
+ */
+class Team implements \JsonSerializable {
+
+ /**
+ * @since 29.0.0
+ */
+ public function __construct(private string $teamId, private string $displayName, private ?string $link) {
+ }
+
+ /**
+ * Unique identifier of the team (singleId of the circle)
+ *
+ * @since 29.0.0
+ */
+ public function getId(): string {
+ return $this->teamId;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getDisplayName(): string {
+ return $this->displayName;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getLink(): ?string {
+ return $this->link;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function jsonSerialize(): array {
+ return [
+ 'teamId' => $this->teamId,
+ 'displayName' => $this->displayName,
+ 'link' => $this->link,
+ ];
+ }
+}
diff --git a/lib/public/Teams/TeamResource.php b/lib/public/Teams/TeamResource.php
new file mode 100644
index 00000000000..569583bb393
--- /dev/null
+++ b/lib/public/Teams/TeamResource.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * @copyright Copyright (c) 2024 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Teams;
+
+/**
+ * @since 29.0.0
+ */
+class TeamResource implements \JsonSerializable {
+ /**
+ * @since 29.0.0
+ */
+ public function __construct(
+ private ITeamResourceProvider $teamResourceProvider,
+ private string $resourceId,
+ private string $label,
+ private string $url,
+ private ?string $iconSvg = null,
+ private ?string $iconURL = null,
+ private ?string $iconEmoji = null,
+ ) {
+ }
+
+ /**
+ * Returns the provider details for the current resource
+ *
+ * @since 29.0.0
+ */
+ public function getProvider(): ITeamResourceProvider {
+ return $this->teamResourceProvider;
+ }
+
+ /**
+ * Unique id of the resource (e.g. primary key id)
+ * @since 29.0.0
+ */
+ public function getId(): string {
+ return $this->resourceId;
+ }
+
+ /**
+ * User visible label when listing resources
+ *
+ * @since 29.0.0
+ */
+ public function getLabel(): string {
+ return $this->label;
+ }
+
+ /**
+ * Absolute url to navigate the user to the resource
+ *
+ * @since 29.0.0
+ */
+ public function getUrl(): string {
+ return $this->url;
+ }
+
+ /**
+ * Svg icon to show next to the name for the resource
+ *
+ * From all icons the first one returning not null will be picked in order: iconEmoji, iconSvg, iconUrl
+ *
+ * @since 29.0.0
+ */
+ public function getIconSvg(): ?string {
+ return $this->iconSvg;
+ }
+
+ /**
+ * Image url of the icon to show next to the name for the resource
+ *
+ * From all icons the first one returning not null will be picked in order: iconEmoji, iconSvg, iconUrl
+ *
+ * @since 29.0.0
+ */
+ public function getIconURL(): ?string {
+ return $this->iconURL;
+ }
+
+ /**
+ * Emoji show next to the name for the resource
+ *
+ * From all icons the first one returning not null will be picked in order: iconEmoji, iconSvg, iconUrl
+ *
+ * @since 29.0.0
+ */
+ public function getIconEmoji(): ?string {
+ return $this->iconEmoji;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function jsonSerialize(): array {
+ return [
+ 'id' => $this->resourceId,
+ 'label' => $this->label,
+ 'url' => $this->url,
+ 'iconSvg' => $this->iconSvg,
+ 'iconURL' => $this->iconURL,
+ 'iconEmoji' => $this->iconEmoji,
+ 'provider' => [
+ 'id' => $this->teamResourceProvider->getId(),
+ 'name' => $this->teamResourceProvider->getName(),
+ 'icon' => $this->teamResourceProvider->getIconSvg(),
+ ]
+ ];
+ }
+}