diff options
Diffstat (limited to 'lib')
80 files changed, 833 insertions, 436 deletions
diff --git a/lib/base.php b/lib/base.php index 20065006a8c..45d06da0303 100644 --- a/lib/base.php +++ b/lib/base.php @@ -68,6 +68,7 @@ use OCP\Share; use OC\Encryption\HookManager; use OC\Files\Filesystem; use OC\Share20\Hooks; +use OCP\User\Events\UserChangedEvent; require_once 'public/Constants.php'; @@ -387,7 +388,7 @@ class OC { if (!empty($incompatibleShippedApps)) { $l = \OC::$server->getL10N('core'); $hint = $l->t('The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]); - throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint); + throw new \OCP\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint); } $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion)); @@ -843,8 +844,9 @@ class OC { } private static function registerAccountHooks() { - $hookHandler = \OC::$server->get(\OC\Accounts\Hooks::class); - \OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook'); + /** @var IEventDispatcher $dispatcher */ + $dispatcher = \OC::$server->get(IEventDispatcher::class); + $dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class); } private static function registerAppRestrictionsHooks() { diff --git a/lib/composer/composer/ClassLoader.php b/lib/composer/composer/ClassLoader.php index 247294d66ee..6d0c3f2d001 100644 --- a/lib/composer/composer/ClassLoader.php +++ b/lib/composer/composer/ClassLoader.php @@ -338,7 +338,7 @@ class ClassLoader * Loads the given class or interface. * * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise + * @return true|null True if loaded, null otherwise */ public function loadClass($class) { @@ -347,6 +347,8 @@ class ClassLoader return true; } + + return null; } /** diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 437a46be92b..82c40c800a4 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -64,6 +64,7 @@ return array( 'OCP\\AppFramework\\Http\\Template\\LinkMenuAction' => $baseDir . '/lib/public/AppFramework/Http/Template/LinkMenuAction.php', 'OCP\\AppFramework\\Http\\Template\\PublicTemplateResponse' => $baseDir . '/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php', 'OCP\\AppFramework\\Http\\Template\\SimpleMenuAction' => $baseDir . '/lib/public/AppFramework/Http/Template/SimpleMenuAction.php', + 'OCP\\AppFramework\\Http\\TextPlainResponse' => $baseDir . '/lib/public/AppFramework/Http/TextPlainResponse.php', 'OCP\\AppFramework\\Http\\TooManyRequestsResponse' => $baseDir . '/lib/public/AppFramework/Http/TooManyRequestsResponse.php', 'OCP\\AppFramework\\Http\\ZipResponse' => $baseDir . '/lib/public/AppFramework/Http/ZipResponse.php', 'OCP\\AppFramework\\IAppContainer' => $baseDir . '/lib/public/AppFramework/IAppContainer.php', @@ -370,6 +371,7 @@ return array( 'OCP\\Group\\Events\\UserAddedEvent' => $baseDir . '/lib/public/Group/Events/UserAddedEvent.php', 'OCP\\Group\\Events\\UserRemovedEvent' => $baseDir . '/lib/public/Group/Events/UserRemovedEvent.php', 'OCP\\Group\\ISubAdmin' => $baseDir . '/lib/public/Group/ISubAdmin.php', + 'OCP\\HintException' => $baseDir . '/lib/public/HintException.php', 'OCP\\Http\\Client\\IClient' => $baseDir . '/lib/public/Http/Client/IClient.php', 'OCP\\Http\\Client\\IClientService' => $baseDir . '/lib/public/Http/Client/IClientService.php', 'OCP\\Http\\Client\\IResponse' => $baseDir . '/lib/public/Http/Client/IResponse.php', @@ -692,6 +694,7 @@ return array( 'OC\\Authentication\\Listeners\\UserDeletedFilesCleanupListener' => $baseDir . '/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php', 'OC\\Authentication\\Listeners\\UserDeletedStoreCleanupListener' => $baseDir . '/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php', 'OC\\Authentication\\Listeners\\UserDeletedTokenCleanupListener' => $baseDir . '/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php', + 'OC\\Authentication\\Listeners\\UserDeletedWebAuthnCleanupListener' => $baseDir . '/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php', 'OC\\Authentication\\Listeners\\UserLoggedInListener' => $baseDir . '/lib/private/Authentication/Listeners/UserLoggedInListener.php', 'OC\\Authentication\\LoginCredentials\\Credentials' => $baseDir . '/lib/private/Authentication/LoginCredentials/Credentials.php', 'OC\\Authentication\\LoginCredentials\\Store' => $baseDir . '/lib/private/Authentication/LoginCredentials/Store.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 5430bfdeac4..423fd814d6b 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -93,6 +93,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\AppFramework\\Http\\Template\\LinkMenuAction' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Template/LinkMenuAction.php', 'OCP\\AppFramework\\Http\\Template\\PublicTemplateResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php', 'OCP\\AppFramework\\Http\\Template\\SimpleMenuAction' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Template/SimpleMenuAction.php', + 'OCP\\AppFramework\\Http\\TextPlainResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/TextPlainResponse.php', 'OCP\\AppFramework\\Http\\TooManyRequestsResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/TooManyRequestsResponse.php', 'OCP\\AppFramework\\Http\\ZipResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/ZipResponse.php', 'OCP\\AppFramework\\IAppContainer' => __DIR__ . '/../../..' . '/lib/public/AppFramework/IAppContainer.php', @@ -399,6 +400,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Group\\Events\\UserAddedEvent' => __DIR__ . '/../../..' . '/lib/public/Group/Events/UserAddedEvent.php', 'OCP\\Group\\Events\\UserRemovedEvent' => __DIR__ . '/../../..' . '/lib/public/Group/Events/UserRemovedEvent.php', 'OCP\\Group\\ISubAdmin' => __DIR__ . '/../../..' . '/lib/public/Group/ISubAdmin.php', + 'OCP\\HintException' => __DIR__ . '/../../..' . '/lib/public/HintException.php', 'OCP\\Http\\Client\\IClient' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IClient.php', 'OCP\\Http\\Client\\IClientService' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IClientService.php', 'OCP\\Http\\Client\\IResponse' => __DIR__ . '/../../..' . '/lib/public/Http/Client/IResponse.php', @@ -721,6 +723,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Authentication\\Listeners\\UserDeletedFilesCleanupListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php', 'OC\\Authentication\\Listeners\\UserDeletedStoreCleanupListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php', 'OC\\Authentication\\Listeners\\UserDeletedTokenCleanupListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php', + 'OC\\Authentication\\Listeners\\UserDeletedWebAuthnCleanupListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php', 'OC\\Authentication\\Listeners\\UserLoggedInListener' => __DIR__ . '/../../..' . '/lib/private/Authentication/Listeners/UserLoggedInListener.php', 'OC\\Authentication\\LoginCredentials\\Credentials' => __DIR__ . '/../../..' . '/lib/private/Authentication/LoginCredentials/Credentials.php', 'OC\\Authentication\\LoginCredentials\\Store' => __DIR__ . '/../../..' . '/lib/private/Authentication/LoginCredentials/Store.php', diff --git a/lib/l10n/cs.js b/lib/l10n/cs.js index 56896f0c10d..010cf1a8377 100644 --- a/lib/l10n/cs.js +++ b/lib/l10n/cs.js @@ -32,12 +32,12 @@ OC.L10N.register( "Server version %s or lower is required." : "Je potřeba verze serveru %s nebo starší.", "Logged in user must be an admin or sub admin" : "Je třeba, aby přihlášený uživatel byl správcem či správcem pro dílčí oblast", "Logged in user must be an admin" : "Je třeba, aby přihlášený uživatel byl správce", - "Wiping of device %s has started" : "Vymazávání zařízení %s zahájeno", - "Wiping of device »%s« has started" : "Vymazávání zařízení „%s“ zahájeno", + "Wiping of device %s has started" : "Vymazávání ze zařízení %s zahájeno", + "Wiping of device »%s« has started" : "Vymazávání ze zařízení „%s“ zahájeno", "»%s« started remote wipe" : "„%s“ zahájilo vymazávání na dálku", "Device or application »%s« has started the remote wipe process. You will receive another email once the process has finished" : "Přístroj či aplikace »%s« spustila proces vzdáleného vymazání. Obdržíte další e-mail poté co bude proces ukončen", - "Wiping of device %s has finished" : "Vymazávání zařízení %s dokončeno", - "Wiping of device »%s« has finished" : "Vymazávání zařízení „%s“ dokončeno", + "Wiping of device %s has finished" : "Vymazávání ze zařízení %s dokončeno", + "Wiping of device »%s« has finished" : "Vymazávání ze zařízení „%s“ dokončeno", "»%s« finished remote wipe" : "„%s“ dokončilo vymazání na dálku", "Device or application »%s« has finished the remote wipe process." : "Přístroj či aplikace „%s“ dokončila proces vymazání na dálku.", "Remote wipe started" : "Vymazání na dálku zahájeno", @@ -59,7 +59,7 @@ OC.L10N.register( "_%n month ago_::_%n months ago_" : ["před %n měsícem","před %n měsíci","před %n měsíci","před %n měsíci"], "next year" : "následující rok", "last year" : "minulý rok", - "_in %n year_::_in %n years_" : ["během %n roku","během %n roků","během %n roků","během %n roků"], + "_in %n year_::_in %n years_" : ["během %n roku","během %n let","během %n let","během %n let"], "_%n year ago_::_%n years ago_" : ["před rokem","před %n lety","před %n lety","před %n lety"], "_in %n hour_::_in %n hours_" : ["během %n hodiny","během %n hodin","během %n hodin","během %n hodin"], "_%n hour ago_::_%n hours ago_" : ["před %n hodinou","před %n hodinami","před %n hodinami","před %n hodinami"], diff --git a/lib/l10n/cs.json b/lib/l10n/cs.json index bbe18badae5..0950cbdf1c0 100644 --- a/lib/l10n/cs.json +++ b/lib/l10n/cs.json @@ -30,12 +30,12 @@ "Server version %s or lower is required." : "Je potřeba verze serveru %s nebo starší.", "Logged in user must be an admin or sub admin" : "Je třeba, aby přihlášený uživatel byl správcem či správcem pro dílčí oblast", "Logged in user must be an admin" : "Je třeba, aby přihlášený uživatel byl správce", - "Wiping of device %s has started" : "Vymazávání zařízení %s zahájeno", - "Wiping of device »%s« has started" : "Vymazávání zařízení „%s“ zahájeno", + "Wiping of device %s has started" : "Vymazávání ze zařízení %s zahájeno", + "Wiping of device »%s« has started" : "Vymazávání ze zařízení „%s“ zahájeno", "»%s« started remote wipe" : "„%s“ zahájilo vymazávání na dálku", "Device or application »%s« has started the remote wipe process. You will receive another email once the process has finished" : "Přístroj či aplikace »%s« spustila proces vzdáleného vymazání. Obdržíte další e-mail poté co bude proces ukončen", - "Wiping of device %s has finished" : "Vymazávání zařízení %s dokončeno", - "Wiping of device »%s« has finished" : "Vymazávání zařízení „%s“ dokončeno", + "Wiping of device %s has finished" : "Vymazávání ze zařízení %s dokončeno", + "Wiping of device »%s« has finished" : "Vymazávání ze zařízení „%s“ dokončeno", "»%s« finished remote wipe" : "„%s“ dokončilo vymazání na dálku", "Device or application »%s« has finished the remote wipe process." : "Přístroj či aplikace „%s“ dokončila proces vymazání na dálku.", "Remote wipe started" : "Vymazání na dálku zahájeno", @@ -57,7 +57,7 @@ "_%n month ago_::_%n months ago_" : ["před %n měsícem","před %n měsíci","před %n měsíci","před %n měsíci"], "next year" : "následující rok", "last year" : "minulý rok", - "_in %n year_::_in %n years_" : ["během %n roku","během %n roků","během %n roků","během %n roků"], + "_in %n year_::_in %n years_" : ["během %n roku","během %n let","během %n let","během %n let"], "_%n year ago_::_%n years ago_" : ["před rokem","před %n lety","před %n lety","před %n lety"], "_in %n hour_::_in %n hours_" : ["během %n hodiny","během %n hodin","během %n hodin","během %n hodin"], "_%n hour ago_::_%n hours ago_" : ["před %n hodinou","před %n hodinami","před %n hodinami","před %n hodinami"], diff --git a/lib/l10n/de.js b/lib/l10n/de.js index 44773c26cba..75503166af4 100644 --- a/lib/l10n/de.js +++ b/lib/l10n/de.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!", "This can usually be fixed by giving the webserver write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Wenn Du jedoch möchtest dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true.", "See %s" : "Siehe %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %1$swurden nicht korrekt ersetzt. Stelle sicher, dass es sich um eine mit dem Server kompatible Version handelt.", "Sample configuration detected" : "Beispielkonfiguration gefunden", diff --git a/lib/l10n/de.json b/lib/l10n/de.json index 04a697fac72..fef1bbcc3e5 100644 --- a/lib/l10n/de.json +++ b/lib/l10n/de.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!", "This can usually be fixed by giving the webserver write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Wenn Du jedoch möchtest dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true.", "See %s" : "Siehe %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %1$swurden nicht korrekt ersetzt. Stelle sicher, dass es sich um eine mit dem Server kompatible Version handelt.", "Sample configuration detected" : "Beispielkonfiguration gefunden", diff --git a/lib/l10n/it.js b/lib/l10n/it.js index 3501cfb2760..a1116c03840 100644 --- a/lib/l10n/it.js +++ b/lib/l10n/it.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "Impossibile scrivere nella cartella \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory." : "Ciò può essere normalmente corretto 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", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "I file dell'applicazione %1$s non sono stati sostituiti correttamente. Assicurati che sia una versione compatibile con il server.", "Sample configuration detected" : "Configurazione di esempio rilevata", diff --git a/lib/l10n/it.json b/lib/l10n/it.json index 33fca8e3aca..404d6896c11 100644 --- a/lib/l10n/it.json +++ b/lib/l10n/it.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "Impossibile scrivere nella cartella \"config\"!", "This can usually be fixed by giving the webserver write access to the config directory." : "Ciò può essere normalmente corretto 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", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "I file dell'applicazione %1$s non sono stati sostituiti correttamente. Assicurati che sia una versione compatibile con il server.", "Sample configuration detected" : "Configurazione di esempio rilevata", diff --git a/lib/l10n/ja.js b/lib/l10n/ja.js index b3c673c410a..e342233bbf3 100644 --- a/lib/l10n/ja.js +++ b/lib/l10n/ja.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "\"config\"ディレクトリに書き込めません!", "This can usually be fixed by giving the webserver write access to the config directory." : "多くの場合、これはWebサーバーにconfigディレクトリへの書き込み権限を与えることで解決できます。", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "しかし、config.phpファイルを読み取り専用にしたい場合は、オプションの \"config_is_read_only\"をtrueに設定してください。", "See %s" : "%s を閲覧", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "アプリ %1$s のファイルが正しく置き換えられませんでした。サーバーと互換性のあるバージョンであることを確認してください。", "Sample configuration detected" : "サンプル設定が見つかりました。", @@ -103,6 +104,7 @@ OC.L10N.register( "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "php.ini から open_basedir 設定を削除するか、64bit PHPに切り替えてください。", "Set an admin username." : "管理者のユーザー名を設定", "Set an admin password." : "管理者のパスワードを設定", + "Cannot create or write into the data directory %s" : " データディレクトリ%sに作成、書き込みができません", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "%s のバックエンドの共有には、OCP\\Share_Backend インターフェースを実装しなければなりません。", "Sharing backend %s not found" : "共有バックエンド %s が見つかりません", "Sharing backend for %s not found" : "%s のための共有バックエンドが見つかりません", @@ -112,7 +114,11 @@ OC.L10N.register( "Open »%s«" : "»%s«を開く", "%1$s via %2$s" : "%1$s via %2$s", "You are not allowed to share %s" : "%s を共有することを許可されていません。", + "Cannot increase permissions of %s" : "%sの権限を追加できません ", + "Files cannot be shared with delete permissions" : "削除権限付きでファイルを共有できません", + "Files cannot be shared with create permissions" : "作成権限付きでファイルを共有できません", "Expiration date is in the past" : "有効期限が切れています", + "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["%s 日以上先の有効期限は設定できません "], "Sharing is only allowed with group members" : "共有はグループメンバーにのみ許可されます", "Sharing %s failed, because this item is already shared with user %s" : "このアイテム%sはすでにユーザー%sと共有されているため、共有に失敗しました", "%1$s shared »%2$s« with you" : "%1$s は »%2$s« をあなたと共有しました", @@ -179,6 +185,7 @@ OC.L10N.register( "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 read file" : "ファイルを読み込めません", "Application is not enabled" : "アプリケーションは無効です", "Authentication error" : "認証エラー", "Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。", @@ -187,6 +194,7 @@ OC.L10N.register( "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "多くの場合、Webサーバーの configディレクトリ に書き込み権限を与えることで直ります。%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" : "\"apps\" ディレクトリに書き込みができません", + "This can usually be fixed by giving the webserver write access to the apps directory or disabling the App Store in the config file." : "これは通常、Webサーバーにappsディレクトリへの書き込み権限を許可するか、構成ファイルでAppStoreを無効にすることで解決できます。", "Cannot create \"data\" directory" : "\"data\" ディレクトリを作成できません", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "多くの場合、Webサーバーのルートディレクトリに書き込み権限を与えることで直ります。%s を見てください。", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Webサーバーのルートディレクトリに書き込み権限パーミッションが必要です。%s を見てください。", diff --git a/lib/l10n/ja.json b/lib/l10n/ja.json index 8874892f42a..022108d3a91 100644 --- a/lib/l10n/ja.json +++ b/lib/l10n/ja.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "\"config\"ディレクトリに書き込めません!", "This can usually be fixed by giving the webserver write access to the config directory." : "多くの場合、これはWebサーバーにconfigディレクトリへの書き込み権限を与えることで解決できます。", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "しかし、config.phpファイルを読み取り専用にしたい場合は、オプションの \"config_is_read_only\"をtrueに設定してください。", "See %s" : "%s を閲覧", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "アプリ %1$s のファイルが正しく置き換えられませんでした。サーバーと互換性のあるバージョンであることを確認してください。", "Sample configuration detected" : "サンプル設定が見つかりました。", @@ -101,6 +102,7 @@ "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "php.ini から open_basedir 設定を削除するか、64bit PHPに切り替えてください。", "Set an admin username." : "管理者のユーザー名を設定", "Set an admin password." : "管理者のパスワードを設定", + "Cannot create or write into the data directory %s" : " データディレクトリ%sに作成、書き込みができません", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "%s のバックエンドの共有には、OCP\\Share_Backend インターフェースを実装しなければなりません。", "Sharing backend %s not found" : "共有バックエンド %s が見つかりません", "Sharing backend for %s not found" : "%s のための共有バックエンドが見つかりません", @@ -110,7 +112,11 @@ "Open »%s«" : "»%s«を開く", "%1$s via %2$s" : "%1$s via %2$s", "You are not allowed to share %s" : "%s を共有することを許可されていません。", + "Cannot increase permissions of %s" : "%sの権限を追加できません ", + "Files cannot be shared with delete permissions" : "削除権限付きでファイルを共有できません", + "Files cannot be shared with create permissions" : "作成権限付きでファイルを共有できません", "Expiration date is in the past" : "有効期限が切れています", + "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["%s 日以上先の有効期限は設定できません "], "Sharing is only allowed with group members" : "共有はグループメンバーにのみ許可されます", "Sharing %s failed, because this item is already shared with user %s" : "このアイテム%sはすでにユーザー%sと共有されているため、共有に失敗しました", "%1$s shared »%2$s« with you" : "%1$s は »%2$s« をあなたと共有しました", @@ -177,6 +183,7 @@ "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 read file" : "ファイルを読み込めません", "Application is not enabled" : "アプリケーションは無効です", "Authentication error" : "認証エラー", "Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。", @@ -185,6 +192,7 @@ "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "多くの場合、Webサーバーの configディレクトリ に書き込み権限を与えることで直ります。%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" : "\"apps\" ディレクトリに書き込みができません", + "This can usually be fixed by giving the webserver write access to the apps directory or disabling the App Store in the config file." : "これは通常、Webサーバーにappsディレクトリへの書き込み権限を許可するか、構成ファイルでAppStoreを無効にすることで解決できます。", "Cannot create \"data\" directory" : "\"data\" ディレクトリを作成できません", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "多くの場合、Webサーバーのルートディレクトリに書き込み権限を与えることで直ります。%s を見てください。", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Webサーバーのルートディレクトリに書き込み権限パーミッションが必要です。%s を見てください。", diff --git a/lib/l10n/nl.js b/lib/l10n/nl.js index d97d05ed4d9..4508d75f178 100644 --- a/lib/l10n/nl.js +++ b/lib/l10n/nl.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "Kan niet schrijven naar de \"config\" directory!", "This can usually be fixed by giving the webserver write access to the config directory." : "Dit kan opgelost worden door de config map op de webserver schrijfrechten te geven.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Maar, als je liever config.php alleen-lezen wilt houden, stel dan de optie \"config_is_read_only\" in op true.", "See %s" : "Zie %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "De bestanden van app %1$szijn niet correct vervangen. Zorg ervoor dat de versie compatible is met de server.", "Sample configuration detected" : "Voorbeeld configuratie gevonden", diff --git a/lib/l10n/nl.json b/lib/l10n/nl.json index 7475ffa4a5f..f379bbb2035 100644 --- a/lib/l10n/nl.json +++ b/lib/l10n/nl.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "Kan niet schrijven naar de \"config\" directory!", "This can usually be fixed by giving the webserver write access to the config directory." : "Dit kan opgelost worden door de config map op de webserver schrijfrechten te geven.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Maar, als je liever config.php alleen-lezen wilt houden, stel dan de optie \"config_is_read_only\" in op true.", "See %s" : "Zie %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "De bestanden van app %1$szijn niet correct vervangen. Zorg ervoor dat de versie compatible is met de server.", "Sample configuration detected" : "Voorbeeld configuratie gevonden", diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js index 92f0176f4a1..aa20c04624b 100644 --- a/lib/l10n/sv.js +++ b/lib/l10n/sv.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "Kan inte skriva till \"config\" katalogen!", "This can usually be fixed by giving the webserver write access to the config directory." : "Detta kan vanligtvis åtgärdas genom att ge webbservern skrivåtkomst till config-katalogen.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Men, om du föredrar att behålla config.php skrivskyddad, sätt alternativet \"config_is_read_only\" till true i den.", "See %s" : "Se %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Filerna i appen %1$s ersattes inte korrekt. Kontrollera att det är en version som är kompatibel med servern.", "Sample configuration detected" : "Exempel-konfiguration detekterad", @@ -15,10 +16,12 @@ OC.L10N.register( "Education Edition" : "Utbildningspaket", "Enterprise bundle" : "Företagspaketet", "Groupware bundle" : "Gruppvarupaket", + "Hub bundle" : "Hub-paket", "Social sharing bundle" : "Kommunikationspaket", "PHP %s or higher is required." : "PHP %s eller högre krävs.", "PHP with a version lower than %s is required." : "PHP med version lägre än %s krävs.", "%sbit or higher PHP required." : "%sbit eller nyare PHP-version krävs.", + "The following architectures are supported: %s" : "Följande arkitekturer stöds: %s", "The following databases are supported: %s" : "Följande databaser stöds: %s", "The command line tool %s could not be found" : "Kommandoradsverktyget %s hittades inte.", "The library %s is not available." : "Biblioteket %s är inte tillgängligt.", @@ -66,6 +69,10 @@ OC.L10N.register( "seconds ago" : "sekunder sedan", "Empty file" : "Tom fil", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finns inte längre. Vänligen aktivera det i dina appinställningar eller kontakta din administratör.", + "File already exists" : "Filen existerar redan", + "Invalid path" : "Ogiltig sökväg", + "Failed to create file from template" : "Kunde skapa fil från mall", + "Templates" : "Mallar", "File name is a reserved word" : "Filnamnet är ett reserverat ord", "File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken", "File name is too long" : "Filnamnet är för långt", @@ -97,6 +104,7 @@ OC.L10N.register( "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Vänligen ta bort open_basedir-inställningen i din php.ini eller växla till 64-bitars PHP.", "Set an admin username." : "Ange ett användarnamn för administratören.", "Set an admin password." : "Ange ett administratörslösenord.", + "Cannot create or write into the data directory %s" : "Kan inte skapa eller skriva till data-katalogen %s", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Delningsgränssnittet %s måste implementera gränssnittet OCP\\Share_Backend", "Sharing backend %s not found" : "Delningsgränssnittet %s hittades inte", "Sharing backend for %s not found" : "Delningsgränssnittet för %s hittades inte", @@ -106,7 +114,13 @@ OC.L10N.register( "Open »%s«" : "Öppna »%s«", "%1$s via %2$s" : "%1$s via %2$s", "You are not allowed to share %s" : "Du har inte rätt att dela %s", + "Cannot increase permissions of %s" : "Kan inte höja behörigheter av %s", + "Files cannot be shared with delete permissions" : "Filer kan inte delas med rättighet att radera", + "Files cannot be shared with create permissions" : "Filer kan inte delas med rättighet att skapa", "Expiration date is in the past" : "Utgångsdatum är i det förflutna", + "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Kan inte sätta ett utgångsdatum längre fram än %n dag","Kan inte sätta ett utgångsdatum längre fram än %n dagar"], + "Sharing is only allowed with group members" : "Delning är endast tillåten med gruppmedlemmar", + "Sharing %s failed, because this item is already shared with user %s" : "Delning av %s misslyckades eftersom detta redan är delat med användaren %s", "%1$s shared »%2$s« with you" : "%1$s delade »%2$s« med dig", "%1$s shared »%2$s« with you." : "%1$s delade »%2$s« med dig.", "Click the button below to open it." : "Klicka på knappen nedan för att öppna det.", @@ -157,6 +171,7 @@ OC.L10N.register( "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", + "The user limit has been reached and the user was not created." : "Maximala användarantalet har uppnåtts och användaren skapades inte.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Endast följande tecken är tillåtna i användarnamnet: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Ett giltigt användarnamn måste anges", "Username contains whitespace at the beginning or at the end" : "Användarnamnet består av ett mellanslag i början eller i slutet", @@ -168,8 +183,9 @@ OC.L10N.register( "User disabled" : "Användare inaktiverad", "Login canceled by app" : "Inloggningen avbruten av appen", "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Appen \"%1$s\" kan inte installeras eftersom följande beroenden inte är uppfyllda: %2$s", - "a safe home for all your data" : "ett säkert hem för alla dina data", + "a safe home for all your data" : "ett säkert hem för all din data", "File is currently busy, please try again later" : "Filen är för tillfället upptagen, vänligen försök igen senare", + "Cannot read file" : "Kan inte läsa fil", "Application is not enabled" : "Applikationen är inte aktiverad", "Authentication error" : "Fel vid autentisering", "Token expired. Please reload page." : "Token har löpt ut. Vänligen uppdatera sidan.", @@ -178,6 +194,7 @@ OC.L10N.register( "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Detta kan vanligtvis åtgärda genom att ge webbservern skrivåtkomst till konfigureringsmappen. Se %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" : "Eller, om du föredrar att behålla config.php skrivskyddad, sätt alternativet \"config_is_read_only\" till true i den. Se %s", "Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!", + "This can usually be fixed by giving the webserver write access to the apps directory or disabling the App Store in the config file." : "Detta kan vanligtvis åtgärdas genom att ge webbservern skrivrättigheter till applikationskatalogen eller stänga av appbutiken i konfigurationsfilen.", "Cannot create \"data\" directory" : "Kan inte skapa \"data\"-mapp", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Detta kan vanligtvis åtgärda genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Rättigheter kan vanligtvis fixas genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s.", @@ -202,7 +219,7 @@ OC.L10N.register( "Your data directory must be an absolute path" : "Du måste specificera en korrekt sökväg till datamappen", "Check the value of \"datadirectory\" in your configuration" : "Kontrollera värdet av \"datakatalog\" i din konfiguration", "Your data directory is invalid" : "Din datamapp är ogiltig", - "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Säkerställ att du har filen \".ocdata\" i huvudkatalogen för dina data.", + "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Säkerställ att du har filen \".ocdata\" i huvudkatalogen för din data.", "Action \"%s\" not supported or implemented." : "Åtgärd \"%s\" stöds inte eller är inte implementerad.", "Authentication failed, wrong token or provider ID given" : "Autentisering misslyckades, felaktig token eller leverantörs-ID", "Parameters missing in order to complete the request. Missing Parameters: \"%s\"" : "Parametrar saknas för att slutföra förfrågan. Saknade parametrar: \"%s\"", diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json index db2ed216378..cfedb5cdd87 100644 --- a/lib/l10n/sv.json +++ b/lib/l10n/sv.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "Kan inte skriva till \"config\" katalogen!", "This can usually be fixed by giving the webserver write access to the config directory." : "Detta kan vanligtvis åtgärdas genom att ge webbservern skrivåtkomst till config-katalogen.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Men, om du föredrar att behålla config.php skrivskyddad, sätt alternativet \"config_is_read_only\" till true i den.", "See %s" : "Se %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Filerna i appen %1$s ersattes inte korrekt. Kontrollera att det är en version som är kompatibel med servern.", "Sample configuration detected" : "Exempel-konfiguration detekterad", @@ -13,10 +14,12 @@ "Education Edition" : "Utbildningspaket", "Enterprise bundle" : "Företagspaketet", "Groupware bundle" : "Gruppvarupaket", + "Hub bundle" : "Hub-paket", "Social sharing bundle" : "Kommunikationspaket", "PHP %s or higher is required." : "PHP %s eller högre krävs.", "PHP with a version lower than %s is required." : "PHP med version lägre än %s krävs.", "%sbit or higher PHP required." : "%sbit eller nyare PHP-version krävs.", + "The following architectures are supported: %s" : "Följande arkitekturer stöds: %s", "The following databases are supported: %s" : "Följande databaser stöds: %s", "The command line tool %s could not be found" : "Kommandoradsverktyget %s hittades inte.", "The library %s is not available." : "Biblioteket %s är inte tillgängligt.", @@ -64,6 +67,10 @@ "seconds ago" : "sekunder sedan", "Empty file" : "Tom fil", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finns inte längre. Vänligen aktivera det i dina appinställningar eller kontakta din administratör.", + "File already exists" : "Filen existerar redan", + "Invalid path" : "Ogiltig sökväg", + "Failed to create file from template" : "Kunde skapa fil från mall", + "Templates" : "Mallar", "File name is a reserved word" : "Filnamnet är ett reserverat ord", "File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken", "File name is too long" : "Filnamnet är för långt", @@ -95,6 +102,7 @@ "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Vänligen ta bort open_basedir-inställningen i din php.ini eller växla till 64-bitars PHP.", "Set an admin username." : "Ange ett användarnamn för administratören.", "Set an admin password." : "Ange ett administratörslösenord.", + "Cannot create or write into the data directory %s" : "Kan inte skapa eller skriva till data-katalogen %s", "Sharing backend %s must implement the interface OCP\\Share_Backend" : "Delningsgränssnittet %s måste implementera gränssnittet OCP\\Share_Backend", "Sharing backend %s not found" : "Delningsgränssnittet %s hittades inte", "Sharing backend for %s not found" : "Delningsgränssnittet för %s hittades inte", @@ -104,7 +112,13 @@ "Open »%s«" : "Öppna »%s«", "%1$s via %2$s" : "%1$s via %2$s", "You are not allowed to share %s" : "Du har inte rätt att dela %s", + "Cannot increase permissions of %s" : "Kan inte höja behörigheter av %s", + "Files cannot be shared with delete permissions" : "Filer kan inte delas med rättighet att radera", + "Files cannot be shared with create permissions" : "Filer kan inte delas med rättighet att skapa", "Expiration date is in the past" : "Utgångsdatum är i det förflutna", + "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Kan inte sätta ett utgångsdatum längre fram än %n dag","Kan inte sätta ett utgångsdatum längre fram än %n dagar"], + "Sharing is only allowed with group members" : "Delning är endast tillåten med gruppmedlemmar", + "Sharing %s failed, because this item is already shared with user %s" : "Delning av %s misslyckades eftersom detta redan är delat med användaren %s", "%1$s shared »%2$s« with you" : "%1$s delade »%2$s« med dig", "%1$s shared »%2$s« with you." : "%1$s delade »%2$s« med dig.", "Click the button below to open it." : "Klicka på knappen nedan för att öppna det.", @@ -155,6 +169,7 @@ "Oct." : "Okt.", "Nov." : "Nov.", "Dec." : "Dec.", + "The user limit has been reached and the user was not created." : "Maximala användarantalet har uppnåtts och användaren skapades inte.", "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Endast följande tecken är tillåtna i användarnamnet: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"", "A valid username must be provided" : "Ett giltigt användarnamn måste anges", "Username contains whitespace at the beginning or at the end" : "Användarnamnet består av ett mellanslag i början eller i slutet", @@ -166,8 +181,9 @@ "User disabled" : "Användare inaktiverad", "Login canceled by app" : "Inloggningen avbruten av appen", "App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Appen \"%1$s\" kan inte installeras eftersom följande beroenden inte är uppfyllda: %2$s", - "a safe home for all your data" : "ett säkert hem för alla dina data", + "a safe home for all your data" : "ett säkert hem för all din data", "File is currently busy, please try again later" : "Filen är för tillfället upptagen, vänligen försök igen senare", + "Cannot read file" : "Kan inte läsa fil", "Application is not enabled" : "Applikationen är inte aktiverad", "Authentication error" : "Fel vid autentisering", "Token expired. Please reload page." : "Token har löpt ut. Vänligen uppdatera sidan.", @@ -176,6 +192,7 @@ "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Detta kan vanligtvis åtgärda genom att ge webbservern skrivåtkomst till konfigureringsmappen. Se %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" : "Eller, om du föredrar att behålla config.php skrivskyddad, sätt alternativet \"config_is_read_only\" till true i den. Se %s", "Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!", + "This can usually be fixed by giving the webserver write access to the apps directory or disabling the App Store in the config file." : "Detta kan vanligtvis åtgärdas genom att ge webbservern skrivrättigheter till applikationskatalogen eller stänga av appbutiken i konfigurationsfilen.", "Cannot create \"data\" directory" : "Kan inte skapa \"data\"-mapp", "This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Detta kan vanligtvis åtgärda genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s", "Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Rättigheter kan vanligtvis fixas genom att ge webbservern skrivåtkomst till rotkatalogen. Se %s.", @@ -200,7 +217,7 @@ "Your data directory must be an absolute path" : "Du måste specificera en korrekt sökväg till datamappen", "Check the value of \"datadirectory\" in your configuration" : "Kontrollera värdet av \"datakatalog\" i din konfiguration", "Your data directory is invalid" : "Din datamapp är ogiltig", - "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Säkerställ att du har filen \".ocdata\" i huvudkatalogen för dina data.", + "Ensure there is a file called \".ocdata\" in the root of the data directory." : "Säkerställ att du har filen \".ocdata\" i huvudkatalogen för din data.", "Action \"%s\" not supported or implemented." : "Åtgärd \"%s\" stöds inte eller är inte implementerad.", "Authentication failed, wrong token or provider ID given" : "Autentisering misslyckades, felaktig token eller leverantörs-ID", "Parameters missing in order to complete the request. Missing Parameters: \"%s\"" : "Parametrar saknas för att slutföra förfrågan. Saknade parametrar: \"%s\"", diff --git a/lib/l10n/tr.js b/lib/l10n/tr.js index 039a0e8b15a..fd7fb2b415e 100644 --- a/lib/l10n/tr.js +++ b/lib/l10n/tr.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "\"config\" klasörüne yazılamadı!", "This can usually be fixed by giving the webserver write access to the config directory." : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Ama config.php dosyasının salt okunur olarak kalmasını istiyorsanız içindeki \"config_is_read_only\" seçeneğini true olarak ayarlayın.", "See %s" : "Şuraya bakın: %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "%1$s uygulamasının dosyaları doğru şekilde değiştirilmedi. Sunucu ile uyumlu dosyaların yüklü olduğundan emin olun.", "Sample configuration detected" : "Örnek yapılandırma algılandı", diff --git a/lib/l10n/tr.json b/lib/l10n/tr.json index 52fdfb5433e..a0a860d2c44 100644 --- a/lib/l10n/tr.json +++ b/lib/l10n/tr.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "\"config\" klasörüne yazılamadı!", "This can usually be fixed by giving the webserver write access to the config directory." : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir.", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Ama config.php dosyasının salt okunur olarak kalmasını istiyorsanız içindeki \"config_is_read_only\" seçeneğini true olarak ayarlayın.", "See %s" : "Şuraya bakın: %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "%1$s uygulamasının dosyaları doğru şekilde değiştirilmedi. Sunucu ile uyumlu dosyaların yüklü olduğundan emin olun.", "Sample configuration detected" : "Örnek yapılandırma algılandı", diff --git a/lib/l10n/zh_HK.js b/lib/l10n/zh_HK.js index c706d20b97f..cb2616ee74e 100644 --- a/lib/l10n/zh_HK.js +++ b/lib/l10n/zh_HK.js @@ -172,7 +172,7 @@ OC.L10N.register( "Nov." : "十一月", "Dec." : "十二月", "The user limit has been reached and the user was not created." : "用戶數量已達上限,無法創建新用戶。", - "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "用戶名稱當中只能包含下列字元:\"a-z\", \"A-Z\", \"0-9\", 和 \"_。@-'\"", + "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "用戶名稱當中只能包含下列字元:\"a-z\", \"A-Z\", \"0-9\", 和 \"_.@-'\"", "A valid username must be provided" : "必須提供一個有效的用戶名", "Username contains whitespace at the beginning or at the end" : "用戶名的開頭或結尾有空白", "Username must not consist of dots only" : "用戶名稱不能只包含小數點", @@ -189,7 +189,7 @@ OC.L10N.register( "Application is not enabled" : "應用程式未啟用", "Authentication error" : "認證錯誤", "Token expired. Please reload page." : "Token 過期,請重新整理頁面。", - "No database drivers (sqlite, mysql, or postgresql) installed." : "沒有安裝數據庫驅動程式(sqlite, mysql, 或 postgresql)", + "No database drivers (sqlite, mysql, or postgresql) installed." : "沒有安裝數據庫驅動程式(sqlite, mysql, 或 postgresql)", "Cannot write into \"config\" directory" : "無法寫入 config 目錄", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %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", @@ -204,7 +204,7 @@ OC.L10N.register( "Please ask your server administrator to install the module." : "請詢問系統管理員來安裝這些模組", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 設定值 \"%s\" 沒有被設定為 \"%s\"", "Adjusting this setting in php.ini will make Nextcloud run again" : "調整 php.ini 中的設定,使 Nextcloud 重新運作", - "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring。func_overload 應該要被設定成 \"0\" 而不是目前的設定 \"%s\" ", + "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 應該要被設定成 \"0\" 而不是目前的設定 \"%s\" ", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "為了修正這個問題,請到 php.ini 將 <code>mbstring.func_overload</code> 的值改為 <code>0</code>", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 版本最低需求為 2.7.0。目前安裝版本為 %s。", "To fix this issue update your libxml2 version and restart your web server." : "修正方式為更新您的 libxml2 為 2.7.0 以上版本,再重啟網頁伺服器。", diff --git a/lib/l10n/zh_HK.json b/lib/l10n/zh_HK.json index dc47999610f..e317500f2c6 100644 --- a/lib/l10n/zh_HK.json +++ b/lib/l10n/zh_HK.json @@ -170,7 +170,7 @@ "Nov." : "十一月", "Dec." : "十二月", "The user limit has been reached and the user was not created." : "用戶數量已達上限,無法創建新用戶。", - "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "用戶名稱當中只能包含下列字元:\"a-z\", \"A-Z\", \"0-9\", 和 \"_。@-'\"", + "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "用戶名稱當中只能包含下列字元:\"a-z\", \"A-Z\", \"0-9\", 和 \"_.@-'\"", "A valid username must be provided" : "必須提供一個有效的用戶名", "Username contains whitespace at the beginning or at the end" : "用戶名的開頭或結尾有空白", "Username must not consist of dots only" : "用戶名稱不能只包含小數點", @@ -187,7 +187,7 @@ "Application is not enabled" : "應用程式未啟用", "Authentication error" : "認證錯誤", "Token expired. Please reload page." : "Token 過期,請重新整理頁面。", - "No database drivers (sqlite, mysql, or postgresql) installed." : "沒有安裝數據庫驅動程式(sqlite, mysql, 或 postgresql)", + "No database drivers (sqlite, mysql, or postgresql) installed." : "沒有安裝數據庫驅動程式(sqlite, mysql, 或 postgresql)", "Cannot write into \"config\" directory" : "無法寫入 config 目錄", "This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %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", @@ -202,7 +202,7 @@ "Please ask your server administrator to install the module." : "請詢問系統管理員來安裝這些模組", "PHP setting \"%s\" is not set to \"%s\"." : "PHP 設定值 \"%s\" 沒有被設定為 \"%s\"", "Adjusting this setting in php.ini will make Nextcloud run again" : "調整 php.ini 中的設定,使 Nextcloud 重新運作", - "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring。func_overload 應該要被設定成 \"0\" 而不是目前的設定 \"%s\" ", + "mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload 應該要被設定成 \"0\" 而不是目前的設定 \"%s\" ", "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "為了修正這個問題,請到 php.ini 將 <code>mbstring.func_overload</code> 的值改為 <code>0</code>", "libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 版本最低需求為 2.7.0。目前安裝版本為 %s。", "To fix this issue update your libxml2 version and restart your web server." : "修正方式為更新您的 libxml2 為 2.7.0 以上版本,再重啟網頁伺服器。", diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js index 38775048e48..284f1e40498 100644 --- a/lib/l10n/zh_TW.js +++ b/lib/l10n/zh_TW.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Cannot write into \"config\" directory!" : "無法寫入 \"config\" 目錄!", "This can usually be fixed by giving the webserver write access to the config directory." : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "但如果您比較希望保留 config.php 的唯讀狀態,請在該設定檔中將 \"config_is_read_only\" 設定為 true。", "See %s" : "見 %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "應用程式 %1$s 中的檔案沒有被正確取代,請確認它的版本與伺服器相容。", "Sample configuration detected" : "您目前正在使用範例設定", diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json index 94d05502ef6..b80d22e535e 100644 --- a/lib/l10n/zh_TW.json +++ b/lib/l10n/zh_TW.json @@ -1,6 +1,7 @@ { "translations": { "Cannot write into \"config\" directory!" : "無法寫入 \"config\" 目錄!", "This can usually be fixed by giving the webserver write access to the config directory." : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題", + "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "但如果您比較希望保留 config.php 的唯讀狀態,請在該設定檔中將 \"config_is_read_only\" 設定為 true。", "See %s" : "見 %s", "The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "應用程式 %1$s 中的檔案沒有被正確取代,請確認它的版本與伺服器相容。", "Sample configuration detected" : "您目前正在使用範例設定", diff --git a/lib/private/Accounts/Account.php b/lib/private/Accounts/Account.php index 7d2a51c7d4e..1e4189f2b35 100644 --- a/lib/private/Accounts/Account.php +++ b/lib/private/Accounts/Account.php @@ -33,6 +33,7 @@ use OCP\Accounts\IAccountProperty; use OCP\Accounts\IAccountPropertyCollection; use OCP\Accounts\PropertyDoesNotExistException; use OCP\IUser; +use RuntimeException; class Account implements IAccount { use TAccountsHelper; @@ -116,13 +117,16 @@ class Account implements IAccount { return $this; } - public function getPropertyCollection(string $propertyCollection): IAccountPropertyCollection { - if (!array_key_exists($propertyCollection, $this->properties)) { - throw new PropertyDoesNotExistException($propertyCollection); + public function getPropertyCollection(string $propertyCollectionName): IAccountPropertyCollection { + if (!$this->isCollection($propertyCollectionName)) { + throw new PropertyDoesNotExistException($propertyCollectionName); } - if (!$this->properties[$propertyCollection] instanceof IAccountPropertyCollection) { - throw new \RuntimeException('Requested collection is not an IAccountPropertyCollection'); + if (!array_key_exists($propertyCollectionName, $this->properties)) { + $this->properties[$propertyCollectionName] = new AccountPropertyCollection($propertyCollectionName); } - return $this->properties[$propertyCollection]; + if (!$this->properties[$propertyCollectionName] instanceof IAccountPropertyCollection) { + throw new RuntimeException('Requested collection is not an IAccountPropertyCollection'); + } + return $this->properties[$propertyCollectionName]; } } diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index 4d75c94346b..9fc5accfa08 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -32,6 +32,7 @@ */ namespace OC\Accounts; +use InvalidArgumentException; use libphonenumber\NumberParseException; use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberFormat; @@ -39,6 +40,9 @@ use libphonenumber\PhoneNumberUtil; use OCA\Settings\BackgroundJobs\VerifyUserData; use OCP\Accounts\IAccount; use OCP\Accounts\IAccountManager; +use OCP\Accounts\IAccountProperty; +use OCP\Accounts\IAccountPropertyCollection; +use OCP\Accounts\PropertyDoesNotExistException; use OCP\BackgroundJob\IJobList; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; @@ -48,7 +52,9 @@ use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; use function array_flip; +use function iterator_to_array; use function json_decode; +use function json_encode; use function json_last_error; /** @@ -98,7 +104,7 @@ class AccountManager implements IAccountManager { /** * @param string $input * @return string Provided phone number in E.164 format when it was a valid number - * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code + * @throws InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code */ protected function parsePhoneNumber(string $input): string { $defaultRegion = $this->config->getSystemValueString('default_phone_region', ''); @@ -106,7 +112,7 @@ class AccountManager implements IAccountManager { if ($defaultRegion === '') { // When no default region is set, only +49… numbers are valid if (strpos($input, '+') !== 0) { - throw new \InvalidArgumentException(self::PROPERTY_PHONE); + throw new InvalidArgumentException(self::PROPERTY_PHONE); } $defaultRegion = 'EN'; @@ -121,140 +127,107 @@ class AccountManager implements IAccountManager { } catch (NumberParseException $e) { } - throw new \InvalidArgumentException(self::PROPERTY_PHONE); + throw new InvalidArgumentException(self::PROPERTY_PHONE); } /** * * @param string $input * @return string - * @throws \InvalidArgumentException When the website did not have http(s) as protocol or the host name was empty + * @throws InvalidArgumentException When the website did not have http(s) as protocol or the host name was empty */ protected function parseWebsite(string $input): string { $parts = parse_url($input); if (!isset($parts['scheme']) || ($parts['scheme'] !== 'https' && $parts['scheme'] !== 'http')) { - throw new \InvalidArgumentException(self::PROPERTY_WEBSITE); + throw new InvalidArgumentException(self::PROPERTY_WEBSITE); } if (!isset($parts['host']) || $parts['host'] === '') { - throw new \InvalidArgumentException(self::PROPERTY_WEBSITE); + throw new InvalidArgumentException(self::PROPERTY_WEBSITE); } return $input; } - protected function sanitizeLength(array &$propertyData, bool $throwOnData = false): void { - if (isset($propertyData['value']) && strlen($propertyData['value']) > 2048) { - if ($throwOnData) { - throw new \InvalidArgumentException(); - } else { - $propertyData['value'] = ''; - } - } - } - - protected function testValueLengths(array &$data, bool $throwOnData = false): void { - try { - foreach ($data as $propertyName => &$propertyData) { - if ($this->isCollection($propertyName)) { - $this->testValueLengths($propertyData, $throwOnData); + /** + * @param IAccountProperty[] $properties + */ + protected function testValueLengths(array $properties, bool $throwOnData = false): void { + foreach ($properties as $property) { + if (strlen($property->getValue()) > 2048) { + if ($throwOnData) { + throw new InvalidArgumentException(); } else { - $this->sanitizeLength($propertyData, $throwOnData); + $property->setValue(''); } } - } catch (\InvalidArgumentException $e) { - throw new \InvalidArgumentException($propertyName); } } - protected function testPropertyScopes(array &$data, array $allowedScopes, bool $throwOnData = false, string $parentPropertyName = null): void { - foreach ($data as $propertyNameOrIndex => &$propertyData) { - if ($this->isCollection($propertyNameOrIndex)) { - $this->testPropertyScopes($propertyData, $allowedScopes, $throwOnData); - } elseif (isset($propertyData['scope'])) { - $effectivePropertyName = $parentPropertyName ?? $propertyNameOrIndex; - - if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) { - throw new \InvalidArgumentException('scope'); - } + protected function testPropertyScope(IAccountProperty $property, array $allowedScopes, bool $throwOnData): void { + if ($throwOnData && !in_array($property->getScope(), $allowedScopes, true)) { + throw new InvalidArgumentException('scope'); + } - if ( - $propertyData['scope'] === self::SCOPE_PRIVATE - && ($effectivePropertyName === self::PROPERTY_DISPLAYNAME || $effectivePropertyName === self::PROPERTY_EMAIL) - ) { - if ($throwOnData) { - // v2-private is not available for these fields - throw new \InvalidArgumentException('scope'); - } else { - // default to local - $data[$propertyNameOrIndex]['scope'] = self::SCOPE_LOCAL; - } - } else { - // migrate scope values to the new format - // invalid scopes are mapped to a default value - $data[$propertyNameOrIndex]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']); - } + if ( + $property->getScope() === self::SCOPE_PRIVATE + && in_array($property->getName(), [self::PROPERTY_DISPLAYNAME, self::PROPERTY_EMAIL]) + ) { + if ($throwOnData) { + // v2-private is not available for these fields + throw new InvalidArgumentException('scope'); + } else { + // default to local + $property->setScope(self::SCOPE_LOCAL); } + } else { + // migrate scope values to the new format + // invalid scopes are mapped to a default value + $property->setScope(AccountProperty::mapScopeToV2($property->getScope())); } } - /** - * update user record - * - * @param IUser $user - * @param array $data - * @param bool $throwOnData Set to true if you can inform the user about invalid data - * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format) - * @throws \InvalidArgumentException Message is the property that was invalid - */ - public function updateUser(IUser $user, array $data, bool $throwOnData = false): array { - $userData = $this->getUser($user); - $updated = true; - - if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') { - // Sanitize null value. - $data[self::PROPERTY_PHONE]['value'] = $data[self::PROPERTY_PHONE]['value'] ?? ''; - - try { - $data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']); - } catch (\InvalidArgumentException $e) { - if ($throwOnData) { - throw $e; - } - $data[self::PROPERTY_PHONE]['value'] = ''; + protected function sanitizePhoneNumberValue(IAccountProperty $property, bool $throwOnData = false) { + if ($property->getName() !== self::PROPERTY_PHONE) { + if ($throwOnData) { + throw new InvalidArgumentException(sprintf('sanitizePhoneNumberValue can only sanitize phone numbers, %s given', $property->getName())); } + return; } - - $this->testValueLengths($data); - - if (isset($data[self::PROPERTY_WEBSITE]) && $data[self::PROPERTY_WEBSITE]['value'] !== '') { - try { - $data[self::PROPERTY_WEBSITE]['value'] = $this->parseWebsite($data[self::PROPERTY_WEBSITE]['value']); - } catch (\InvalidArgumentException $e) { - if ($throwOnData) { - throw $e; - } - $data[self::PROPERTY_WEBSITE]['value'] = ''; + if ($property->getValue() === '') { + return; + } + try { + $property->setValue($this->parsePhoneNumber($property->getValue())); + } catch (InvalidArgumentException $e) { + if ($throwOnData) { + throw $e; } + $property->setValue(''); } + } - $allowedScopes = [ - self::SCOPE_PRIVATE, - self::SCOPE_LOCAL, - self::SCOPE_FEDERATED, - self::SCOPE_PUBLISHED, - self::VISIBILITY_PRIVATE, - self::VISIBILITY_CONTACTS_ONLY, - self::VISIBILITY_PUBLIC, - ]; + protected function sanitizeWebsite(IAccountProperty $property, bool $throwOnData = false) { + if ($property->getName() !== self::PROPERTY_WEBSITE) { + if ($throwOnData) { + throw new InvalidArgumentException(sprintf('sanitizeWebsite can only sanitize web domains, %s given', $property->getName())); + } + } + try { + $property->setValue($this->parseWebsite($property->getValue())); + } catch (InvalidArgumentException $e) { + if ($throwOnData) { + throw $e; + } + $property->setValue(''); + } + } - $this->testPropertyScopes($data, $allowedScopes, $throwOnData); + protected function updateUser(IUser $user, array $data, bool $throwOnData = false): array { + $oldUserData = $this->getUser($user, false); + $updated = true; - if (empty($userData)) { - $this->insertNewUser($user, $data); - } elseif ($userData !== $data) { - $data = $this->checkEmailVerification($userData, $data, $user); - $data = $this->updateVerifyStatus($userData, $data); + if ($oldUserData !== $data) { $this->updateExistingUser($user, $data); } else { // nothing needs to be done if new and old data set are the same @@ -301,17 +274,15 @@ class AccountManager implements IAccountManager { /** * get stored data from a given user - * - * @deprecated use getAccount instead to make sure migrated properties work correctly */ - public function getUser(IUser $user, bool $insertIfNotExists = true): array { + protected function getUser(IUser $user, bool $insertIfNotExists = true): array { $uid = $user->getUID(); $query = $this->connection->getQueryBuilder(); $query->select('data') ->from($this->table) ->where($query->expr()->eq('uid', $query->createParameter('uid'))) ->setParameter('uid', $uid); - $result = $query->execute(); + $result = $query->executeQuery(); $accountData = $result->fetchAll(); $result->closeCursor(); @@ -323,10 +294,8 @@ class AccountManager implements IAccountManager { return $userData; } - $userDataArray = json_decode($accountData[0]['data'], true); - $jsonError = json_last_error(); - if ($userDataArray === null || $userDataArray === [] || $jsonError !== JSON_ERROR_NONE) { - $this->logger->critical("User data of $uid contained invalid JSON (error $jsonError), hence falling back to a default user record"); + $userDataArray = $this->importFromJson($accountData[0]['data'], $uid); + if ($userDataArray === null || $userDataArray === []) { return $this->buildDefaultUserRecord($user); } @@ -344,7 +313,7 @@ class AccountManager implements IAccountManager { $matches = []; foreach ($chunks as $chunk) { $query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY); - $result = $query->execute(); + $result = $query->executeQuery(); while ($row = $result->fetch()) { $matches[$row['uid']] = $row['value']; @@ -369,100 +338,75 @@ class AccountManager implements IAccountManager { /** * check if we need to ask the server for email verification, if yes we create a cronjob * - * @param $oldData - * @param $newData - * @param IUser $user - * @return array */ - protected function checkEmailVerification($oldData, $newData, IUser $user): array { - if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) { + protected function checkEmailVerification(IAccount $updatedAccount, array $oldData): void { + try { + $property = $updatedAccount->getProperty(self::PROPERTY_EMAIL); + } catch (PropertyDoesNotExistException $e) { + return; + } + $oldMail = isset($oldData[self::PROPERTY_EMAIL]) ? $oldData[self::PROPERTY_EMAIL]['value']['value'] : ''; + if ($oldMail !== $property->getValue()) { $this->jobList->add(VerifyUserData::class, [ 'verificationCode' => '', - 'data' => $newData[self::PROPERTY_EMAIL]['value'], + 'data' => $property->getValue(), 'type' => self::PROPERTY_EMAIL, - 'uid' => $user->getUID(), + 'uid' => $updatedAccount->getUser()->getUID(), 'try' => 0, 'lastRun' => time() ] ); - $newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS; - } - return $newData; + + + + $property->setVerified(self::VERIFICATION_IN_PROGRESS); + } } /** * make sure that all expected data are set * - * @param array $userData - * @return array */ - protected function addMissingDefaultValues(array $userData) { - foreach ($userData as $key => $value) { - if (!isset($userData[$key]['verified'])) { - $userData[$key]['verified'] = self::NOT_VERIFIED; + protected function addMissingDefaultValues(array $userData): array { + foreach ($userData as $i => $value) { + if (!isset($value['verified'])) { + $userData[$i]['verified'] = self::NOT_VERIFIED; } } return $userData; } - /** - * reset verification status if personal data changed - * - * @param array $oldData - * @param array $newData - * @return array - */ - protected function updateVerifyStatus(array $oldData, array $newData): array { - - // which account was already verified successfully? - $twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED; - $websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED; - $emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED; - - // keep old verification status if we don't have a new one - if (!isset($newData[self::PROPERTY_TWITTER]['verified'])) { - // keep old verification status if value didn't changed and an old value exists - $keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']); - $newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED; - } - - if (!isset($newData[self::PROPERTY_WEBSITE]['verified'])) { - // keep old verification status if value didn't changed and an old value exists - $keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']); - $newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED; - } - - if (!isset($newData[self::PROPERTY_EMAIL]['verified'])) { - // keep old verification status if value didn't changed and an old value exists - $keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']); - $newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS; - } - - // reset verification status if a value from a previously verified data was changed - if ($twitterVerified && - $oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value'] - ) { - $newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED; - } - - if ($websiteVerified && - $oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value'] - ) { - $newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED; - } + protected function updateVerificationStatus(IAccount $updatedAccount, array $oldData): void { + static $propertiesVerifiableByLookupServer = [ + self::PROPERTY_TWITTER, + self::PROPERTY_WEBSITE, + self::PROPERTY_EMAIL, + ]; - if ($emailVerified && - $oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value'] - ) { - $newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED; + foreach ($propertiesVerifiableByLookupServer as $propertyName) { + try { + $property = $updatedAccount->getProperty($propertyName); + } catch (PropertyDoesNotExistException $e) { + continue; + } + $wasVerified = isset($oldData[$propertyName]) + && isset($oldData[$propertyName]['verified']) + && $oldData[$propertyName]['verified'] === self::VERIFIED; + if ((!isset($oldData[$propertyName]) + || !isset($oldData[$propertyName]['value']) + || $property->getValue() !== $oldData[$propertyName]['value']) + && ($property->getVerified() !== self::NOT_VERIFIED + || $wasVerified) + ) { + $property->setVerified(self::NOT_VERIFIED); + } } - - return $newData; } + /** * add new user to accounts table * @@ -471,7 +415,7 @@ class AccountManager implements IAccountManager { */ protected function insertNewUser(IUser $user, array $data): void { $uid = $user->getUID(); - $jsonEncodedData = json_encode($data); + $jsonEncodedData = $this->prepareJson($data); $query = $this->connection->getQueryBuilder(); $query->insert($this->table) ->values( @@ -480,12 +424,55 @@ class AccountManager implements IAccountManager { 'data' => $query->createNamedParameter($jsonEncodedData), ] ) - ->execute(); + ->executeStatement(); $this->deleteUserData($user); $this->writeUserData($user, $data); } + protected function prepareJson(array $data): string { + $preparedData = []; + foreach ($data as $dataRow) { + $propertyName = $dataRow['name']; + unset($dataRow['name']); + if (!$this->isCollection($propertyName)) { + $preparedData[$propertyName] = $dataRow; + continue; + } + if (!isset($preparedData[$propertyName])) { + $preparedData[$propertyName] = []; + } + $preparedData[$propertyName][] = $dataRow; + } + return json_encode($preparedData); + } + + protected function importFromJson(string $json, string $userId): ?array { + $result = []; + $jsonArray = json_decode($json, true); + $jsonError = json_last_error(); + if ($jsonError !== JSON_ERROR_NONE) { + $this->logger->critical( + 'User data of {uid} contained invalid JSON (error {json_error}), hence falling back to a default user record', + [ + 'uid' => $userId, + 'json_error' => $jsonError + ] + ); + return null; + } + foreach ($jsonArray as $propertyName => $row) { + if (!$this->isCollection($propertyName)) { + $result[] = array_merge($row, ['name' => $propertyName]); + continue; + } + foreach ($row as $singleRow) { + $result[] = array_merge($singleRow, ['name' => $propertyName]); + } + } + return $result; + } + /** * update existing user in accounts table * @@ -494,12 +481,12 @@ class AccountManager implements IAccountManager { */ protected function updateExistingUser(IUser $user, array $data): void { $uid = $user->getUID(); - $jsonEncodedData = json_encode($data); + $jsonEncodedData = $this->prepareJson($data); $query = $this->connection->getQueryBuilder(); $query->update($this->table) ->set('data', $query->createNamedParameter($jsonEncodedData)) ->where($query->expr()->eq('uid', $query->createNamedParameter($uid))) - ->execute(); + ->executeStatement(); $this->deleteUserData($user); $this->writeUserData($user, $data); @@ -518,19 +505,16 @@ class AccountManager implements IAccountManager { $this->writeUserDataProperties($query, $data); } - protected function writeUserDataProperties(IQueryBuilder $query, array $data, string $parentPropertyName = null): void { - foreach ($data as $propertyName => $property) { - if ($this->isCollection($propertyName)) { - $this->writeUserDataProperties($query, $property, $propertyName); - continue; - } - if (($parentPropertyName ?? $propertyName) === self::PROPERTY_AVATAR) { + protected function writeUserDataProperties(IQueryBuilder $query, array $data): void { + foreach ($data as $property) { + if ($property['name'] === self::PROPERTY_AVATAR) { continue; } - $query->setParameter('name', $parentPropertyName ?? $propertyName) + + $query->setParameter('name', $property['name']) ->setParameter('value', $property['value'] ?? ''); - $query->execute(); + $query->executeStatement(); } } @@ -542,53 +526,80 @@ class AccountManager implements IAccountManager { */ protected function buildDefaultUserRecord(IUser $user) { return [ - self::PROPERTY_DISPLAYNAME => - [ - 'value' => $user->getDisplayName(), - 'scope' => self::SCOPE_FEDERATED, - 'verified' => self::NOT_VERIFIED, - ], - self::PROPERTY_ADDRESS => - [ - 'value' => '', - 'scope' => self::SCOPE_LOCAL, - 'verified' => self::NOT_VERIFIED, - ], - self::PROPERTY_WEBSITE => - [ - 'value' => '', - 'scope' => self::SCOPE_LOCAL, - 'verified' => self::NOT_VERIFIED, - ], - self::PROPERTY_EMAIL => - [ - 'value' => $user->getEMailAddress(), - 'scope' => self::SCOPE_FEDERATED, - 'verified' => self::NOT_VERIFIED, - ], - self::PROPERTY_AVATAR => - [ - 'scope' => self::SCOPE_FEDERATED - ], - self::PROPERTY_PHONE => - [ - 'value' => '', - 'scope' => self::SCOPE_LOCAL, - 'verified' => self::NOT_VERIFIED, - ], - self::PROPERTY_TWITTER => - [ - 'value' => '', - 'scope' => self::SCOPE_LOCAL, - 'verified' => self::NOT_VERIFIED, - ], + + [ + 'name' => self::PROPERTY_DISPLAYNAME, + 'value' => $user->getDisplayName(), + 'scope' => self::SCOPE_FEDERATED, + 'verified' => self::NOT_VERIFIED, + ], + + [ + 'name' => self::PROPERTY_ADDRESS, + 'value' => '', + 'scope' => self::SCOPE_LOCAL, + 'verified' => self::NOT_VERIFIED, + ], + + [ + 'name' => self::PROPERTY_WEBSITE, + 'value' => '', + 'scope' => self::SCOPE_LOCAL, + 'verified' => self::NOT_VERIFIED, + ], + + [ + 'name' => self::PROPERTY_EMAIL, + 'value' => $user->getEMailAddress(), + 'scope' => self::SCOPE_FEDERATED, + 'verified' => self::NOT_VERIFIED, + ], + + [ + 'name' => self::PROPERTY_AVATAR, + 'scope' => self::SCOPE_FEDERATED + ], + + [ + 'name' => self::PROPERTY_PHONE, + 'value' => '', + 'scope' => self::SCOPE_LOCAL, + 'verified' => self::NOT_VERIFIED, + ], + + [ + 'name' => self::PROPERTY_TWITTER, + 'value' => '', + 'scope' => self::SCOPE_LOCAL, + 'verified' => self::NOT_VERIFIED, + ], + ]; } + private function arrayDataToCollection(IAccount $account, array $data): IAccountPropertyCollection { + $collection = $account->getPropertyCollection($data['name']); + + $p = new AccountProperty( + $data['name'], + $data['value'] ?? '', + $data['scope'] ?? self::SCOPE_LOCAL, + $data['verified'] ?? self::NOT_VERIFIED, + '' + ); + $collection->addProperty($p); + + return $collection; + } + private function parseAccountData(IUser $user, $data): Account { $account = new Account($user); - foreach ($data as $property => $accountData) { - $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED); + foreach ($data as $accountData) { + if ($this->isCollection($accountData['name'])) { + $account->setPropertyCollection($this->arrayDataToCollection($account, $accountData)); + } else { + $account->setProperty($accountData['name'], $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED); + } } return $account; } @@ -598,10 +609,42 @@ class AccountManager implements IAccountManager { } public function updateAccount(IAccount $account): void { - $data = []; + $this->testValueLengths(iterator_to_array($account->getAllProperties()), true); + try { + $property = $account->getProperty(self::PROPERTY_PHONE); + $this->sanitizePhoneNumberValue($property); + } catch (PropertyDoesNotExistException $e) { + // valid case, nothing to do + } + + try { + $property = $account->getProperty(self::PROPERTY_WEBSITE); + $this->sanitizeWebsite($property); + } catch (PropertyDoesNotExistException $e) { + // valid case, nothing to do + } + + static $allowedScopes = [ + self::SCOPE_PRIVATE, + self::SCOPE_LOCAL, + self::SCOPE_FEDERATED, + self::SCOPE_PUBLISHED, + self::VISIBILITY_PRIVATE, + self::VISIBILITY_CONTACTS_ONLY, + self::VISIBILITY_PUBLIC, + ]; + foreach ($account->getAllProperties() as $property) { + $this->testPropertyScope($property, $allowedScopes, true); + } - foreach ($account->getProperties() as $property) { - $data[$property->getName()] = [ + $oldData = $this->getUser($account->getUser(), false); + $this->updateVerificationStatus($account, $oldData); + $this->checkEmailVerification($account, $oldData); + + $data = []; + foreach ($account->getAllProperties() as $property) { + $data[] = [ + 'name' => $property->getName(), 'value' => $property->getValue(), 'scope' => $property->getScope(), 'verified' => $property->getVerified(), diff --git a/lib/private/Accounts/AccountPropertyCollection.php b/lib/private/Accounts/AccountPropertyCollection.php index 84e10e6a507..eb92536a6a0 100644 --- a/lib/private/Accounts/AccountPropertyCollection.php +++ b/lib/private/Accounts/AccountPropertyCollection.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace OC\Accounts; use InvalidArgumentException; +use OCP\Accounts\IAccountManager; use OCP\Accounts\IAccountProperty; use OCP\Accounts\IAccountPropertyCollection; @@ -63,6 +64,18 @@ class AccountPropertyCollection implements IAccountPropertyCollection { return $this; } + public function addPropertyWithDefaults(string $value): IAccountPropertyCollection { + $property = new AccountProperty( + $this->collectionName, + $value, + IAccountManager::SCOPE_LOCAL, + IAccountManager::NOT_VERIFIED, + '' + ); + $this->addProperty($property); + return $this; + } + public function removeProperty(IAccountProperty $property): IAccountPropertyCollection { $ref = array_search($property, $this->properties, true); if ($ref !== false) { diff --git a/lib/private/Accounts/Hooks.php b/lib/private/Accounts/Hooks.php index af078bd1db0..93918284180 100644 --- a/lib/private/Accounts/Hooks.php +++ b/lib/private/Accounts/Hooks.php @@ -25,66 +25,55 @@ namespace OC\Accounts; use OCP\Accounts\IAccountManager; +use OCP\Accounts\PropertyDoesNotExistException; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; use OCP\IUser; +use OCP\User\Events\UserChangedEvent; use Psr\Log\LoggerInterface; -class Hooks { +class Hooks implements IEventListener { - /** @var AccountManager|null */ + /** @var IAccountManager */ private $accountManager; - /** @var LoggerInterface */ private $logger; - public function __construct(LoggerInterface $logger) { + public function __construct(LoggerInterface $logger, IAccountManager $accountManager) { $this->logger = $logger; + $this->accountManager = $accountManager; } /** * update accounts table if email address or display name was changed from outside - * - * @param array $params */ - public function changeUserHook($params) { - $accountManager = $this->getAccountManager(); - - /** @var IUser $user */ - $user = isset($params['user']) ? $params['user'] : null; - $feature = isset($params['feature']) ? $params['feature'] : null; - $newValue = isset($params['value']) ? $params['value'] : null; + public function changeUserHook(IUser $user, string $feature, $newValue): void { + $account = $this->accountManager->getAccount($user); - if (is_null($user) || is_null($feature) || is_null($newValue)) { - $this->logger->warning('Missing expected parameters in change user hook'); + try { + switch ($feature) { + case 'eMailAddress': + $property = $account->getProperty(IAccountManager::PROPERTY_EMAIL); + break; + case 'displayName': + $property = $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME); + break; + } + } catch (PropertyDoesNotExistException $e) { + $this->logger->debug($e->getMessage(), ['exception' => $e]); return; } - $accountData = $accountManager->getUser($user); - - switch ($feature) { - case 'eMailAddress': - if ($accountData[IAccountManager::PROPERTY_EMAIL]['value'] !== $newValue) { - $accountData[IAccountManager::PROPERTY_EMAIL]['value'] = $newValue; - $accountManager->updateUser($user, $accountData); - } - break; - case 'displayName': - if ($accountData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] !== $newValue) { - $accountData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $newValue; - $accountManager->updateUser($user, $accountData); - } - break; + if (isset($property) && $property->getValue() !== (string)$newValue) { + $property->setValue($newValue); + $this->accountManager->updateAccount($account); } } - /** - * return instance of accountManager - * - * @return AccountManager - */ - protected function getAccountManager(): AccountManager { - if ($this->accountManager === null) { - $this->accountManager = \OC::$server->query(AccountManager::class); + public function handle(Event $event): void { + if (!$event instanceof UserChangedEvent) { + return; } - return $this->accountManager; + $this->changeUserHook($event->getUser(), $event->getFeature(), $event->getValue()); } } diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php index 08634fd11f9..1605e5bd230 100644 --- a/lib/private/App/AppStore/Fetcher/AppFetcher.php +++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php @@ -143,7 +143,9 @@ class AppFetcher extends Fetcher { foreach ($releases as $release) { $versions[] = $release['version']; } - usort($versions, 'version_compare'); + usort($versions, function ($version1, $version2) { + return version_compare($version1, $version2); + }); $versions = array_reverse($versions); if (isset($versions[0])) { $highestVersion = $versions[0]; diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php index 37cc0b71e3f..6c2f905afa5 100644 --- a/lib/private/AppFramework/App.php +++ b/lib/private/AppFramework/App.php @@ -34,11 +34,11 @@ namespace OC\AppFramework; use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Request; -use OC\HintException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\ICallbackResponse; use OCP\AppFramework\Http\IOutput; use OCP\AppFramework\QueryException; +use OCP\HintException; use OCP\IRequest; /** @@ -105,6 +105,7 @@ class App { /** * Shortcut for calling a controller method and printing the result + * * @param string $controllerName the name of the controller under which it is * stored in the DI container * @param string $methodName the method that you want to call diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index dacba0aba93..30ac63281d1 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -305,12 +305,20 @@ class RegistrationContext { */ public function delegateCapabilityRegistrations(array $apps): void { while (($registration = array_shift($this->capabilities)) !== null) { + $appId = $registration->getAppId(); + if (!isset($apps[$appId])) { + // If we land here something really isn't right. But at least we caught the + // notice that is otherwise emitted for the undefined index + $this->logger->error("App $appId not loaded for the capability registration"); + + continue; + } + try { - $apps[$registration->getAppId()] + $apps[$appId] ->getContainer() ->registerCapability($registration->getService()); } catch (Throwable $e) { - $appId = $registration->getAppId(); $this->logger->error("Error during capability registration of $appId: " . $e->getMessage(), [ 'exception' => $e, ]); @@ -372,11 +380,20 @@ class RegistrationContext { */ public function delegateContainerRegistrations(array $apps): void { while (($registration = array_shift($this->services)) !== null) { + $appId = $registration->getAppId(); + if (!isset($apps[$appId])) { + // If we land here something really isn't right. But at least we caught the + // notice that is otherwise emitted for the undefined index + $this->logger->error("App $appId not loaded for the container service registration"); + + continue; + } + try { /** * Register the service and convert the callable into a \Closure if necessary */ - $apps[$registration->getAppId()] + $apps[$appId] ->getContainer() ->registerService( $registration->getName(), @@ -384,7 +401,6 @@ class RegistrationContext { $registration->isShared() ); } catch (Throwable $e) { - $appId = $registration->getAppId(); $this->logger->error("Error during service registration of $appId: " . $e->getMessage(), [ 'exception' => $e, ]); @@ -392,15 +408,23 @@ class RegistrationContext { } while (($registration = array_shift($this->aliases)) !== null) { + $appId = $registration->getAppId(); + if (!isset($apps[$appId])) { + // If we land here something really isn't right. But at least we caught the + // notice that is otherwise emitted for the undefined index + $this->logger->error("App $appId not loaded for the container alias registration"); + + continue; + } + try { - $apps[$registration->getAppId()] + $apps[$appId] ->getContainer() ->registerAlias( $registration->getAlias(), $registration->getTarget() ); } catch (Throwable $e) { - $appId = $registration->getAppId(); $this->logger->error("Error during service alias registration of $appId: " . $e->getMessage(), [ 'exception' => $e, ]); @@ -408,16 +432,24 @@ class RegistrationContext { } while (($registration = array_shift($this->parameters)) !== null) { + $appId = $registration->getAppId(); + if (!isset($apps[$appId])) { + // If we land here something really isn't right. But at least we caught the + // notice that is otherwise emitted for the undefined index + $this->logger->error("App $appId not loaded for the container parameter registration"); + + continue; + } + try { - $apps[$registration->getAppId()] + $apps[$appId] ->getContainer() ->registerParameter( $registration->getName(), $registration->getValue() ); } catch (Throwable $e) { - $appId = $registration->getAppId(); - $this->logger->error("Error during service alias registration of $appId: " . $e->getMessage(), [ + $this->logger->error("Error during service parameter registration of $appId: " . $e->getMessage(), [ 'exception' => $e, ]); } @@ -429,12 +461,20 @@ class RegistrationContext { */ public function delegateMiddlewareRegistrations(array $apps): void { while (($middleware = array_shift($this->middlewares)) !== null) { + $appId = $middleware->getAppId(); + if (!isset($apps[$appId])) { + // If we land here something really isn't right. But at least we caught the + // notice that is otherwise emitted for the undefined index + $this->logger->error("App $appId not loaded for the container middleware registration"); + + continue; + } + try { - $apps[$middleware->getAppId()] + $apps[$appId] ->getContainer() ->registerMiddleWare($middleware->getService()); } catch (Throwable $e) { - $appId = $middleware->getAppId(); $this->logger->error("Error during capability registration of $appId: " . $e->getMessage(), [ 'exception' => $e, ]); diff --git a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php index dd3f202b6c1..4ebdf10f403 100644 --- a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php +++ b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php @@ -46,7 +46,7 @@ class NotModifiedMiddleware extends Middleware { } $modifiedSinceHeader = $this->request->getHeader('IF_MODIFIED_SINCE'); - if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTime::RFC2822)) { + if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC2822)) { $response->setStatus(Http::STATUS_NOT_MODIFIED); return $response; } diff --git a/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php new file mode 100644 index 00000000000..a56ac3bd54e --- /dev/null +++ b/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2021 Morris Jobke <hey@morrisjobke.de> + * + * @author Morris Jobke <hey@morrisjobke.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Authentication\Listeners; + +use OC\Authentication\WebAuthn\Db\PublicKeyCredentialMapper; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\User\Events\UserDeletedEvent; + +class UserDeletedWebAuthnCleanupListener implements IEventListener { + + /** @var PublicKeyCredentialMapper */ + private $credentialMapper; + + public function __construct(PublicKeyCredentialMapper $credentialMapper) { + $this->credentialMapper = $credentialMapper; + } + + public function handle(Event $event): void { + if (!($event instanceof UserDeletedEvent)) { + return; + } + + $this->credentialMapper->deleteByUid($event->getUser()->getUID()); + } +} diff --git a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php index 2fbf6fd375d..72c557eb53a 100644 --- a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php +++ b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php @@ -84,4 +84,17 @@ class PublicKeyCredentialMapper extends QBMapper { return $this->findEntity($qb); } + + /** + * @throws \OCP\DB\Exception + */ + public function deleteByUid(string $uid) { + $qb = $this->db->getQueryBuilder(); + + $qb->delete($this->getTableName()) + ->where( + $qb->expr()->eq('uid', $qb->createNamedParameter($uid)) + ); + $qb->executeStatement(); + } } diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php index 83b2b4b5c3d..c3afd8094c7 100644 --- a/lib/private/Avatar/AvatarManager.php +++ b/lib/private/Avatar/AvatarManager.php @@ -39,6 +39,7 @@ use OC\KnownUser\KnownUserService; use OC\User\Manager; use OC\User\NoUserException; use OCP\Accounts\IAccountManager; +use OCP\Accounts\PropertyDoesNotExistException; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; @@ -126,9 +127,13 @@ class AvatarManager implements IAvatarManager { $folder = $this->appData->newFolder($userId); } - $account = $this->accountManager->getAccount($user); - $avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR); - $avatarScope = $avatarProperties->getScope(); + try { + $account = $this->accountManager->getAccount($user); + $avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR); + $avatarScope = $avatarProperties->getScope(); + } catch (PropertyDoesNotExistException $e) { + $avatarScope = ''; + } if ( // v2-private scope hides the avatar from public access and from unknown users diff --git a/lib/private/Config.php b/lib/private/Config.php index e3ace96c359..2a83d2300dc 100644 --- a/lib/private/Config.php +++ b/lib/private/Config.php @@ -38,6 +38,8 @@ */ namespace OC; +use OCP\HintException; + /** * This class is responsible for reading and writing config.php, the very basic * configuration file of Nextcloud. diff --git a/lib/private/Console/TimestampFormatter.php b/lib/private/Console/TimestampFormatter.php index fcaa6ebfbf1..59e480b39e8 100644 --- a/lib/private/Console/TimestampFormatter.php +++ b/lib/private/Console/TimestampFormatter.php @@ -103,7 +103,7 @@ class TimestampFormatter implements OutputFormatterInterface { $timeZone = $timeZone !== null ? new \DateTimeZone($timeZone) : null; $time = new \DateTime('now', $timeZone); - $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', \DateTime::ATOM)); + $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', \DateTimeInterface::ATOM)); return $timestampInfo . ' ' . $this->formatter->format($message); } diff --git a/lib/private/DatabaseSetupException.php b/lib/private/DatabaseSetupException.php index b1b4956db41..44583416dbc 100644 --- a/lib/private/DatabaseSetupException.php +++ b/lib/private/DatabaseSetupException.php @@ -22,5 +22,7 @@ */ namespace OC; +use OCP\HintException; + class DatabaseSetupException extends HintException { } diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 0b7e96bbba3..131c0780dce 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -777,10 +777,6 @@ class Filesystem { * @return string */ public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) { - if (is_null(self::$normalizedPathCache)) { - self::$normalizedPathCache = new CappedMemoryCache(2048); - } - /** * FIXME: This is a workaround for existing classes and files which call * this function with another type than a valid string. This @@ -789,16 +785,20 @@ class Filesystem { */ $path = (string)$path; + if ($path === '') { + return '/'; + } + + if (is_null(self::$normalizedPathCache)) { + self::$normalizedPathCache = new CappedMemoryCache(2048); + } + $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]); if ($cacheKey && isset(self::$normalizedPathCache[$cacheKey])) { return self::$normalizedPathCache[$cacheKey]; } - if ($path === '') { - return '/'; - } - //normalize unicode if possible if (!$keepUnicode) { $path = \OC_Util::normalizeUnicode($path); diff --git a/lib/private/Files/Mount/MountPoint.php b/lib/private/Files/Mount/MountPoint.php index 886c137bbcc..368be0a917e 100644 --- a/lib/private/Files/Mount/MountPoint.php +++ b/lib/private/Files/Mount/MountPoint.php @@ -268,7 +268,13 @@ class MountPoint implements IMountPoint { */ public function getStorageRootId() { if (is_null($this->rootId) || $this->rootId === -1) { - $this->rootId = (int)$this->getStorage()->getCache()->getId(''); + $storage = $this->getStorage(); + // if we can't create the storage return -1 as root id, this is then handled the same as if the root isn't scanned yet + if ($storage === null) { + $this->rootId = -1; + } else { + $this->rootId = (int)$storage->getCache()->getId(''); + } } return $this->rootId; } diff --git a/lib/private/Files/ObjectStore/S3Signature.php b/lib/private/Files/ObjectStore/S3Signature.php index ec2e6bbe3ac..1e5ef697835 100644 --- a/lib/private/Files/ObjectStore/S3Signature.php +++ b/lib/private/Files/ObjectStore/S3Signature.php @@ -129,7 +129,7 @@ class S3Signature implements SignatureInterface { ) { $modify = [ 'remove_headers' => ['X-Amz-Date'], - 'set_headers' => ['Date' => gmdate(\DateTime::RFC2822)] + 'set_headers' => ['Date' => gmdate(\DateTimeInterface::RFC2822)] ]; // Add the security token header if one is being used by the credentials diff --git a/lib/private/HintException.php b/lib/private/HintException.php index e3173ba79fd..735832266cf 100644 --- a/lib/private/HintException.php +++ b/lib/private/HintException.php @@ -1,28 +1,28 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. + * @copyright Copyright (c) 2021 Gary Kim <gary@garykim.dev> * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Gary Kim <gary@garykim.dev> * - * @license AGPL-3.0 + * @license GNU AGPL version 3 or any later version * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. + * 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 + * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC; /** @@ -31,48 +31,7 @@ namespace OC; * An Exception class with the intention to be presented to the end user * * @package OC + * @depreacted 23.0.0 Use \OCP\HintException */ -class HintException extends \Exception { - private $hint; - - /** - * HintException constructor. - * - * @param string $message The error message. It will be not revealed to the - * the user (unless the hint is empty) and thus - * should be not translated. - * @param string $hint A useful message that is presented to the end - * user. It should be translated, but must not - * contain sensitive data. - * @param int $code - * @param \Exception|null $previous - */ - public function __construct($message, $hint = '', $code = 0, \Exception $previous = null) { - $this->hint = $hint; - parent::__construct($message, $code, $previous); - } - - /** - * Returns a string representation of this Exception that includes the error - * code, the message and the hint. - * - * @return string - */ - public function __toString() { - return __CLASS__ . ": [{$this->code}]: {$this->message} ({$this->hint})\n"; - } - - /** - * Returns the hint with the intention to be presented to the end user. If - * an empty hint was specified upon instatiation, the message is returned - * instead. - * - * @return string - */ - public function getHint() { - if (empty($this->hint)) { - return $this->message; - } - return $this->hint; - } +class HintException extends \OCP\HintException { } diff --git a/lib/private/Installer.php b/lib/private/Installer.php index d1e903424ed..19e70aad147 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -48,6 +48,7 @@ use OC\DB\Connection; use OC\DB\MigrationService; use OC_App; use OC_Helper; +use OCP\HintException; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\ILogger; diff --git a/lib/private/Log/ErrorHandler.php b/lib/private/Log/ErrorHandler.php index 1d2376befbf..6c969fa093c 100644 --- a/lib/private/Log/ErrorHandler.php +++ b/lib/private/Log/ErrorHandler.php @@ -39,7 +39,7 @@ class ErrorHandler { * @return string */ protected static function removePassword($msg) { - return preg_replace('/\/\/(.*):(.*)@/', '//xxx:xxx@', $msg); + return preg_replace('#//(.*):(.*)@#', '//xxx:xxx@', $msg); } public static function register($debug = false) { diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php index aba5586297a..bab22ab4aac 100644 --- a/lib/private/Log/ExceptionSerializer.php +++ b/lib/private/Log/ExceptionSerializer.php @@ -29,10 +29,10 @@ namespace OC\Log; use OC\Core\Controller\SetupController; -use OC\HintException; use OC\Security\IdentityProof\Key; use OC\Setup; use OC\SystemConfig; +use OCP\HintException; class ExceptionSerializer { public const methodsWithSensitiveParameters = [ diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php index 5ca8231eecb..87ce0396594 100644 --- a/lib/private/Log/LogDetails.php +++ b/lib/private/Log/LogDetails.php @@ -37,7 +37,7 @@ abstract class LogDetails { public function logDetails(string $app, $message, int $level): array { // default to ISO8601 - $format = $this->config->getValue('logdateformat', \DateTime::ATOM); + $format = $this->config->getValue('logdateformat', \DateTimeInterface::ATOM); $logTimeZone = $this->config->getValue('logtimezone', 'UTC'); try { $timezone = new \DateTimeZone($logTimeZone); diff --git a/lib/private/Log/Systemdlog.php b/lib/private/Log/Systemdlog.php index 17778891efe..a01826c0b05 100644 --- a/lib/private/Log/Systemdlog.php +++ b/lib/private/Log/Systemdlog.php @@ -25,8 +25,8 @@ */ namespace OC\Log; -use OC\HintException; use OC\SystemConfig; +use OCP\HintException; use OCP\ILogger; use OCP\Log\IWriter; diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php index b04fa903ba8..efe1a6eef1d 100644 --- a/lib/private/Mail/EMailTemplate.php +++ b/lib/private/Mail/EMailTemplate.php @@ -619,7 +619,11 @@ EOF; public function addFooter(string $text = '', ?string $lang = null) { if ($text === '') { $l10n = $this->l10nFactory->get('lib', $lang); - $text = $this->themingDefaults->getName() . ' - ' . $this->themingDefaults->getSlogan($lang) . '<br>' . $l10n->t('This is an automatically sent email, please do not reply.'); + $slogan = $this->themingDefaults->getSlogan($lang); + if ($slogan !== '') { + $slogan = ' - ' . $slogan; + } + $text = $this->themingDefaults->getName() . $slogan . '<br>' . $l10n->t('This is an automatically sent email, please do not reply.'); } if ($this->footerAdded) { diff --git a/lib/private/Memcache/Factory.php b/lib/private/Memcache/Factory.php index 9810f068148..28a84308edc 100644 --- a/lib/private/Memcache/Factory.php +++ b/lib/private/Memcache/Factory.php @@ -86,12 +86,12 @@ class Factory implements ICacheFactory { $missingCacheMessage = 'Memcache {class} not available for {use} cache'; $missingCacheHint = 'Is the matching PHP module installed and enabled?'; if (!class_exists($localCacheClass) || !$localCacheClass::isAvailable()) { - throw new \OC\HintException(strtr($missingCacheMessage, [ + throw new \OCP\HintException(strtr($missingCacheMessage, [ '{class}' => $localCacheClass, '{use}' => 'local' ]), $missingCacheHint); } if (!class_exists($distributedCacheClass) || !$distributedCacheClass::isAvailable()) { - throw new \OC\HintException(strtr($missingCacheMessage, [ + throw new \OCP\HintException(strtr($missingCacheMessage, [ '{class}' => $distributedCacheClass, '{use}' => 'distributed' ]), $missingCacheHint); } diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php index cd1af031d8a..b3414a7d290 100644 --- a/lib/private/Memcache/Memcached.php +++ b/lib/private/Memcache/Memcached.php @@ -31,7 +31,7 @@ */ namespace OC\Memcache; -use OC\HintException; +use OCP\HintException; use OCP\IMemcache; class Memcached extends Cache implements IMemcache { diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php index fddd64a7d48..0bccb8190cd 100644 --- a/lib/private/Route/Router.php +++ b/lib/private/Route/Router.php @@ -233,29 +233,32 @@ class Router implements IRouter { * @throws \Exception * @return array */ - public function findMatchingRoute(string $url, bool $loadAll = false): array { - if (strpos($url, '/apps/') === 0) { + public function findMatchingRoute(string $url): array { + if (substr($url, 0, 6) === '/apps/') { // empty string / 'apps' / $app / rest of the route [, , $app,] = explode('/', $url, 4); $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); - } elseif (strpos($url, '/ocsapp/apps/') === 0) { + } elseif (substr($url, 0, 13) === '/ocsapp/apps/') { // empty string / 'ocsapp' / 'apps' / $app / rest of the route [, , , $app,] = explode('/', $url, 5); $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); - } elseif (strpos($url, '/settings/') === 0) { + } elseif (substr($url, 0, 10) === '/settings/') { $this->loadRoutes('settings'); + } elseif (substr($url, 0, 6) === '/core/') { + \OC::$REQUESTEDAPP = $url; + if (!\OC::$server->getConfig()->getSystemValueBool('maintenance') && !Util::needUpgrade()) { + \OC_App::loadApps(); + } + $this->loadRoutes('core'); + } else { + $this->loadRoutes(); } - \OC::$REQUESTEDAPP = $url; - if (!\OC::$server->getConfig()->getSystemValueBool('maintenance') && !Util::needUpgrade()) { - \OC_App::loadApps(); - } - $this->loadRoutes('core'); $matcher = new UrlMatcher($this->root, $this->context); try { @@ -268,11 +271,6 @@ class Router implements IRouter { try { $parameters = $matcher->match($url . '/'); } catch (ResourceNotFoundException $newException) { - // Attempt to fallback to load all routes if none of the above route patterns matches and the route is not in core - if (!$loadAll) { - $this->loadRoutes(); - return $this->findMatchingRoute($url, true); - } // If we still didn't match a route, we throw the original exception throw $e; } diff --git a/lib/private/Server.php b/lib/private/Server.php index 207c53bdae3..03d6a4146ed 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1031,7 +1031,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(ILDAPProviderFactory::class, function (ContainerInterface $c) { $config = $c->get(\OCP\IConfig::class); $factoryClass = $config->getSystemValue('ldapProviderFactory', null); - if (is_null($factoryClass)) { + if (is_null($factoryClass) || !class_exists($factoryClass)) { return new NullLDAPProviderFactory($this); } /** @var \OCP\LDAP\ILDAPProviderFactory $factory */ diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 8e7e6ae6aea..d39fa069286 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -224,7 +224,7 @@ class Setup { try { $util = new \OC_Util(); $htAccessWorking = $util->isHtaccessWorking(\OC::$server->getConfig()); - } catch (\OC\HintException $e) { + } catch (\OCP\HintException $e) { $errors[] = [ 'error' => $e->getMessage(), 'exception' => $e, diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index b5e1b944bc5..7047c32e339 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -43,7 +43,6 @@ namespace OC\Share20; use OC\Cache\CappedMemoryCache; use OC\Files\Mount\MoveableMount; -use OC\HintException; use OC\Share20\Exception\ProviderException; use OCA\Files_Sharing\ISharedStorage; use OCP\EventDispatcher\IEventDispatcher; @@ -52,6 +51,7 @@ use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; use OCP\Files\Node; +use OCP\HintException; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; diff --git a/lib/private/Support/CrashReport/Registry.php b/lib/private/Support/CrashReport/Registry.php index a5c39de98f0..96796d6370f 100644 --- a/lib/private/Support/CrashReport/Registry.php +++ b/lib/private/Support/CrashReport/Registry.php @@ -35,6 +35,7 @@ use OCP\Support\CrashReport\IMessageReporter; use OCP\Support\CrashReport\IRegistry; use OCP\Support\CrashReport\IReporter; use Throwable; +use function array_shift; class Registry implements IRegistry { @@ -119,8 +120,7 @@ class Registry implements IRegistry { } private function loadLazyProviders(): void { - $classes = $this->lazyReporters; - foreach ($classes as $class) { + while (($class = array_shift($this->lazyReporters)) !== null) { try { /** @var IReporter $reporter */ $reporter = $this->serverContainer->query($class); @@ -151,6 +151,5 @@ class Registry implements IRegistry { ]); } } - $this->lazyReporters = []; } } diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 5fa41e48c0c..cffdac310cb 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -45,6 +45,7 @@ use OC\DB\MigrationService; use OC\Hooks\BasicEmitter; use OC\IntegrityCheck\Checker; use OC_App; +use OCP\HintException; use OCP\IConfig; use OCP\ILogger; use OCP\Util; diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 07a599cf017..12c615d500b 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -33,10 +33,10 @@ */ namespace OC\User; -use OC\HintException; use OC\Hooks\PublicEmitter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\EventDispatcher\IEventDispatcher; +use OCP\HintException; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; diff --git a/lib/private/legacy/OC_Hook.php b/lib/private/legacy/OC_Hook.php index d9e4c99e498..b223b0fa6d6 100644 --- a/lib/private/legacy/OC_Hook.php +++ b/lib/private/legacy/OC_Hook.php @@ -81,7 +81,7 @@ class OC_Hook { * @param string $signalName name of signal * @param mixed $params default: array() array with additional data * @return bool true if slots exists or false if not - * @throws \OC\HintException + * @throws \OCP\HintException * @throws \OC\ServerNotAvailableException Emits a signal. To get data from the slot use references! * * TODO: write example @@ -107,7 +107,7 @@ class OC_Hook { } catch (Exception $e) { self::$thrownExceptions[] = $e; \OC::$server->getLogger()->logException($e); - if ($e instanceof \OC\HintException) { + if ($e instanceof \OCP\HintException) { throw $e; } if ($e instanceof \OC\ServerNotAvailableException) { diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index 87964f98374..333b621e359 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -63,6 +63,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + use bantu\IniGetWrapper\IniGetWrapper; use OC\AppFramework\Http\Request; use OC\Files\Storage\LocalRootStorage; @@ -1214,7 +1215,7 @@ class OC_Util { $fp = @fopen($testFile, 'w'); if (!$fp) { - throw new OC\HintException('Can\'t create test file to check for working .htaccess file.', + throw new \OCP\HintException('Can\'t create test file to check for working .htaccess file.', 'Make sure it is possible for the webserver to write to ' . $testFile); } fwrite($fp, $testContent); @@ -1225,10 +1226,11 @@ class OC_Util { /** * Check if the .htaccess file is working + * * @param \OCP\IConfig $config * @return bool * @throws Exception - * @throws \OC\HintException If the test file can't get written. + * @throws \OCP\HintException If the test file can't get written. */ public function isHtaccessWorking(\OCP\IConfig $config) { if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) { @@ -1422,7 +1424,7 @@ class OC_Util { * * @param \OC\SystemConfig $config * @return bool whether the core or any app needs an upgrade - * @throws \OC\HintException When the upgrade from the given version is not allowed + * @throws \OCP\HintException When the upgrade from the given version is not allowed */ public static function needUpgrade(\OC\SystemConfig $config) { if ($config->getValue('installed', false)) { @@ -1442,11 +1444,11 @@ class OC_Util { return true; } else { // downgrade attempt, throw exception - throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); + throw new \OCP\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); } } elseif ($versionDiff < 0) { // downgrade attempt, throw exception - throw new \OC\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); + throw new \OCP\HintException('Downgrading is not supported and is likely to cause unpredictable issues (from ' . $installedVersion . ' to ' . $currentVersion . ')'); } // also check for upgrades for apps (independently from the user) diff --git a/lib/public/Accounts/IAccount.php b/lib/public/Accounts/IAccount.php index 72c207537dc..8d4d8b5c023 100644 --- a/lib/public/Accounts/IAccount.php +++ b/lib/public/Accounts/IAccount.php @@ -94,9 +94,10 @@ interface IAccount extends \JsonSerializable { /** * Returns the requestes propery collection (multi-value properties) * + * @throws PropertyDoesNotExistException against invalid collection name * @since 22.0.0 */ - public function getPropertyCollection(string $propertyCollection): IAccountPropertyCollection; + public function getPropertyCollection(string $propertyCollectionName): IAccountPropertyCollection; /** * Get all properties that match the provided filters for scope and verification status diff --git a/lib/public/Accounts/IAccountPropertyCollection.php b/lib/public/Accounts/IAccountPropertyCollection.php index 9e026f4ce5b..779fb1299b4 100644 --- a/lib/public/Accounts/IAccountPropertyCollection.php +++ b/lib/public/Accounts/IAccountPropertyCollection.php @@ -69,6 +69,14 @@ interface IAccountPropertyCollection extends JsonSerializable { public function addProperty(IAccountProperty $property): IAccountPropertyCollection; /** + * adds a property to this collection with only specifying the value + * + * @throws InvalidArgumentException + * @since 22.0.0 + */ + public function addPropertyWithDefaults(string $value): IAccountPropertyCollection; + + /** * removes a property of this collection * * @since 22.0.0 diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php index e28fa0b2c81..ee10d483b25 100644 --- a/lib/public/AppFramework/Http/Response.php +++ b/lib/public/AppFramework/Http/Response.php @@ -115,7 +115,7 @@ class Response { $time = \OC::$server->query(ITimeFactory::class); $expires->setTimestamp($time->getTime()); $expires->add(new \DateInterval('PT'.$cacheSeconds.'S')); - $this->addHeader('Expires', $expires->format(\DateTime::RFC2822)); + $this->addHeader('Expires', $expires->format(\DateTimeInterface::RFC2822)); } else { $this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); unset($this->headers['Expires'], $this->headers['Pragma']); @@ -244,7 +244,7 @@ class Response { if ($this->lastModified) { $mergeWith['Last-Modified'] = - $this->lastModified->format(\DateTime::RFC2822); + $this->lastModified->format(\DateTimeInterface::RFC2822); } $this->headers['Content-Security-Policy'] = $this->getContentSecurityPolicy()->buildPolicy(); diff --git a/lib/public/AppFramework/Http/TextPlainResponse.php b/lib/public/AppFramework/Http/TextPlainResponse.php new file mode 100644 index 00000000000..93edf704863 --- /dev/null +++ b/lib/public/AppFramework/Http/TextPlainResponse.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2021 Lukas Reschke <lukas@statuscode.ch> + * + * @author 2021 Lukas Reschke <lukas@statuscode.ch> + * + * @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\AppFramework\Http; + +use OCP\AppFramework\Http; + +/** + * A renderer for text responses + * @since 22.0.0 + */ +class TextPlainResponse extends Response { + /** @var string */ + private $text = ''; + + /** + * constructor of TextPlainResponse + * @param string $text The text body + * @param int $statusCode the Http status code, defaults to 200 + * @since 22.0.0 + */ + public function __construct(string $text = '', int $statusCode = Http::STATUS_OK) { + parent::__construct(); + + $this->text = $text; + $this->setStatus($statusCode); + $this->addHeader('Content-Type', 'text/plain'); + } + + + /** + * Returns the text + * @return string + * @since 22.0.0 + * @throws \Exception If data could not get encoded + */ + public function render() : string { + return $this->text; + } +} diff --git a/lib/public/AppFramework/Utility/IControllerMethodReflector.php b/lib/public/AppFramework/Utility/IControllerMethodReflector.php index ef51be0688c..fbeb14b1a35 100644 --- a/lib/public/AppFramework/Utility/IControllerMethodReflector.php +++ b/lib/public/AppFramework/Utility/IControllerMethodReflector.php @@ -33,6 +33,8 @@ namespace OCP\AppFramework\Utility; * Reads and parses annotations from doc comments * * @since 8.0.0 + * @deprecated 22.0.0 will be obsolete with native attributes in PHP8 + * @see https://help.nextcloud.com/t/how-should-we-use-php8-attributes/104278 */ interface IControllerMethodReflector { @@ -53,12 +55,14 @@ interface IControllerMethodReflector { * @return string|null type in the type parameters (@param int $something) * would return int or null if not existing * @since 8.0.0 + * @deprecated 22.0.0 this method is only used internally */ public function getType(string $parameter); /** * @return array the arguments of the method with key => default value * @since 8.0.0 + * @deprecated 22.0.0 this method is only used internally */ public function getParameters(): array; @@ -68,6 +72,8 @@ interface IControllerMethodReflector { * @param string $name the name of the annotation * @return bool true if the annotation is found * @since 8.0.0 + * @deprecated 22.0.0 will be obsolete with native attributes in PHP8 + * @see https://help.nextcloud.com/t/how-should-we-use-php8-attributes/104278 */ public function hasAnnotation(string $name): bool; } diff --git a/lib/public/Encryption/Exceptions/GenericEncryptionException.php b/lib/public/Encryption/Exceptions/GenericEncryptionException.php index 9f349a28a76..aef4114bb1b 100644 --- a/lib/public/Encryption/Exceptions/GenericEncryptionException.php +++ b/lib/public/Encryption/Exceptions/GenericEncryptionException.php @@ -26,7 +26,7 @@ */ namespace OCP\Encryption\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class GenericEncryptionException diff --git a/lib/public/Federation/Exceptions/ActionNotSupportedException.php b/lib/public/Federation/Exceptions/ActionNotSupportedException.php index 3ec729e415a..7b2ca3d9597 100644 --- a/lib/public/Federation/Exceptions/ActionNotSupportedException.php +++ b/lib/public/Federation/Exceptions/ActionNotSupportedException.php @@ -22,7 +22,7 @@ */ namespace OCP\Federation\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class ActionNotSupportedException diff --git a/lib/public/Federation/Exceptions/AuthenticationFailedException.php b/lib/public/Federation/Exceptions/AuthenticationFailedException.php index 61ea080f5d4..dfcd9636035 100644 --- a/lib/public/Federation/Exceptions/AuthenticationFailedException.php +++ b/lib/public/Federation/Exceptions/AuthenticationFailedException.php @@ -22,7 +22,7 @@ */ namespace OCP\Federation\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class AuthenticationFailedException diff --git a/lib/public/Federation/Exceptions/BadRequestException.php b/lib/public/Federation/Exceptions/BadRequestException.php index bea6fba2b18..8054c0e89f0 100644 --- a/lib/public/Federation/Exceptions/BadRequestException.php +++ b/lib/public/Federation/Exceptions/BadRequestException.php @@ -23,7 +23,7 @@ */ namespace OCP\Federation\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class BadRequestException diff --git a/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php b/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php index 48aa883a028..b6d4b7e9bce 100644 --- a/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php +++ b/lib/public/Federation/Exceptions/ProviderAlreadyExistsException.php @@ -24,7 +24,7 @@ */ namespace OCP\Federation\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class ProviderAlreadyExistsException diff --git a/lib/public/Federation/Exceptions/ProviderCouldNotAddShareException.php b/lib/public/Federation/Exceptions/ProviderCouldNotAddShareException.php index c647b2d9c9e..efcc8788386 100644 --- a/lib/public/Federation/Exceptions/ProviderCouldNotAddShareException.php +++ b/lib/public/Federation/Exceptions/ProviderCouldNotAddShareException.php @@ -22,8 +22,8 @@ */ namespace OCP\Federation\Exceptions; -use OC\HintException; use OCP\AppFramework\Http; +use OCP\HintException; /** * Class ProviderCouldNotAddShareException diff --git a/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php b/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php index b56a6ef8d4c..69c69f17d99 100644 --- a/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php +++ b/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php @@ -22,7 +22,7 @@ */ namespace OCP\Federation\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class ProviderDoesNotExistsException diff --git a/lib/public/Files/StorageNotAvailableException.php b/lib/public/Files/StorageNotAvailableException.php index e166df90d77..f600ef80808 100644 --- a/lib/public/Files/StorageNotAvailableException.php +++ b/lib/public/Files/StorageNotAvailableException.php @@ -32,7 +32,7 @@ namespace OCP\Files; -use OC\HintException; +use OCP\HintException; /** * Storage is temporarily not available diff --git a/lib/public/HintException.php b/lib/public/HintException.php new file mode 100644 index 00000000000..b5ee7642b03 --- /dev/null +++ b/lib/public/HintException.php @@ -0,0 +1,82 @@ +<?php +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * @author Bart Visscher <bartv@thisnet.nl> + * @author Lukas Reschke <lukas@statuscode.ch> + * @author Michael Gapczynski <GapczynskiM@gmail.com> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCP; + +/** + * Class HintException + * + * An Exception class with the intention to be presented to the end user + * + * @package OCP + * @since 23.0.0 + */ +class HintException extends \Exception { + private $hint; + + /** + * HintException constructor. + * + * @since 23.0.0 + * @param string $message The error message. It will be not revealed to the + * the user (unless the hint is empty) and thus + * should be not translated. + * @param string $hint A useful message that is presented to the end + * user. It should be translated, but must not + * contain sensitive data. + * @param int $code + * @param \Exception|null $previous + */ + public function __construct($message, $hint = '', $code = 0, \Exception $previous = null) { + $this->hint = $hint; + parent::__construct($message, $code, $previous); + } + + /** + * Returns a string representation of this Exception that includes the error + * code, the message and the hint. + * + * @since 23.0.0 + * @return string + */ + public function __toString(): string { + return __CLASS__ . ": [{$this->code}]: {$this->message} ({$this->hint})\n"; + } + + /** + * Returns the hint with the intention to be presented to the end user. If + * an empty hint was specified upon instatiation, the message is returned + * instead. + * + * @since 23.0.0 + * @return string + */ + public function getHint(): string { + if (empty($this->hint)) { + return $this->message; + } + return $this->hint; + } +} diff --git a/lib/public/Remote/Api/IApiCollection.php b/lib/public/Remote/Api/IApiCollection.php index 17349e7bca9..0216507db3e 100644 --- a/lib/public/Remote/Api/IApiCollection.php +++ b/lib/public/Remote/Api/IApiCollection.php @@ -26,12 +26,14 @@ namespace OCP\Remote\Api; * Provides access to the various apis of a remote instance * * @since 13.0.0 + * @deprecated 23.0.0 */ interface IApiCollection { /** * @return IUserApi * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getUserApi(); @@ -39,6 +41,7 @@ interface IApiCollection { * @return ICapabilitiesApi * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getCapabilitiesApi(); } diff --git a/lib/public/Remote/Api/IApiFactory.php b/lib/public/Remote/Api/IApiFactory.php index a60634bfe13..6d367f86207 100644 --- a/lib/public/Remote/Api/IApiFactory.php +++ b/lib/public/Remote/Api/IApiFactory.php @@ -27,6 +27,7 @@ use OCP\Remote\IInstance; /** * @since 13.0.0 + * @deprecated 23.0.0 */ interface IApiFactory { /** @@ -35,6 +36,7 @@ interface IApiFactory { * @return IApiCollection * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getApiCollection(IInstance $instance, ICredentials $credentials); } diff --git a/lib/public/Remote/Api/ICapabilitiesApi.php b/lib/public/Remote/Api/ICapabilitiesApi.php index f481f0fd749..cecc9d3f943 100644 --- a/lib/public/Remote/Api/ICapabilitiesApi.php +++ b/lib/public/Remote/Api/ICapabilitiesApi.php @@ -24,12 +24,14 @@ namespace OCP\Remote\Api; /** * @since 13.0.0 + * @deprecated 23.0.0 */ interface ICapabilitiesApi { /** * @return array The capabilities in the form of [$appId => [$capability => $value]] * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getCapabilities(); } diff --git a/lib/public/Remote/Api/IUserApi.php b/lib/public/Remote/Api/IUserApi.php index 76f61eddaa0..71bf5e3fd2b 100644 --- a/lib/public/Remote/Api/IUserApi.php +++ b/lib/public/Remote/Api/IUserApi.php @@ -26,6 +26,7 @@ use OCP\Remote\IUser; /** * @since 13.0.0 + * @deprecated 23.0.0 */ interface IUserApi { /** @@ -33,6 +34,7 @@ interface IUserApi { * @return IUser * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getUser($userId); } diff --git a/lib/public/Remote/ICredentials.php b/lib/public/Remote/ICredentials.php index bbf753f998a..db8fa9fa109 100644 --- a/lib/public/Remote/ICredentials.php +++ b/lib/public/Remote/ICredentials.php @@ -26,12 +26,14 @@ namespace OCP\Remote; * The credentials for a remote user * * @since 13.0.0 + * @deprecated 23.0.0 */ interface ICredentials { /** * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getUsername(); @@ -39,6 +41,7 @@ interface ICredentials { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getPassword(); } diff --git a/lib/public/Remote/IInstance.php b/lib/public/Remote/IInstance.php index 29c70933e2c..8c048805434 100644 --- a/lib/public/Remote/IInstance.php +++ b/lib/public/Remote/IInstance.php @@ -26,12 +26,14 @@ namespace OCP\Remote; * Provides some basic info about a remote Nextcloud instance * * @since 13.0.0 + * @deprecated 23.0.0 */ interface IInstance { /** * @return string The url of the remote server without protocol * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getUrl(); @@ -39,6 +41,7 @@ interface IInstance { * @return string The of of the remote server with protocol * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getFullUrl(); @@ -46,6 +49,7 @@ interface IInstance { * @return string The full version string in '13.1.2.3' format * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getVersion(); @@ -53,6 +57,7 @@ interface IInstance { * @return string 'http' or 'https' * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getProtocol(); @@ -60,6 +65,7 @@ interface IInstance { * Check that the remote server is installed and not in maintenance mode * * @since 13.0.0 + * @deprecated 23.0.0 * * @return bool */ diff --git a/lib/public/Remote/IInstanceFactory.php b/lib/public/Remote/IInstanceFactory.php index 1f16e92382d..10a8b733a04 100644 --- a/lib/public/Remote/IInstanceFactory.php +++ b/lib/public/Remote/IInstanceFactory.php @@ -24,6 +24,7 @@ namespace OCP\Remote; /** * @since 13.0.0 + * @deprecated 23.0.0 */ interface IInstanceFactory { /** @@ -31,6 +32,7 @@ interface IInstanceFactory { * @return IInstance * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getInstance($url); } diff --git a/lib/public/Remote/IUser.php b/lib/public/Remote/IUser.php index f0644250d8f..27fa8bc8b43 100644 --- a/lib/public/Remote/IUser.php +++ b/lib/public/Remote/IUser.php @@ -26,12 +26,14 @@ namespace OCP\Remote; * User info for a remote user * * @since 13.0.0 + * @deprecated 23.0.0 */ interface IUser { /** * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getUserId(); @@ -39,6 +41,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getEmail(); @@ -46,6 +49,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getDisplayName(); @@ -53,6 +57,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getPhone(); @@ -60,6 +65,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getAddress(); @@ -67,6 +73,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getWebsite(); @@ -74,6 +81,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getTwitter(); @@ -81,6 +89,7 @@ interface IUser { * @return string[] * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getGroups(); @@ -88,6 +97,7 @@ interface IUser { * @return string * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getLanguage(); @@ -95,6 +105,7 @@ interface IUser { * @return int * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getUsedSpace(); @@ -102,6 +113,7 @@ interface IUser { * @return int * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getFreeSpace(); @@ -109,6 +121,7 @@ interface IUser { * @return int * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getTotalSpace(); @@ -116,6 +129,7 @@ interface IUser { * @return int * * @since 13.0.0 + * @deprecated 23.0.0 */ public function getQuota(); } diff --git a/lib/public/Share/Exceptions/GenericShareException.php b/lib/public/Share/Exceptions/GenericShareException.php index 46a46ac0ab7..85c440e2387 100644 --- a/lib/public/Share/Exceptions/GenericShareException.php +++ b/lib/public/Share/Exceptions/GenericShareException.php @@ -24,7 +24,7 @@ */ namespace OCP\Share\Exceptions; -use OC\HintException; +use OCP\HintException; /** * Class GenericEncryptionException |