diff options
Diffstat (limited to 'lib')
30 files changed, 362 insertions, 110 deletions
diff --git a/lib/composer/composer/InstalledVersions.php b/lib/composer/composer/InstalledVersions.php index 6d29bff66aa..2052022fd8e 100644 --- a/lib/composer/composer/InstalledVersions.php +++ b/lib/composer/composer/InstalledVersions.php @@ -27,6 +27,12 @@ use Composer\Semver\VersionParser; class InstalledVersions { /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null */ @@ -323,6 +329,18 @@ class InstalledVersions } /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** * @return array[] * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> */ @@ -336,7 +354,7 @@ class InstalledVersions $copiedLocalDir = false; if (self::$canGetVendors) { - $selfDir = strtr(__DIR__, '\\', '/'); + $selfDir = self::getSelfDir(); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 3f2dbe5edf9..fda8798fe43 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -696,6 +696,7 @@ return array( 'OCP\\Notification\\IManager' => $baseDir . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => $baseDir . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => $baseDir . '/lib/public/Notification/INotifier.php', + 'OCP\\Notification\\IPreloadableNotifier' => $baseDir . '/lib/public/Notification/IPreloadableNotifier.php', 'OCP\\Notification\\IncompleteNotificationException' => $baseDir . '/lib/public/Notification/IncompleteNotificationException.php', 'OCP\\Notification\\IncompleteParsedNotificationException' => $baseDir . '/lib/public/Notification/IncompleteParsedNotificationException.php', 'OCP\\Notification\\InvalidValueException' => $baseDir . '/lib/public/Notification/InvalidValueException.php', @@ -1510,6 +1511,7 @@ return array( 'OC\\Core\\Migrations\\Version31000Date20240814184402' => $baseDir . '/core/Migrations/Version31000Date20240814184402.php', 'OC\\Core\\Migrations\\Version31000Date20250213102442' => $baseDir . '/core/Migrations/Version31000Date20250213102442.php', 'OC\\Core\\Migrations\\Version32000Date20250620081925' => $baseDir . '/core/Migrations/Version32000Date20250620081925.php', + 'OC\\Core\\Migrations\\Version32000Date20250731062008' => $baseDir . '/core/Migrations/Version32000Date20250731062008.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 233b667add9..69e3c41284b 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -737,6 +737,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Notification\\IManager' => __DIR__ . '/../../..' . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => __DIR__ . '/../../..' . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/INotifier.php', + 'OCP\\Notification\\IPreloadableNotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/IPreloadableNotifier.php', 'OCP\\Notification\\IncompleteNotificationException' => __DIR__ . '/../../..' . '/lib/public/Notification/IncompleteNotificationException.php', 'OCP\\Notification\\IncompleteParsedNotificationException' => __DIR__ . '/../../..' . '/lib/public/Notification/IncompleteParsedNotificationException.php', 'OCP\\Notification\\InvalidValueException' => __DIR__ . '/../../..' . '/lib/public/Notification/InvalidValueException.php', @@ -1551,6 +1552,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version31000Date20240814184402' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240814184402.php', 'OC\\Core\\Migrations\\Version31000Date20250213102442' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20250213102442.php', 'OC\\Core\\Migrations\\Version32000Date20250620081925' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250620081925.php', + 'OC\\Core\\Migrations\\Version32000Date20250731062008' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250731062008.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/composer/composer/installed.php b/lib/composer/composer/installed.php index 1cfe4bf1d74..cd89ef10785 100644 --- a/lib/composer/composer/installed.php +++ b/lib/composer/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'b7422ba97b7b42a9955a52031a32457ca521d740', + 'reference' => '3fce359f4c606737b21b1b4213efd5bc5536e867', 'type' => 'library', 'install_path' => __DIR__ . '/../../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => 'b7422ba97b7b42a9955a52031a32457ca521d740', + 'reference' => '3fce359f4c606737b21b1b4213efd5bc5536e867', 'type' => 'library', 'install_path' => __DIR__ . '/../../../', 'aliases' => array(), diff --git a/lib/l10n/be.js b/lib/l10n/be.js index eb38c0ed05e..4d73f079394 100644 --- a/lib/l10n/be.js +++ b/lib/l10n/be.js @@ -1,11 +1,39 @@ OC.L10N.register( "lib", { + "%1$s and %2$s" : "%1$s і %2$s", + "%1$s, %2$s and %3$s" : "%1$s, %2$s і %3$s", + "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s і %4$s", + "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s і %5$s", "Authentication" : "Аўтэнтыфікацыя", "Unknown filetype" : "Невядомы тып файла", + "Avatar image is not square" : "Відарыс аватара не квадратны", "Files" : "Файлы", + "_%nh_::_%nh_" : ["%n г","%n г","%n г","%n г"], + "_%nm_::_%nm_" : ["%n хв","%n хв","%n хв","%n хв"], + "Local time: %s" : "Мясцовы час: %s", + "today" : "сёння", + "tomorrow" : "заўтра", + "yesterday" : "учора", + "_in %n day_::_in %n days_" : ["праз %n дзень","праз %n дні","праз %n дзён","праз %n дзён"], + "_%n day ago_::_%n days ago_" : ["%n дзень таму","%n дні таму","%n дзён таму","%n дзён таму"], + "next month" : "у наступным месяцы", + "last month" : "у мінулым месяцы", + "_in %n month_::_in %n months_" : ["праз %n месяц","праз %n месяцы","праз %n месяцаў","праз %n месяцаў"], + "_%n month ago_::_%n months ago_" : ["%n месяц таму","%n месяцы таму","%n месяцаў таму","%n месяцаў таму"], + "next year" : "у наступным годзе", + "last year" : "у мінулым годзе", + "_in %n year_::_in %n years_" : ["праз %n год","праз %n гады","праз %n гадоў","праз %n гадоў"], "_%n year ago_::_%n years ago_" : ["%n год таму","%n гады таму","%n гадоў таму","%n гадоў таму"], + "_in %n hour_::_in %n hours_" : ["праз %n гадзіну","праз %n гадзіны","праз %n гадзін","праз %n гадзін"], + "_%n hour ago_::_%n hours ago_" : ["%n гадзіну таму","%n гадзіны таму","%n гадзін таму","%n гадзін таму"], + "_in %n minute_::_in %n minutes_" : ["праз %n хвіліну","праз %n хвіліны","праз %n хвілін","праз %n хвілін"], + "_%n minute ago_::_%n minutes ago_" : ["%n хвіліну таму","%n хвіліны таму","%n хвілін таму","%n хвілін таму"], + "in a few seconds" : "праз некалькі секунд", "seconds ago" : "с таму", + "Empty file" : "Пусты файл", + "Could not convert file" : "Не атрымалася канвертаваць файл", + "%1$s (renamed)" : "%1$s (перайменаваны)", "Templates" : "Шаблоны", "__language_name__" : "Беларуская", "Apps" : "Праграмы", @@ -15,6 +43,7 @@ OC.L10N.register( "Twitter" : "Twitter", "Role" : "Роля", "Pronouns" : "Займеннікі", + "Could not find category \"%s\"" : "Не ўдалося знайсці катэгорыю \"%s\"", "Sunday" : "Нядзеля", "Monday" : "Панядзелак", "Tuesday" : "Аўторак", @@ -41,7 +70,20 @@ OC.L10N.register( "October" : "Кастрычнік", "November" : "Лістапад", "December" : "Снежань", + "Account disabled" : "Уліковы запіс адключаны", + "Application is not enabled" : "Праграма не ўключана", + "Authentication error" : "Памылка аўтэнтыфікацыі", + "Images" : "Відарысы", + "Question" : "Пытанне", + "Number of images" : "Колькасць відарысаў", + "Chat" : "Чат", + "Chat with the assistant" : "Чат з памочнікам", "Text" : "Тэкст", - "Translate" : "Перакласці" + "Translate" : "Перакласці", + "Result" : "Вынік", + "The translated text" : "Перакладзены тэкст", + "Organisation" : "Арганізацыя", + "Cannot download file" : "Немагчыма спампаваць файл", + "Login is too long" : "Лагін занадта доўгі" }, "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"); diff --git a/lib/l10n/be.json b/lib/l10n/be.json index d944caed749..9a410c17f7d 100644 --- a/lib/l10n/be.json +++ b/lib/l10n/be.json @@ -1,9 +1,37 @@ { "translations": { + "%1$s and %2$s" : "%1$s і %2$s", + "%1$s, %2$s and %3$s" : "%1$s, %2$s і %3$s", + "%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s і %4$s", + "%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s і %5$s", "Authentication" : "Аўтэнтыфікацыя", "Unknown filetype" : "Невядомы тып файла", + "Avatar image is not square" : "Відарыс аватара не квадратны", "Files" : "Файлы", + "_%nh_::_%nh_" : ["%n г","%n г","%n г","%n г"], + "_%nm_::_%nm_" : ["%n хв","%n хв","%n хв","%n хв"], + "Local time: %s" : "Мясцовы час: %s", + "today" : "сёння", + "tomorrow" : "заўтра", + "yesterday" : "учора", + "_in %n day_::_in %n days_" : ["праз %n дзень","праз %n дні","праз %n дзён","праз %n дзён"], + "_%n day ago_::_%n days ago_" : ["%n дзень таму","%n дні таму","%n дзён таму","%n дзён таму"], + "next month" : "у наступным месяцы", + "last month" : "у мінулым месяцы", + "_in %n month_::_in %n months_" : ["праз %n месяц","праз %n месяцы","праз %n месяцаў","праз %n месяцаў"], + "_%n month ago_::_%n months ago_" : ["%n месяц таму","%n месяцы таму","%n месяцаў таму","%n месяцаў таму"], + "next year" : "у наступным годзе", + "last year" : "у мінулым годзе", + "_in %n year_::_in %n years_" : ["праз %n год","праз %n гады","праз %n гадоў","праз %n гадоў"], "_%n year ago_::_%n years ago_" : ["%n год таму","%n гады таму","%n гадоў таму","%n гадоў таму"], + "_in %n hour_::_in %n hours_" : ["праз %n гадзіну","праз %n гадзіны","праз %n гадзін","праз %n гадзін"], + "_%n hour ago_::_%n hours ago_" : ["%n гадзіну таму","%n гадзіны таму","%n гадзін таму","%n гадзін таму"], + "_in %n minute_::_in %n minutes_" : ["праз %n хвіліну","праз %n хвіліны","праз %n хвілін","праз %n хвілін"], + "_%n minute ago_::_%n minutes ago_" : ["%n хвіліну таму","%n хвіліны таму","%n хвілін таму","%n хвілін таму"], + "in a few seconds" : "праз некалькі секунд", "seconds ago" : "с таму", + "Empty file" : "Пусты файл", + "Could not convert file" : "Не атрымалася канвертаваць файл", + "%1$s (renamed)" : "%1$s (перайменаваны)", "Templates" : "Шаблоны", "__language_name__" : "Беларуская", "Apps" : "Праграмы", @@ -13,6 +41,7 @@ "Twitter" : "Twitter", "Role" : "Роля", "Pronouns" : "Займеннікі", + "Could not find category \"%s\"" : "Не ўдалося знайсці катэгорыю \"%s\"", "Sunday" : "Нядзеля", "Monday" : "Панядзелак", "Tuesday" : "Аўторак", @@ -39,7 +68,20 @@ "October" : "Кастрычнік", "November" : "Лістапад", "December" : "Снежань", + "Account disabled" : "Уліковы запіс адключаны", + "Application is not enabled" : "Праграма не ўключана", + "Authentication error" : "Памылка аўтэнтыфікацыі", + "Images" : "Відарысы", + "Question" : "Пытанне", + "Number of images" : "Колькасць відарысаў", + "Chat" : "Чат", + "Chat with the assistant" : "Чат з памочнікам", "Text" : "Тэкст", - "Translate" : "Перакласці" + "Translate" : "Перакласці", + "Result" : "Вынік", + "The translated text" : "Перакладзены тэкст", + "Organisation" : "Арганізацыя", + "Cannot download file" : "Немагчыма спампаваць файл", + "Login is too long" : "Лагін занадта доўгі" },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);" }
\ No newline at end of file diff --git a/lib/l10n/fr.js b/lib/l10n/fr.js index edfe25a6362..3356ec5f0f2 100644 --- a/lib/l10n/fr.js +++ b/lib/l10n/fr.js @@ -371,10 +371,10 @@ OC.L10N.register( "Output images" : "Images de sortie", "The generated images" : "Les images générées", "Generate speech" : "Générer une synthèse vocale", - "Generate speech from a transcript" : "Générer la vocalisation à partir d'une transcription", - "Write transcript that you want the assistant to generate speech from" : "Écrire la transcription à partir de laquelle vous voulez générer la vocalisation", - "Output speech" : "Sortie de la vocalisation", - "The generated speech" : "La vocalisation générée", + "Generate speech from a transcript" : "Générer synthèse vocale à partir d'une transcription", + "Write transcript that you want the assistant to generate speech from" : "Écrire la transcription à partir de laquelle vous voulez générer la synthèse vocale", + "Output speech" : "Synthèse vocale", + "The generated speech" : "La synthèse vocale générée", "Free text to text prompt" : "Texte libre à texte libre", "Runs an arbitrary prompt through a language model that returns a reply" : "Exécute une commande arbitraire à l'aide d'un modèle linguistique qui génère une réponse", "Describe a task that you want the assistant to do or ask a question" : "Décrivez une tâche que vous voulez que l'assistant effectue ou posez une question", diff --git a/lib/l10n/fr.json b/lib/l10n/fr.json index 6f1edd70da2..4d7ac492a36 100644 --- a/lib/l10n/fr.json +++ b/lib/l10n/fr.json @@ -369,10 +369,10 @@ "Output images" : "Images de sortie", "The generated images" : "Les images générées", "Generate speech" : "Générer une synthèse vocale", - "Generate speech from a transcript" : "Générer la vocalisation à partir d'une transcription", - "Write transcript that you want the assistant to generate speech from" : "Écrire la transcription à partir de laquelle vous voulez générer la vocalisation", - "Output speech" : "Sortie de la vocalisation", - "The generated speech" : "La vocalisation générée", + "Generate speech from a transcript" : "Générer synthèse vocale à partir d'une transcription", + "Write transcript that you want the assistant to generate speech from" : "Écrire la transcription à partir de laquelle vous voulez générer la synthèse vocale", + "Output speech" : "Synthèse vocale", + "The generated speech" : "La synthèse vocale générée", "Free text to text prompt" : "Texte libre à texte libre", "Runs an arbitrary prompt through a language model that returns a reply" : "Exécute une commande arbitraire à l'aide d'un modèle linguistique qui génère une réponse", "Describe a task that you want the assistant to do or ask a question" : "Décrivez une tâche que vous voulez que l'assistant effectue ou posez une question", diff --git a/lib/l10n/mk.js b/lib/l10n/mk.js index 8b49f0914e5..102926f9e1d 100644 --- a/lib/l10n/mk.js +++ b/lib/l10n/mk.js @@ -77,6 +77,9 @@ OC.L10N.register( "Empty file" : "Празна датотека", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул со ИД: %s не постои. Овозможете го во параметрите на апликациите или контактирајте администратор.", "Dot files are not allowed" : "Датотеки само со точки не се дозволени", + "%1$s (renamed)" : "%1$s (преименувано)", + "renamed file" : "преименувана датотека", + "Filenames must not end with \"%1$s\"." : "Името неможе да завршува со \"%1$s\".", "File already exists" : "Датотека веќе постои", "Invalid path" : "Невалидна патека", "Failed to create file from template" : "Неуспешно креирање на датотека од шаблон", diff --git a/lib/l10n/mk.json b/lib/l10n/mk.json index db83bccd774..d9de1976caf 100644 --- a/lib/l10n/mk.json +++ b/lib/l10n/mk.json @@ -75,6 +75,9 @@ "Empty file" : "Празна датотека", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул со ИД: %s не постои. Овозможете го во параметрите на апликациите или контактирајте администратор.", "Dot files are not allowed" : "Датотеки само со точки не се дозволени", + "%1$s (renamed)" : "%1$s (преименувано)", + "renamed file" : "преименувана датотека", + "Filenames must not end with \"%1$s\"." : "Името неможе да завршува со \"%1$s\".", "File already exists" : "Датотека веќе постои", "Invalid path" : "Невалидна патека", "Failed to create file from template" : "Неуспешно креирање на датотека од шаблон", diff --git a/lib/l10n/sw.js b/lib/l10n/sw.js index 9e10b43b403..0eca41da17e 100644 --- a/lib/l10n/sw.js +++ b/lib/l10n/sw.js @@ -45,9 +45,27 @@ OC.L10N.register( "Storage is temporarily not available" : "Uhifadhi haupo kwa muda", "Images" : "Picha", "Question" : "Swali", + "Voice chat with the assistant" : "Voice chat with the assistant", + "System prompt" : "System prompt", + "Define rules and assumptions that the assistant should follow during the conversation." : "Define rules and assumptions that the assistant should follow during the conversation.", + "Chat voice message" : "Chat voice message", + "Chat history" : "Chat history", + "Input transcript" : "Input transcript", + "Transcription of the audio input" : "Transcription of the audio input", + "Response voice message" : "Response voice message", + "The generated voice response as part of the conversation" : "The generated voice response as part of the conversation", + "Output transcript" : "Output transcript", + "Transcription of the audio output" : "Transcription of the audio output", + "Transcribe audio" : "Transcribe audio", + "Audio input" : "Audio input", "Confirmation" : "Uthibitisho", + "Prompt" : "Prompt", + "Describe a task that you want the assistant to do or ask a question" : "Describe a task that you want the assistant to do or ask a question", + "The history of chat messages before the current message, starting with a message by the user" : "The history of chat messages before the current message, starting with a message by the user", "Text" : "Maandishi", + "Summarize" : "Summarize", "Summary" : "Muhtasari", - "Translate" : "Tafsiri" + "Translate" : "Tafsiri", + "Result" : "Result" }, "nplurals=2; plural=(n != 1);"); diff --git a/lib/l10n/sw.json b/lib/l10n/sw.json index a645af4e6a8..5759a8f07f4 100644 --- a/lib/l10n/sw.json +++ b/lib/l10n/sw.json @@ -43,9 +43,27 @@ "Storage is temporarily not available" : "Uhifadhi haupo kwa muda", "Images" : "Picha", "Question" : "Swali", + "Voice chat with the assistant" : "Voice chat with the assistant", + "System prompt" : "System prompt", + "Define rules and assumptions that the assistant should follow during the conversation." : "Define rules and assumptions that the assistant should follow during the conversation.", + "Chat voice message" : "Chat voice message", + "Chat history" : "Chat history", + "Input transcript" : "Input transcript", + "Transcription of the audio input" : "Transcription of the audio input", + "Response voice message" : "Response voice message", + "The generated voice response as part of the conversation" : "The generated voice response as part of the conversation", + "Output transcript" : "Output transcript", + "Transcription of the audio output" : "Transcription of the audio output", + "Transcribe audio" : "Transcribe audio", + "Audio input" : "Audio input", "Confirmation" : "Uthibitisho", + "Prompt" : "Prompt", + "Describe a task that you want the assistant to do or ask a question" : "Describe a task that you want the assistant to do or ask a question", + "The history of chat messages before the current message, starting with a message by the user" : "The history of chat messages before the current message, starting with a message by the user", "Text" : "Maandishi", + "Summarize" : "Summarize", "Summary" : "Muhtasari", - "Translate" : "Tafsiri" + "Translate" : "Tafsiri", + "Result" : "Result" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php index e719ea19f90..77135986d5f 100644 --- a/lib/private/AppFramework/App.php +++ b/lib/private/AppFramework/App.php @@ -50,19 +50,8 @@ class App { if (isset($appInfo['namespace'])) { self::$nameSpaceCache[$appId] = trim($appInfo['namespace']); } else { - if ($appId !== 'spreed') { - // if the tag is not found, fall back to uppercasing the first letter - self::$nameSpaceCache[$appId] = ucfirst($appId); - } else { - // For the Talk app (appid spreed) the above fallback doesn't work. - // This leads to a problem when trying to install it freshly, - // because the apps namespace is already registered before the - // app is downloaded from the appstore, because of the hackish - // global route index.php/call/{token} which is registered via - // the core/routes.php so it does not have the app namespace. - // @ref https://github.com/nextcloud/server/pull/19433 - self::$nameSpaceCache[$appId] = 'Talk'; - } + // if the tag is not found, fall back to uppercasing the first letter + self::$nameSpaceCache[$appId] = ucfirst($appId); } return $topNamespace . self::$nameSpaceCache[$appId]; diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php index e662cb8679a..7cc7467675c 100644 --- a/lib/private/AppFramework/Http/Request.php +++ b/lib/private/AppFramework/Http/Request.php @@ -14,6 +14,7 @@ use OC\Security\TrustedDomainHelper; use OCP\IConfig; use OCP\IRequest; use OCP\IRequestId; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\IpUtils; /** @@ -627,36 +628,46 @@ class Request implements \ArrayAccess, \Countable, IRequest { /** * Returns the server protocol. It respects one or more reverse proxies servers - * and load balancers + * and load balancers. Precedence: + * 1. `overwriteprotocol` config value + * 2. `X-Forwarded-Proto` header value + * 3. $_SERVER['HTTPS'] value + * If an invalid protocol is provided, defaults to http, continues, but logs as an error. + * * @return string Server protocol (http or https) */ public function getServerProtocol(): string { - if ($this->config->getSystemValueString('overwriteprotocol') !== '' - && $this->isOverwriteCondition()) { - return $this->config->getSystemValueString('overwriteprotocol'); - } + $proto = 'http'; - if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) { + if ($this->config->getSystemValueString('overwriteprotocol') !== '' + && $this->isOverwriteCondition() + ) { + $proto = strtolower($this->config->getSystemValueString('overwriteprotocol')); + } elseif ($this->fromTrustedProxy() + && isset($this->server['HTTP_X_FORWARDED_PROTO']) + ) { if (str_contains($this->server['HTTP_X_FORWARDED_PROTO'], ',')) { $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']); $proto = strtolower(trim($parts[0])); } else { $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']); } - - // Verify that the protocol is always HTTP or HTTPS - // default to http if an invalid value is provided - return $proto === 'https' ? 'https' : 'http'; + } elseif (!empty($this->server['HTTPS']) + && $this->server['HTTPS'] !== 'off' + ) { + $proto = 'https'; } - if (isset($this->server['HTTPS']) - && $this->server['HTTPS'] !== null - && $this->server['HTTPS'] !== 'off' - && $this->server['HTTPS'] !== '') { - return 'https'; + if ($proto !== 'https' && $proto !== 'http') { + // log unrecognized value so admin has a chance to fix it + \OCP\Server::get(LoggerInterface::class)->critical( + 'Server protocol is malformed [falling back to http] (check overwriteprotocol and/or X-Forwarded-Proto to remedy): ' . $proto, + ['app' => 'core'] + ); } - return 'http'; + // default to http if provided an invalid value + return $proto === 'https' ? 'https' : 'http'; } /** @@ -743,11 +754,11 @@ class Request implements \ArrayAccess, \Countable, IRequest { } /** - * Get PathInfo from request + * Get PathInfo from request (rawurldecoded) * @throws \Exception * @return string|false Path info or false when not found */ - public function getPathInfo() { + public function getPathInfo(): string|false { $pathInfo = $this->getRawPathInfo(); return \Sabre\HTTP\decodePath($pathInfo); } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php index 8fae6275916..47a8eaa6fd0 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php @@ -81,12 +81,12 @@ class OCIFunctionBuilder extends FunctionBuilder { public function octetLength($field, $alias = ''): IQueryFunction { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; $quotedName = $this->helper->quoteColumnName($field); - return new QueryFunction('LENGTHB(' . $quotedName . ')' . $alias); + return new QueryFunction('COALESCE(LENGTHB(' . $quotedName . '), 0)' . $alias); } public function charLength($field, $alias = ''): IQueryFunction { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; $quotedName = $this->helper->quoteColumnName($field); - return new QueryFunction('LENGTH(' . $quotedName . ')' . $alias); + return new QueryFunction('COALESCE(LENGTH(' . $quotedName . '), 0)' . $alias); } } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 8b224c28dfe..1d1ccd29bf7 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -161,7 +161,7 @@ class QueryBuilder implements IQueryBuilder { try { $params = []; foreach ($this->getParameters() as $placeholder => $value) { - if ($value instanceof \DateTime) { + if ($value instanceof \DateTimeInterface) { $params[] = $placeholder . ' => DateTime:\'' . $value->format('c') . '\''; } elseif (is_array($value)) { $params[] = $placeholder . ' => (\'' . implode('\', \'', $value) . '\')'; diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index 10ee6aec167..9ab11f8a3df 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -475,6 +475,9 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil 'original-storage' => $this->getId(), 'original-path' => $path, ]; + if ($size) { + $metadata['size'] = $size; + } $stat['mimetype'] = $mimetype; $stat['etag'] = $this->getETag($path); @@ -496,32 +499,27 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil $urn = $this->getURN($fileId); try { //upload to object storage - if ($size === null) { - $countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) { + + $totalWritten = 0; + $countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, $size, $exists, &$totalWritten) { + if (is_null($size) && !$exists) { $this->getCache()->update($fileId, [ 'size' => $writtenSize, ]); - $size = $writtenSize; - }); - if ($this->objectStore instanceof IObjectStoreMetaData) { - $this->objectStore->writeObjectWithMetaData($urn, $countStream, $metadata); - } else { - $this->objectStore->writeObject($urn, $countStream, $metadata['mimetype']); } - if (is_resource($countStream)) { - fclose($countStream); - } - $stat['size'] = $size; + $totalWritten = $writtenSize; + }); + + if ($this->objectStore instanceof IObjectStoreMetaData) { + $this->objectStore->writeObjectWithMetaData($urn, $countStream, $metadata); } else { - if ($this->objectStore instanceof IObjectStoreMetaData) { - $this->objectStore->writeObjectWithMetaData($urn, $stream, $metadata); - } else { - $this->objectStore->writeObject($urn, $stream, $metadata['mimetype']); - } - if (is_resource($stream)) { - fclose($stream); - } + $this->objectStore->writeObject($urn, $countStream, $metadata['mimetype']); } + if (is_resource($countStream)) { + fclose($countStream); + } + + $stat['size'] = $totalWritten; } catch (\Exception $ex) { if (!$exists) { /* @@ -545,7 +543,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil ] ); } - throw $ex; // make this bubble up + throw new GenericFileException('Error while writing stream to object store', 0, $ex); } if ($exists) { @@ -561,7 +559,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil } } - return $size; + return $totalWritten; } public function getObjectStore(): IObjectStore { diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php index 5e6dcf88a42..89405de2e8e 100644 --- a/lib/private/Files/ObjectStore/S3ObjectTrait.php +++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php @@ -6,6 +6,8 @@ */ namespace OC\Files\ObjectStore; +use Aws\Command; +use Aws\Exception\MultipartUploadException; use Aws\S3\Exception\S3MultipartUploadException; use Aws\S3\MultipartCopy; use Aws\S3\MultipartUploader; @@ -96,7 +98,9 @@ trait S3ObjectTrait { protected function writeSingle(string $urn, StreamInterface $stream, array $metaData): void { $mimetype = $metaData['mimetype'] ?? null; unset($metaData['mimetype']); - $this->getConnection()->putObject([ + unset($metaData['size']); + + $args = [ 'Bucket' => $this->bucket, 'Key' => $urn, 'Body' => $stream, @@ -104,7 +108,13 @@ trait S3ObjectTrait { 'ContentType' => $mimetype, 'Metadata' => $this->buildS3Metadata($metaData), 'StorageClass' => $this->storageClass, - ] + $this->getSSECParameters()); + ] + $this->getSSECParameters(); + + if ($size = $stream->getSize()) { + $args['ContentLength'] = $size; + } + + $this->getConnection()->putObject($args); } @@ -119,12 +129,15 @@ trait S3ObjectTrait { protected function writeMultiPart(string $urn, StreamInterface $stream, array $metaData): void { $mimetype = $metaData['mimetype'] ?? null; unset($metaData['mimetype']); + unset($metaData['size']); $attempts = 0; $uploaded = false; $concurrency = $this->concurrency; $exception = null; $state = null; + $size = $stream->getSize(); + $totalWritten = 0; // retry multipart upload once with concurrency at half on failure while (!$uploaded && $attempts <= 1) { @@ -139,6 +152,15 @@ trait S3ObjectTrait { 'Metadata' => $this->buildS3Metadata($metaData), 'StorageClass' => $this->storageClass, ] + $this->getSSECParameters(), + 'before_upload' => function (Command $command) use (&$totalWritten) { + $totalWritten += $command['ContentLength']; + }, + 'before_complete' => function ($_command) use (&$totalWritten, $size, &$uploader, &$attempts) { + if ($size !== null && $totalWritten != $size) { + $e = new \Exception('Incomplete multi part upload, expected ' . $size . ' bytes, wrote ' . $totalWritten); + throw new MultipartUploadException($uploader->getState(), $e); + } + }, ]); try { @@ -155,6 +177,9 @@ trait S3ObjectTrait { if ($stream->isSeekable()) { $stream->rewind(); } + } catch (MultipartUploadException $e) { + $exception = $e; + break; } } @@ -180,7 +205,9 @@ trait S3ObjectTrait { public function writeObjectWithMetaData(string $urn, $stream, array $metaData): void { $canSeek = fseek($stream, 0, SEEK_CUR) === 0; - $psrStream = Utils::streamFor($stream); + $psrStream = Utils::streamFor($stream, [ + 'size' => $metaData['size'] ?? null, + ]); $size = $psrStream->getSize(); diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 37ecd5779e6..b92c608a81d 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -292,7 +292,7 @@ class SetupManager { $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) { return !in_array($mount->getMountProvider(), $previouslySetupProviders); }); - $this->userMountCache->registerMounts($user, $mounts, $newProviders); + $this->registerMounts($user, $mounts, $newProviders); $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); if ($cacheDuration > 0) { @@ -457,7 +457,7 @@ class SetupManager { } if (count($mounts)) { - $this->userMountCache->registerMounts($user, $mounts, $currentProviders); + $this->registerMounts($user, $mounts, $currentProviders); $this->setupForUserWith($user, function () use ($mounts) { array_walk($mounts, [$this->mountManager, 'addMount']); }); @@ -528,7 +528,7 @@ class SetupManager { $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers); } - $this->userMountCache->registerMounts($user, $mounts, $providers); + $this->registerMounts($user, $mounts, $providers); $this->setupForUserWith($user, function () use ($mounts) { array_walk($mounts, [$this->mountManager, 'addMount']); }); @@ -600,4 +600,10 @@ class SetupManager { }); } } + + private function registerMounts(IUser $user, array $mounts, ?array $mountProviderClasses = null): void { + if ($this->lockdownManager->canAccessFilesystem()) { + $this->userMountCache->registerMounts($user, $mounts, $mountProviderClasses); + } + } } diff --git a/lib/private/Files/Type/Detection.php b/lib/private/Files/Type/Detection.php index d5810a90868..6af6ce1a0b1 100644 --- a/lib/private/Files/Type/Detection.php +++ b/lib/private/Files/Type/Detection.php @@ -55,7 +55,8 @@ class Detection implements IMimeTypeDetector { * @param string $mimeType * @param string|null $secureMimeType */ - public function registerType(string $extension, + public function registerType( + string $extension, string $mimeType, ?string $secureMimeType = null): void { // Make sure the extension is a string @@ -217,14 +218,10 @@ class Detection implements IMimeTypeDetector { return 'httpd/unix-directory'; } - if (function_exists('finfo_open') - && function_exists('finfo_file') - && $finfo = finfo_open(FILEINFO_MIME)) { - $info = @finfo_file($finfo, $path); - finfo_close($finfo); - if ($info) { - $info = strtolower($info); - $mimeType = str_contains($info, ';') ? substr($info, 0, strpos($info, ';')) : $info; + if (class_exists(finfo::class)) { + $finfo = new finfo(FILEINFO_MIME_TYPE); + $mimeType = @$finfo->file($path); + if ($mimeType) { $mimeType = $this->getSecureMimeType($mimeType); if ($mimeType !== 'application/octet-stream') { return $mimeType; @@ -240,7 +237,7 @@ class Detection implements IMimeTypeDetector { if (function_exists('mime_content_type')) { // use mime magic extension if available $mimeType = mime_content_type($path); - if ($mimeType !== false) { + if ($mimeType) { $mimeType = $this->getSecureMimeType($mimeType); if ($mimeType !== 'application/octet-stream') { return $mimeType; @@ -258,7 +255,7 @@ class Detection implements IMimeTypeDetector { if ($fp !== false) { $mimeType = fgets($fp); pclose($fp); - if ($mimeType !== false) { + if ($mimeType) { //trim the newline $mimeType = trim($mimeType); $mimeType = $this->getSecureMimeType($mimeType); @@ -293,19 +290,21 @@ class Detection implements IMimeTypeDetector { * @return string */ public function detectString($data): string { - if (function_exists('finfo_open') && function_exists('finfo_file')) { - $finfo = finfo_open(FILEINFO_MIME); - $info = finfo_buffer($finfo, $data); - return str_contains($info, ';') ? substr($info, 0, strpos($info, ';')) : $info; + if (class_exists(finfo::class)) { + $finfo = new finfo(FILEINFO_MIME_TYPE); + $mimeType = $finfo->buffer($data); + if ($mimeType) { + return $mimeType; + } } $tmpFile = \OCP\Server::get(ITempManager::class)->getTemporaryFile(); $fh = fopen($tmpFile, 'wb'); fwrite($fh, $data, 8024); fclose($fh); - $mime = $this->detect($tmpFile); + $mimeType = $this->detect($tmpFile); unset($tmpFile); - return $mime; + return $mimeType; } /** diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index e9ed351b27b..576cb66b3cf 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -205,7 +205,10 @@ class Scanner extends PublicEmitter { foreach (['', 'files'] as $path) { if (!$storage->isCreatable($path)) { $fullPath = $storage->getSourcePath($path); - if (!$storage->is_dir($path) && $storage->getCache()->inCache($path)) { + if (isset($mounts[$mount->getMountPoint() . $path . '/'])) { + // /<user>/files is overwritten by a mountpoint, so this check is irrelevant + break; + } elseif (!$storage->is_dir($path) && $storage->getCache()->inCache($path)) { throw new NotFoundException("User folder $fullPath exists in cache but not on disk"); } elseif ($storage->is_dir($path)) { $ownerUid = fileowner($fullPath); @@ -213,9 +216,6 @@ class Scanner extends PublicEmitter { $owner = $owner['name'] ?? $ownerUid; $permissions = decoct(fileperms($fullPath)); throw new ForbiddenException("User folder $fullPath is not writable, folders is owned by $owner and has mode $permissions"); - } elseif (isset($mounts[$mount->getMountPoint() . $path . '/'])) { - // /<user>/files is overwritten by a mountpoint, so this check is irrelevant - break; } else { // if the root exists in neither the cache nor the storage the user isn't setup yet break 2; diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php index 8c457db8beb..0cbda651a8b 100644 --- a/lib/private/Notification/Manager.php +++ b/lib/private/Notification/Manager.php @@ -21,6 +21,7 @@ use OCP\Notification\IncompleteNotificationException; use OCP\Notification\IncompleteParsedNotificationException; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\IPreloadableNotifier; use OCP\Notification\UnknownNotificationException; use OCP\RichObjectStrings\IRichTextFormatter; use OCP\RichObjectStrings\IValidator; @@ -390,6 +391,17 @@ class Manager implements IManager { return $notification; } + public function preloadDataForParsing(array $notifications, string $languageCode): void { + $notifiers = $this->getNotifiers(); + foreach ($notifiers as $notifier) { + if (!($notifier instanceof IPreloadableNotifier)) { + continue; + } + + $notifier->preloadDataForParsing($notifications, $languageCode); + } + } + /** * @param INotification $notification */ diff --git a/lib/private/Security/IdentityProof/Manager.php b/lib/private/Security/IdentityProof/Manager.php index 935c18bb81d..c16b8314beb 100644 --- a/lib/private/Security/IdentityProof/Manager.php +++ b/lib/private/Security/IdentityProof/Manager.php @@ -11,6 +11,8 @@ namespace OC\Security\IdentityProof; use OC\Files\AppData\Factory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IUser; use OCP\Security\ICrypto; @@ -19,13 +21,17 @@ use Psr\Log\LoggerInterface; class Manager { private IAppData $appData; + protected ICache $cache; + public function __construct( Factory $appDataFactory, private ICrypto $crypto, private IConfig $config, private LoggerInterface $logger, + private ICacheFactory $cacheFactory, ) { $this->appData = $appDataFactory->get('identityproof'); + $this->cache = $this->cacheFactory->createDistributed('identityproof::'); } /** @@ -96,12 +102,24 @@ class Manager { */ protected function retrieveKey(string $id): Key { try { + $cachedPublicKey = $this->cache->get($id . '-public'); + $cachedPrivateKey = $this->cache->get($id . '-private'); + + if ($cachedPublicKey !== null && $cachedPrivateKey !== null) { + $decryptedPrivateKey = $this->crypto->decrypt($cachedPrivateKey); + + return new Key($cachedPublicKey, $decryptedPrivateKey); + } + $folder = $this->appData->getFolder($id); - $privateKey = $this->crypto->decrypt( - $folder->getFile('private')->getContent() - ); + $privateKey = $folder->getFile('private')->getContent(); $publicKey = $folder->getFile('public')->getContent(); - return new Key($publicKey, $privateKey); + + $this->cache->set($id . '-public', $publicKey); + $this->cache->set($id . '-private', $privateKey); + + $decryptedPrivateKey = $this->crypto->decrypt($privateKey); + return new Key($publicKey, $decryptedPrivateKey); } catch (\Exception $e) { return $this->generateKey($id); } diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php index ec4ce040090..8f6294faa66 100644 --- a/lib/private/Setup/AbstractDatabase.php +++ b/lib/private/Setup/AbstractDatabase.php @@ -77,7 +77,6 @@ abstract class AbstractDatabase { $this->config->setValues([ 'dbname' => $dbName, 'dbhost' => $dbHost, - 'dbport' => $dbPort, 'dbtableprefix' => $dbTablePrefix, ]); diff --git a/lib/private/Tags.php b/lib/private/Tags.php index 0a37f4c9f4e..fe4a4137e10 100644 --- a/lib/private/Tags.php +++ b/lib/private/Tags.php @@ -273,7 +273,6 @@ class Tags implements ITags { return false; } if ($this->userHasTag($name, $this->user)) { - // TODO use unique db properties instead of an additional check $this->logger->debug(__METHOD__ . ' Tag with name already exists', ['app' => 'core']); return false; } diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php index a9c9f1e1ca2..11fb2bed559 100644 --- a/lib/private/TaskProcessing/Manager.php +++ b/lib/private/TaskProcessing/Manager.php @@ -31,9 +31,9 @@ use OCP\Files\Node; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Http\Client\IClientService; +use OCP\IAppConfig; use OCP\ICache; use OCP\ICacheFactory; -use OCP\IConfig; use OCP\IL10N; use OCP\IServerContainer; use OCP\IUserManager; @@ -73,6 +73,11 @@ class Manager implements IManager { public const LEGACY_PREFIX_TEXTTOIMAGE = 'legacy:TextToImage:'; public const LEGACY_PREFIX_SPEECHTOTEXT = 'legacy:SpeechToText:'; + public const LAZY_CONFIG_KEYS = [ + 'ai.taskprocessing_type_preferences', + 'ai.taskprocessing_provider_preferences', + ]; + /** @var list<IProvider>|null */ private ?array $providers = null; @@ -92,7 +97,7 @@ class Manager implements IManager { private ?GetTaskProcessingProvidersEvent $eventResult = null; public function __construct( - private IConfig $config, + private IAppConfig $appConfig, private Coordinator $coordinator, private IServerContainer $serverContainer, private LoggerInterface $logger, @@ -630,7 +635,7 @@ class Manager implements IManager { */ private function _getTaskTypeSettings(): array { try { - $json = $this->config->getAppValue('core', 'ai.taskprocessing_type_preferences', ''); + $json = $this->appConfig->getValueString('core', 'ai.taskprocessing_type_preferences', '', lazy: true); if ($json === '') { return []; } @@ -788,7 +793,11 @@ class Manager implements IManager { if ($this->preferences === null) { $this->preferences = $this->distributedCache->get('ai.taskprocessing_provider_preferences'); if ($this->preferences === null) { - $this->preferences = json_decode($this->config->getAppValue('core', 'ai.taskprocessing_provider_preferences', 'null'), associative: true, flags: JSON_THROW_ON_ERROR); + $this->preferences = json_decode( + $this->appConfig->getValueString('core', 'ai.taskprocessing_provider_preferences', 'null', lazy: true), + associative: true, + flags: JSON_THROW_ON_ERROR, + ); $this->distributedCache->set('ai.taskprocessing_provider_preferences', $this->preferences, 60 * 3); } } @@ -889,7 +898,7 @@ class Manager implements IManager { $user = $this->userManager->get($userId); } - $guestsAllowed = $this->config->getAppValue('core', 'ai.taskprocessing_guests', 'false'); + $guestsAllowed = $this->appConfig->getValueString('core', 'ai.taskprocessing_guests', 'false'); if ($guestsAllowed == 'true' || !class_exists(\OCA\Guests\UserBackend::class) || !($user->getBackend() instanceof \OCA\Guests\UserBackend)) { return true; } diff --git a/lib/public/Notification/IManager.php b/lib/public/Notification/IManager.php index 23664af17cd..207a89344b0 100644 --- a/lib/public/Notification/IManager.php +++ b/lib/public/Notification/IManager.php @@ -11,7 +11,7 @@ namespace OCP\Notification; use OCP\AppFramework\Attribute\Consumable; #[Consumable(since: '9.0.0')] -interface IManager extends IApp, INotifier { +interface IManager extends IApp, IPreloadableNotifier { /** * @param string $appClass The service must implement IApp, otherwise a * \InvalidArgumentException is thrown later diff --git a/lib/public/Notification/INotifier.php b/lib/public/Notification/INotifier.php index bdc7207216f..b6851e3dfb3 100644 --- a/lib/public/Notification/INotifier.php +++ b/lib/public/Notification/INotifier.php @@ -10,6 +10,11 @@ namespace OCP\Notification; use OCP\AppFramework\Attribute\Implementable; +/** + * Please consider implementing {@see IPreloadableNotifier} to improve performance. It allows to + * preload and cache data for many notifications at once instead of loading the data for each + * prepared notification separately. + */ #[Implementable(since: '9.0.0')] interface INotifier { /** diff --git a/lib/public/Notification/IPreloadableNotifier.php b/lib/public/Notification/IPreloadableNotifier.php new file mode 100644 index 00000000000..2bdcd84d254 --- /dev/null +++ b/lib/public/Notification/IPreloadableNotifier.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\Notification; + +use OCP\AppFramework\Attribute\Implementable; + +/** + * Allow notifier implementations to preload and cache data for many notifications at once to + * improve performance by, for example, bundling SQL queries. + */ +#[Implementable(since: '32.0.0')] +interface IPreloadableNotifier extends INotifier { + /** + * This method provides a way for notifier implementations to preload and cache data for many + * notifications. The data is meant to be consumed later in the {@see INotifier::prepare()} + * method to improve performance. + * + * @since 32.0.0 + * + * @param INotification[] $notifications The notifications which are about to be prepared in the next step. + * @param string $languageCode The code of the language that should be used to prepare the notification. + */ + public function preloadDataForParsing(array $notifications, string $languageCode): void; +} diff --git a/lib/public/TaskProcessing/IManager.php b/lib/public/TaskProcessing/IManager.php index f161030f5f4..723eca8f615 100644 --- a/lib/public/TaskProcessing/IManager.php +++ b/lib/public/TaskProcessing/IManager.php @@ -26,6 +26,7 @@ use OCP\TaskProcessing\Exception\ValidationException; * @since 30.0.0 */ interface IManager { + /** * @since 30.0.0 */ |