diff options
Diffstat (limited to 'lib')
31 files changed, 2046 insertions, 382 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 63ba07e2e4f..f1de00a49bf 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -273,6 +273,10 @@ return array( 'OCP\\EventDispatcher\\GenericEvent' => $baseDir . '/lib/public/EventDispatcher/GenericEvent.php', 'OCP\\EventDispatcher\\IEventDispatcher' => $baseDir . '/lib/public/EventDispatcher/IEventDispatcher.php', 'OCP\\EventDispatcher\\IEventListener' => $baseDir . '/lib/public/EventDispatcher/IEventListener.php', + 'OCP\\Exceptions\\AppConfigException' => $baseDir . '/lib/public/Exceptions/AppConfigException.php', + 'OCP\\Exceptions\\AppConfigIncorrectTypeException' => $baseDir . '/lib/public/Exceptions/AppConfigIncorrectTypeException.php', + 'OCP\\Exceptions\\AppConfigTypeConflictException' => $baseDir . '/lib/public/Exceptions/AppConfigTypeConflictException.php', + 'OCP\\Exceptions\\AppConfigUnknownKeyException' => $baseDir . '/lib/public/Exceptions/AppConfigUnknownKeyException.php', 'OCP\\Federation\\Events\\TrustedServerRemovedEvent' => $baseDir . '/lib/public/Federation/Events/TrustedServerRemovedEvent.php', 'OCP\\Federation\\Exceptions\\ActionNotSupportedException' => $baseDir . '/lib/public/Federation/Exceptions/ActionNotSupportedException.php', 'OCP\\Federation\\Exceptions\\AuthenticationFailedException' => $baseDir . '/lib/public/Federation/Exceptions/AuthenticationFailedException.php', @@ -706,6 +710,7 @@ return array( 'OCP\\TextToImage\\Exception\\TextToImageException' => $baseDir . '/lib/public/TextToImage/Exception/TextToImageException.php', 'OCP\\TextToImage\\IManager' => $baseDir . '/lib/public/TextToImage/IManager.php', 'OCP\\TextToImage\\IProvider' => $baseDir . '/lib/public/TextToImage/IProvider.php', + 'OCP\\TextToImage\\IProviderWithUserId' => $baseDir . '/lib/public/TextToImage/IProviderWithUserId.php', 'OCP\\TextToImage\\Task' => $baseDir . '/lib/public/TextToImage/Task.php', 'OCP\\Translation\\CouldNotTranslateException' => $baseDir . '/lib/public/Translation/CouldNotTranslateException.php', 'OCP\\Translation\\IDetectLanguageProvider' => $baseDir . '/lib/public/Translation/IDetectLanguageProvider.php', @@ -1023,6 +1028,7 @@ return array( 'OC\\Core\\Command\\Background\\Ajax' => $baseDir . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => $baseDir . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php', + 'OC\\Core\\Command\\Background\\Delete' => $baseDir . '/core/Command/Background/Delete.php', 'OC\\Core\\Command\\Background\\Job' => $baseDir . '/core/Command/Background/Job.php', 'OC\\Core\\Command\\Background\\ListCommand' => $baseDir . '/core/Command/Background/ListCommand.php', 'OC\\Core\\Command\\Background\\WebCron' => $baseDir . '/core/Command/Background/WebCron.php', @@ -1235,6 +1241,7 @@ return array( 'OC\\Core\\Migrations\\Version28000Date20230906104802' => $baseDir . '/core/Migrations/Version28000Date20230906104802.php', 'OC\\Core\\Migrations\\Version28000Date20231004103301' => $baseDir . '/core/Migrations/Version28000Date20231004103301.php', 'OC\\Core\\Migrations\\Version28000Date20231103104802' => $baseDir . '/core/Migrations/Version28000Date20231103104802.php', + 'OC\\Core\\Migrations\\Version29000Date20231126110901' => $baseDir . '/core/Migrations/Version29000Date20231126110901.php', 'OC\\Core\\Migrations\\Version29000Date20231213104850' => $baseDir . '/core/Migrations/Version29000Date20231213104850.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index bf1a13d49de..017918b3f44 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -306,6 +306,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\EventDispatcher\\GenericEvent' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/GenericEvent.php', 'OCP\\EventDispatcher\\IEventDispatcher' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventDispatcher.php', 'OCP\\EventDispatcher\\IEventListener' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventListener.php', + 'OCP\\Exceptions\\AppConfigException' => __DIR__ . '/../../..' . '/lib/public/Exceptions/AppConfigException.php', + 'OCP\\Exceptions\\AppConfigIncorrectTypeException' => __DIR__ . '/../../..' . '/lib/public/Exceptions/AppConfigIncorrectTypeException.php', + 'OCP\\Exceptions\\AppConfigTypeConflictException' => __DIR__ . '/../../..' . '/lib/public/Exceptions/AppConfigTypeConflictException.php', + 'OCP\\Exceptions\\AppConfigUnknownKeyException' => __DIR__ . '/../../..' . '/lib/public/Exceptions/AppConfigUnknownKeyException.php', 'OCP\\Federation\\Events\\TrustedServerRemovedEvent' => __DIR__ . '/../../..' . '/lib/public/Federation/Events/TrustedServerRemovedEvent.php', 'OCP\\Federation\\Exceptions\\ActionNotSupportedException' => __DIR__ . '/../../..' . '/lib/public/Federation/Exceptions/ActionNotSupportedException.php', 'OCP\\Federation\\Exceptions\\AuthenticationFailedException' => __DIR__ . '/../../..' . '/lib/public/Federation/Exceptions/AuthenticationFailedException.php', @@ -739,6 +743,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\TextToImage\\Exception\\TextToImageException' => __DIR__ . '/../../..' . '/lib/public/TextToImage/Exception/TextToImageException.php', 'OCP\\TextToImage\\IManager' => __DIR__ . '/../../..' . '/lib/public/TextToImage/IManager.php', 'OCP\\TextToImage\\IProvider' => __DIR__ . '/../../..' . '/lib/public/TextToImage/IProvider.php', + 'OCP\\TextToImage\\IProviderWithUserId' => __DIR__ . '/../../..' . '/lib/public/TextToImage/IProviderWithUserId.php', 'OCP\\TextToImage\\Task' => __DIR__ . '/../../..' . '/lib/public/TextToImage/Task.php', 'OCP\\Translation\\CouldNotTranslateException' => __DIR__ . '/../../..' . '/lib/public/Translation/CouldNotTranslateException.php', 'OCP\\Translation\\IDetectLanguageProvider' => __DIR__ . '/../../..' . '/lib/public/Translation/IDetectLanguageProvider.php', @@ -1056,6 +1061,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Command\\Background\\Ajax' => __DIR__ . '/../../..' . '/core/Command/Background/Ajax.php', 'OC\\Core\\Command\\Background\\Base' => __DIR__ . '/../../..' . '/core/Command/Background/Base.php', 'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php', + 'OC\\Core\\Command\\Background\\Delete' => __DIR__ . '/../../..' . '/core/Command/Background/Delete.php', 'OC\\Core\\Command\\Background\\Job' => __DIR__ . '/../../..' . '/core/Command/Background/Job.php', 'OC\\Core\\Command\\Background\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/Background/ListCommand.php', 'OC\\Core\\Command\\Background\\WebCron' => __DIR__ . '/../../..' . '/core/Command/Background/WebCron.php', @@ -1268,6 +1274,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version28000Date20230906104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20230906104802.php', 'OC\\Core\\Migrations\\Version28000Date20231004103301' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20231004103301.php', 'OC\\Core\\Migrations\\Version28000Date20231103104802' => __DIR__ . '/../../..' . '/core/Migrations/Version28000Date20231103104802.php', + 'OC\\Core\\Migrations\\Version29000Date20231126110901' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20231126110901.php', 'OC\\Core\\Migrations\\Version29000Date20231213104850' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20231213104850.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/l10n/es.js b/lib/l10n/es.js index 4db9814b89f..237fb7df4de 100644 --- a/lib/l10n/es.js +++ b/lib/l10n/es.js @@ -237,7 +237,7 @@ OC.L10N.register( "PHP setting \"%s\" is not set to \"%s\"." : "La opción PHP \"%s\" no es \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar esta configuración en php.ini hará que Nextcloud funcione de nuevo", "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> está establecida como <code>%s</code> en lugar del valor esperado: <code>0</code>.", - "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establece <code>mbstring.func_overload</code> en<code>0</code> en tu php.ini.", + "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establezca <code>mbstring.func_overload</code> en<code>0</code> en su php.ini.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP está aparentemente configurado para eliminar bloques de documentos en línea. Esto hará que varias aplicaciones principales estén inaccesibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran", @@ -273,7 +273,7 @@ OC.L10N.register( "404" : "404", "Full name" : "Nombre completo", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 es requerido en esta o en versiones superiores. Ahora mismo tienes instalada %s.", - "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este error, actualiza la versión de tu libxml2 y reinicia el servidor web.", + "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este problema, actualice su versión de libxml2 y reinicie el servidor web.", "PostgreSQL >= 9 required." : "PostgreSQL >= 9 requerido.", "Please upgrade your database version." : "Por favor, actualiza la versión de tu base de datos." }, diff --git a/lib/l10n/es.json b/lib/l10n/es.json index 66d7b7128fb..26023daed56 100644 --- a/lib/l10n/es.json +++ b/lib/l10n/es.json @@ -235,7 +235,7 @@ "PHP setting \"%s\" is not set to \"%s\"." : "La opción PHP \"%s\" no es \"%s\".", "Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar esta configuración en php.ini hará que Nextcloud funcione de nuevo", "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> está establecida como <code>%s</code> en lugar del valor esperado: <code>0</code>.", - "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establece <code>mbstring.func_overload</code> en<code>0</code> en tu php.ini.", + "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establezca <code>mbstring.func_overload</code> en<code>0</code> en su php.ini.", "PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP está aparentemente configurado para eliminar bloques de documentos en línea. Esto hará que varias aplicaciones principales estén inaccesibles.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.", "PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran", @@ -271,7 +271,7 @@ "404" : "404", "Full name" : "Nombre completo", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 es requerido en esta o en versiones superiores. Ahora mismo tienes instalada %s.", - "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este error, actualiza la versión de tu libxml2 y reinicia el servidor web.", + "To fix this issue update your libxml2 version and restart your web server." : "Para corregir este problema, actualice su versión de libxml2 y reinicie el servidor web.", "PostgreSQL >= 9 required." : "PostgreSQL >= 9 requerido.", "Please upgrade your database version." : "Por favor, actualiza la versión de tu base de datos." },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" diff --git a/lib/l10n/it.js b/lib/l10n/it.js index c9ef9c3cef2..32a3237a846 100644 --- a/lib/l10n/it.js +++ b/lib/l10n/it.js @@ -5,14 +5,14 @@ OC.L10N.register( "This can usually be fixed by giving the web server write access to the config directory." : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella config.", "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Ma, se preferisci mantenere il file config.php in sola lettura, imposta l'opzione \"config_is_read_only\" a true.", "See %s" : "Vedi %s", - "Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory." : "L'applicazione %1$s non è presente o ha una versione non compatibile con questo server. Controlla l'elenco delle app.", + "Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory." : "L'applicazione %1$s non è presente o ha una versione non compatibile con questo server. Controlla l'elenco delle applicazioni.", "Sample configuration detected" : "Configurazione di esempio rilevata", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "È stato rilevato che la configurazione di esempio è stata copiata. Ciò può compromettere la tua installazione e non è supportato. Leggi la documentazione prima di modificare il file config.php", "The page could not be found on the server." : "Impossibile trovare la pagina sul server.", "%s email verification" : "Verifica email di %s", "Email verification" : "Verifica email", - "Click the following button to confirm your email." : "Clicca il pulsante seguente per confermare la tua email.", - "Click the following link to confirm your email." : "Clicca il collegamento seguente per confermare la tua email.", + "Click the following button to confirm your email." : "Fai clic sul pulsante seguente per confermare la tua email.", + "Click the following link to confirm your email." : "Fai clic sul collegamento seguente per confermare la tua email.", "Confirm your email" : "Conferma la tua email", "Other activities" : "Altre attività", "%1$s and %2$s" : "%1$s e %2$s", @@ -217,7 +217,7 @@ OC.L10N.register( "a safe home for all your data" : "un posto sicuro per tutti i tuoi dati", "File is currently busy, please try again later" : "Il file è attualmente occupato, riprova più tardi", "Cannot download file" : "Impossibile scaricare il file", - "Application is not enabled" : "L'applicazione non è abilitata", + "Application is not enabled" : "L'applicazione non è abilitata", "Authentication error" : "Errore di autenticazione", "Token expired. Please reload page." : "Token scaduto. Ricarica la pagina.", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nessun driver di database (sqlite, mysql o postgresql) installato", @@ -225,7 +225,7 @@ OC.L10N.register( "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella config. Vedi %s", "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "O, se preferisci mantenere il file config.php in sola lettura, imposta l'opzione \"config_is_read_only\" a true. Vedi %s", "Cannot write into \"apps\" directory." : "Impossibile scrivere nella cartella \"apps\".", - "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella apps o disattivando il negozio delle applicazioni nel file di configurazione.", + "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella delle applicazioni o disattivando il negozio delle applicazioni nel file di configurazione.", "Cannot create \"data\" directory." : "Impossibile creare la cartella \"data\".", "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella radice. Vedi %s", "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "I permessi possono essere corretti di solito fornendo al server web accesso in scrittura alla cartella radice. Vedi %s.", diff --git a/lib/l10n/it.json b/lib/l10n/it.json index c30ee15f522..9d32ca8c58a 100644 --- a/lib/l10n/it.json +++ b/lib/l10n/it.json @@ -3,14 +3,14 @@ "This can usually be fixed by giving the web server write access to the config directory." : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella config.", "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Ma, se preferisci mantenere il file config.php in sola lettura, imposta l'opzione \"config_is_read_only\" a true.", "See %s" : "Vedi %s", - "Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory." : "L'applicazione %1$s non è presente o ha una versione non compatibile con questo server. Controlla l'elenco delle app.", + "Application %1$s is not present or has a non-compatible version with this server. Please check the apps directory." : "L'applicazione %1$s non è presente o ha una versione non compatibile con questo server. Controlla l'elenco delle applicazioni.", "Sample configuration detected" : "Configurazione di esempio rilevata", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "È stato rilevato che la configurazione di esempio è stata copiata. Ciò può compromettere la tua installazione e non è supportato. Leggi la documentazione prima di modificare il file config.php", "The page could not be found on the server." : "Impossibile trovare la pagina sul server.", "%s email verification" : "Verifica email di %s", "Email verification" : "Verifica email", - "Click the following button to confirm your email." : "Clicca il pulsante seguente per confermare la tua email.", - "Click the following link to confirm your email." : "Clicca il collegamento seguente per confermare la tua email.", + "Click the following button to confirm your email." : "Fai clic sul pulsante seguente per confermare la tua email.", + "Click the following link to confirm your email." : "Fai clic sul collegamento seguente per confermare la tua email.", "Confirm your email" : "Conferma la tua email", "Other activities" : "Altre attività", "%1$s and %2$s" : "%1$s e %2$s", @@ -215,7 +215,7 @@ "a safe home for all your data" : "un posto sicuro per tutti i tuoi dati", "File is currently busy, please try again later" : "Il file è attualmente occupato, riprova più tardi", "Cannot download file" : "Impossibile scaricare il file", - "Application is not enabled" : "L'applicazione non è abilitata", + "Application is not enabled" : "L'applicazione non è abilitata", "Authentication error" : "Errore di autenticazione", "Token expired. Please reload page." : "Token scaduto. Ricarica la pagina.", "No database drivers (sqlite, mysql, or postgresql) installed." : "Nessun driver di database (sqlite, mysql o postgresql) installato", @@ -223,7 +223,7 @@ "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella config. Vedi %s", "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "O, se preferisci mantenere il file config.php in sola lettura, imposta l'opzione \"config_is_read_only\" a true. Vedi %s", "Cannot write into \"apps\" directory." : "Impossibile scrivere nella cartella \"apps\".", - "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella apps o disattivando il negozio delle applicazioni nel file di configurazione.", + "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella delle applicazioni o disattivando il negozio delle applicazioni nel file di configurazione.", "Cannot create \"data\" directory." : "Impossibile creare la cartella \"data\".", "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Ciò può essere corretto di solito fornendo al server web accesso in scrittura alla cartella radice. Vedi %s", "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "I permessi possono essere corretti di solito fornendo al server web accesso in scrittura alla cartella radice. Vedi %s.", diff --git a/lib/l10n/lt_LT.js b/lib/l10n/lt_LT.js index 24240599936..72dc4935acc 100644 --- a/lib/l10n/lt_LT.js +++ b/lib/l10n/lt_LT.js @@ -5,6 +5,7 @@ OC.L10N.register( "See %s" : "Žiūrėkite %s", "Sample configuration detected" : "Aptiktas konfigūracijos pavyzdys", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Pastebėta, kad nukopijuota pavyzdinė konfigūracija. Tai gali pažeisti jūsų diegimą ir yra nepalaikoma. Prieš atliekant pakeitimus config.php faile, prašome perskaityti dokumentaciją.", + "The page could not be found on the server." : "Šio puslapio nepavyko rasti serveryje.", "Other activities" : "Kitos veiklos", "%1$s and %2$s" : "%1$s ir %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ir %3$s", diff --git a/lib/l10n/lt_LT.json b/lib/l10n/lt_LT.json index 9fcddb78fdb..bb4ccc9e355 100644 --- a/lib/l10n/lt_LT.json +++ b/lib/l10n/lt_LT.json @@ -3,6 +3,7 @@ "See %s" : "Žiūrėkite %s", "Sample configuration detected" : "Aptiktas konfigūracijos pavyzdys", "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Pastebėta, kad nukopijuota pavyzdinė konfigūracija. Tai gali pažeisti jūsų diegimą ir yra nepalaikoma. Prieš atliekant pakeitimus config.php faile, prašome perskaityti dokumentaciją.", + "The page could not be found on the server." : "Šio puslapio nepavyko rasti serveryje.", "Other activities" : "Kitos veiklos", "%1$s and %2$s" : "%1$s ir %2$s", "%1$s, %2$s and %3$s" : "%1$s, %2$s ir %3$s", diff --git a/lib/l10n/uk.js b/lib/l10n/uk.js index 10020525224..755b8358060 100644 --- a/lib/l10n/uk.js +++ b/lib/l10n/uk.js @@ -78,7 +78,7 @@ OC.L10N.register( "in a few seconds" : "через кілька секунд", "seconds ago" : "кілька секунд тому", "Empty file" : "Порожній файл", - "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль з ID: %s не існує. Будь ласка, увімкніть це в налаштуваннях програми або зверніться до адміністратора.", + "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль з ID: %s не існує. Будь ласка, увімкніть це в налаштуваннях застосунку або зверніться до адміністратора.", "File already exists" : "Файл вже існує", "Invalid path" : "Недійсний шлях", "Failed to create file from template" : "Не вдалося створити файл із шаблону", @@ -89,7 +89,7 @@ OC.L10N.register( "Dot files are not allowed" : "Файли які починаються з крапки не допустимі", "Empty filename is not allowed" : "Порожні імена файлів не допускаються", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Застосунок \"%s\" не може бути встановлений через те, що файл appinfo не може бути прочитано.", - "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Програму \"%s\" неможливо встановити, оскільки вона не сумісна з цією версією сервера.", + "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Застосунок \"%s\" неможливо встановити, оскільки він не сумісний з цією версією сервера.", "__language_name__" : "Українська", "This is an automatically sent email, please do not reply." : "Це автоматично надісланий електронний лист, будь ласка, не відповідайте.", "Help" : "Допомога", @@ -213,7 +213,7 @@ OC.L10N.register( "Username is invalid because files already exist for this user" : "Ім'я користувача недійсне, оскільки файли для цього користувача вже існують", "User disabled" : "Користувач виключений", "Login canceled by app" : "Вхід скасовано застосунком", - "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Програму \"%1$s\" неможливо встановити, оскільки не виконано такі залежності: %2$s", + "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Застосунок \"%1$s\" неможливо встановити, оскільки не виконано такі залежності: %2$s", "a safe home for all your data" : "безпечний дім для ваших даних", "File is currently busy, please try again later" : "Файл на разі зайнятий, будь ласка, спробуйте пізніше", "Cannot download file" : "Неможливо завантажити файл", @@ -224,8 +224,8 @@ OC.L10N.register( "Cannot write into \"config\" directory." : "Не вдається записати в каталог \"config\".", "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Зазвичай це можна виправити, надавши веб-серверу доступ для запису до каталогу конфігурації. Побачити %s", "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Або, якщо ви віддаєте перевагу файлу config.php лише для читання, встановіть для параметра \"config_is_read_only\" значення true. Побачити %s", - "Cannot write into \"apps\" directory." : "Не вдається записати в каталог \"програми\".", - "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Зазвичай це можна виправити, надавши веб-серверу доступ для запису до каталогу програм або вимкнувши App Store у конфігураційному файлі.", + "Cannot write into \"apps\" directory." : "Відсутній доступ на запис до каталогу застосунків \"apps\".", + "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Зазвичай це можна виправити, якщо надати вебсерверу доступ для запису до каталогу застосунків або вимкнути App Store у конфігураційному файлі.", "Cannot create \"data\" directory." : "Неможливо створити каталог \"data\".", "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Зазвичай це можна виправити, надавши веб-серверу доступ на запис до кореневого каталогу. Побачити %s", "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Дозволи зазвичай можна виправити, надавши веб-серверу доступ на запис до кореневого каталогу. Побачити %s. ", @@ -269,7 +269,7 @@ OC.L10N.register( "Summarizes text by reducing its length without losing key information." : "Викокремлює головне у тексті шляхом зменшення довжини тексту без втрати ключової інформації.", "Extract topics" : "Виділити теми", "Extracts topics from a text and outputs them separated by commas." : "Виділяє теми, які висвітлює текст, зводить їх у перелік, що розділено комами.", - "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Файли програми %1$s замінено неправильно. Переконайтеся, що це версія, сумісна з сервером.", + "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Файли застосунку %1$s не було коректно оновлено. Переконайтеся, що ця версія, сумісна із сервером.", "404" : "404", "Full name" : "Повна назва", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Необхідно libxml2 версії принаймні 2.7.0. На разі встановлена %s.", diff --git a/lib/l10n/uk.json b/lib/l10n/uk.json index 490b8882499..11cb4a8e4e4 100644 --- a/lib/l10n/uk.json +++ b/lib/l10n/uk.json @@ -76,7 +76,7 @@ "in a few seconds" : "через кілька секунд", "seconds ago" : "кілька секунд тому", "Empty file" : "Порожній файл", - "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль з ID: %s не існує. Будь ласка, увімкніть це в налаштуваннях програми або зверніться до адміністратора.", + "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модуль з ID: %s не існує. Будь ласка, увімкніть це в налаштуваннях застосунку або зверніться до адміністратора.", "File already exists" : "Файл вже існує", "Invalid path" : "Недійсний шлях", "Failed to create file from template" : "Не вдалося створити файл із шаблону", @@ -87,7 +87,7 @@ "Dot files are not allowed" : "Файли які починаються з крапки не допустимі", "Empty filename is not allowed" : "Порожні імена файлів не допускаються", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Застосунок \"%s\" не може бути встановлений через те, що файл appinfo не може бути прочитано.", - "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Програму \"%s\" неможливо встановити, оскільки вона не сумісна з цією версією сервера.", + "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Застосунок \"%s\" неможливо встановити, оскільки він не сумісний з цією версією сервера.", "__language_name__" : "Українська", "This is an automatically sent email, please do not reply." : "Це автоматично надісланий електронний лист, будь ласка, не відповідайте.", "Help" : "Допомога", @@ -211,7 +211,7 @@ "Username is invalid because files already exist for this user" : "Ім'я користувача недійсне, оскільки файли для цього користувача вже існують", "User disabled" : "Користувач виключений", "Login canceled by app" : "Вхід скасовано застосунком", - "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Програму \"%1$s\" неможливо встановити, оскільки не виконано такі залежності: %2$s", + "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Застосунок \"%1$s\" неможливо встановити, оскільки не виконано такі залежності: %2$s", "a safe home for all your data" : "безпечний дім для ваших даних", "File is currently busy, please try again later" : "Файл на разі зайнятий, будь ласка, спробуйте пізніше", "Cannot download file" : "Неможливо завантажити файл", @@ -222,8 +222,8 @@ "Cannot write into \"config\" directory." : "Не вдається записати в каталог \"config\".", "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Зазвичай це можна виправити, надавши веб-серверу доступ для запису до каталогу конфігурації. Побачити %s", "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Або, якщо ви віддаєте перевагу файлу config.php лише для читання, встановіть для параметра \"config_is_read_only\" значення true. Побачити %s", - "Cannot write into \"apps\" directory." : "Не вдається записати в каталог \"програми\".", - "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Зазвичай це можна виправити, надавши веб-серверу доступ для запису до каталогу програм або вимкнувши App Store у конфігураційному файлі.", + "Cannot write into \"apps\" directory." : "Відсутній доступ на запис до каталогу застосунків \"apps\".", + "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Зазвичай це можна виправити, якщо надати вебсерверу доступ для запису до каталогу застосунків або вимкнути App Store у конфігураційному файлі.", "Cannot create \"data\" directory." : "Неможливо створити каталог \"data\".", "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Зазвичай це можна виправити, надавши веб-серверу доступ на запис до кореневого каталогу. Побачити %s", "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Дозволи зазвичай можна виправити, надавши веб-серверу доступ на запис до кореневого каталогу. Побачити %s. ", @@ -267,7 +267,7 @@ "Summarizes text by reducing its length without losing key information." : "Викокремлює головне у тексті шляхом зменшення довжини тексту без втрати ключової інформації.", "Extract topics" : "Виділити теми", "Extracts topics from a text and outputs them separated by commas." : "Виділяє теми, які висвітлює текст, зводить їх у перелік, що розділено комами.", - "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Файли програми %1$s замінено неправильно. Переконайтеся, що це версія, сумісна з сервером.", + "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Файли застосунку %1$s не було коректно оновлено. Переконайтеся, що ця версія, сумісна із сервером.", "404" : "404", "Full name" : "Повна назва", "libxml2 2.7.0 is at least required. Currently %s is installed." : "Необхідно libxml2 версії принаймні 2.7.0. На разі встановлена %s.", diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index 92178d64635..ab4359c798f 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -43,7 +43,6 @@ use OCP\PreConditionNotMetException; * Class to combine all the configuration options ownCloud offers */ class AllConfig implements IConfig { - private SystemConfig $systemConfig; private ?IDBConnection $connection = null; /** @@ -68,9 +67,10 @@ class AllConfig implements IConfig { */ private CappedMemoryCache $userCache; - public function __construct(SystemConfig $systemConfig) { + public function __construct( + private SystemConfig $systemConfig + ) { $this->userCache = new CappedMemoryCache(); - $this->systemConfig = $systemConfig; } /** @@ -190,6 +190,7 @@ class AllConfig implements IConfig { * * @param string $appName the appName that we stored the value under * @return string[] the keys stored for the app + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppKeys($appName) { return \OC::$server->get(AppConfig::class)->getKeys($appName); @@ -201,6 +202,7 @@ class AllConfig implements IConfig { * @param string $appName the appName that we want to store the value under * @param string $key the key of the value, under which will be saved * @param string|float|int $value the value that should be stored + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function setAppValue($appName, $key, $value) { \OC::$server->get(AppConfig::class)->setValue($appName, $key, $value); @@ -213,6 +215,7 @@ class AllConfig implements IConfig { * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set * @return string the saved value + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppValue($appName, $key, $default = '') { return \OC::$server->get(AppConfig::class)->getValue($appName, $key, $default); @@ -223,6 +226,7 @@ class AllConfig implements IConfig { * * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValue($appName, $key) { \OC::$server->get(AppConfig::class)->deleteKey($appName, $key); @@ -232,6 +236,7 @@ class AllConfig implements IConfig { * Removes all keys in appconfig belonging to the app * * @param string $appName the appName the configs are stored under + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValues($appName) { \OC::$server->get(AppConfig::class)->deleteApp($appName); diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index ad5fdc5afed..60e55a314d6 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -289,7 +289,7 @@ class AppManager implements IAppManager { * Check if an app is enabled for user * * @param string $appId - * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used + * @param \OCP\IUser|null $user (optional) if not defined, the currently logged in user will be used * @return bool */ public function isEnabledForUser($appId, $user = null) { @@ -702,10 +702,7 @@ class AppManager implements IAppManager { /** * Returns the app information from "appinfo/info.xml". * - * @param string $appId app id - * - * @param bool $path - * @param null $lang + * @param string|null $lang * @return array|null app info */ public function getAppInfo(string $appId, bool $path = false, $lang = null) { @@ -817,7 +814,7 @@ class AppManager implements IAppManager { /** * @inheritdoc */ - public function getDefaultEnabledApps():array { + public function getDefaultEnabledApps(): array { $this->loadShippedJson(); return $this->defaultEnabled; diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php index 3e76ab2d5da..a693804f50f 100644 --- a/lib/private/App/AppStore/Fetcher/Fetcher.php +++ b/lib/private/App/AppStore/Fetcher/Fetcher.php @@ -109,10 +109,13 @@ abstract class Fetcher { ]; } - // If we have a valid subscription key, send it to the appstore - $subscriptionKey = $this->config->getAppValue('support', 'subscription_key'); - if ($this->registry->delegateHasValidSubscription() && $subscriptionKey) { - $options['headers']['X-NC-Subscription-Key'] = $subscriptionKey; + if ($this->config->getSystemValueString('appstoreurl', 'https://apps.nextcloud.com/api/v1') === 'https://apps.nextcloud.com/api/v1') { + // If we have a valid subscription key, send it to the appstore + $subscriptionKey = $this->config->getAppValue('support', 'subscription_key'); + if ($this->registry->delegateHasValidSubscription() && $subscriptionKey) { + $options['headers'] ??= []; + $options['headers']['X-NC-Subscription-Key'] = $subscriptionKey; + } } $client = $this->clientService->newClient(); diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index f92abb2caad..d15aff37c5b 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -1,4 +1,6 @@ <?php + +declare(strict_types=1); /** * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> * @copyright Copyright (c) 2016, ownCloud, Inc. @@ -9,6 +11,7 @@ * @author Jakob Sack <mail@jakobsack.de> * @author Joas Schilling <coding@schilljs.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Maxence Lange <maxence@artificial-owl.com> * @author michaelletzgus <michaelletzgus@users.noreply.github.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> @@ -30,416 +33,1411 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC; -use OC\DB\Connection; -use OC\DB\OracleConnection; +use InvalidArgumentException; +use JsonException; +use OCP\DB\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Exceptions\AppConfigIncorrectTypeException; +use OCP\Exceptions\AppConfigTypeConflictException; +use OCP\Exceptions\AppConfigUnknownKeyException; use OCP\IAppConfig; use OCP\IConfig; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; /** * This class provides an easy way for apps to store config values in the * database. + * + * **Note:** since 29.0.0, it supports **lazy loading** + * + * ### What is lazy loading ? + * In order to avoid loading useless config values into memory for each request, + * only non-lazy values are now loaded. + * + * Once a value that is lazy is requested, all lazy values will be loaded. + * + * Similarly, some methods from this class are marked with a warning about ignoring + * lazy loading. Use them wisely and only on parts of the code that are called + * during specific requests or actions to avoid loading the lazy values all the time. + * + * @since 7.0.0 + * @since 29.0.0 - Supporting types and lazy loading */ class AppConfig implements IAppConfig { - /** @var array[] */ - protected $sensitiveValues = [ - 'circles' => [ - '/^key_pairs$/', - '/^local_gskey$/', - ], - 'external' => [ - '/^sites$/', - ], - 'integration_discourse' => [ - '/^private_key$/', - '/^public_key$/', - ], - 'integration_dropbox' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_github' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_gitlab' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_google' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_jira' => [ - '/^client_id$/', - '/^client_secret$/', - '/^forced_instance_url$/', - ], - 'integration_onedrive' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_openproject' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_reddit' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_suitecrm' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_twitter' => [ - '/^consumer_key$/', - '/^consumer_secret$/', - '/^followed_user$/', - ], - 'integration_zammad' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'notify_push' => [ - '/^cookie$/', - ], - 'serverinfo' => [ - '/^token$/', - ], - 'spreed' => [ - '/^bridge_bot_password$/', - '/^hosted-signaling-server-(.*)$/', - '/^recording_servers$/', - '/^signaling_servers$/', - '/^signaling_ticket_secret$/', - '/^signaling_token_privkey_(.*)$/', - '/^signaling_token_pubkey_(.*)$/', - '/^sip_bridge_dialin_info$/', - '/^sip_bridge_shared_secret$/', - '/^stun_servers$/', - '/^turn_servers$/', - '/^turn_server_secret$/', - ], - 'support' => [ - '/^last_response$/', - '/^potential_subscription_key$/', - '/^subscription_key$/', - ], - 'theming' => [ - '/^imprintUrl$/', - '/^privacyUrl$/', - '/^slogan$/', - '/^url$/', - ], - 'user_ldap' => [ - '/^(s..)?ldap_agent_password$/', - ], - 'user_saml' => [ - '/^idp-x509cert$/', - ], - ]; - - /** @var Connection */ - protected $conn; - - /** @var array[] */ - private $cache = []; - - /** @var bool */ - private $configLoaded = false; - - /** - * @param Connection $conn - */ - public function __construct(Connection $conn) { - $this->conn = $conn; + private const APP_MAX_LENGTH = 32; + private const KEY_MAX_LENGTH = 64; + + /** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */ + private array $fastCache = []; // cache for normal config keys + /** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */ + private array $lazyCache = []; // cache for lazy config keys + /** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */ + private array $valueTypes = []; // type for all config values + private bool $fastLoaded = false; + private bool $lazyLoaded = false; + + /** + * $migrationCompleted is only needed to manage the previous structure + * of the database during the upgrading process to nc29. + * @TODO: remove this value in Nextcloud 30+ + */ + private bool $migrationCompleted = true; + + public function __construct( + protected IDBConnection $connection, + private LoggerInterface $logger, + ) { } /** - * @param string $app - * @return array + * @inheritDoc + * + * @return string[] list of app ids + * @since 7.0.0 + */ + public function getApps(): array { + $this->loadConfigAll(); + $apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache)); + sort($apps); + + return array_values(array_unique($apps)); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * + * @return string[] list of stored config keys + * @since 29.0.0 */ - private function getAppValues($app) { - $this->loadConfigValues(); + public function getKeys(string $app): array { + $this->assertParams($app); + $this->loadConfigAll(); + $keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? [])); + sort($keys); + + return array_values(array_unique($keys)); + } - if (isset($this->cache[$app])) { - return $this->cache[$app]; + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config + * + * @return bool TRUE if key exists + * @since 7.0.0 + * @since 29.0.0 Added the $lazy argument + */ + public function hasKey(string $app, string $key, ?bool $lazy = false): bool { + $this->assertParams($app, $key); + $this->loadConfig($lazy); + + if ($lazy === null) { + $appCache = $this->getAllValues($app); + return isset($appCache[$key]); + } + + if ($lazy) { + return isset($this->lazyCache[$app][$key]); + } + + return isset($this->fastCache[$app][$key]); + } + + /** + * @param string $app id of the app + * @param string $key config key + * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config + * + * @return bool + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + */ + public function isSensitive(string $app, string $key, ?bool $lazy = false): bool { + $this->assertParams($app, $key); + $this->loadConfig($lazy); + + if (!isset($this->valueTypes[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); } - return []; + return $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key]); } /** - * Get all apps using the config + * @inheritDoc * - * @return string[] an array of app ids + * @param string $app if of the app + * @param string $key config key * - * This function returns a list of all apps that have at least one - * entry in the appconfig table. + * @return bool TRUE if config is lazy loaded + * @throws AppConfigUnknownKeyException if config key is not known + * @see IAppConfig for details about lazy loading + * @since 29.0.0 */ - public function getApps() { - $this->loadConfigValues(); + public function isLazy(string $app, string $key): bool { + // there is a huge probability the non-lazy config are already loaded + if ($this->hasKey($app, $key, false)) { + return false; + } - return $this->getSortedKeys($this->cache); + // key not found, we search in the lazy config + if ($this->hasKey($app, $key, true)) { + return true; + } + + throw new AppConfigUnknownKeyException('unknown config key'); } + /** - * Get the available keys for an app + * @inheritDoc * - * @param string $app the app we are looking for - * @return array an array of key names + * @param string $app id of the app + * @param string $key config keys prefix to search + * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE} * - * This function gets all keys of an app. Please note that the values are - * not returned. + * @return array<string, string> [configKey => configValue] + * @since 29.0.0 */ - public function getKeys($app) { - $this->loadConfigValues(); + public function getAllValues(string $app, string $key = '', bool $filtered = false): array { + $this->assertParams($app, $key); + // if we want to filter values, we need to get sensitivity + $this->loadConfigAll(); + // array_merge() will remove numeric keys (here config keys), so addition arrays instead + $values = ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? []); - if (isset($this->cache[$app])) { - return $this->getSortedKeys($this->cache[$app]); + if (!$filtered) { + return $values; } - return []; + /** + * Using the old (deprecated) list of sensitive values. + */ + foreach ($this->getSensitiveKeys($app) as $sensitiveKeyExp) { + $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); + foreach ($sensitiveKeys as $sensitiveKey) { + $this->valueTypes[$app][$sensitiveKey] = ($this->valueTypes[$app][$sensitiveKey] ?? 0) | self::VALUE_SENSITIVE; + } + } + + $result = []; + foreach ($values as $key => $value) { + $result[$key] = $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key] ?? 0) ? IConfig::SENSITIVE_VALUE : $value; + } + + return $result; } - public function getSortedKeys($data) { - $keys = array_keys($data); - sort($keys); - return $keys; + /** + * @inheritDoc + * + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * + * @return array<string, string> [appId => configValue] + * @since 29.0.0 + */ + public function searchValues(string $key, bool $lazy = false): array { + $this->assertParams('', $key, true); + $this->loadConfig($lazy); + $values = []; + + /** @var array<array-key, array<array-key, mixed>> $cache */ + if ($lazy) { + $cache = $this->lazyCache; + } else { + $cache = $this->fastCache; + } + + foreach (array_keys($cache) as $app) { + if (isset($cache[$app][$key])) { + $values[$app] = $cache[$app][$key]; + } + } + + return $values; } + /** - * Gets the config value + * Get the config value as string. + * If the value does not exist the given default will be returned. + * + * Set lazy to `null` to ignore it and get the value from either source. + * + * **WARNING:** Method is internal and **SHOULD** not be used, as it is better to get the value with a type. + * + * @param string $app id of the app + * @param string $key config key + * @param string $default config value + * @param null|bool $lazy get config as lazy loaded or not. can be NULL * - * @param string $app app - * @param string $key key - * @param string $default = null, default value if the key does not exist * @return string the value or $default + * @internal + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueFloat() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueMixed( + string $app, + string $key, + string $default = '', + ?bool $lazy = false + ): string { + try { + $lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy; + } catch (AppConfigUnknownKeyException $e) { + return $default; + } + + return $this->getTypedValue( + $app, + $key, + $default, + $lazy, + self::VALUE_MIXED + ); + } + + /** + * @inheritDoc * - * This function gets a value from the appconfig table. If the key does - * not exist the default value will be returned + * @param string $app id of the app + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * + * @return string stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading */ - public function getValue($app, $key, $default = null) { - $this->loadConfigValues(); + public function getValueString( + string $app, + string $key, + string $default = '', + bool $lazy = false + ): string { + return $this->getTypedValue($app, $key, $default, $lazy, self::VALUE_STRING); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param int $default default value + * @param bool $lazy search within lazy loaded config + * + * @return int stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueInt( + string $app, + string $key, + int $default = 0, + bool $lazy = false + ): int { + return (int)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_INT); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param float $default default value + * @param bool $lazy search within lazy loaded config + * + * @return float stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float { + return (float)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_FLOAT); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool $default default value + * @param bool $lazy search within lazy loaded config + * + * @return bool stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool { + $b = strtolower($this->getTypedValue($app, $key, $default ? 'true' : 'false', $lazy, self::VALUE_BOOL)); + return in_array($b, ['1', 'true', 'yes', 'on']); + } - if ($this->hasKey($app, $key)) { - return $this->cache[$app][$key]; + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param array $default default value + * @param bool $lazy search within lazy loaded config + * + * @return array stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueArray( + string $app, + string $key, + array $default = [], + bool $lazy = false + ): array { + try { + $defaultJson = json_encode($default, JSON_THROW_ON_ERROR); + $value = json_decode($this->getTypedValue($app, $key, $defaultJson, $lazy, self::VALUE_ARRAY), true, flags: JSON_THROW_ON_ERROR); + + return is_array($value) ? $value : []; + } catch (JsonException) { + return []; } + } - return $default; + /** + * @param string $app id of the app + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT}{@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY} + * + * @return string + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @throws InvalidArgumentException + */ + private function getTypedValue( + string $app, + string $key, + string $default, + bool $lazy, + int $type + ): string { + $this->assertParams($app, $key, valueType: $type); + $this->loadConfig($lazy); + + /** + * We ignore check if mixed type is requested. + * If type of stored value is set as mixed, we don't filter. + * If type of stored value is defined, we compare with the one requested. + */ + $knownType = $this->valueTypes[$app][$key] ?? 0; + if (!$this->isTyped(self::VALUE_MIXED, $type) + && $knownType > 0 + && !$this->isTyped(self::VALUE_MIXED, $knownType) + && !$this->isTyped($type, $knownType)) { + $this->logger->warning('conflict with value type from database', ['app' => $app, 'key' => $key, 'type' => $type, 'knownType' => $knownType]); + throw new AppConfigTypeConflictException('conflict with value type from database'); + } + + if ($lazy) { + return $this->lazyCache[$app][$key] ?? $default; + } + + return $this->fastCache[$app][$key] ?? $default; } /** - * check if a key is set in the appconfig + * @inheritDoc * - * @param string $app - * @param string $key - * @return bool + * @param string $app id of the app + * @param string $key config key + * + * @return int type of the value + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + * @see VALUE_STRING + * @see VALUE_INT + * @see VALUE_FLOAT + * @see VALUE_BOOL + * @see VALUE_ARRAY */ - public function hasKey($app, $key) { - $this->loadConfigValues(); + public function getValueType(string $app, string $key): int { + $this->assertParams($app, $key); + $this->loadConfigAll(); + + if (!isset($this->valueTypes[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); + } - return isset($this->cache[$app][$key]); + $type = $this->valueTypes[$app][$key]; + $type &= ~self::VALUE_SENSITIVE; + return $type; } + /** - * Sets a value. If the key did not exist before it will be created. + * Store a config key and its value in database as VALUE_MIXED * - * @param string $app app - * @param string $key key - * @param string|float|int $value value - * @return bool True if the value was inserted or updated, false if the value was the same + * **WARNING:** Method is internal and **MUST** not be used as it is best to set a real value type + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED + * @internal + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueFloat() + * @see setValueBool() + * @see setValueArray() */ - public function setValue($app, $key, $value) { - if (!$this->hasKey($app, $key)) { - $inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [ - 'appid' => $app, - 'configkey' => $key, - 'configvalue' => $value, - ], [ - 'appid', - 'configkey', - ]); - - if ($inserted) { - if (!isset($this->cache[$app])) { - $this->cache[$app] = []; - } + public function setValueMixed( + string $app, + string $key, + string $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->setTypedValue( + $app, + $key, + $value, + $lazy, + self::VALUE_MIXED | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } - $this->cache[$app][$key] = $value; - return true; - } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueString( + string $app, + string $key, + string $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->setTypedValue( + $app, + $key, + $value, + $lazy, + self::VALUE_STRING | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param int $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueInt( + string $app, + string $key, + int $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + if ($value > 2000000000) { + $this->logger->debug('You are trying to store an integer value around/above 2,147,483,647. This is a reminder that reaching this theoretical limit on 32 bits system will throw an exception.'); } - $sql = $this->conn->getQueryBuilder(); - $sql->update('appconfig') - ->set('configvalue', $sql->createNamedParameter($value)) - ->where($sql->expr()->eq('appid', $sql->createNamedParameter($app))) - ->andWhere($sql->expr()->eq('configkey', $sql->createNamedParameter($key))); + return $this->setTypedValue( + $app, + $key, + (string)$value, + $lazy, + self::VALUE_INT | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param float $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueFloat( + string $app, + string $key, + float $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->setTypedValue( + $app, + $key, + (string)$value, + $lazy, + self::VALUE_FLOAT | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool $value config value + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueBool( + string $app, + string $key, + bool $value, + bool $lazy = false + ): bool { + return $this->setTypedValue( + $app, + $key, + ($value) ? '1' : '0', + $lazy, + self::VALUE_BOOL + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param array $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @throws JsonException + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueArray( + string $app, + string $key, + array $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + try { + return $this->setTypedValue( + $app, + $key, + json_encode($value, JSON_THROW_ON_ERROR), + $lazy, + self::VALUE_ARRAY | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } catch (JsonException $e) { + $this->logger->warning('could not setValueArray', ['app' => $app, 'key' => $key, 'exception' => $e]); + throw $e; + } + } + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value and same sensitive/lazy status, the + * database is not updated. If config value was previously stored as sensitive, status will not be + * altered. + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $lazy config set as lazy loaded + * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY} + * + * @return bool TRUE if value was updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @see IAppConfig for explanation about lazy loading + */ + private function setTypedValue( + string $app, + string $key, + string $value, + bool $lazy, + int $type + ): bool { + $this->assertParams($app, $key); + $this->loadConfig($lazy); + + $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type); /* - * Only limit to the existing value for non-Oracle DBs: - * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286 - * > Large objects (LOBs) are not supported in comparison conditions. + * no update if key is already known with set lazy status, or value is + * different, or sensitivity switched from false to true. */ - if (!($this->conn instanceof OracleConnection)) { - /* - * Only update the value when it is not the same - * Note that NULL requires some special handling. Since comparing - * against null can have special results. + if ($this->hasKey($app, $key, $lazy) + && $value === $this->getTypedValue($app, $key, $value, $lazy, $type) + && (!$sensitive || $this->isSensitive($app, $key, $lazy))) { + return false; + } + + $refreshCache = false; + $insert = $this->connection->getQueryBuilder(); + $insert->insert('appconfig') + ->setValue('appid', $insert->createNamedParameter($app)) + ->setValue('lazy', $insert->createNamedParameter($lazy, IQueryBuilder::PARAM_BOOL)) + ->setValue('type', $insert->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->setValue('configkey', $insert->createNamedParameter($key)) + ->setValue('configvalue', $insert->createNamedParameter($value)); + try { + $insert->executeStatement(); + } catch (DBException $e) { + if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; // TODO: throw exception or just log and returns false !? + } + + $currType = $this->valueTypes[$app][$key] ?? 0; + if ($currType === 0) { // this might happen when switching lazy loading status + $this->loadConfigAll(); + $currType = $this->valueTypes[$app][$key] ?? 0; + } + + /** + * This should only happen during the upgrade process from 28 to 29. + * We only log a warning and set it to VALUE_MIXED. */ + if ($currType === 0) { + $this->logger->warning('Value type is set to zero (0) in database. This is fine only during the upgrade process from 28 to 29.', ['app' => $app, 'key' => $key]); + $currType = self::VALUE_MIXED; + } - if ($value === null) { - $sql->andWhere( - $sql->expr()->isNotNull('configvalue') - ); - } else { - $sql->andWhere( - $sql->expr()->orX( - $sql->expr()->isNull('configvalue'), - $sql->expr()->neq('configvalue', $sql->createNamedParameter($value), IQueryBuilder::PARAM_STR) - ) - ); + /** + * we only accept a different type from the one stored in database + * if the one stored in database is not-defined (VALUE_MIXED) + */ + if (!$this->isTyped(self::VALUE_MIXED, $currType) && + ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) { + try { + $currType = $this->convertTypeToString($currType); + $type = $this->convertTypeToString($type); + } catch (AppConfigIncorrectTypeException) { + // can be ignored, this was just needed for a better exception message. + } + throw new AppConfigTypeConflictException('conflict between new type (' . $type . ') and old type (' . $currType . ')'); + } + + // 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; } + + if ($lazy !== $this->isLazy($app, $key)) { + $refreshCache = true; + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('configvalue', $update->createNamedParameter($value)) + ->set('lazy', $update->createNamedParameter($lazy, IQueryBuilder::PARAM_BOOL)) + ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + + $update->executeStatement(); } - $changedRow = (bool) $sql->execute(); + if ($refreshCache) { + $this->clearCache(); + return true; + } - $this->cache[$app][$key] = $value; + // update local cache + if ($lazy) { + $cache = &$this->lazyCache; + } else { + $cache = &$this->fastCache; + } + $cache[$app][$key] = $value; + $this->valueTypes[$app][$key] = $type; - return $changedRow; + return true; } /** - * Deletes a key + * Change the type of config value. * - * @param string $app app - * @param string $key key - * @return boolean + * **WARNING:** Method is internal and **MUST** not be used as it may break things. + * + * @param string $app id of the app + * @param string $key config key + * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY} + * + * @return bool TRUE if database update were necessary + * @throws AppConfigUnknownKeyException if $key is now known in database + * @throws AppConfigIncorrectTypeException if $type is not valid + * @internal + * @since 29.0.0 */ - public function deleteKey($app, $key) { - $this->loadConfigValues(); + public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool { + $this->assertParams($app, $key); + $this->loadConfigAll(); + $lazy = $this->isLazy($app, $key); - $sql = $this->conn->getQueryBuilder(); - $sql->delete('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) - ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) - ->setParameter('app', $app) - ->setParameter('configkey', $key); - $sql->execute(); + if (!$this->hasKey($app, $key, $lazy)) { + throw new AppConfigUnknownKeyException('Unknown config key'); + } - unset($this->cache[$app][$key]); - return false; + // 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])) { + throw new AppConfigIncorrectTypeException('Unknown value type'); + } + + $currType = $this->valueTypes[$app][$key]; + if (($type | self::VALUE_SENSITIVE) === ($currType | self::VALUE_SENSITIVE)) { + return false; + } + + // we complete with sensitive flag if the stored value is set as sensitive + if ($this->isTyped(self::VALUE_SENSITIVE, $currType)) { + $type = $type | self::VALUE_SENSITIVE; + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); + $this->valueTypes[$app][$key] = $type; + + return true; } + /** - * Remove app from appconfig + * @inheritDoc * - * @param string $app app - * @return boolean + * @param string $app id of the app + * @param string $key config key + * @param bool $sensitive TRUE to set as sensitive, FALSE to unset * - * Removes all keys in appconfig belonging to the app. + * @return bool TRUE if database update were necessary + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 */ - public function deleteApp($app) { - $this->loadConfigValues(); + public function updateSensitive(string $app, string $key, bool $sensitive): bool { + $this->assertParams($app, $key); + $this->loadConfigAll(); - $sql = $this->conn->getQueryBuilder(); - $sql->delete('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) - ->setParameter('app', $app); - $sql->execute(); + if ($sensitive === $this->isSensitive($app, $key, null)) { + return false; + } + + /** + * 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); + if ($sensitive) { + $type = $type | self::VALUE_SENSITIVE; + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); - unset($this->cache[$app]); - return false; + $this->valueTypes[$app][$key] = $type; + + return true; } /** - * get multiple values, either the app or key can be used as wildcard by setting it to false + * @inheritDoc * - * @param string|false $app - * @param string|false $key - * @return array|false + * @param string $app id of the app + * @param string $key config key + * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset + * + * @return bool TRUE if database update was necessary + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 */ - public function getValues($app, $key) { - if (($app !== false) === ($key !== false)) { + public function updateLazy(string $app, string $key, bool $lazy): bool { + $this->assertParams($app, $key); + $this->loadConfigAll(); + + if ($lazy === $this->isLazy($app, $key)) { return false; } - if ($key === false) { - return $this->getAppValues($app); + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('lazy', $update->createNamedParameter($lazy, IQueryBuilder::PARAM_BOOL)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); + + // At this point, it is a lot safer to clean cache + $this->clearCache(); + + return true; + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * + * @return array + * @throws AppConfigUnknownKeyException if config key is not known in database + * @since 29.0.0 + */ + public function getDetails(string $app, string $key): array { + $this->assertParams($app, $key); + $this->loadConfigAll(); + $lazy = $this->isLazy($app, $key); + + if ($lazy) { + $cache = $this->lazyCache; } else { - $appIds = $this->getApps(); - $values = array_map(function ($appId) use ($key) { - return $this->cache[$appId][$key] ?? null; - }, $appIds); - $result = array_combine($appIds, $values); + $cache = $this->fastCache; + } + + $type = $this->getValueType($app, $key); + try { + $typeString = $this->convertTypeToString($type); + } catch (AppConfigIncorrectTypeException $e) { + $this->logger->warning('type stored in database is not correct', ['exception' => $e, 'type' => $type]); + $typeString = (string)$type; + } - return array_filter($result); + if (!isset($cache[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); } + + return [ + 'app' => $app, + 'key' => $key, + 'value' => $cache[$app][$key], + 'type' => $type, + 'lazy' => $lazy, + 'typeString' => $typeString, + 'sensitive' => $this->isSensitive($app, $key, null) + ]; } /** - * get all values of the app or and filters out sensitive data + * @param string $type + * + * @return int + * @throws AppConfigIncorrectTypeException + * @since 29.0.0 + */ + public function convertTypeToInt(string $type): int { + return match (strtolower($type)) { + 'mixed' => IAppConfig::VALUE_MIXED, + 'string' => IAppConfig::VALUE_STRING, + 'integer' => IAppConfig::VALUE_INT, + 'float' => IAppConfig::VALUE_FLOAT, + 'boolean' => IAppConfig::VALUE_BOOL, + 'array' => IAppConfig::VALUE_ARRAY, + default => throw new AppConfigIncorrectTypeException('Unknown type ' . $type) + }; + } + + /** + * @param int $type + * + * @return string + * @throws AppConfigIncorrectTypeException + * @since 29.0.0 + */ + public function convertTypeToString(int $type): string { + $type &= ~self::VALUE_SENSITIVE; + + return match ($type) { + IAppConfig::VALUE_MIXED => 'mixed', + IAppConfig::VALUE_STRING => 'string', + IAppConfig::VALUE_INT => 'integer', + IAppConfig::VALUE_FLOAT => 'float', + IAppConfig::VALUE_BOOL => 'boolean', + IAppConfig::VALUE_ARRAY => 'array', + default => throw new AppConfigIncorrectTypeException('Unknown numeric type ' . $type) + }; + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * + * @since 29.0.0 + */ + public function deleteKey(string $app, string $key): void { + $this->assertParams($app, $key); + $qb = $this->connection->getQueryBuilder(); + $qb->delete('appconfig') + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))) + ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); + $qb->executeStatement(); + + unset($this->lazyCache[$app][$key]); + unset($this->fastCache[$app][$key]); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * + * @since 29.0.0 + */ + public function deleteApp(string $app): void { + $this->assertParams($app); + $qb = $this->connection->getQueryBuilder(); + $qb->delete('appconfig') + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))); + $qb->executeStatement(); + + $this->clearCache(); + } + + /** + * @inheritDoc + * + * @param bool $reload set to TRUE to refill cache instantly after clearing it + * + * @since 29.0.0 + */ + public function clearCache(bool $reload = false): void { + $this->lazyLoaded = $this->fastLoaded = false; + $this->lazyCache = $this->fastCache = $this->valueTypes = []; + + if (!$reload) { + return; + } + + $this->loadConfigAll(); + } + + + /** + * For debug purpose. + * Returns the cached data. * - * @param string $app * @return array + * @since 29.0.0 + * @internal */ - public function getFilteredValues($app) { - $values = $this->getValues($app, false); + public function statusCache(): array { + return [ + 'fastLoaded' => $this->fastLoaded, + 'fastCache' => $this->fastCache, + 'lazyLoaded' => $this->lazyLoaded, + 'lazyCache' => $this->lazyCache, + ]; + } - if (isset($this->sensitiveValues[$app])) { - foreach ($this->sensitiveValues[$app] as $sensitiveKeyExp) { - $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); - foreach ($sensitiveKeys as $sensitiveKey) { - $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE; - } + /** + * @param int $needle bitflag to search + * @param int $type known value + * + * @return bool TRUE if bitflag $needle is set in $type + */ + private function isTyped(int $needle, int $type): bool { + return (($needle & $type) !== 0); + } + + /** + * Confirm the string set for app and key fit the database description + * + * @param string $app assert $app fit in database + * @param string $configKey assert config key fit in database + * @param bool $allowEmptyApp $app can be empty string + * @param int $valueType assert value type is only one type + * + * @throws InvalidArgumentException + */ + private function assertParams(string $app = '', string $configKey = '', bool $allowEmptyApp = false, int $valueType = -1): void { + if (!$allowEmptyApp && $app === '') { + throw new InvalidArgumentException('app cannot be an empty string'); + } + if (strlen($app) > self::APP_MAX_LENGTH) { + throw new InvalidArgumentException( + 'Value (' . $app . ') for app is too long (' . self::APP_MAX_LENGTH . ')' + ); + } + if (strlen($configKey) > self::KEY_MAX_LENGTH) { + throw new InvalidArgumentException('Value (' . $configKey . ') for key is too long (' . self::KEY_MAX_LENGTH . ')'); + } + if ($valueType > -1) { + $valueType &= ~self::VALUE_SENSITIVE; + if (!in_array($valueType, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) { + throw new InvalidArgumentException('Unknown value type'); } } + } - return $values; + private function loadConfigAll(): void { + $this->loadConfig(null); } /** - * Load all the app config values + * Load normal config or config set as lazy loaded + * + * @param bool|null $lazy set to TRUE to load config set as lazy loaded, set to NULL to load all config */ - protected function loadConfigValues() { - if ($this->configLoaded) { + private function loadConfig(?bool $lazy = false): void { + if ($this->isLoaded($lazy)) { return; } - $this->cache = []; + $qb = $this->connection->getQueryBuilder(); + $qb->from('appconfig'); + + /** + * The use of $this->>migrationCompleted is only needed to manage the + * database during the upgrading process to nc29. + */ + if (!$this->migrationCompleted) { + $qb->select('appid', 'configkey', 'configvalue'); + } else { + $qb->select('appid', 'configkey', 'configvalue', 'type', 'lazy'); + if ($lazy !== null) { + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { + // Oracle does not like empty string nor false boolean !? + if ($lazy) { + $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter('1', IQueryBuilder::PARAM_INT))); + } else { + $qb->where($qb->expr()->orX( + $qb->expr()->isNull('lazy'), + $qb->expr()->eq('lazy', $qb->createNamedParameter('0', IQueryBuilder::PARAM_INT)) + )); + } + } else { + $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter($lazy, IQueryBuilder::PARAM_BOOL))); + } + } + } - $sql = $this->conn->getQueryBuilder(); - $sql->select('*') - ->from('appconfig'); - $result = $sql->execute(); + try { + $result = $qb->executeQuery(); + } catch (DBException $e) { + /** + * in case of issue with field name, it means that migration is not completed. + * Falling back to a request without select on lazy. + * This whole try/catch and the migrationCompleted variable can be removed in NC30. + */ + if ($e->getReason() !== DBException::REASON_INVALID_FIELD_NAME) { + throw $e; + } + + $this->migrationCompleted = false; + $this->loadConfig($lazy); + + return; + } - // we are going to store the result in memory anyway $rows = $result->fetchAll(); foreach ($rows as $row) { - if (!isset($this->cache[$row['appid']])) { - $this->cache[(string)$row['appid']] = []; + // if migration is not completed, 'lazy' and 'type' does not exist in $row + // also on oracle, lazy can be null ... + if ($row['lazy'] ?? false) { + $cache = &$this->lazyCache; + } else { + $cache = &$this->fastCache; } - - $this->cache[(string)$row['appid']][(string)$row['configkey']] = (string)$row['configvalue']; + $cache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? ''; + $this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0); } $result->closeCursor(); + $this->setAsLoaded($lazy); + } + + /** + * if $lazy is: + * - false: will returns true if fast config is loaded + * - true : will returns true if lazy config is loaded + * - null : will returns true if both config are loaded + * + * @param bool $lazy + * + * @return bool + */ + private function isLoaded(?bool $lazy): bool { + if ($lazy === null) { + return $this->lazyLoaded && $this->fastLoaded; + } - $this->configLoaded = true; + return $lazy ? $this->lazyLoaded : $this->fastLoaded; } + /** + * if $lazy is: + * - false: set fast config as loaded + * - true : set lazy config as loaded + * - null : set both config as loaded + * + * @param bool $lazy + */ + private function setAsLoaded(?bool $lazy): void { + if ($lazy === null) { + $this->fastLoaded = true; + $this->lazyLoaded = true; + + return; + } + + if ($lazy) { + $this->lazyLoaded = true; + } else { + $this->fastLoaded = true; + } + } + + /** + * Gets the config value + * + * @param string $app app + * @param string $key key + * @param string $default = null, default value if the key does not exist + * + * @return string the value or $default + * @deprecated - use getValue*() + * + * This function gets a value from the appconfig table. If the key does + * not exist the default value will be returned + */ + public function getValue($app, $key, $default = null) { + $this->loadConfig(); + + return $this->fastCache[$app][$key] ?? $default; + } + + /** + * Sets a value. If the key did not exist before it will be created. + * + * @param string $app app + * @param string $key key + * @param string|float|int $value value + * + * @return bool True if the value was inserted or updated, false if the value was the same + * @throws AppConfigTypeConflictException + * @throws AppConfigUnknownKeyException + * @deprecated + */ + public function setValue($app, $key, $value) { + return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED); + } + + + /** + * get multiple values, either the app or key can be used as wildcard by setting it to false + * + * @param string|false $app + * @param string|false $key + * + * @return array|false + * @deprecated 29.0.0 use getAllValues() + */ + public function getValues($app, $key) { + if (($app !== false) === ($key !== false)) { + return false; + } + + $key = ($key === false) ? '' : $key; + if (!$app) { + return $this->searchValues($key); + } else { + return $this->getAllValues($app, $key); + } + } + + /** + * get all values of the app or and filters out sensitive data + * + * @param string $app + * + * @return array + * @deprecated 29.0.0 use getAllValues() + */ + public function getFilteredValues($app) { + return $this->getAllValues($app, filtered: true); + } + + /** + * @param string $app + * + * @return string[] + * @deprecated data sensitivity should be set when calling setValue*() + */ + private function getSensitiveKeys(string $app): array { + $sensitiveValues = [ + 'circles' => [ + '/^key_pairs$/', + '/^local_gskey$/', + ], + 'external' => [ + '/^sites$/', + ], + 'integration_discourse' => [ + '/^private_key$/', + '/^public_key$/', + ], + 'integration_dropbox' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_github' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_gitlab' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_google' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_jira' => [ + '/^client_id$/', + '/^client_secret$/', + '/^forced_instance_url$/', + ], + 'integration_onedrive' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_openproject' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_reddit' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_suitecrm' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_twitter' => [ + '/^consumer_key$/', + '/^consumer_secret$/', + '/^followed_user$/', + ], + 'integration_zammad' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'notify_push' => [ + '/^cookie$/', + ], + 'serverinfo' => [ + '/^token$/', + ], + 'spreed' => [ + '/^bridge_bot_password$/', + '/^hosted-signaling-server-(.*)$/', + '/^recording_servers$/', + '/^signaling_servers$/', + '/^signaling_ticket_secret$/', + '/^signaling_token_privkey_(.*)$/', + '/^signaling_token_pubkey_(.*)$/', + '/^sip_bridge_dialin_info$/', + '/^sip_bridge_shared_secret$/', + '/^stun_servers$/', + '/^turn_servers$/', + '/^turn_server_secret$/', + ], + 'support' => [ + '/^last_response$/', + '/^potential_subscription_key$/', + '/^subscription_key$/', + ], + 'theming' => [ + '/^imprintUrl$/', + '/^privacyUrl$/', + '/^slogan$/', + '/^url$/', + ], + 'user_ldap' => [ + '/^(s..)?ldap_agent_password$/', + ], + 'user_saml' => [ + '/^idp-x509cert$/', + ], + ]; + + return $sensitiveValues[$app] ?? []; + } /** * Clear all the cached app config values * New cache will be generated next time a config value is retrieved + * + * @deprecated use {@see clearCache()} */ public function clearCachedConfig(): void { - $this->configLoaded = false; + $this->clearCache(); } } diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index eeb6ae56bc1..25a90e5f0f7 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -334,14 +334,15 @@ class ContactsStore implements IContactsStore { private function contactArrayToEntry(array $contact): Entry { $entry = new Entry(); - if (isset($contact['UID'])) { + if (!empty($contact['UID'])) { $uid = $contact['UID']; $entry->setId($uid); $entry->setProperty('isUser', false); + // overloaded usage so leaving as-is for now if (isset($contact['isLocalSystemBook'])) { $avatar = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $uid, 'size' => 64]); $entry->setProperty('isUser', true); - } elseif (isset($contact['FN'])) { + } elseif (!empty($contact['FN'])) { $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $contact['FN'], 'size' => 64]); } else { $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $uid, 'size' => 64]); @@ -349,23 +350,23 @@ class ContactsStore implements IContactsStore { $entry->setAvatar($avatar); } - if (isset($contact['FN'])) { + if (!empty($contact['FN'])) { $entry->setFullName($contact['FN']); } $avatarPrefix = "VALUE=uri:"; - if (isset($contact['PHOTO']) && str_starts_with($contact['PHOTO'], $avatarPrefix)) { + if (!empty($contact['PHOTO']) && str_starts_with($contact['PHOTO'], $avatarPrefix)) { $entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix))); } - if (isset($contact['EMAIL'])) { + if (!empty($contact['EMAIL'])) { foreach ($contact['EMAIL'] as $email) { $entry->addEMailAddress($email); } } // Provide profile parameters for core/src/OC/contactsmenu/contact.handlebars template - if (isset($contact['UID']) && isset($contact['FN'])) { + if (!empty($contact['UID']) && !empty($contact['FN'])) { $targetUserId = $contact['UID']; $targetUser = $this->userManager->get($targetUserId); if (!empty($targetUser)) { diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index a5f41cc66c9..09c321aedb8 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -41,6 +41,7 @@ use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\ConnectionLost; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -78,6 +79,7 @@ class Connection extends PrimaryReadReplicaConnection { /** @var DbDataCollector|null */ protected $dbDataCollector = null; + private array $lastConnectionCheck = []; protected ?float $transactionActiveSince = null; @@ -127,10 +129,13 @@ class Connection extends PrimaryReadReplicaConnection { public function connect($connectionName = null) { try { if ($this->_conn) { + $this->reconnectIfNeeded(); /** @psalm-suppress InternalMethod */ return parent::connect(); } + $this->lastConnectionCheck[$this->getConnectionName()] = time(); + // Only trigger the event logger for the initial connect call $eventLogger = \OC::$server->get(IEventLogger::class); $eventLogger->start('connect:db', 'db connection opened'); @@ -260,13 +265,16 @@ class Connection extends PrimaryReadReplicaConnection { */ public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile $qcp = null): Result { $tables = $this->getQueriedTables($sql); - if (count(array_intersect($this->tableDirtyWrites, $tables)) === 0 && !$this->isTransactionActive()) { + if ($this->isTransactionActive()) { + // Transacted queries go to the primary. The consistency of the primary guarantees that we can not run + // into a dirty read. + } elseif (count(array_intersect($this->tableDirtyWrites, $tables)) === 0) { // No tables read that could have been written already in the same request and no transaction active // so we can switch back to the replica for reading as long as no writes happen that switch back to the primary // We cannot log here as this would log too early in the server boot process $this->ensureConnectedToReplica(); } else { - // Read to a table that was previously written to + // Read to a table that has been written to previously // While this might not necessarily mean that we did a read after write it is an indication for a code path to check $this->logger->debug('dirty table reads: ' . $sql, ['tables' => $this->tableDirtyWrites, 'reads' => $tables, 'exception' => new \Exception()]); } @@ -316,7 +324,6 @@ class Connection extends PrimaryReadReplicaConnection { public function executeStatement($sql, array $params = [], array $types = []): int { $tables = $this->getQueriedTables($sql); $this->tableDirtyWrites = array_unique(array_merge($this->tableDirtyWrites, $tables)); - $this->logger->debug('dirty table writes: ' . $sql, ['tables' => $this->tableDirtyWrites]); $sql = $this->replaceTablePrefix($sql); $sql = $this->adapter->fixupStatement($sql); $this->queriesExecuted++; @@ -639,16 +646,6 @@ class Connection extends PrimaryReadReplicaConnection { } } - protected function performConnect(?string $connectionName = null): bool { - $before = $this->isConnectedToPrimary(); - $result = parent::performConnect($connectionName); - $after = $this->isConnectedToPrimary(); - if (!$before && $after) { - $this->logger->debug('Switched to primary database', ['exception' => new \Exception()]); - } - return $result; - } - public function beginTransaction() { if (!$this->inTransaction()) { $this->transactionActiveSince = microtime(true); @@ -679,4 +676,26 @@ class Connection extends PrimaryReadReplicaConnection { } return $result; } + + private function reconnectIfNeeded(): void { + if ( + !isset($this->lastConnectionCheck[$this->getConnectionName()]) || + $this->lastConnectionCheck[$this->getConnectionName()] + 30 >= time() || + $this->isTransactionActive() + ) { + return; + } + + try { + $this->_conn->query($this->getDriver()->getDatabasePlatform()->getDummySelectSQL()); + $this->lastConnectionCheck[$this->getConnectionName()] = time(); + } catch (ConnectionLost|\Exception $e) { + $this->logger->warning('Exception during connectivity check, closing and reconnecting', ['exception' => $e]); + $this->close(); + } + } + + private function getConnectionName(): string { + return $this->isConnectedToPrimary() ? 'primary' : 'replica'; + } } diff --git a/lib/private/Dashboard/Manager.php b/lib/private/Dashboard/Manager.php index 5a7e4f3c6dc..25a2df5d9da 100644 --- a/lib/private/Dashboard/Manager.php +++ b/lib/private/Dashboard/Manager.php @@ -115,7 +115,7 @@ class Manager implements IManager { $endTime = microtime(true); $duration = $endTime - $startTime; if ($duration > 1) { - \OC::$server->get(LoggerInterface::class)->error( + \OC::$server->get(LoggerInterface::class)->info( 'Dashboard widget {widget} took {duration} seconds to load.', [ 'widget' => $widget->getId(), diff --git a/lib/private/Preview/Watcher.php b/lib/private/Preview/Watcher.php index 7f4593f9fe3..ad6fed56020 100644 --- a/lib/private/Preview/Watcher.php +++ b/lib/private/Preview/Watcher.php @@ -61,6 +61,9 @@ class Watcher { } try { + if (is_null($node->getId())) { + return; + } $folder = $this->appData->getFolder((string)$node->getId()); $folder->delete(); } catch (NotFoundException $e) { diff --git a/lib/private/RichObjectStrings/Validator.php b/lib/private/RichObjectStrings/Validator.php index 4585cbfc814..d7329c945e9 100644 --- a/lib/private/RichObjectStrings/Validator.php +++ b/lib/private/RichObjectStrings/Validator.php @@ -95,7 +95,7 @@ class Validator implements IValidator { $missingKeys = array_diff($requiredParameters, array_keys($parameter)); if (!empty($missingKeys)) { - throw new InvalidObjectExeption('Object is invalid'); + throw new InvalidObjectExeption('Object is invalid, missing keys:'.json_encode($missingKeys)); } } diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index bba74e96490..f63663fbfe3 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -124,11 +124,9 @@ class SystemConfig { ], ]; - /** @var Config */ - private $config; - - public function __construct(Config $config) { - $this->config = $config; + public function __construct( + private Config $config, + ) { } /** diff --git a/lib/private/TextToImage/Manager.php b/lib/private/TextToImage/Manager.php index 40cab81b5b1..b549f386b6a 100644 --- a/lib/private/TextToImage/Manager.php +++ b/lib/private/TextToImage/Manager.php @@ -43,6 +43,7 @@ use OCP\TextToImage\Exception\TaskFailureException; use OCP\TextToImage\Exception\TaskNotFoundException; use OCP\TextToImage\IManager; use OCP\TextToImage\IProvider; +use OCP\TextToImage\IProviderWithUserId; use OCP\TextToImage\Task; use Psr\Log\LoggerInterface; use RuntimeException; @@ -158,6 +159,9 @@ class Manager implements IManager { } } $this->logger->debug('Calling Text2Image provider\'s generate method'); + if ($provider instanceof IProviderWithUserId) { + $provider->setUserId($task->getUserId()); + } $provider->generate($task->getInput(), $resources); for ($i = 0; $i < $task->getNumberOfImages(); $i++) { if (is_resource($resources[$i])) { diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 02a7a7e9e16..a411326c93f 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -460,7 +460,8 @@ class Session implements IUserSession, Emitter { if ($isTokenPassword) { $dbToken = $this->tokenProvider->getToken($password); $userFromToken = $this->manager->get($dbToken->getUID()); - $isValidEmailLogin = $userFromToken->getEMailAddress() === $user; + $isValidEmailLogin = $userFromToken->getEMailAddress() === $user + && $this->validateTokenLoginName($userFromToken->getEMailAddress(), $dbToken); } else { $users = $this->manager->getByEmail($user); $isValidEmailLogin = (\count($users) === 1 && $this->login($users[0]->getUID(), $password)); @@ -800,18 +801,7 @@ class Session implements IUserSession, Emitter { return false; } - // Check if login names match - if (!is_null($user) && $dbToken->getLoginName() !== $user) { - // TODO: this makes it impossible to use different login names on browser and client - // e.g. login by e-mail 'user@example.com' on browser for generating the token will not - // allow to use the client token with the login name 'user'. - $this->logger->error('App token login name does not match', [ - 'tokenLoginName' => $dbToken->getLoginName(), - 'sessionLoginName' => $user, - 'app' => 'core', - 'user' => $dbToken->getUID(), - ]); - + if (!is_null($user) && !$this->validateTokenLoginName($user, $dbToken)) { return false; } @@ -832,6 +822,27 @@ class Session implements IUserSession, Emitter { } /** + * Check if login names match + */ + private function validateTokenLoginName(?string $loginName, IToken $token): bool { + if ($token->getLoginName() !== $loginName) { + // TODO: this makes it impossible to use different login names on browser and client + // e.g. login by e-mail 'user@example.com' on browser for generating the token will not + // allow to use the client token with the login name 'user'. + $this->logger->error('App token login name does not match', [ + 'tokenLoginName' => $token->getLoginName(), + 'sessionLoginName' => $loginName, + 'app' => 'core', + 'user' => $token->getUID(), + ]); + + return false; + } + + return true; + } + + /** * Tries to login the user with auth token header * * @param IRequest $request @@ -842,13 +853,16 @@ class Session implements IUserSession, Emitter { $authHeader = $request->getHeader('Authorization'); if (str_starts_with($authHeader, 'Bearer ')) { $token = substr($authHeader, 7); - } else { - // No auth header, let's try session id + } elseif ($request->getCookie($this->config->getSystemValueString('instanceid')) !== null) { + // No auth header, let's try session id, but only if this is an existing + // session and the request has a session cookie try { $token = $this->session->getId(); } catch (SessionNotAvailableException $ex) { return false; } + } else { + return false; } if (!$this->loginWithToken($token)) { diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 395c1f44c03..6e4b40b4165 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -63,6 +63,7 @@ use OCP\App\IAppManager; use OCP\App\ManagerEvent; use OCP\Authentication\IAlternativeLogin; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; @@ -730,8 +731,9 @@ class OC_App { static $versions; if (!$versions) { - $appConfig = \OC::$server->getAppConfig(); - $versions = $appConfig->getValues(false, 'installed_version'); + /** @var IAppConfig $appConfig */ + $appConfig = \OCP\Server::get(IAppConfig::class); + $versions = $appConfig->searchValues('installed_version'); } return $versions; } diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php index 4667cf13f0f..d15cfdbea96 100644 --- a/lib/public/App/IAppManager.php +++ b/lib/public/App/IAppManager.php @@ -45,8 +45,8 @@ interface IAppManager { /** * Returns the app information from "appinfo/info.xml". * - * @param string $appId - * @return mixed + * @param string|null $lang + * @return array|null * @since 14.0.0 */ public function getAppInfo(string $appId, bool $path = false, $lang = null); @@ -65,7 +65,7 @@ interface IAppManager { * Check if an app is enabled for user * * @param string $appId - * @param \OCP\IUser $user (optional) if not defined, the currently loggedin user will be used + * @param \OCP\IUser|null $user (optional) if not defined, the currently loggedin user will be used * @return bool * @since 8.0.0 */ diff --git a/lib/public/Exceptions/AppConfigException.php b/lib/public/Exceptions/AppConfigException.php new file mode 100644 index 00000000000..73c91d9f018 --- /dev/null +++ b/lib/public/Exceptions/AppConfigException.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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\Exceptions; + +use Exception; + +/** + * @since 29.0.0 + */ +class AppConfigException extends Exception { +} diff --git a/lib/public/Exceptions/AppConfigIncorrectTypeException.php b/lib/public/Exceptions/AppConfigIncorrectTypeException.php new file mode 100644 index 00000000000..1284e4b193e --- /dev/null +++ b/lib/public/Exceptions/AppConfigIncorrectTypeException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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\Exceptions; + +/** + * @since 29.0.0 + */ +class AppConfigIncorrectTypeException extends AppConfigException { +} diff --git a/lib/public/Exceptions/AppConfigTypeConflictException.php b/lib/public/Exceptions/AppConfigTypeConflictException.php new file mode 100644 index 00000000000..599fed0cb3b --- /dev/null +++ b/lib/public/Exceptions/AppConfigTypeConflictException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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\Exceptions; + +/** + * @since 29.0.0 + */ +class AppConfigTypeConflictException extends AppConfigException { +} diff --git a/lib/public/Exceptions/AppConfigUnknownKeyException.php b/lib/public/Exceptions/AppConfigUnknownKeyException.php new file mode 100644 index 00000000000..e2b9d7fd3dc --- /dev/null +++ b/lib/public/Exceptions/AppConfigUnknownKeyException.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); +/** + * @copyright 2023 Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @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\Exceptions; + +/** + * @since 29.0.0 + */ +class AppConfigUnknownKeyException extends AppConfigException { +} diff --git a/lib/public/IAppConfig.php b/lib/public/IAppConfig.php index cf387a8a44c..9bdeb14b295 100644 --- a/lib/public/IAppConfig.php +++ b/lib/public/IAppConfig.php @@ -1,9 +1,12 @@ <?php + +declare(strict_types=1); /** * @copyright Copyright (c) 2016, ownCloud, Inc. * * @author Bart Visscher <bartv@thisnet.nl> * @author Joas Schilling <coding@schilljs.com> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Robin McCorkell <robin@mccorkell.me.uk> @@ -26,28 +29,486 @@ */ namespace OCP; +use OCP\Exceptions\AppConfigUnknownKeyException; + /** * This class provides an easy way for apps to store config values in the * database. + * + * **Note:** since 29.0.0, it supports **lazy loading** + * + * ### What is lazy loading ? + * In order to avoid loading useless config values into memory for each request, + * only non-lazy values are now loaded. + * + * Once a value that is lazy is requested, all lazy values will be loaded. + * + * Similarly, some methods from this class are marked with a warning about ignoring + * lazy loading. Use them wisely and only on parts of the code that are called + * during specific requests or actions to avoid loading the lazy values all the time. + * * @since 7.0.0 + * @since 29.0.0 - Supporting types and lazy loading */ interface IAppConfig { + /** @since 29.0.0 */ + public const VALUE_SENSITIVE = 1; + /** @since 29.0.0 */ + public const VALUE_MIXED = 2; + /** @since 29.0.0 */ + public const VALUE_STRING = 4; + /** @since 29.0.0 */ + public const VALUE_INT = 8; + /** @since 29.0.0 */ + public const VALUE_FLOAT = 16; + /** @since 29.0.0 */ + public const VALUE_BOOL = 32; + /** @since 29.0.0 */ + public const VALUE_ARRAY = 64; + /** - * check if a key is set in the appconfig - * @param string $app - * @param string $key - * @return bool + * Get list of all apps that have at least one config value stored in database + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @return string[] list of app ids * @since 7.0.0 */ - public function hasKey($app, $key); + public function getApps(): array; + + /** + * Returns all keys stored in database, related to an app. + * Please note that the values are not returned. + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * + * @return string[] list of stored config keys + * @since 29.0.0 + */ + public function getKeys(string $app): array; + + /** + * Check if a key exists in the list of stored config values. + * + * @param string $app id of the app + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * + * @return bool TRUE if key exists + * @since 29.0.0 Added the $lazy argument + * @since 7.0.0 + */ + public function hasKey(string $app, string $key, ?bool $lazy = false): bool; + + /** + * best way to see if a value is set as sensitive (not displayed in report) + * + * @param string $app id of the app + * @param string $key config key + * @param bool|null $lazy search within lazy loaded config + * + * @return bool TRUE if value is sensitive + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + */ + public function isSensitive(string $app, string $key, ?bool $lazy = false): bool; + + /** + * Returns if the config key stored in database is lazy loaded + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config key + * + * @return bool TRUE if config is lazy loaded + * @throws AppConfigUnknownKeyException if config key is not known + * @see IAppConfig for details about lazy loading + * @since 29.0.0 + */ + public function isLazy(string $app, string $key): bool; + + /** + * List all config values from an app with config key starting with $key. + * Returns an array with config key as key, stored value as value. + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config keys prefix to search, can be empty. + * @param bool $filtered filter sensitive config values + * + * @return array<string, string> [configKey => configValue] + * @since 29.0.0 + */ + public function getAllValues(string $app, string $key = '', bool $filtered = false): array; + + /** + * List all apps storing a specific config key and its stored value. + * Returns an array with appId as key, stored value as value. + * + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * + * @return array<string, string> [appId => configValue] + * @since 29.0.0 + */ + public function searchValues(string $key, bool $lazy = false): array; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * + * @return string stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueInt() + * @see getValueFloat() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueString(string $app, string $key, string $default = '', bool $lazy = false): string; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param int $default default value + * @param bool $lazy search within lazy loaded config + * + * @return int stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueFloat() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueInt(string $app, string $key, int $default = 0, bool $lazy = false): int; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param float $default default value + * @param bool $lazy search within lazy loaded config + * + * @return float stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param bool $default default value + * @param bool $lazy search within lazy loaded config + * + * @return bool stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueFloat() + * @see getValueArray() + */ + public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool; + + /** + * Get config value assigned to a config key. + * If config key is not found in database, default value is returned. + * If config key is set as lazy loaded, the $lazy argument needs to be set to TRUE. + * + * @param string $app id of the app + * @param string $key config key + * @param array $default default value + * @param bool $lazy search within lazy loaded config + * + * @return array stored config value or $default if not set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueFloat() + * @see getValueBool() + */ + public function getValueArray(string $app, string $key, array $default = [], bool $lazy = false): array; + + /** + * returns the type of config value + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config key + * + * @return int + * @throws AppConfigUnknownKeyException + * @since 29.0.0 + * @see VALUE_STRING + * @see VALUE_INT + * @see VALUE_FLOAT + * @see VALUE_BOOL + * @see VALUE_ARRAY + */ + public function getValueType(string $app, string $key): int; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueInt() + * @see setValueFloat() + * @see setValueBool() + * @see setValueArray() + */ + public function setValueString(string $app, string $key, string $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database + * + * When handling huge value around and/or above 2,147,483,647, a debug log will be generated + * on 64bits system, as php int type reach its limit (and throw an exception) on 32bits when using huge numbers. + * + * When using huge numbers, it is advised to use {@see \OCP\Util::numericToNumber()} and {@see setValueString()} + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param int $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueFloat() + * @see setValueBool() + * @see setValueArray() + */ + public function setValueInt(string $app, string $key, int $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database. + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param float $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueBool() + * @see setValueArray() + */ + public function setValueFloat(string $app, string $key, float $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param bool $value config value + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueFloat() + * @see setValueArray() + */ + public function setValueBool(string $app, string $key, bool $value, bool $lazy = false): bool; + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value, the database is not updated. + * If config key is not supposed to be read during the boot of the cloud, it is advised to set it as lazy loaded. + * + * If config value was previously stored as sensitive or lazy loaded, status cannot be altered without using {@see deleteKey()} first + * + * @param string $app id of the app + * @param string $key config key + * @param array $value config value + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueFloat() + * @see setValueBool() + */ + public function setValueArray(string $app, string $key, array $value, bool $lazy = false, bool $sensitive = false): bool; + + /** + * switch sensitive status of a config value + * + * **WARNING:** ignore lazy filtering, all config values are loaded from database + * + * @param string $app id of the app + * @param string $key config key + * @param bool $sensitive TRUE to set as sensitive, FALSE to unset + * + * @return bool TRUE if database update were necessary + * @since 29.0.0 + */ + public function updateSensitive(string $app, string $key, bool $sensitive): bool; + + /** + * switch lazy loading status of a config value + * + * @param string $app id of the app + * @param string $key config key + * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset + * + * @return bool TRUE if database update was necessary + * @since 29.0.0 + */ + public function updateLazy(string $app, string $key, bool $lazy): bool; + + /** + * returns an array contains details about a config value + * + * ``` + * [ + * "app" => "myapp", + * "key" => "mykey", + * "value" => "its_value", + * "lazy" => false, + * "type" => 4, + * "typeString" => "string", + * 'sensitive' => true + * ] + * ``` + * + * @param string $app id of the app + * @param string $key config key + * + * @return array + * @throws AppConfigUnknownKeyException if config key is not known in database + * @since 29.0.0 + */ + public function getDetails(string $app, string $key): array; + + /** + * Convert string like 'string', 'integer', 'float', 'bool' or 'array' to + * to bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT}, + * {@see VALUE_BOOL} and {@see VALUE_ARRAY} + * + * @param string $type + * + * @return int + * @since 29.0.0 + */ + public function convertTypeToInt(string $type): int; + + /** + * Convert bitflag {@see VALUE_STRING}, {@see VALUE_INT}, {@see VALUE_FLOAT}, + * {@see VALUE_BOOL} and {@see VALUE_ARRAY} to human-readable string + * + * @param int $type + * + * @return string + * @since 29.0.0 + */ + public function convertTypeToString(int $type): string; + + /** + * Delete single config key from database. + * + * @param string $app id of the app + * @param string $key config key + * @since 29.0.0 + */ + public function deleteKey(string $app, string $key): void; + + /** + * delete all config keys linked to an app + * + * @param string $app id of the app + * @since 29.0.0 + */ + public function deleteApp(string $app): void; + + /** + * Clear the cache. + * + * The cache will be rebuilt only the next time a config value is requested. + * + * @param bool $reload set to TRUE to refill cache instantly after clearing it + * @since 29.0.0 + */ + public function clearCache(bool $reload = false): void; /** * get multiply values, either the app or key can be used as wildcard by setting it to false * * @param string|false $key * @param string|false $app + * * @return array|false * @since 7.0.0 + * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()} */ public function getValues($app, $key); @@ -55,18 +516,10 @@ interface IAppConfig { * get all values of the app or and filters out sensitive data * * @param string $app + * * @return array * @since 12.0.0 + * @deprecated 29.0.0 Use {@see getAllValues()} or {@see searchValues()} */ public function getFilteredValues($app); - - /** - * Get all apps using the config - * @return string[] an array of app ids - * - * This function returns a list of all apps that have at least one - * entry in the appconfig table. - * @since 7.0.0 - */ - public function getApps(); } diff --git a/lib/public/IConfig.php b/lib/public/IConfig.php index 0e7a7523218..706e4776221 100644 --- a/lib/public/IConfig.php +++ b/lib/public/IConfig.php @@ -126,6 +126,7 @@ interface IConfig { * @param string $appName the appName that we stored the value under * @return string[] the keys stored for the app * @since 8.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppKeys($appName); @@ -137,6 +138,7 @@ interface IConfig { * @param string $value the value that should be stored * @return void * @since 6.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function setAppValue($appName, $key, $value); @@ -146,8 +148,10 @@ interface IConfig { * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set + * * @return string the saved value * @since 6.0.0 - parameter $default was added in 7.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppValue($appName, $key, $default = ''); @@ -157,6 +161,7 @@ interface IConfig { * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @since 8.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValue($appName, $key); @@ -165,6 +170,7 @@ interface IConfig { * * @param string $appName the appName the configs are stored under * @since 8.0.0 + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValues($appName); diff --git a/lib/public/TextToImage/IProviderWithUserId.php b/lib/public/TextToImage/IProviderWithUserId.php new file mode 100644 index 00000000000..8afb0e56fbb --- /dev/null +++ b/lib/public/TextToImage/IProviderWithUserId.php @@ -0,0 +1,15 @@ +<?php + +declare(strict_types=1); + +namespace OCP\TextToImage; + +/** + * @since 29.0.0 + */ +interface IProviderWithUserId extends IProvider { + /** + * @since 29.0.0 + */ + public function setUserId(?string $userId): void; +} |