aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php10
-rw-r--r--lib/composer/composer/autoload_static.php10
-rw-r--r--lib/l10n/fr.js9
-rw-r--r--lib/l10n/fr.json9
-rw-r--r--lib/l10n/nl.js9
-rw-r--r--lib/l10n/nl.json9
-rw-r--r--lib/l10n/pt_PT.js2
-rw-r--r--lib/l10n/pt_PT.json2
-rw-r--r--lib/l10n/sw.js9
-rw-r--r--lib/l10n/sw.json9
-rw-r--r--lib/private/App/AppManager.php15
-rw-r--r--lib/private/AppConfig.php80
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php4
-rw-r--r--lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php3
-rw-r--r--lib/private/Config/ConfigManager.php250
-rw-r--r--lib/private/Config/UserConfig.php91
-rw-r--r--lib/private/Log/Rotate.php10
-rw-r--r--lib/private/Repair/ConfigKeyMigration.php29
-rw-r--r--lib/private/Route/Router.php12
-rw-r--r--lib/private/Server.php16
-rw-r--r--lib/private/legacy/OC_App.php43
-rw-r--r--lib/private/legacy/OC_Util.php3
-rw-r--r--lib/public/App/Events/AppUpdateEvent.php8
-rw-r--r--lib/unstable/Config/Lexicon/ConfigLexiconEntry.php25
24 files changed, 588 insertions, 79 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 3bf63efce0f..5300af5242c 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -1190,6 +1190,7 @@ return array(
'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php',
'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php',
'OC\\Config' => $baseDir . '/lib/private/Config.php',
+ 'OC\\Config\\ConfigManager' => $baseDir . '/lib/private/Config/ConfigManager.php',
'OC\\Config\\Lexicon\\CoreConfigLexicon' => $baseDir . '/lib/private/Config/Lexicon/CoreConfigLexicon.php',
'OC\\Config\\UserConfig' => $baseDir . '/lib/private/Config/UserConfig.php',
'OC\\Console\\Application' => $baseDir . '/lib/private/Console/Application.php',
@@ -1233,6 +1234,7 @@ return array(
'OC\\Core\\Command\\Config\\Import' => $baseDir . '/core/Command/Config/Import.php',
'OC\\Core\\Command\\Config\\ListConfigs' => $baseDir . '/core/Command/Config/ListConfigs.php',
'OC\\Core\\Command\\Config\\System\\Base' => $baseDir . '/core/Command/Config/System/Base.php',
+ 'OC\\Core\\Command\\Config\\System\\CastHelper' => $baseDir . '/core/Command/Config/System/CastHelper.php',
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => $baseDir . '/core/Command/Config/System/DeleteConfig.php',
'OC\\Core\\Command\\Config\\System\\GetConfig' => $baseDir . '/core/Command/Config/System/GetConfig.php',
'OC\\Core\\Command\\Config\\System\\SetConfig' => $baseDir . '/core/Command/Config/System/SetConfig.php',
@@ -1291,11 +1293,17 @@ return array(
'OC\\Core\\Command\\Maintenance\\RepairShareOwnership' => $baseDir . '/core/Command/Maintenance/RepairShareOwnership.php',
'OC\\Core\\Command\\Maintenance\\UpdateHtaccess' => $baseDir . '/core/Command/Maintenance/UpdateHtaccess.php',
'OC\\Core\\Command\\Maintenance\\UpdateTheme' => $baseDir . '/core/Command/Maintenance/UpdateTheme.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedClear' => $baseDir . '/core/Command/Memcache/DistributedClear.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedDelete' => $baseDir . '/core/Command/Memcache/DistributedDelete.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedGet' => $baseDir . '/core/Command/Memcache/DistributedGet.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedSet' => $baseDir . '/core/Command/Memcache/DistributedSet.php',
'OC\\Core\\Command\\Memcache\\RedisCommand' => $baseDir . '/core/Command/Memcache/RedisCommand.php',
'OC\\Core\\Command\\Preview\\Cleanup' => $baseDir . '/core/Command/Preview/Cleanup.php',
'OC\\Core\\Command\\Preview\\Generate' => $baseDir . '/core/Command/Preview/Generate.php',
'OC\\Core\\Command\\Preview\\Repair' => $baseDir . '/core/Command/Preview/Repair.php',
'OC\\Core\\Command\\Preview\\ResetRenderedTexts' => $baseDir . '/core/Command/Preview/ResetRenderedTexts.php',
+ 'OC\\Core\\Command\\Router\\ListRoutes' => $baseDir . '/core/Command/Router/ListRoutes.php',
+ 'OC\\Core\\Command\\Router\\MatchRoute' => $baseDir . '/core/Command/Router/MatchRoute.php',
'OC\\Core\\Command\\Security\\BruteforceAttempts' => $baseDir . '/core/Command/Security/BruteforceAttempts.php',
'OC\\Core\\Command\\Security\\BruteforceResetAttempts' => $baseDir . '/core/Command/Security/BruteforceResetAttempts.php',
'OC\\Core\\Command\\Security\\ExportCertificates' => $baseDir . '/core/Command/Security/ExportCertificates.php',
@@ -1472,6 +1480,7 @@ return array(
'OC\\Core\\Migrations\\Version31000Date20240101084401' => $baseDir . '/core/Migrations/Version31000Date20240101084401.php',
'OC\\Core\\Migrations\\Version31000Date20240814184402' => $baseDir . '/core/Migrations/Version31000Date20240814184402.php',
'OC\\Core\\Migrations\\Version31000Date20250213102442' => $baseDir . '/core/Migrations/Version31000Date20250213102442.php',
+ 'OC\\Core\\Migrations\\Version32000Date20250620081925' => $baseDir . '/core/Migrations/Version32000Date20250620081925.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
@@ -1900,6 +1909,7 @@ return array(
'OC\\Repair\\ClearGeneratedAvatarCache' => $baseDir . '/lib/private/Repair/ClearGeneratedAvatarCache.php',
'OC\\Repair\\ClearGeneratedAvatarCacheJob' => $baseDir . '/lib/private/Repair/ClearGeneratedAvatarCacheJob.php',
'OC\\Repair\\Collation' => $baseDir . '/lib/private/Repair/Collation.php',
+ 'OC\\Repair\\ConfigKeyMigration' => $baseDir . '/lib/private/Repair/ConfigKeyMigration.php',
'OC\\Repair\\Events\\RepairAdvanceEvent' => $baseDir . '/lib/private/Repair/Events/RepairAdvanceEvent.php',
'OC\\Repair\\Events\\RepairErrorEvent' => $baseDir . '/lib/private/Repair/Events/RepairErrorEvent.php',
'OC\\Repair\\Events\\RepairFinishEvent' => $baseDir . '/lib/private/Repair/Events/RepairFinishEvent.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index f076aaca783..69231e62689 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -1231,6 +1231,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php',
'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php',
'OC\\Config' => __DIR__ . '/../../..' . '/lib/private/Config.php',
+ 'OC\\Config\\ConfigManager' => __DIR__ . '/../../..' . '/lib/private/Config/ConfigManager.php',
'OC\\Config\\Lexicon\\CoreConfigLexicon' => __DIR__ . '/../../..' . '/lib/private/Config/Lexicon/CoreConfigLexicon.php',
'OC\\Config\\UserConfig' => __DIR__ . '/../../..' . '/lib/private/Config/UserConfig.php',
'OC\\Console\\Application' => __DIR__ . '/../../..' . '/lib/private/Console/Application.php',
@@ -1274,6 +1275,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Command\\Config\\Import' => __DIR__ . '/../../..' . '/core/Command/Config/Import.php',
'OC\\Core\\Command\\Config\\ListConfigs' => __DIR__ . '/../../..' . '/core/Command/Config/ListConfigs.php',
'OC\\Core\\Command\\Config\\System\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/System/Base.php',
+ 'OC\\Core\\Command\\Config\\System\\CastHelper' => __DIR__ . '/../../..' . '/core/Command/Config/System/CastHelper.php',
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/DeleteConfig.php',
'OC\\Core\\Command\\Config\\System\\GetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/GetConfig.php',
'OC\\Core\\Command\\Config\\System\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/SetConfig.php',
@@ -1332,11 +1334,17 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Command\\Maintenance\\RepairShareOwnership' => __DIR__ . '/../../..' . '/core/Command/Maintenance/RepairShareOwnership.php',
'OC\\Core\\Command\\Maintenance\\UpdateHtaccess' => __DIR__ . '/../../..' . '/core/Command/Maintenance/UpdateHtaccess.php',
'OC\\Core\\Command\\Maintenance\\UpdateTheme' => __DIR__ . '/../../..' . '/core/Command/Maintenance/UpdateTheme.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedClear' => __DIR__ . '/../../..' . '/core/Command/Memcache/DistributedClear.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedDelete' => __DIR__ . '/../../..' . '/core/Command/Memcache/DistributedDelete.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedGet' => __DIR__ . '/../../..' . '/core/Command/Memcache/DistributedGet.php',
+ 'OC\\Core\\Command\\Memcache\\DistributedSet' => __DIR__ . '/../../..' . '/core/Command/Memcache/DistributedSet.php',
'OC\\Core\\Command\\Memcache\\RedisCommand' => __DIR__ . '/../../..' . '/core/Command/Memcache/RedisCommand.php',
'OC\\Core\\Command\\Preview\\Cleanup' => __DIR__ . '/../../..' . '/core/Command/Preview/Cleanup.php',
'OC\\Core\\Command\\Preview\\Generate' => __DIR__ . '/../../..' . '/core/Command/Preview/Generate.php',
'OC\\Core\\Command\\Preview\\Repair' => __DIR__ . '/../../..' . '/core/Command/Preview/Repair.php',
'OC\\Core\\Command\\Preview\\ResetRenderedTexts' => __DIR__ . '/../../..' . '/core/Command/Preview/ResetRenderedTexts.php',
+ 'OC\\Core\\Command\\Router\\ListRoutes' => __DIR__ . '/../../..' . '/core/Command/Router/ListRoutes.php',
+ 'OC\\Core\\Command\\Router\\MatchRoute' => __DIR__ . '/../../..' . '/core/Command/Router/MatchRoute.php',
'OC\\Core\\Command\\Security\\BruteforceAttempts' => __DIR__ . '/../../..' . '/core/Command/Security/BruteforceAttempts.php',
'OC\\Core\\Command\\Security\\BruteforceResetAttempts' => __DIR__ . '/../../..' . '/core/Command/Security/BruteforceResetAttempts.php',
'OC\\Core\\Command\\Security\\ExportCertificates' => __DIR__ . '/../../..' . '/core/Command/Security/ExportCertificates.php',
@@ -1513,6 +1521,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Migrations\\Version31000Date20240101084401' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240101084401.php',
'OC\\Core\\Migrations\\Version31000Date20240814184402' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240814184402.php',
'OC\\Core\\Migrations\\Version31000Date20250213102442' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20250213102442.php',
+ 'OC\\Core\\Migrations\\Version32000Date20250620081925' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250620081925.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
@@ -1941,6 +1950,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Repair\\ClearGeneratedAvatarCache' => __DIR__ . '/../../..' . '/lib/private/Repair/ClearGeneratedAvatarCache.php',
'OC\\Repair\\ClearGeneratedAvatarCacheJob' => __DIR__ . '/../../..' . '/lib/private/Repair/ClearGeneratedAvatarCacheJob.php',
'OC\\Repair\\Collation' => __DIR__ . '/../../..' . '/lib/private/Repair/Collation.php',
+ 'OC\\Repair\\ConfigKeyMigration' => __DIR__ . '/../../..' . '/lib/private/Repair/ConfigKeyMigration.php',
'OC\\Repair\\Events\\RepairAdvanceEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairAdvanceEvent.php',
'OC\\Repair\\Events\\RepairErrorEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairErrorEvent.php',
'OC\\Repair\\Events\\RepairFinishEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairFinishEvent.php',
diff --git a/lib/l10n/fr.js b/lib/l10n/fr.js
index 9c247440a40..bcdeeeabd83 100644
--- a/lib/l10n/fr.js
+++ b/lib/l10n/fr.js
@@ -203,6 +203,7 @@ OC.L10N.register(
"Path is already shared with this group" : "Le chemin est déjà partagé avec ce groupe",
"Link sharing is not allowed" : "Le partage de liens n'est pas autorisé",
"Public upload is not allowed" : "Le téléversement public n'est pas autorisé",
+ "You cannot share a folder that contains other shares" : "Vous ne pouvez pas partager un dossier qui contient déjà d'autres partages.",
"Sharing is disabled" : "Le partage est désactivé",
"Sharing is disabled for you" : "Le partage est désactivé pour vous",
"Cannot share with the share owner" : "Impossible de partager avec le propriétaire de l'action",
@@ -365,6 +366,11 @@ OC.L10N.register(
"How many images to generate" : "Nombre d'images à générer",
"Output images" : "Images de sortie",
"The generated images" : "Les images générées",
+ "Generate speech" : "Générer la vocalisation",
+ "Generate speech from a transcript" : "Générer la vocalisation à partir d'une transcription",
+ "Write transcript that you want the assistant to generate speech from" : "Écrire la transcription à partir de laquelle vous voulez générer la vocalisation",
+ "Output speech" : "Sortie de la vocalisation",
+ "The generated speech" : "La vocalisation générée",
"Free text to text prompt" : "Texte libre à texte libre",
"Runs an arbitrary prompt through a language model that returns a reply" : "Exécute une commande arbitraire à l'aide d'un modèle linguistique qui génère une réponse",
"Describe a task that you want the assistant to do or ask a question" : "Décrivez une tâche que vous voulez que l'assistant effectue ou posez une question",
@@ -445,6 +451,7 @@ OC.L10N.register(
"Summarizes text by reducing its length without losing key information." : "Résume un texte en réduisant sa longueur sans perdre d’informations essentielles.",
"Extracts topics from a text and outputs them separated by commas." : "Extrait les thèmes d'un texte et les restitue séparés par des virgules.",
"File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard",
- "Cannot download file" : "Impossible de télécharger le fichier"
+ "Cannot download file" : "Impossible de télécharger le fichier",
+ "Login is too long" : "L'authentification est trop longue"
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/fr.json b/lib/l10n/fr.json
index 422df8e5cdc..e6d19d589f0 100644
--- a/lib/l10n/fr.json
+++ b/lib/l10n/fr.json
@@ -201,6 +201,7 @@
"Path is already shared with this group" : "Le chemin est déjà partagé avec ce groupe",
"Link sharing is not allowed" : "Le partage de liens n'est pas autorisé",
"Public upload is not allowed" : "Le téléversement public n'est pas autorisé",
+ "You cannot share a folder that contains other shares" : "Vous ne pouvez pas partager un dossier qui contient déjà d'autres partages.",
"Sharing is disabled" : "Le partage est désactivé",
"Sharing is disabled for you" : "Le partage est désactivé pour vous",
"Cannot share with the share owner" : "Impossible de partager avec le propriétaire de l'action",
@@ -363,6 +364,11 @@
"How many images to generate" : "Nombre d'images à générer",
"Output images" : "Images de sortie",
"The generated images" : "Les images générées",
+ "Generate speech" : "Générer la vocalisation",
+ "Generate speech from a transcript" : "Générer la vocalisation à partir d'une transcription",
+ "Write transcript that you want the assistant to generate speech from" : "Écrire la transcription à partir de laquelle vous voulez générer la vocalisation",
+ "Output speech" : "Sortie de la vocalisation",
+ "The generated speech" : "La vocalisation générée",
"Free text to text prompt" : "Texte libre à texte libre",
"Runs an arbitrary prompt through a language model that returns a reply" : "Exécute une commande arbitraire à l'aide d'un modèle linguistique qui génère une réponse",
"Describe a task that you want the assistant to do or ask a question" : "Décrivez une tâche que vous voulez que l'assistant effectue ou posez une question",
@@ -443,6 +449,7 @@
"Summarizes text by reducing its length without losing key information." : "Résume un texte en réduisant sa longueur sans perdre d’informations essentielles.",
"Extracts topics from a text and outputs them separated by commas." : "Extrait les thèmes d'un texte et les restitue séparés par des virgules.",
"File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard",
- "Cannot download file" : "Impossible de télécharger le fichier"
+ "Cannot download file" : "Impossible de télécharger le fichier",
+ "Login is too long" : "L'authentification est trop longue"
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/nl.js b/lib/l10n/nl.js
index fec337c601d..66af2d8ba2b 100644
--- a/lib/l10n/nl.js
+++ b/lib/l10n/nl.js
@@ -53,6 +53,7 @@ OC.L10N.register(
"Files" : "Bestanden",
"View profile" : "Bekijk profiel",
"_%nh_::_%nh_" : ["%nh","%nh"],
+ "Local time: %s" : "Lokale tijd: %s",
"today" : "vandaag",
"tomorrow" : "morgen",
"yesterday" : "gisteren",
@@ -75,6 +76,7 @@ OC.L10N.register(
"Empty file" : "Leeg bestand",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module met ID: %s bestaat niet. Schakel die in binnen de app-instellingen of neem contact op met je beheerder.",
"Dot files are not allowed" : "Punt-bestanden zijn niet toegestaan",
+ "%1$s (renamed)" : "%1$s (hernoemd)",
"renamed file" : "bestand hernoemd",
"File already exists" : "Bestand bestaat al",
"Invalid path" : "Ongeldig pad",
@@ -104,6 +106,7 @@ OC.L10N.register(
"Address" : "Adres",
"Profile picture" : "Profielafbeelding",
"About" : "Over",
+ "Display name" : "Weergave naam",
"Headline" : "Hoofdlijn",
"Organisation" : "Organisatie",
"Role" : "Rol",
@@ -118,7 +121,12 @@ OC.L10N.register(
"Sharing backend %s must implement the interface OCP\\Share_Backend" : "De gedeelde achtergrond %s moet de OCP\\Share_Backend interface implementeren",
"Sharing backend %s not found" : "De gedeelde backend %s is niet gevonden",
"Sharing backend for %s not found" : "De gedeelde backend voor %s is niet gevonden",
+ "%1$s shared %2$s with you" : "%1$s deelde %2$s met jou",
+ "Open %s" : "%s openen",
"%1$s via %2$s" : "%1$s via %2$s",
+ "%1$s shared %2$s with you and wants to add:" : "%1$s deelde %2$s met jou en wil toevoegen:",
+ "%1$s shared %2$s with you and wants to add" : "%1$s deelde %2$s met jou en wil toevoegen",
+ "%s added a note to a file shared with you" : "%s heeft een notitie toegevoegd aan een bestand dat met jou werd gedeeld",
"Unknown share type" : "Onbekend type gedeelde folder",
"You are not allowed to share %s" : "Je bent niet bevoegd om %s te delen",
"Cannot increase permissions of %s" : "Kan de machtiging van %s niet verhogen.",
@@ -127,6 +135,7 @@ OC.L10N.register(
"Expiration date is in the past" : "De vervaldatum ligt in het verleden",
"_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Kan de vervaldatum niet meer dan %s dag in de toekomst instellen","Kan de vervaldatum niet meer dan %s dagen in de toekomst instellen"],
"Sharing is only allowed with group members" : "Delen kan alleen met groepsleden",
+ "Sharing %s failed, because this item is already shared with the account %s" : "Het delen van%s is mislukt, omdat dit item al gedeeld wordt met het account %s",
"The requested share does not exist anymore" : "De toegang tot de gedeelde folder bestaat niet meer",
"Could not find category \"%s\"" : "Kan categorie \"%s\" niet vinden",
"Sunday" : "Zondag",
diff --git a/lib/l10n/nl.json b/lib/l10n/nl.json
index 75f2c23423e..c2bb3c5831b 100644
--- a/lib/l10n/nl.json
+++ b/lib/l10n/nl.json
@@ -51,6 +51,7 @@
"Files" : "Bestanden",
"View profile" : "Bekijk profiel",
"_%nh_::_%nh_" : ["%nh","%nh"],
+ "Local time: %s" : "Lokale tijd: %s",
"today" : "vandaag",
"tomorrow" : "morgen",
"yesterday" : "gisteren",
@@ -73,6 +74,7 @@
"Empty file" : "Leeg bestand",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Module met ID: %s bestaat niet. Schakel die in binnen de app-instellingen of neem contact op met je beheerder.",
"Dot files are not allowed" : "Punt-bestanden zijn niet toegestaan",
+ "%1$s (renamed)" : "%1$s (hernoemd)",
"renamed file" : "bestand hernoemd",
"File already exists" : "Bestand bestaat al",
"Invalid path" : "Ongeldig pad",
@@ -102,6 +104,7 @@
"Address" : "Adres",
"Profile picture" : "Profielafbeelding",
"About" : "Over",
+ "Display name" : "Weergave naam",
"Headline" : "Hoofdlijn",
"Organisation" : "Organisatie",
"Role" : "Rol",
@@ -116,7 +119,12 @@
"Sharing backend %s must implement the interface OCP\\Share_Backend" : "De gedeelde achtergrond %s moet de OCP\\Share_Backend interface implementeren",
"Sharing backend %s not found" : "De gedeelde backend %s is niet gevonden",
"Sharing backend for %s not found" : "De gedeelde backend voor %s is niet gevonden",
+ "%1$s shared %2$s with you" : "%1$s deelde %2$s met jou",
+ "Open %s" : "%s openen",
"%1$s via %2$s" : "%1$s via %2$s",
+ "%1$s shared %2$s with you and wants to add:" : "%1$s deelde %2$s met jou en wil toevoegen:",
+ "%1$s shared %2$s with you and wants to add" : "%1$s deelde %2$s met jou en wil toevoegen",
+ "%s added a note to a file shared with you" : "%s heeft een notitie toegevoegd aan een bestand dat met jou werd gedeeld",
"Unknown share type" : "Onbekend type gedeelde folder",
"You are not allowed to share %s" : "Je bent niet bevoegd om %s te delen",
"Cannot increase permissions of %s" : "Kan de machtiging van %s niet verhogen.",
@@ -125,6 +133,7 @@
"Expiration date is in the past" : "De vervaldatum ligt in het verleden",
"_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Kan de vervaldatum niet meer dan %s dag in de toekomst instellen","Kan de vervaldatum niet meer dan %s dagen in de toekomst instellen"],
"Sharing is only allowed with group members" : "Delen kan alleen met groepsleden",
+ "Sharing %s failed, because this item is already shared with the account %s" : "Het delen van%s is mislukt, omdat dit item al gedeeld wordt met het account %s",
"The requested share does not exist anymore" : "De toegang tot de gedeelde folder bestaat niet meer",
"Could not find category \"%s\"" : "Kan categorie \"%s\" niet vinden",
"Sunday" : "Zondag",
diff --git a/lib/l10n/pt_PT.js b/lib/l10n/pt_PT.js
index dabcf027355..092db1edb8e 100644
--- a/lib/l10n/pt_PT.js
+++ b/lib/l10n/pt_PT.js
@@ -56,6 +56,8 @@ OC.L10N.register(
"Empty file" : "Ficheiro vazio",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Módulo com ID: %s não existe. Por favor ative-o nas definições da aplicação ou contacte o administrador.",
"Dot files are not allowed" : "Ficheiros dot não são permitidos",
+ "%1$s (renamed)" : "%1$s (renomeado)",
+ "renamed file" : "ficheiro renomeado",
"File already exists" : "O ficheiro já existe",
"Invalid path" : "Caminho inválido!",
"Failed to create file from template" : "Erro ao criar o ficheiro a partir do modelo",
diff --git a/lib/l10n/pt_PT.json b/lib/l10n/pt_PT.json
index 6aeeff65c0b..01f08729953 100644
--- a/lib/l10n/pt_PT.json
+++ b/lib/l10n/pt_PT.json
@@ -54,6 +54,8 @@
"Empty file" : "Ficheiro vazio",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Módulo com ID: %s não existe. Por favor ative-o nas definições da aplicação ou contacte o administrador.",
"Dot files are not allowed" : "Ficheiros dot não são permitidos",
+ "%1$s (renamed)" : "%1$s (renomeado)",
+ "renamed file" : "ficheiro renomeado",
"File already exists" : "O ficheiro já existe",
"Invalid path" : "Caminho inválido!",
"Failed to create file from template" : "Erro ao criar o ficheiro a partir do modelo",
diff --git a/lib/l10n/sw.js b/lib/l10n/sw.js
index 76cbebee52c..ed407a1c902 100644
--- a/lib/l10n/sw.js
+++ b/lib/l10n/sw.js
@@ -1,18 +1,24 @@
OC.L10N.register(
"lib",
{
+ "Authentication" : "Uthibitisho",
"Unknown filetype" : "Aina ya faili haijulikani",
"Invalid image" : "Taswira si halisi",
"Files" : "Mafaili",
+ "View profile" : "Angalia wasifu",
+ "_%n year ago_::_%n years ago_" : ["%n year ago","%n years ago"],
"seconds ago" : "sukunde zilizopita",
"%1$s (renamed)" : "%1$s (iliyopew jina jipya)",
"renamed file" : "Faili iliyopewa jina jipya",
"Filenames must not end with \"%1$s\"." : "Majina ya faili hayapaswi kuishia na \"%1$s\"",
"File already exists" : "Faili lipo tayari",
"Templates" : "Violezo",
+ "Apps" : "Maombi",
"Settings" : "Mipangilio",
+ "Log out" : "Ondoka",
"Accounts" : "Akaunti",
"Email" : "Barua pepe",
+ "About" : "Kuhusu",
"Additional settings" : "Mipangilio ya nyongeza",
"Sunday" : "Jumapili",
"Monday" : "Jumatatu",
@@ -24,6 +30,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Uhifadhi haupo kwa muda",
"Confirmation" : "Uthibitisho",
"Text" : "Maandishi",
- "Summary" : "Muhtasari"
+ "Summary" : "Muhtasari",
+ "Translate" : "Tafsiri"
},
"nplurals=2; plural=(n != 1);");
diff --git a/lib/l10n/sw.json b/lib/l10n/sw.json
index c8301c72a98..a94d2dc64e4 100644
--- a/lib/l10n/sw.json
+++ b/lib/l10n/sw.json
@@ -1,16 +1,22 @@
{ "translations": {
+ "Authentication" : "Uthibitisho",
"Unknown filetype" : "Aina ya faili haijulikani",
"Invalid image" : "Taswira si halisi",
"Files" : "Mafaili",
+ "View profile" : "Angalia wasifu",
+ "_%n year ago_::_%n years ago_" : ["%n year ago","%n years ago"],
"seconds ago" : "sukunde zilizopita",
"%1$s (renamed)" : "%1$s (iliyopew jina jipya)",
"renamed file" : "Faili iliyopewa jina jipya",
"Filenames must not end with \"%1$s\"." : "Majina ya faili hayapaswi kuishia na \"%1$s\"",
"File already exists" : "Faili lipo tayari",
"Templates" : "Violezo",
+ "Apps" : "Maombi",
"Settings" : "Mipangilio",
+ "Log out" : "Ondoka",
"Accounts" : "Akaunti",
"Email" : "Barua pepe",
+ "About" : "Kuhusu",
"Additional settings" : "Mipangilio ya nyongeza",
"Sunday" : "Jumapili",
"Monday" : "Jumatatu",
@@ -22,6 +28,7 @@
"Storage is temporarily not available" : "Uhifadhi haupo kwa muda",
"Confirmation" : "Uthibitisho",
"Text" : "Maandishi",
- "Summary" : "Muhtasari"
+ "Summary" : "Muhtasari",
+ "Translate" : "Tafsiri"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php
index 303a88e9ceb..eb4700020d2 100644
--- a/lib/private/App/AppManager.php
+++ b/lib/private/App/AppManager.php
@@ -8,6 +8,7 @@ namespace OC\App;
use OC\AppConfig;
use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Config\ConfigManager;
use OCP\Activity\IManager as IActivityManager;
use OCP\App\AppPathNotFoundException;
use OCP\App\Events\AppDisableEvent;
@@ -27,6 +28,7 @@ use OCP\INavigationManager;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserSession;
+use OCP\Server;
use OCP\ServerVersion;
use OCP\Settings\IManager as ISettingsManager;
use Psr\Log\LoggerInterface;
@@ -82,12 +84,13 @@ class AppManager implements IAppManager {
private IEventDispatcher $dispatcher,
private LoggerInterface $logger,
private ServerVersion $serverVersion,
+ private ConfigManager $configManager,
) {
}
private function getNavigationManager(): INavigationManager {
if ($this->navigationManager === null) {
- $this->navigationManager = \OCP\Server::get(INavigationManager::class);
+ $this->navigationManager = Server::get(INavigationManager::class);
}
return $this->navigationManager;
}
@@ -113,7 +116,7 @@ class AppManager implements IAppManager {
if (!$this->config->getSystemValueBool('installed', false)) {
throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
}
- $this->appConfig = \OCP\Server::get(AppConfig::class);
+ $this->appConfig = Server::get(AppConfig::class);
return $this->appConfig;
}
@@ -124,7 +127,7 @@ class AppManager implements IAppManager {
if (!$this->config->getSystemValueBool('installed', false)) {
throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
}
- $this->urlGenerator = \OCP\Server::get(IURLGenerator::class);
+ $this->urlGenerator = Server::get(IURLGenerator::class);
return $this->urlGenerator;
}
@@ -459,7 +462,7 @@ class AppManager implements IAppManager {
]);
}
- $coordinator = \OCP\Server::get(Coordinator::class);
+ $coordinator = Server::get(Coordinator::class);
$coordinator->bootApp($app);
$eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it");
@@ -568,6 +571,8 @@ class AppManager implements IAppManager {
ManagerEvent::EVENT_APP_ENABLE, $appId
));
$this->clearAppsCache();
+
+ $this->configManager->migrateConfigLexiconKeys($appId);
}
/**
@@ -626,6 +631,8 @@ class AppManager implements IAppManager {
ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
));
$this->clearAppsCache();
+
+ $this->configManager->migrateConfigLexiconKeys($appId);
}
/**
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index adbfc58978b..b6412b410bb 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -15,6 +15,7 @@ use NCU\Config\Lexicon\ConfigLexiconEntry;
use NCU\Config\Lexicon\ConfigLexiconStrictness;
use NCU\Config\Lexicon\IConfigLexicon;
use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Config\ConfigManager;
use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Exceptions\AppConfigIncorrectTypeException;
@@ -24,6 +25,7 @@ use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
+use OCP\Server;
use Psr\Log\LoggerInterface;
/**
@@ -59,8 +61,9 @@ class AppConfig implements IAppConfig {
private array $valueTypes = []; // type for all config values
private bool $fastLoaded = false;
private bool $lazyLoaded = false;
- /** @var array<array-key, array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
+ /** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
private array $configLexiconDetails = [];
+ private bool $ignoreLexiconAliases = false;
/** @var ?array<string, string> */
private ?array $appVersionsCache = null;
@@ -117,6 +120,7 @@ class AppConfig implements IAppConfig {
public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($app, $key);
$this->loadConfig($app, $lazy);
+ $this->matchAndApplyLexiconDefinition($app, $key);
if ($lazy === null) {
$appCache = $this->getAllValues($app);
@@ -142,6 +146,7 @@ class AppConfig implements IAppConfig {
public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($app, $key);
$this->loadConfig(null, $lazy);
+ $this->matchAndApplyLexiconDefinition($app, $key);
if (!isset($this->valueTypes[$app][$key])) {
throw new AppConfigUnknownKeyException('unknown config key');
@@ -162,6 +167,9 @@ class AppConfig implements IAppConfig {
* @since 29.0.0
*/
public function isLazy(string $app, string $key): bool {
+ $this->assertParams($app, $key);
+ $this->matchAndApplyLexiconDefinition($app, $key);
+
// there is a huge probability the non-lazy config are already loaded
if ($this->hasKey($app, $key, false)) {
return false;
@@ -284,7 +292,7 @@ class AppConfig implements IAppConfig {
): string {
try {
$lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy;
- } catch (AppConfigUnknownKeyException $e) {
+ } catch (AppConfigUnknownKeyException) {
return $default;
}
@@ -429,6 +437,7 @@ class AppConfig implements IAppConfig {
int $type,
): string {
$this->assertParams($app, $key, valueType: $type);
+ $origKey = $key;
if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $default)) {
return $default; // returns default if strictness of lexicon is set to WARNING (block and report)
}
@@ -469,6 +478,14 @@ class AppConfig implements IAppConfig {
$value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
}
+ // in case the key was modified while running matchAndApplyLexiconDefinition() we are
+ // interested to check options in case a modification of the value is needed
+ // ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN
+ if ($origKey !== $key && $type === self::VALUE_BOOL) {
+ $configManager = Server::get(ConfigManager::class);
+ $value = ($configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0';
+ }
+
return $value;
}
@@ -863,7 +880,8 @@ class AppConfig implements IAppConfig {
public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
$this->assertParams($app, $key);
$this->loadConfigAll();
- $lazy = $this->isLazy($app, $key);
+ $this->matchAndApplyLexiconDefinition($app, $key);
+ $this->isLazy($app, $key); // confirm key exists
// type can only be one type
if (!in_array($type, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
@@ -905,6 +923,7 @@ class AppConfig implements IAppConfig {
public function updateSensitive(string $app, string $key, bool $sensitive): bool {
$this->assertParams($app, $key);
$this->loadConfigAll();
+ $this->matchAndApplyLexiconDefinition($app, $key);
try {
if ($sensitive === $this->isSensitive($app, $key, null)) {
@@ -964,6 +983,7 @@ class AppConfig implements IAppConfig {
public function updateLazy(string $app, string $key, bool $lazy): bool {
$this->assertParams($app, $key);
$this->loadConfigAll();
+ $this->matchAndApplyLexiconDefinition($app, $key);
try {
if ($lazy === $this->isLazy($app, $key)) {
@@ -999,6 +1019,7 @@ class AppConfig implements IAppConfig {
public function getDetails(string $app, string $key): array {
$this->assertParams($app, $key);
$this->loadConfigAll();
+ $this->matchAndApplyLexiconDefinition($app, $key);
$lazy = $this->isLazy($app, $key);
if ($lazy) {
@@ -1086,6 +1107,8 @@ class AppConfig implements IAppConfig {
*/
public function deleteKey(string $app, string $key): void {
$this->assertParams($app, $key);
+ $this->matchAndApplyLexiconDefinition($app, $key);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('appconfig')
->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
@@ -1293,6 +1316,7 @@ class AppConfig implements IAppConfig {
*/
public function getValue($app, $key, $default = null) {
$this->loadConfig($app);
+ $this->matchAndApplyLexiconDefinition($app, $key);
return $this->fastCache[$app][$key] ?? $default;
}
@@ -1372,7 +1396,7 @@ class AppConfig implements IAppConfig {
foreach ($values as $key => $value) {
try {
$type = $this->getValueType($app, $key, $lazy);
- } catch (AppConfigUnknownKeyException $e) {
+ } catch (AppConfigUnknownKeyException) {
continue;
}
@@ -1556,7 +1580,8 @@ class AppConfig implements IAppConfig {
}
/**
- * match and apply current use of config values with defined lexicon
+ * Match and apply current use of config values with defined lexicon.
+ * Set $lazy to NULL only if only interested into checking that $key is alias.
*
* @throws AppConfigUnknownKeyException
* @throws AppConfigTypeConflictException
@@ -1564,9 +1589,9 @@ class AppConfig implements IAppConfig {
*/
private function matchAndApplyLexiconDefinition(
string $app,
- string $key,
- bool &$lazy,
- int &$type,
+ string &$key,
+ ?bool &$lazy = null,
+ int &$type = self::VALUE_MIXED,
string &$default = '',
): bool {
if (in_array($key,
@@ -1578,11 +1603,18 @@ class AppConfig implements IAppConfig {
return true; // we don't break stuff for this list of config keys.
}
$configDetails = $this->getConfigDetailsFromLexicon($app);
+ if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
+ // in case '$rename' is set in ConfigLexiconEntry, we use the new config key
+ $key = $configDetails['aliases'][$key];
+ }
+
if (!array_key_exists($key, $configDetails['entries'])) {
- return $this->applyLexiconStrictness(
- $configDetails['strictness'],
- 'The app config key ' . $app . '/' . $key . ' is not defined in the config lexicon'
- );
+ return $this->applyLexiconStrictness($configDetails['strictness'], 'The app config key ' . $app . '/' . $key . ' is not defined in the config lexicon');
+ }
+
+ // if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon
+ if ($lazy === null) {
+ return true;
}
/** @var ConfigLexiconEntry $configValue */
@@ -1644,20 +1676,25 @@ class AppConfig implements IAppConfig {
* extract details from registered $appId's config lexicon
*
* @param string $appId
+ * @internal
*
- * @return array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}
+ * @return array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}
*/
- private function getConfigDetailsFromLexicon(string $appId): array {
+ public function getConfigDetailsFromLexicon(string $appId): array {
if (!array_key_exists($appId, $this->configLexiconDetails)) {
- $entries = [];
+ $entries = $aliases = [];
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
$entries[$configEntry->getKey()] = $configEntry;
+ if ($configEntry->getRename() !== null) {
+ $aliases[$configEntry->getRename()] = $configEntry->getKey();
+ }
}
$this->configLexiconDetails[$appId] = [
'entries' => $entries,
+ 'aliases' => $aliases,
'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE
];
}
@@ -1665,6 +1702,19 @@ class AppConfig implements IAppConfig {
return $this->configLexiconDetails[$appId];
}
+ private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntry {
+ return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null;
+ }
+
+ /**
+ * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class
+ *
+ * @internal
+ */
+ public function ignoreLexiconAliases(bool $ignore): void {
+ $this->ignoreLexiconAliases = $ignore;
+ }
+
/**
* Returns the installed versions of all apps
*
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
index 7e950f2c976..edf25c2cbe7 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
@@ -14,7 +14,7 @@ use OCP\AppFramework\Http;
* @package OC\AppFramework\Middleware\Security\Exceptions
*/
class NotConfirmedException extends SecurityException {
- public function __construct() {
- parent::__construct('Password confirmation is required', Http::STATUS_FORBIDDEN);
+ public function __construct(string $message = 'Password confirmation is required') {
+ parent::__construct($message, Http::STATUS_FORBIDDEN);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
index d00840084a3..e65fe94d608 100644
--- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
@@ -79,6 +79,9 @@ class PasswordConfirmationMiddleware extends Middleware {
if ($this->isPasswordConfirmationStrict($reflectionMethod)) {
$authHeader = $this->request->getHeader('Authorization');
+ if (!str_starts_with(strtolower($authHeader), 'basic ')) {
+ throw new NotConfirmedException('Required authorization header missing');
+ }
[, $password] = explode(':', base64_decode(substr($authHeader, 6)), 2);
$loginName = $this->session->get('loginname');
$loginResult = $this->userManager->checkPassword($loginName, $password);
diff --git a/lib/private/Config/ConfigManager.php b/lib/private/Config/ConfigManager.php
new file mode 100644
index 00000000000..1980269e2ca
--- /dev/null
+++ b/lib/private/Config/ConfigManager.php
@@ -0,0 +1,250 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Config;
+
+use JsonException;
+use NCU\Config\Exceptions\TypeConflictException;
+use NCU\Config\IUserConfig;
+use NCU\Config\Lexicon\ConfigLexiconEntry;
+use NCU\Config\ValueType;
+use OC\AppConfig;
+use OCP\App\IAppManager;
+use OCP\IAppConfig;
+use OCP\Server;
+use Psr\Log\LoggerInterface;
+
+/**
+ * tools to maintains configurations
+ *
+ * @since 32.0.0
+ */
+class ConfigManager {
+ /** @var AppConfig|null $appConfig */
+ private ?IAppConfig $appConfig = null;
+ /** @var UserConfig|null $userConfig */
+ private ?IUserConfig $userConfig = null;
+
+ public function __construct(
+ private readonly LoggerInterface $logger,
+ ) {
+ }
+
+ /**
+ * Use the rename values from the list of ConfigLexiconEntry defined in each app ConfigLexicon
+ * to migrate config value to a new config key.
+ * Migration will only occur if new config key has no value in database.
+ * The previous value from the key set in rename will be deleted from the database when migration
+ * is over.
+ *
+ * This method should be mainly called during a new upgrade or when a new app is enabled.
+ *
+ * @see ConfigLexiconEntry
+ * @internal
+ * @since 32.0.0
+ * @param string|null $appId when set to NULL the method will be executed for all enabled apps of the instance
+ */
+ public function migrateConfigLexiconKeys(?string $appId = null): void {
+ if ($appId === null) {
+ $this->migrateConfigLexiconKeys('core');
+ $appManager = Server::get(IAppManager::class);
+ foreach ($appManager->getEnabledApps() as $app) {
+ $this->migrateConfigLexiconKeys($app);
+ }
+
+ return;
+ }
+
+ $this->loadConfigServices();
+
+ // it is required to ignore aliases when moving config values
+ $this->appConfig->ignoreLexiconAliases(true);
+ $this->userConfig->ignoreLexiconAliases(true);
+
+ $this->migrateAppConfigKeys($appId);
+ $this->migrateUserConfigKeys($appId);
+
+ // switch back to normal behavior
+ $this->appConfig->ignoreLexiconAliases(false);
+ $this->userConfig->ignoreLexiconAliases(false);
+ }
+
+ /**
+ * config services cannot be load at __construct() or install will fail
+ */
+ private function loadConfigServices(): void {
+ if ($this->appConfig === null) {
+ $this->appConfig = Server::get(IAppConfig::class);
+ }
+ if ($this->userConfig === null) {
+ $this->userConfig = Server::get(IUserConfig::class);
+ }
+ }
+
+ /**
+ * Get details from lexicon related to AppConfig and search for entries with rename to initiate
+ * a migration to new config key
+ */
+ private function migrateAppConfigKeys(string $appId): void {
+ $lexicon = $this->appConfig->getConfigDetailsFromLexicon($appId);
+ foreach ($lexicon['entries'] as $entry) {
+ // only interested in entries with rename set
+ if ($entry->getRename() === null) {
+ continue;
+ }
+
+ // only migrate if rename config key has a value and the new config key hasn't
+ if ($this->appConfig->hasKey($appId, $entry->getRename())
+ && !$this->appConfig->hasKey($appId, $entry->getKey())) {
+ try {
+ $this->migrateAppConfigValue($appId, $entry);
+ } catch (TypeConflictException $e) {
+ $this->logger->error('could not migrate AppConfig value', ['appId' => $appId, 'entry' => $entry, 'exception' => $e]);
+ continue;
+ }
+ }
+
+ // we only delete previous config value if migration went fine.
+ $this->appConfig->deleteKey($appId, $entry->getRename());
+ }
+ }
+
+ /**
+ * Get details from lexicon related to UserConfig and search for entries with rename to initiate
+ * a migration to new config key
+ */
+ private function migrateUserConfigKeys(string $appId): void {
+ $lexicon = $this->userConfig->getConfigDetailsFromLexicon($appId);
+ foreach ($lexicon['entries'] as $entry) {
+ // only interested in keys with rename set
+ if ($entry->getRename() === null) {
+ continue;
+ }
+
+ foreach ($this->userConfig->getValuesByUsers($appId, $entry->getRename()) as $userId => $value) {
+ if ($this->userConfig->hasKey($userId, $appId, $entry->getKey())) {
+ continue;
+ }
+
+ try {
+ $this->migrateUserConfigValue($userId, $appId, $entry);
+ } catch (TypeConflictException $e) {
+ $this->logger->error('could not migrate UserConfig value', ['userId' => $userId, 'appId' => $appId, 'entry' => $entry, 'exception' => $e]);
+ continue;
+ }
+
+ $this->userConfig->deleteUserConfig($userId, $appId, $entry->getRename());
+ }
+ }
+ }
+
+
+ /**
+ * converting value from rename to the new key
+ *
+ * @throws TypeConflictException if previous value does not fit the expected type
+ */
+ private function migrateAppConfigValue(string $appId, ConfigLexiconEntry $entry): void {
+ $value = $this->appConfig->getValueMixed($appId, $entry->getRename(), lazy: null);
+ switch ($entry->getValueType()) {
+ case ValueType::STRING:
+ $this->appConfig->setValueString($appId, $entry->getKey(), $value);
+ return;
+
+ case ValueType::INT:
+ $this->appConfig->setValueInt($appId, $entry->getKey(), $this->convertToInt($value));
+ return;
+
+ case ValueType::FLOAT:
+ $this->appConfig->setValueFloat($appId, $entry->getKey(), $this->convertToFloat($value));
+ return;
+
+ case ValueType::BOOL:
+ $this->appConfig->setValueBool($appId, $entry->getKey(), $this->convertToBool($value, $entry));
+ return;
+
+ case ValueType::ARRAY:
+ $this->appConfig->setValueArray($appId, $entry->getKey(), $this->convertToArray($value));
+ return;
+ }
+ }
+
+ /**
+ * converting value from rename to the new key
+ *
+ * @throws TypeConflictException if previous value does not fit the expected type
+ */
+ private function migrateUserConfigValue(string $userId, string $appId, ConfigLexiconEntry $entry): void {
+ $value = $this->userConfig->getValueMixed($userId, $appId, $entry->getRename(), lazy: null);
+ switch ($entry->getValueType()) {
+ case ValueType::STRING:
+ $this->userConfig->setValueString($userId, $appId, $entry->getKey(), $value);
+ return;
+
+ case ValueType::INT:
+ $this->userConfig->setValueInt($userId, $appId, $entry->getKey(), $this->convertToInt($value));
+ return;
+
+ case ValueType::FLOAT:
+ $this->userConfig->setValueFloat($userId, $appId, $entry->getKey(), $this->convertToFloat($value));
+ return;
+
+ case ValueType::BOOL:
+ $this->userConfig->setValueBool($userId, $appId, $entry->getKey(), $this->convertToBool($value, $entry));
+ return;
+
+ case ValueType::ARRAY:
+ $this->userConfig->setValueArray($userId, $appId, $entry->getKey(), $this->convertToArray($value));
+ return;
+ }
+ }
+
+ public function convertToInt(string $value): int {
+ if (!is_numeric($value) || (float)$value <> (int)$value) {
+ throw new TypeConflictException('Value is not an integer');
+ }
+
+ return (int)$value;
+ }
+
+ public function convertToFloat(string $value): float {
+ if (!is_numeric($value)) {
+ throw new TypeConflictException('Value is not a float');
+ }
+
+ return (float)$value;
+ }
+
+ public function convertToBool(string $value, ?ConfigLexiconEntry $entry = null): bool {
+ if (in_array(strtolower($value), ['true', '1', 'on', 'yes'])) {
+ $valueBool = true;
+ } elseif (in_array(strtolower($value), ['false', '0', 'off', 'no'])) {
+ $valueBool = false;
+ } else {
+ throw new TypeConflictException('Value cannot be converted to boolean');
+ }
+ if ($entry?->hasOption(ConfigLexiconEntry::RENAME_INVERT_BOOLEAN) === true) {
+ $valueBool = !$valueBool;
+ }
+
+ return $valueBool;
+ }
+
+ public function convertToArray(string $value): array {
+ try {
+ $valueArray = json_decode($value, true, flags: JSON_THROW_ON_ERROR);
+ } catch (JsonException) {
+ throw new TypeConflictException('Value is not a valid json');
+ }
+ if (!is_array($valueArray)) {
+ throw new TypeConflictException('Value is not an array');
+ }
+
+ return $valueArray;
+ }
+}
diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php
index 77a86a5e1c7..f8c59a13d3d 100644
--- a/lib/private/Config/UserConfig.php
+++ b/lib/private/Config/UserConfig.php
@@ -25,6 +25,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
+use OCP\Server;
use Psr\Log\LoggerInterface;
/**
@@ -62,8 +63,9 @@ class UserConfig implements IUserConfig {
private array $fastLoaded = [];
/** @var array<string, boolean> ['user_id' => bool] */
private array $lazyLoaded = [];
- /** @var array<array-key, array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
+ /** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
private array $configLexiconDetails = [];
+ private bool $ignoreLexiconAliases = false;
public function __construct(
protected IDBConnection $connection,
@@ -150,6 +152,7 @@ class UserConfig implements IUserConfig {
public function hasKey(string $userId, string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if ($lazy === null) {
$appCache = $this->getValues($userId, $app);
@@ -178,6 +181,7 @@ class UserConfig implements IUserConfig {
public function isSensitive(string $userId, string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key])) {
throw new UnknownKeyException('unknown config key');
@@ -201,6 +205,7 @@ class UserConfig implements IUserConfig {
public function isIndexed(string $userId, string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key])) {
throw new UnknownKeyException('unknown config key');
@@ -222,6 +227,8 @@ class UserConfig implements IUserConfig {
* @since 31.0.0
*/
public function isLazy(string $userId, string $app, string $key): bool {
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+
// there is a huge probability the non-lazy config are already loaded
// meaning that we can start by only checking if a current non-lazy key exists
if ($this->hasKey($userId, $app, $key, false)) {
@@ -349,6 +356,7 @@ class UserConfig implements IUserConfig {
?array $userIds = null,
): array {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
$qb = $this->connection->getQueryBuilder();
$qb->select('userid', 'configvalue', 'type')
@@ -464,6 +472,7 @@ class UserConfig implements IUserConfig {
*/
private function searchUsersByTypedValue(string $app, string $key, string|array $value, bool $caseInsensitive = false): Generator {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
$qb = $this->connection->getQueryBuilder();
$qb->from('preferences');
@@ -541,6 +550,7 @@ class UserConfig implements IUserConfig {
string $default = '',
?bool $lazy = false,
): string {
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
$lazy ??= $this->isLazy($userId, $app, $key);
} catch (UnknownKeyException) {
@@ -710,6 +720,7 @@ class UserConfig implements IUserConfig {
ValueType $type,
): string {
$this->assertParams($userId, $app, $key);
+ $origKey = $key;
if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default)) {
// returns default if strictness of lexicon is set to WARNING (block and report)
return $default;
@@ -746,6 +757,15 @@ class UserConfig implements IUserConfig {
}
$this->decryptSensitiveValue($userId, $app, $key, $value);
+
+ // in case the key was modified while running matchAndApplyLexiconDefinition() we are
+ // interested to check options in case a modification of the value is needed
+ // ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN
+ if ($origKey !== $key && $type === ValueType::BOOL) {
+ $configManager = Server::get(ConfigManager::class);
+ $value = ($configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0';
+ }
+
return $value;
}
@@ -764,6 +784,7 @@ class UserConfig implements IUserConfig {
public function getValueType(string $userId, string $app, string $key, ?bool $lazy = null): ValueType {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key]['type'])) {
throw new UnknownKeyException('unknown config key');
@@ -788,6 +809,7 @@ class UserConfig implements IUserConfig {
public function getValueFlags(string $userId, string $app, string $key, bool $lazy = false): int {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key])) {
throw new UnknownKeyException('unknown config key');
@@ -1045,6 +1067,11 @@ class UserConfig implements IUserConfig {
int $flags,
ValueType $type,
): bool {
+ // Primary email addresses are always(!) expected to be lowercase
+ if ($app === 'settings' && $key === 'email') {
+ $value = strtolower($value);
+ }
+
$this->assertParams($userId, $app, $key);
if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, $flags)) {
// returns false as database is not updated
@@ -1197,8 +1224,8 @@ class UserConfig implements IUserConfig {
public function updateType(string $userId, string $app, string $key, ValueType $type = ValueType::MIXED): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
- // confirm key exists
- $this->isLazy($userId, $app, $key);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+ $this->isLazy($userId, $app, $key); // confirm key exists
$update = $this->connection->getQueryBuilder();
$update->update('preferences')
@@ -1227,6 +1254,7 @@ class UserConfig implements IUserConfig {
public function updateSensitive(string $userId, string $app, string $key, bool $sensitive): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
if ($sensitive === $this->isSensitive($userId, $app, $key, null)) {
@@ -1282,6 +1310,8 @@ class UserConfig implements IUserConfig {
*/
public function updateGlobalSensitive(string $app, string $key, bool $sensitive): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
+
foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) {
try {
$this->updateSensitive($userId, $app, $key, $sensitive);
@@ -1311,6 +1341,7 @@ class UserConfig implements IUserConfig {
public function updateIndexed(string $userId, string $app, string $key, bool $indexed): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
if ($indexed === $this->isIndexed($userId, $app, $key, null)) {
@@ -1366,6 +1397,8 @@ class UserConfig implements IUserConfig {
*/
public function updateGlobalIndexed(string $app, string $key, bool $indexed): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
+
foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) {
try {
$this->updateIndexed($userId, $app, $key, $indexed);
@@ -1392,6 +1425,7 @@ class UserConfig implements IUserConfig {
public function updateLazy(string $userId, string $app, string $key, bool $lazy): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
if ($lazy === $this->isLazy($userId, $app, $key)) {
@@ -1426,6 +1460,7 @@ class UserConfig implements IUserConfig {
*/
public function updateGlobalLazy(string $app, string $key, bool $lazy): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
$update = $this->connection->getQueryBuilder();
$update->update('preferences')
@@ -1451,6 +1486,8 @@ class UserConfig implements IUserConfig {
public function getDetails(string $userId, string $app, string $key): array {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+
$lazy = $this->isLazy($userId, $app, $key);
if ($lazy) {
@@ -1498,6 +1535,8 @@ class UserConfig implements IUserConfig {
*/
public function deleteUserConfig(string $userId, string $app, string $key): void {
$this->assertParams($userId, $app, $key);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId)))
@@ -1520,6 +1559,8 @@ class UserConfig implements IUserConfig {
*/
public function deleteKey(string $app, string $key): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
@@ -1538,6 +1579,7 @@ class UserConfig implements IUserConfig {
*/
public function deleteApp(string $app): void {
$this->assertParams('', $app, allowEmptyUser: true);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)));
@@ -1830,7 +1872,8 @@ class UserConfig implements IUserConfig {
}
/**
- * match and apply current use of config values with defined lexicon
+ * Match and apply current use of config values with defined lexicon.
+ * Set $lazy to NULL only if only interested into checking that $key is alias.
*
* @throws UnknownKeyException
* @throws TypeConflictException
@@ -1839,17 +1882,27 @@ class UserConfig implements IUserConfig {
private function matchAndApplyLexiconDefinition(
string $userId,
string $app,
- string $key,
- bool &$lazy,
- ValueType &$type,
+ string &$key,
+ ?bool &$lazy = null,
+ ValueType &$type = ValueType::MIXED,
int &$flags = 0,
string &$default = '',
): bool {
$configDetails = $this->getConfigDetailsFromLexicon($app);
+ if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
+ // in case '$rename' is set in ConfigLexiconEntry, we use the new config key
+ $key = $configDetails['aliases'][$key];
+ }
+
if (!array_key_exists($key, $configDetails['entries'])) {
return $this->applyLexiconStrictness($configDetails['strictness'], 'The user config key ' . $app . '/' . $key . ' is not defined in the config lexicon');
}
+ // if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon
+ if ($lazy === null) {
+ return true;
+ }
+
/** @var ConfigLexiconEntry $configValue */
$configValue = $configDetails['entries'][$key];
if ($type === ValueType::MIXED) {
@@ -1934,24 +1987,42 @@ class UserConfig implements IUserConfig {
* extract details from registered $appId's config lexicon
*
* @param string $appId
+ * @internal
*
- * @return array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}
+ * @return array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}
*/
- private function getConfigDetailsFromLexicon(string $appId): array {
+ public function getConfigDetailsFromLexicon(string $appId): array {
if (!array_key_exists($appId, $this->configLexiconDetails)) {
- $entries = [];
+ $entries = $aliases = [];
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
foreach ($configLexicon?->getUserConfigs() ?? [] as $configEntry) {
$entries[$configEntry->getKey()] = $configEntry;
+ if ($configEntry->getRename() !== null) {
+ $aliases[$configEntry->getRename()] = $configEntry->getKey();
+ }
}
$this->configLexiconDetails[$appId] = [
'entries' => $entries,
+ 'aliases' => $aliases,
'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE
];
}
return $this->configLexiconDetails[$appId];
}
+
+ private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntry {
+ return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null;
+ }
+
+ /**
+ * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class
+ *
+ * @internal
+ */
+ public function ignoreLexiconAliases(bool $ignore): void {
+ $this->ignoreLexiconAliases = $ignore;
+ }
}
diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php
index 839c40726b7..ee1593b87ac 100644
--- a/lib/private/Log/Rotate.php
+++ b/lib/private/Log/Rotate.php
@@ -7,6 +7,8 @@
*/
namespace OC\Log;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
use OCP\IConfig;
use OCP\Log\RotationTrait;
use Psr\Log\LoggerInterface;
@@ -17,9 +19,15 @@ use Psr\Log\LoggerInterface;
* For more professional log management set the 'logfile' config to a different
* location and manage that with your own tools.
*/
-class Rotate extends \OCP\BackgroundJob\Job {
+class Rotate extends TimedJob {
use RotationTrait;
+ public function __construct(ITimeFactory $time) {
+ parent::__construct($time);
+
+ $this->setInterval(3600);
+ }
+
public function run($argument): void {
$config = \OCP\Server::get(IConfig::class);
$this->filePath = $config->getSystemValueString('logfile', $config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log');
diff --git a/lib/private/Repair/ConfigKeyMigration.php b/lib/private/Repair/ConfigKeyMigration.php
new file mode 100644
index 00000000000..da4aa153dc5
--- /dev/null
+++ b/lib/private/Repair/ConfigKeyMigration.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Repair;
+
+use OC\Config\ConfigManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class ConfigKeyMigration implements IRepairStep {
+ public function __construct(
+ private ConfigManager $configManager,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Migrate config keys';
+ }
+
+ public function run(IOutput $output) {
+ $this->configManager->migrateConfigLexiconKeys();
+ }
+}
diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php
index 22dfb21d4f3..90225212e9a 100644
--- a/lib/private/Route/Router.php
+++ b/lib/private/Route/Router.php
@@ -74,6 +74,14 @@ class Router implements IRouter {
$this->root = $this->getCollection('root');
}
+ public function setContext(RequestContext $context): void {
+ $this->context = $context;
+ }
+
+ public function getRouteCollection() {
+ return $this->root;
+ }
+
/**
* Get the files to load the routes from
*
@@ -102,7 +110,7 @@ class Router implements IRouter {
*
* @param null|string $app
*/
- public function loadRoutes($app = null) {
+ public function loadRoutes(?string $app = null, bool $skipLoadingCore = false): void {
if (is_string($app)) {
$app = $this->appManager->cleanAppId($app);
}
@@ -165,7 +173,7 @@ class Router implements IRouter {
}
$this->eventLogger->end('route:load:files');
- if (!isset($this->loadedApps['core'])) {
+ if (!$skipLoadingCore && !isset($this->loadedApps['core'])) {
$this->loadedApps['core'] = true;
$this->useCollection('root');
$this->setupRoutes($this->getAttributeRoutes('core'), 'core');
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 5964a979e21..c78decd90cb 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -572,6 +572,7 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(IAppConfig::class, \OC\AppConfig::class);
$this->registerAlias(IUserConfig::class, \OC\Config\UserConfig::class);
+ $this->registerAlias(IAppManager::class, AppManager::class);
$this->registerService(IFactory::class, function (Server $c) {
return new \OC\L10N\Factory(
@@ -780,21 +781,6 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerAlias(ITempManager::class, TempManager::class);
-
- $this->registerService(AppManager::class, function (ContainerInterface $c) {
- // TODO: use auto-wiring
- return new \OC\App\AppManager(
- $c->get(IUserSession::class),
- $c->get(\OCP\IConfig::class),
- $c->get(IGroupManager::class),
- $c->get(ICacheFactory::class),
- $c->get(IEventDispatcher::class),
- $c->get(LoggerInterface::class),
- $c->get(ServerVersion::class),
- );
- });
- $this->registerAlias(IAppManager::class, AppManager::class);
-
$this->registerAlias(IDateTimeZone::class, DateTimeZone::class);
$this->registerService(IDateTimeFormatter::class, function (Server $c) {
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index 4f0fff8884e..24982ab9e80 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Config\ConfigManager;
use OC\DB\MigrationService;
use OC\Installer;
use OC\Repair;
@@ -211,7 +212,7 @@ class OC_App {
array $groups = []) {
// Check if app is already downloaded
/** @var Installer $installer */
- $installer = \OCP\Server::get(Installer::class);
+ $installer = Server::get(Installer::class);
$isDownloaded = $installer->isDownloaded($appId);
if (!$isDownloaded) {
@@ -246,7 +247,7 @@ class OC_App {
}
}
- \OCP\Server::get(LoggerInterface::class)->error('No application directories are marked as writable.', ['app' => 'core']);
+ Server::get(LoggerInterface::class)->error('No application directories are marked as writable.', ['app' => 'core']);
return null;
}
@@ -310,7 +311,7 @@ class OC_App {
* @param string $appId
* @param bool $refreshAppPath should be set to true only during install/upgrade
* @return string|false
- * @deprecated 11.0.0 use \OCP\Server::get(IAppManager)->getAppPath()
+ * @deprecated 11.0.0 use Server::get(IAppManager)->getAppPath()
*/
public static function getAppPath(string $appId, bool $refreshAppPath = false) {
$appId = self::cleanAppId($appId);
@@ -349,7 +350,7 @@ class OC_App {
*/
public static function getAppVersionByPath(string $path): string {
$infoFile = $path . '/appinfo/info.xml';
- $appData = \OCP\Server::get(IAppManager::class)->getAppInfoByPath($infoFile);
+ $appData = Server::get(IAppManager::class)->getAppInfoByPath($infoFile);
return $appData['version'] ?? '';
}
@@ -391,7 +392,7 @@ class OC_App {
* @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
*/
public static function registerLogIn(array $entry) {
- \OCP\Server::get(LoggerInterface::class)->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
+ Server::get(LoggerInterface::class)->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
self::$altLogin[] = $entry;
}
@@ -400,11 +401,11 @@ class OC_App {
*/
public static function getAlternativeLogIns(): array {
/** @var Coordinator $bootstrapCoordinator */
- $bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
+ $bootstrapCoordinator = Server::get(Coordinator::class);
foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) {
if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) {
- \OCP\Server::get(LoggerInterface::class)->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
+ Server::get(LoggerInterface::class)->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
'option' => $registration->getService(),
'interface' => IAlternativeLogin::class,
'app' => $registration->getAppId(),
@@ -414,9 +415,9 @@ class OC_App {
try {
/** @var IAlternativeLogin $provider */
- $provider = \OCP\Server::get($registration->getService());
+ $provider = Server::get($registration->getService());
} catch (ContainerExceptionInterface $e) {
- \OCP\Server::get(LoggerInterface::class)->error('Alternative login option {option} can not be initialized.',
+ Server::get(LoggerInterface::class)->error('Alternative login option {option} can not be initialized.',
[
'exception' => $e,
'option' => $registration->getService(),
@@ -433,7 +434,7 @@ class OC_App {
'class' => $provider->getClass(),
];
} catch (Throwable $e) {
- \OCP\Server::get(LoggerInterface::class)->error('Alternative login option {option} had an error while loading.',
+ Server::get(LoggerInterface::class)->error('Alternative login option {option} had an error while loading.',
[
'exception' => $e,
'option' => $registration->getService(),
@@ -452,7 +453,7 @@ class OC_App {
* @deprecated 31.0.0 Use IAppManager::getAllAppsInAppsFolders instead
*/
public static function getAllApps(): array {
- return \OCP\Server::get(IAppManager::class)->getAllAppsInAppsFolders();
+ return Server::get(IAppManager::class)->getAllAppsInAppsFolders();
}
/**
@@ -461,7 +462,7 @@ class OC_App {
* @deprecated 32.0.0 Use \OCP\Support\Subscription\IRegistry::delegateGetSupportedApps instead
*/
public function getSupportedApps(): array {
- $subscriptionRegistry = \OCP\Server::get(\OCP\Support\Subscription\IRegistry::class);
+ $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class);
$supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
return $supportedApps;
}
@@ -486,12 +487,12 @@ class OC_App {
if (!in_array($app, $blacklist)) {
$info = $appManager->getAppInfo($app, false, $langCode);
if (!is_array($info)) {
- \OCP\Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']);
+ Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']);
continue;
}
if (!isset($info['name'])) {
- \OCP\Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']);
+ Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']);
continue;
}
@@ -558,7 +559,7 @@ class OC_App {
public static function shouldUpgrade(string $app): bool {
$versions = self::getAppVersions();
- $currentVersion = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppVersion($app);
+ $currentVersion = Server::get(\OCP\App\IAppManager::class)->getAppVersion($app);
if ($currentVersion && isset($versions[$app])) {
$installedVersion = $versions[$app];
if (!version_compare($currentVersion, $installedVersion, '=')) {
@@ -647,7 +648,7 @@ class OC_App {
* @deprecated 32.0.0 Use IAppManager::getAppInstalledVersions or IAppConfig::getAppInstalledVersions instead
*/
public static function getAppVersions(): array {
- return \OCP\Server::get(IAppConfig::class)->getAppInstalledVersions();
+ return Server::get(IAppConfig::class)->getAppInstalledVersions();
}
/**
@@ -665,13 +666,13 @@ class OC_App {
}
if (is_file($appPath . '/appinfo/database.xml')) {
- \OCP\Server::get(LoggerInterface::class)->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
+ Server::get(LoggerInterface::class)->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
return false;
}
\OC::$server->getAppManager()->clearAppsCache();
$l = \OC::$server->getL10N('core');
- $appData = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode());
+ $appData = Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode());
$ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []);
$ignoreMax = in_array($appId, $ignoreMaxApps, true);
@@ -711,9 +712,13 @@ class OC_App {
self::setAppTypes($appId);
- $version = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId);
+ $version = Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId);
\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
+ // migrate eventual new config keys in the process
+ /** @psalm-suppress InternalMethod */
+ Server::get(ConfigManager::class)->migrateConfigLexiconKeys($appId);
+
\OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId));
\OC::$server->get(IEventDispatcher::class)->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
ManagerEvent::EVENT_APP_UPDATE, $appId
diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php
index 87447af8adc..41ac787aa80 100644
--- a/lib/private/legacy/OC_Util.php
+++ b/lib/private/legacy/OC_Util.php
@@ -187,14 +187,13 @@ class OC_Util {
$child = $target->newFolder($file);
self::copyr($source . '/' . $file, $child);
} else {
- $child = $target->newFile($file);
$sourceStream = fopen($source . '/' . $file, 'r');
if ($sourceStream === false) {
$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
closedir($dir);
return;
}
- $child->putContent($sourceStream);
+ $target->newFile($file, $sourceStream);
}
}
}
diff --git a/lib/public/App/Events/AppUpdateEvent.php b/lib/public/App/Events/AppUpdateEvent.php
index 344e7def080..2cf59ff7949 100644
--- a/lib/public/App/Events/AppUpdateEvent.php
+++ b/lib/public/App/Events/AppUpdateEvent.php
@@ -16,15 +16,13 @@ use OCP\EventDispatcher\Event;
* @since 27.0.0
*/
class AppUpdateEvent extends Event {
- private string $appId;
-
/**
* @since 27.0.0
*/
- public function __construct(string $appId) {
+ public function __construct(
+ private readonly string $appId,
+ ) {
parent::__construct();
-
- $this->appId = $appId;
}
/**
diff --git a/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php b/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php
index d7d781d8e26..d0d9b4cbd23 100644
--- a/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php
+++ b/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php
@@ -17,6 +17,9 @@ use NCU\Config\ValueType;
* @experimental 31.0.0
*/
class ConfigLexiconEntry {
+ /** @experimental 32.0.0 */
+ public const RENAME_INVERT_BOOLEAN = 1;
+
private string $definition = '';
private ?string $default = null;
@@ -26,6 +29,7 @@ class ConfigLexiconEntry {
* @param string $definition optional description of config key available when using occ command
* @param bool $lazy set config value as lazy
* @param int $flags set flags
+ * @param string|null $rename previous config key to migrate config value from
* @param bool $deprecated set config key as deprecated
*
* @experimental 31.0.0
@@ -40,6 +44,8 @@ class ConfigLexiconEntry {
private readonly bool $lazy = false,
private readonly int $flags = 0,
private readonly bool $deprecated = false,
+ private readonly ?string $rename = null,
+ private readonly int $options = 0,
) {
/** @psalm-suppress UndefinedClass */
if (\OC::$CLI) { // only store definition if ran from CLI
@@ -199,6 +205,25 @@ class ConfigLexiconEntry {
}
/**
+ * should be called/used only during migration/upgrade.
+ * link to an old config key.
+ *
+ * @return string|null not NULL if value can be imported from a previous key
+ * @experimental 32.0.0
+ */
+ public function getRename(): ?string {
+ return $this->rename;
+ }
+
+ /**
+ * @experimental 32.0.0
+ * @return bool TRUE if $option was set during the creation of the entry.
+ */
+ public function hasOption(int $option): bool {
+ return (($option & $this->options) !== 0);
+ }
+
+ /**
* returns if config key is set as deprecated
*
* @return bool TRUE if config si deprecated