aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/l10n/et_EE.js11
-rw-r--r--lib/l10n/et_EE.json11
-rw-r--r--lib/l10n/fi.js2
-rw-r--r--lib/l10n/fi.json2
-rw-r--r--lib/l10n/hu.js2
-rw-r--r--lib/l10n/hu.json2
-rw-r--r--lib/l10n/nl.js2
-rw-r--r--lib/l10n/nl.json2
-rw-r--r--lib/l10n/uk.js7
-rw-r--r--lib/l10n/uk.json7
-rw-r--r--lib/l10n/zh_TW.js2
-rw-r--r--lib/l10n/zh_TW.json2
-rw-r--r--lib/private/AppFramework/App.php22
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php22
-rw-r--r--lib/private/AppFramework/Http/Request.php18
-rw-r--r--lib/private/Authentication/Token/PublicKeyTokenMapper.php27
-rw-r--r--lib/private/Authentication/Token/PublicKeyTokenProvider.php55
-rw-r--r--lib/private/Collaboration/Reference/File/FileReferenceProvider.php8
-rw-r--r--lib/private/Config.php7
-rw-r--r--lib/private/Contacts/ContactsMenu/ContactsStore.php8
-rw-r--r--lib/private/DB/BacktraceDebugStack.php1
-rw-r--r--lib/private/Files/Config/MountProviderCollection.php38
-rw-r--r--lib/private/Files/Config/UserMountCache.php12
-rw-r--r--lib/private/Files/Mount/Manager.php9
-rw-r--r--lib/private/Files/Mount/MountPoint.php24
-rw-r--r--lib/private/Files/Node/File.php2
-rw-r--r--lib/private/Files/Node/Folder.php8
-rw-r--r--lib/private/Files/Node/Node.php56
-rw-r--r--lib/private/Files/Node/Root.php4
-rw-r--r--lib/private/Files/SetupManager.php39
-rw-r--r--lib/private/Files/View.php17
-rw-r--r--lib/private/Memcache/WithLocalCache.php54
-rw-r--r--lib/private/Preview/IMagickSupport.php40
-rw-r--r--lib/private/PreviewManager.php14
-rw-r--r--lib/private/Route/CachingRouter.php27
-rw-r--r--lib/private/Route/Router.php39
-rw-r--r--lib/private/Server.php22
-rw-r--r--lib/private/TemplateLayout.php4
-rw-r--r--lib/private/User/Manager.php3
-rw-r--r--lib/private/legacy/OC_App.php16
-rw-r--r--lib/private/legacy/OC_Response.php2
-rw-r--r--lib/public/AppFramework/Controller.php3
-rw-r--r--lib/public/AppFramework/Http/Response.php2
-rw-r--r--lib/public/Files/Mount/IMountPoint.php4
46 files changed, 520 insertions, 143 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 6e0934c8fa1..62f66dca67b 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -1353,6 +1353,7 @@ return array(
'OC\\Memcache\\NullCache' => $baseDir . '/lib/private/Memcache/NullCache.php',
'OC\\Memcache\\ProfilerWrapperCache' => $baseDir . '/lib/private/Memcache/ProfilerWrapperCache.php',
'OC\\Memcache\\Redis' => $baseDir . '/lib/private/Memcache/Redis.php',
+ 'OC\\Memcache\\WithLocalCache' => $baseDir . '/lib/private/Memcache/WithLocalCache.php',
'OC\\MemoryInfo' => $baseDir . '/lib/private/MemoryInfo.php',
'OC\\Metadata\\Capabilities' => $baseDir . '/lib/private/Metadata/Capabilities.php',
'OC\\Metadata\\FileEventListener' => $baseDir . '/lib/private/Metadata/FileEventListener.php',
@@ -1391,6 +1392,7 @@ return array(
'OC\\Preview\\Generator' => $baseDir . '/lib/private/Preview/Generator.php',
'OC\\Preview\\GeneratorHelper' => $baseDir . '/lib/private/Preview/GeneratorHelper.php',
'OC\\Preview\\HEIC' => $baseDir . '/lib/private/Preview/HEIC.php',
+ 'OC\\Preview\\IMagickSupport' => $baseDir . '/lib/private/Preview/IMagickSupport.php',
'OC\\Preview\\Illustrator' => $baseDir . '/lib/private/Preview/Illustrator.php',
'OC\\Preview\\Image' => $baseDir . '/lib/private/Preview/Image.php',
'OC\\Preview\\Imaginary' => $baseDir . '/lib/private/Preview/Imaginary.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 50aaa84ae77..33d63a26e3e 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -1386,6 +1386,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Memcache\\NullCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/NullCache.php',
'OC\\Memcache\\ProfilerWrapperCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ProfilerWrapperCache.php',
'OC\\Memcache\\Redis' => __DIR__ . '/../../..' . '/lib/private/Memcache/Redis.php',
+ 'OC\\Memcache\\WithLocalCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/WithLocalCache.php',
'OC\\MemoryInfo' => __DIR__ . '/../../..' . '/lib/private/MemoryInfo.php',
'OC\\Metadata\\Capabilities' => __DIR__ . '/../../..' . '/lib/private/Metadata/Capabilities.php',
'OC\\Metadata\\FileEventListener' => __DIR__ . '/../../..' . '/lib/private/Metadata/FileEventListener.php',
@@ -1424,6 +1425,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Preview\\Generator' => __DIR__ . '/../../..' . '/lib/private/Preview/Generator.php',
'OC\\Preview\\GeneratorHelper' => __DIR__ . '/../../..' . '/lib/private/Preview/GeneratorHelper.php',
'OC\\Preview\\HEIC' => __DIR__ . '/../../..' . '/lib/private/Preview/HEIC.php',
+ 'OC\\Preview\\IMagickSupport' => __DIR__ . '/../../..' . '/lib/private/Preview/IMagickSupport.php',
'OC\\Preview\\Illustrator' => __DIR__ . '/../../..' . '/lib/private/Preview/Illustrator.php',
'OC\\Preview\\Image' => __DIR__ . '/../../..' . '/lib/private/Preview/Image.php',
'OC\\Preview\\Imaginary' => __DIR__ . '/../../..' . '/lib/private/Preview/Imaginary.php',
diff --git a/lib/l10n/et_EE.js b/lib/l10n/et_EE.js
index 780dc75930d..4292f2f139d 100644
--- a/lib/l10n/et_EE.js
+++ b/lib/l10n/et_EE.js
@@ -31,6 +31,11 @@ OC.L10N.register(
"_%n hour ago_::_%n hours ago_" : ["%n tund tagasi","%n tundi tagasi"],
"in a few seconds" : "mõne sekundi jooksul",
"seconds ago" : "sekundit tagasi",
+ "Empty file" : "Tühi fail",
+ "File already exists" : "Fail on juba olemas",
+ "Invalid path" : "Vigane kataloogirada",
+ "Failed to create file from template" : "Ei saa luua mallist faili",
+ "Templates" : "Mallid",
"File name is a reserved word" : "Failinimi sisaldab keelatud sõna",
"File name contains at least one invalid character" : "Faili nimesonvähemalt üks keelatud märk",
"File name is too long" : "Faili nimi on liiga pikk",
@@ -40,6 +45,8 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "See on automaatselt saadetud e-kiri, palun ära vasta.",
"Help" : "Abi",
"Apps" : "Rakendused",
+ "Personal settings" : "Isiklikud seaded",
+ "Administration settings" : "Administreerimise seaded",
"Settings" : "Seaded",
"Log out" : "Logi välja",
"Users" : "Kasutajad",
@@ -51,6 +58,8 @@ OC.L10N.register(
"Profile picture" : "Profiili pilt",
"About" : "Info",
"Full name" : "Täisnimi",
+ "Organisation" : "Organisatsioon",
+ "Role" : "Roll",
"Unknown user" : "Tundmatu kasutaja",
"Additional settings" : "Lisaseaded",
"You need to enter details of an existing account." : "Sa pead sisestama olemasoleva konto andmed.",
@@ -135,7 +144,9 @@ OC.L10N.register(
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "See on tõenäoliselt põhjustatud puhver/kiirendist nagu Zend OPcache või eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP moodulid on paigaldatud, kuid neid näitatakse endiselt kui puuduolevad?",
"Please ask your server administrator to restart the web server." : "Palu oma serveri haldajal veebiserver taaskäivitada.",
+ "Please upgrade your database version." : "Palun uuenda oma andmebaasi versioon",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Palun muuda kataloogi õigused 0770-ks, et kataloogi sisu poleks teistele kasutajatele nähtav",
+ "Your data directory is invalid." : "Sinu andmekataloog on vigane",
"Could not obtain lock type %d on \"%s\"." : "Ei suutnud hankida %d tüüpi lukustust \"%s\".",
"Storage is temporarily not available" : "Salvestusruum pole ajutiselt kättesaadav",
"%s enter the database username and name." : "%s sisesta andmebaasi kasutajatunnus ja nimi.",
diff --git a/lib/l10n/et_EE.json b/lib/l10n/et_EE.json
index 05047ef31c1..05bd46aa279 100644
--- a/lib/l10n/et_EE.json
+++ b/lib/l10n/et_EE.json
@@ -29,6 +29,11 @@
"_%n hour ago_::_%n hours ago_" : ["%n tund tagasi","%n tundi tagasi"],
"in a few seconds" : "mõne sekundi jooksul",
"seconds ago" : "sekundit tagasi",
+ "Empty file" : "Tühi fail",
+ "File already exists" : "Fail on juba olemas",
+ "Invalid path" : "Vigane kataloogirada",
+ "Failed to create file from template" : "Ei saa luua mallist faili",
+ "Templates" : "Mallid",
"File name is a reserved word" : "Failinimi sisaldab keelatud sõna",
"File name contains at least one invalid character" : "Faili nimesonvähemalt üks keelatud märk",
"File name is too long" : "Faili nimi on liiga pikk",
@@ -38,6 +43,8 @@
"This is an automatically sent email, please do not reply." : "See on automaatselt saadetud e-kiri, palun ära vasta.",
"Help" : "Abi",
"Apps" : "Rakendused",
+ "Personal settings" : "Isiklikud seaded",
+ "Administration settings" : "Administreerimise seaded",
"Settings" : "Seaded",
"Log out" : "Logi välja",
"Users" : "Kasutajad",
@@ -49,6 +56,8 @@
"Profile picture" : "Profiili pilt",
"About" : "Info",
"Full name" : "Täisnimi",
+ "Organisation" : "Organisatsioon",
+ "Role" : "Roll",
"Unknown user" : "Tundmatu kasutaja",
"Additional settings" : "Lisaseaded",
"You need to enter details of an existing account." : "Sa pead sisestama olemasoleva konto andmed.",
@@ -133,7 +142,9 @@
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "See on tõenäoliselt põhjustatud puhver/kiirendist nagu Zend OPcache või eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP moodulid on paigaldatud, kuid neid näitatakse endiselt kui puuduolevad?",
"Please ask your server administrator to restart the web server." : "Palu oma serveri haldajal veebiserver taaskäivitada.",
+ "Please upgrade your database version." : "Palun uuenda oma andmebaasi versioon",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Palun muuda kataloogi õigused 0770-ks, et kataloogi sisu poleks teistele kasutajatele nähtav",
+ "Your data directory is invalid." : "Sinu andmekataloog on vigane",
"Could not obtain lock type %d on \"%s\"." : "Ei suutnud hankida %d tüüpi lukustust \"%s\".",
"Storage is temporarily not available" : "Salvestusruum pole ajutiselt kättesaadav",
"%s enter the database username and name." : "%s sisesta andmebaasi kasutajatunnus ja nimi.",
diff --git a/lib/l10n/fi.js b/lib/l10n/fi.js
index f724b77763d..23134d43402 100644
--- a/lib/l10n/fi.js
+++ b/lib/l10n/fi.js
@@ -135,6 +135,7 @@ OC.L10N.register(
"%1$s shared »%2$s« with you." : "%1$s jakoi kohteen »%2$s« kanssasi.",
"Click the button below to open it." : "Napsauta alla olevaa painiketta avataksesi sen.",
"The requested share does not exist anymore" : "Pyydettyä jakoa ei ole enää olemassa",
+ "The user was not created because the user limit has been reached. Check your notifications to learn more." : "Käyttäjää ei luotu, koska käyttäjäraja on tullut täyteen. Tarkista ilmoitukset saadaksesi lisätietoja.",
"Could not find category \"%s\"" : "Luokkaa \"%s\" ei löytynyt",
"Sunday" : "sunnuntai",
"Monday" : "maanantai",
@@ -184,6 +185,7 @@ OC.L10N.register(
"A valid password must be provided" : "Anna kelvollinen salasana",
"The username is already being used" : "Käyttäjätunnus on jo käytössä",
"Could not create user" : "Ei voitu luoda käyttäjää",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", spaces and \"_.@-'\"" : "Vain seuraavat merkit ovat sallittuja käyttäjätunnuksessa: \"a-z\", \"A-Z\", \"0-9\", välilyönnit ja \"_.@-'\"",
"A valid username must be provided" : "Anna kelvollinen käyttäjätunnus",
"Username contains whitespace at the beginning or at the end" : "Käyttäjätunnus sisältää tyhjätilaa joko alussa tai lopussa",
"Username must not consist of dots only" : "Käyttäjänimi ei voi koostua vain pisteistä",
diff --git a/lib/l10n/fi.json b/lib/l10n/fi.json
index ee534d2856b..1f028ceb86e 100644
--- a/lib/l10n/fi.json
+++ b/lib/l10n/fi.json
@@ -133,6 +133,7 @@
"%1$s shared »%2$s« with you." : "%1$s jakoi kohteen »%2$s« kanssasi.",
"Click the button below to open it." : "Napsauta alla olevaa painiketta avataksesi sen.",
"The requested share does not exist anymore" : "Pyydettyä jakoa ei ole enää olemassa",
+ "The user was not created because the user limit has been reached. Check your notifications to learn more." : "Käyttäjää ei luotu, koska käyttäjäraja on tullut täyteen. Tarkista ilmoitukset saadaksesi lisätietoja.",
"Could not find category \"%s\"" : "Luokkaa \"%s\" ei löytynyt",
"Sunday" : "sunnuntai",
"Monday" : "maanantai",
@@ -182,6 +183,7 @@
"A valid password must be provided" : "Anna kelvollinen salasana",
"The username is already being used" : "Käyttäjätunnus on jo käytössä",
"Could not create user" : "Ei voitu luoda käyttäjää",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", spaces and \"_.@-'\"" : "Vain seuraavat merkit ovat sallittuja käyttäjätunnuksessa: \"a-z\", \"A-Z\", \"0-9\", välilyönnit ja \"_.@-'\"",
"A valid username must be provided" : "Anna kelvollinen käyttäjätunnus",
"Username contains whitespace at the beginning or at the end" : "Käyttäjätunnus sisältää tyhjätilaa joko alussa tai lopussa",
"Username must not consist of dots only" : "Käyttäjänimi ei voi koostua vain pisteistä",
diff --git a/lib/l10n/hu.js b/lib/l10n/hu.js
index 06770c1f1b0..9fb6d495087 100644
--- a/lib/l10n/hu.js
+++ b/lib/l10n/hu.js
@@ -114,7 +114,7 @@ OC.L10N.register(
"Profile picture" : "Profilkép",
"About" : "Névjegy",
"Full name" : "Teljes név",
- "Headline" : "Főcím",
+ "Headline" : "Címsor",
"Organisation" : "Szervezet",
"Role" : "Szerepkör",
"Unknown user" : "Ismeretlen felhasználó",
diff --git a/lib/l10n/hu.json b/lib/l10n/hu.json
index 2b5dfb58ec6..a36249b02f6 100644
--- a/lib/l10n/hu.json
+++ b/lib/l10n/hu.json
@@ -112,7 +112,7 @@
"Profile picture" : "Profilkép",
"About" : "Névjegy",
"Full name" : "Teljes név",
- "Headline" : "Főcím",
+ "Headline" : "Címsor",
"Organisation" : "Szervezet",
"Role" : "Szerepkör",
"Unknown user" : "Ismeretlen felhasználó",
diff --git a/lib/l10n/nl.js b/lib/l10n/nl.js
index a98b5695f2c..1f5cc16f998 100644
--- a/lib/l10n/nl.js
+++ b/lib/l10n/nl.js
@@ -222,6 +222,8 @@ OC.L10N.register(
"Please ask your server administrator to install the module." : "Vraag je beheerder om de module te installeren.",
"PHP setting \"%s\" is not set to \"%s\"." : "PHP instelling \"%s\" staat niet op \"%s\".",
"Adjusting this setting in php.ini will make Nextcloud run again" : "Het aanpassen van deze instelling in php.ini zorgt ervoor dat Nextcloud weer start",
+ "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> is ingesteld op <code>%s</code> in plaats van de verwachte waarde <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Om dit op te lossen stel je <code>mbstring.func_overload</code> in op <code>0</code> in je php.ini.",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "De minimale versie van libxml2 versie is 2.7.0. Momenteel is versie%s geïnstalleerd.",
"To fix this issue update your libxml2 version and restart your web server." : "Om dit probleem op te lossen, moet je de libxml2 versie bijwerken en je webserver herstarten.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is nu zo ingesteld dat 'inline doc blocks' worden gestript. Hierdoor worden verschillende hoofd modules onbruikbaar.",
diff --git a/lib/l10n/nl.json b/lib/l10n/nl.json
index 978ce6781ca..a33d5186bf9 100644
--- a/lib/l10n/nl.json
+++ b/lib/l10n/nl.json
@@ -220,6 +220,8 @@
"Please ask your server administrator to install the module." : "Vraag je beheerder om de module te installeren.",
"PHP setting \"%s\" is not set to \"%s\"." : "PHP instelling \"%s\" staat niet op \"%s\".",
"Adjusting this setting in php.ini will make Nextcloud run again" : "Het aanpassen van deze instelling in php.ini zorgt ervoor dat Nextcloud weer start",
+ "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> is ingesteld op <code>%s</code> in plaats van de verwachte waarde <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Om dit op te lossen stel je <code>mbstring.func_overload</code> in op <code>0</code> in je php.ini.",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "De minimale versie van libxml2 versie is 2.7.0. Momenteel is versie%s geïnstalleerd.",
"To fix this issue update your libxml2 version and restart your web server." : "Om dit probleem op te lossen, moet je de libxml2 versie bijwerken en je webserver herstarten.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP is nu zo ingesteld dat 'inline doc blocks' worden gestript. Hierdoor worden verschillende hoofd modules onbruikbaar.",
diff --git a/lib/l10n/uk.js b/lib/l10n/uk.js
index 6b290c350f5..5679eb51661 100644
--- a/lib/l10n/uk.js
+++ b/lib/l10n/uk.js
@@ -56,6 +56,7 @@ OC.L10N.register(
"Invalid image" : "Недійсне зображення",
"Avatar image is not square" : "Зображення аватара не квадратне",
"View profile" : "Перегляд профілю",
+ "Local time: %s" : "Місцевий час: %s",
"today" : "сьогодні",
"tomorrow" : "завтра",
"yesterday" : "вчора",
@@ -100,6 +101,7 @@ OC.L10N.register(
"Users" : "Користувачі",
"Email" : "Електронна пошта",
"Mail %s" : "Пошта %s",
+ "View %s on the fediverse" : "Переглянути %s у Fediverse",
"Phone" : "Телефон",
"Call %s" : "Телефонуйте %s",
"Twitter" : "Twitter",
@@ -150,7 +152,8 @@ OC.L10N.register(
"%1$s shared »%2$s« with you" : "%1$s надано доступ до \"%2$s\"",
"%1$s shared »%2$s« with you." : "%1$s надано доступ до \"%2$s\".",
"Click the button below to open it." : "Щоб відкрити файл, натисніть кнопку нижче.",
- "The requested share does not exist anymore" : "Запитувана частка більше не існує",
+ "The requested share does not exist anymore" : "Запитуваний спільний ресурс більше недоступний",
+ "The user was not created because the user limit has been reached. Check your notifications to learn more." : "Користувача не створено, оскільки досягнуто обмеження на кількість користувачів. Перевірте сповіщення для докладної інформації.",
"Could not find category \"%s\"" : "Не вдалося знайти категорію \"%s\"",
"Sunday" : "Неділя",
"Monday" : "Понеділок",
@@ -200,6 +203,7 @@ OC.L10N.register(
"A valid password must be provided" : "Потрібно задати вірний пароль",
"The username is already being used" : "Ім'я користувача вже використовується",
"Could not create user" : "Не вдалося створити користувача",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", spaces 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" : "Ім'я користувача не повинно складатися лише з крапок",
@@ -262,6 +266,7 @@ OC.L10N.register(
"%s enter the database username." : "%s введіть ім'я користувача бази даних.",
"%s enter the database name." : "%s введіть назву бази даних.",
"%s you may not use dots in the database name" : "%s не можна використовувати крапки в назві бази даних",
+ "The user limit has been reached and the user was not created. Check your notifications to learn more." : "Досягнуто обмеження на кількість користувачів, користувача не було створено. Перевірте сповіщення для докладної інформації.",
"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Тільки такі символи допускаються в імені користувача: \"a-z\", \"A-Z\", \"0-9\", і \"_.@-'\""
},
"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);");
diff --git a/lib/l10n/uk.json b/lib/l10n/uk.json
index 2bb32fd83e3..7a0854d23eb 100644
--- a/lib/l10n/uk.json
+++ b/lib/l10n/uk.json
@@ -54,6 +54,7 @@
"Invalid image" : "Недійсне зображення",
"Avatar image is not square" : "Зображення аватара не квадратне",
"View profile" : "Перегляд профілю",
+ "Local time: %s" : "Місцевий час: %s",
"today" : "сьогодні",
"tomorrow" : "завтра",
"yesterday" : "вчора",
@@ -98,6 +99,7 @@
"Users" : "Користувачі",
"Email" : "Електронна пошта",
"Mail %s" : "Пошта %s",
+ "View %s on the fediverse" : "Переглянути %s у Fediverse",
"Phone" : "Телефон",
"Call %s" : "Телефонуйте %s",
"Twitter" : "Twitter",
@@ -148,7 +150,8 @@
"%1$s shared »%2$s« with you" : "%1$s надано доступ до \"%2$s\"",
"%1$s shared »%2$s« with you." : "%1$s надано доступ до \"%2$s\".",
"Click the button below to open it." : "Щоб відкрити файл, натисніть кнопку нижче.",
- "The requested share does not exist anymore" : "Запитувана частка більше не існує",
+ "The requested share does not exist anymore" : "Запитуваний спільний ресурс більше недоступний",
+ "The user was not created because the user limit has been reached. Check your notifications to learn more." : "Користувача не створено, оскільки досягнуто обмеження на кількість користувачів. Перевірте сповіщення для докладної інформації.",
"Could not find category \"%s\"" : "Не вдалося знайти категорію \"%s\"",
"Sunday" : "Неділя",
"Monday" : "Понеділок",
@@ -198,6 +201,7 @@
"A valid password must be provided" : "Потрібно задати вірний пароль",
"The username is already being used" : "Ім'я користувача вже використовується",
"Could not create user" : "Не вдалося створити користувача",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", spaces 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" : "Ім'я користувача не повинно складатися лише з крапок",
@@ -260,6 +264,7 @@
"%s enter the database username." : "%s введіть ім'я користувача бази даних.",
"%s enter the database name." : "%s введіть назву бази даних.",
"%s you may not use dots in the database name" : "%s не можна використовувати крапки в назві бази даних",
+ "The user limit has been reached and the user was not created. Check your notifications to learn more." : "Досягнуто обмеження на кількість користувачів, користувача не було створено. Перевірте сповіщення для докладної інформації.",
"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Тільки такі символи допускаються в імені користувача: \"a-z\", \"A-Z\", \"0-9\", і \"_.@-'\""
},"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);"
} \ No newline at end of file
diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js
index 6124af85631..7e3c03ad5a8 100644
--- a/lib/l10n/zh_TW.js
+++ b/lib/l10n/zh_TW.js
@@ -57,6 +57,7 @@ OC.L10N.register(
"Invalid image" : "無效的圖片",
"Avatar image is not square" : "頭像不是正方形",
"View profile" : "檢視個人檔案",
+ "Local time: %s" : "本機時間:%s",
"today" : "今天",
"tomorrow" : "明天",
"yesterday" : "昨天",
@@ -204,6 +205,7 @@ OC.L10N.register(
"A valid password must be provided" : "須提供有效的密碼",
"The username is already being used" : "這個使用者名稱已經有人使用了",
"Could not create user" : "無法建立使用者",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", spaces 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" : "使用者名稱不能只包含小數點",
diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json
index c9a8f17835d..743e5d90ffd 100644
--- a/lib/l10n/zh_TW.json
+++ b/lib/l10n/zh_TW.json
@@ -55,6 +55,7 @@
"Invalid image" : "無效的圖片",
"Avatar image is not square" : "頭像不是正方形",
"View profile" : "檢視個人檔案",
+ "Local time: %s" : "本機時間:%s",
"today" : "今天",
"tomorrow" : "明天",
"yesterday" : "昨天",
@@ -202,6 +203,7 @@
"A valid password must be provided" : "須提供有效的密碼",
"The username is already being used" : "這個使用者名稱已經有人使用了",
"Could not create user" : "無法建立使用者",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", spaces 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" : "使用者名稱不能只包含小數點",
diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php
index d2ef7da9e46..abf8a08a44b 100644
--- a/lib/private/AppFramework/App.php
+++ b/lib/private/AppFramework/App.php
@@ -34,7 +34,6 @@ namespace OC\AppFramework;
use OC\AppFramework\DependencyInjection\DIContainer;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Request;
-use OC\Diagnostics\EventLogger;
use OCP\Profiler\IProfiler;
use OC\Profiler\RoutingDataCollector;
use OCP\AppFramework\QueryException;
@@ -43,7 +42,6 @@ use OCP\AppFramework\Http\ICallbackResponse;
use OCP\AppFramework\Http\IOutput;
use OCP\Diagnostics\IEventLogger;
use OCP\HintException;
-use OCP\IConfig;
use OCP\IRequest;
/**
@@ -120,7 +118,7 @@ class App {
public static function main(string $controllerName, string $methodName, DIContainer $container, array $urlParams = null) {
/** @var IProfiler $profiler */
$profiler = $container->get(IProfiler::class);
- $config = $container->get(IConfig::class);
+ $eventLogger = $container->get(IEventLogger::class);
// Disable profiler on the profiler UI
$profiler->setEnabled($profiler->isEnabled() && !is_null($urlParams) && isset($urlParams['_route']) && !str_starts_with($urlParams['_route'], 'profiler.'));
if ($profiler->isEnabled()) {
@@ -128,6 +126,8 @@ class App {
$profiler->add(new RoutingDataCollector($container['AppName'], $controllerName, $methodName));
}
+ $eventLogger->start('app:controller:params', 'Gather controller parameters');
+
if (!is_null($urlParams)) {
/** @var Request $request */
$request = $container->get(IRequest::class);
@@ -139,6 +139,10 @@ class App {
}
$appName = $container['AppName'];
+ $eventLogger->end('app:controller:params');
+
+ $eventLogger->start('app:controller:load', 'Load app controller');
+
// first try $controllerName then go for \OCA\AppName\Controller\$controllerName
try {
$controller = $container->get($controllerName);
@@ -158,10 +162,18 @@ class App {
$controller = $container->query($controllerName);
}
+ $eventLogger->end('app:controller:load');
+
+ $eventLogger->start('app:controller:dispatcher', 'Initialize dispatcher and pre-middleware');
+
// initialize the dispatcher and run all the middleware before the controller
/** @var Dispatcher $dispatcher */
$dispatcher = $container['Dispatcher'];
+ $eventLogger->end('app:controller:dispatcher');
+
+ $eventLogger->start('app:controller:run', 'Run app controller');
+
[
$httpHeaders,
$responseHeaders,
@@ -170,11 +182,11 @@ class App {
$response
] = $dispatcher->dispatch($controller, $methodName);
+ $eventLogger->end('app:controller:run');
+
$io = $container[IOutput::class];
if ($profiler->isEnabled()) {
- /** @var EventLogger $eventLogger */
- $eventLogger = $container->get(IEventLogger::class);
$eventLogger->end('runtime');
$profile = $profiler->collect($container->get(IRequest::class), $response);
$profiler->saveProfile($profile);
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index ff04196fef6..f41b734a25b 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -98,11 +98,14 @@ class Coordinator {
* @param string[] $appIds
*/
private function registerApps(array $appIds): void {
+ $this->eventLogger->start('bootstrap:register_apps', '');
if ($this->registrationContext === null) {
$this->registrationContext = new RegistrationContext($this->logger);
}
$apps = [];
foreach ($appIds as $appId) {
+ $this->eventLogger->start("bootstrap:register_app:$appId", "Register $appId");
+ $this->eventLogger->start("bootstrap:register_app:$appId:autoloader", "Setup autoloader for $appId");
/*
* First, we have to enable the app's autoloader
*
@@ -114,36 +117,43 @@ class Coordinator {
continue;
}
OC_App::registerAutoloading($appId, $path);
+ $this->eventLogger->end("bootstrap:register_app:$appId:autoloader");
/*
- * Next we check if there is an application class and it implements
+ * Next we check if there is an application class, and it implements
* the \OCP\AppFramework\Bootstrap\IBootstrap interface
*/
$appNameSpace = App::buildAppNamespace($appId);
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
try {
if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
+ $this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId");
try {
/** @var IBootstrap|App $application */
$apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
} catch (QueryException $e) {
// Weird, but ok
+ $this->eventLogger->end("bootstrap:register_app:$appId");
continue;
}
+ $this->eventLogger->end("bootstrap:register_app:$appId:application");
- $this->eventLogger->start('bootstrap:register_app_' . $appId, '');
+ $this->eventLogger->start("bootstrap:register_app:$appId:register", "`Application::register` for $appId");
$application->register($this->registrationContext->for($appId));
- $this->eventLogger->end('bootstrap:register_app_' . $appId);
+ $this->eventLogger->end("bootstrap:register_app:$appId:register");
}
} catch (Throwable $e) {
$this->logger->emergency('Error during app service registration: ' . $e->getMessage(), [
'exception' => $e,
'app' => $appId,
]);
+ $this->eventLogger->end("bootstrap:register_app:$appId");
continue;
}
+ $this->eventLogger->end("bootstrap:register_app:$appId");
}
+ $this->eventLogger->start('bootstrap:register_apps:apply', 'Apply all the registered service by apps');
/**
* Now that all register methods have been called, we can delegate the registrations
* to the actual services
@@ -153,6 +163,8 @@ class Coordinator {
$this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager);
$this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
$this->registrationContext->delegateContainerRegistrations($apps);
+ $this->eventLogger->end('bootstrap:register_apps:apply');
+ $this->eventLogger->end('bootstrap:register_apps');
}
public function getRegistrationContext(): ?RegistrationContext {
@@ -178,7 +190,7 @@ class Coordinator {
* the instance was already created for register, but any other
* (legacy) code will now do their magic via the constructor.
*/
- $this->eventLogger->start('bootstrap:boot_app_' . $appId, '');
+ $this->eventLogger->start('bootstrap:boot_app:' . $appId, "Call `Application::boot` for $appId");
try {
/** @var App $application */
$application = $this->serverContainer->query($applicationClassName);
@@ -196,7 +208,7 @@ class Coordinator {
'exception' => $e,
]);
}
- $this->eventLogger->end('bootstrap:boot_app_' . $appId);
+ $this->eventLogger->end('bootstrap:boot_app:' . $appId);
}
public function isBootable(string $appId) {
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index ac162f6565e..1f32d6c5461 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -260,6 +260,9 @@ class Request implements \ArrayAccess, \Countable, IRequest {
: null;
case 'parameters':
case 'params':
+ if ($this->isPutStreamContent()) {
+ return $this->items['parameters'];
+ }
return $this->getContent();
default:
return isset($this[$name])
@@ -391,12 +394,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
*/
protected function getContent() {
// If the content can't be parsed into an array then return a stream resource.
- if ($this->method === 'PUT'
- && $this->getHeader('Content-Length') !== '0'
- && $this->getHeader('Content-Length') !== ''
- && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
- && strpos($this->getHeader('Content-Type'), 'application/json') === false
- ) {
+ if ($this->isPutStreamContent()) {
if ($this->content === false) {
throw new \LogicException(
'"put" can only be accessed once if not '
@@ -411,6 +409,14 @@ class Request implements \ArrayAccess, \Countable, IRequest {
}
}
+ private function isPutStreamContent(): bool {
+ return $this->method === 'PUT'
+ && $this->getHeader('Content-Length') !== '0'
+ && $this->getHeader('Content-Length') !== ''
+ && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
+ && strpos($this->getHeader('Content-Type'), 'application/json') === false;
+ }
+
/**
* Attempt to decode the content and populate parameters
*/
diff --git a/lib/private/Authentication/Token/PublicKeyTokenMapper.php b/lib/private/Authentication/Token/PublicKeyTokenMapper.php
index 7b11ef8adf3..8feb275b3b7 100644
--- a/lib/private/Authentication/Token/PublicKeyTokenMapper.php
+++ b/lib/private/Authentication/Token/PublicKeyTokenMapper.php
@@ -229,4 +229,31 @@ class PublicKeyTokenMapper extends QBMapper {
);
$update->executeStatement();
}
+
+ public function updateHashesForUser(string $userId, string $passwordHash): void {
+ $qb = $this->db->getQueryBuilder();
+ $update = $qb->update($this->getTableName())
+ ->set('password_hash', $qb->createNamedParameter($passwordHash))
+ ->where(
+ $qb->expr()->eq('uid', $qb->createNamedParameter($userId))
+ );
+ $update->executeStatement();
+ }
+
+ public function getFirstTokenForUser(string $userId): ?PublicKeyToken {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('uid', $qb->createNamedParameter($userId)))
+ ->setMaxResults(1)
+ ->orderBy('id');
+ $result = $qb->executeQuery();
+
+ $data = $result->fetch();
+ $result->closeCursor();
+ if ($data === false) {
+ return null;
+ }
+ return PublicKeyToken::fromRow($data);
+ }
}
diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
index c8adec24b31..84708065070 100644
--- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php
+++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
@@ -46,6 +46,8 @@ use OCP\Security\IHasher;
use Psr\Log\LoggerInterface;
class PublicKeyTokenProvider implements IProvider {
+ public const TOKEN_MIN_LENGTH = 22;
+
use TTransactional;
/** @var PublicKeyTokenMapper */
@@ -98,13 +100,33 @@ class PublicKeyTokenProvider implements IProvider {
string $name,
int $type = IToken::TEMPORARY_TOKEN,
int $remember = IToken::DO_NOT_REMEMBER): IToken {
+ if (strlen($token) < self::TOKEN_MIN_LENGTH) {
+ $exception = new InvalidTokenException('Token is too short, minimum of ' . self::TOKEN_MIN_LENGTH . ' characters is required, ' . strlen($token) . ' characters given');
+ $this->logger->error('Invalid token provided when generating new token', ['exception' => $exception]);
+ throw $exception;
+ }
+
if (mb_strlen($name) > 128) {
$name = mb_substr($name, 0, 120) . '…';
}
+ // We need to check against one old token to see if there is a password
+ // hash that we can reuse for detecting outdated passwords
+ $randomOldToken = $this->mapper->getFirstTokenForUser($uid);
+ $oldTokenMatches = $randomOldToken && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash());
+
$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
+
+ if ($oldTokenMatches) {
+ $dbToken->setPasswordHash($randomOldToken->getPasswordHash());
+ }
+
$this->mapper->insert($dbToken);
+ if (!$oldTokenMatches && $password !== null) {
+ $this->updatePasswords($uid, $password);
+ }
+
// Add the token to the cache
$this->cache[$dbToken->getToken()] = $dbToken;
@@ -112,6 +134,27 @@ class PublicKeyTokenProvider implements IProvider {
}
public function getToken(string $tokenId): IToken {
+ /**
+ * Token length: 72
+ * @see \OC\Core\Controller\ClientFlowLoginController::generateAppPassword
+ * @see \OC\Core\Controller\AppPasswordController::getAppPassword
+ * @see \OC\Core\Command\User\AddAppPassword::execute
+ * @see \OC\Core\Service\LoginFlowV2Service::flowDone
+ * @see \OCA\Talk\MatterbridgeManager::generatePassword
+ * @see \OCA\Preferred_Providers\Controller\PasswordController::generateAppPassword
+ * @see \OCA\GlobalSiteSelector\TokenHandler::generateAppPassword
+ *
+ * Token length: 22-256 - https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length
+ * @see \OC\User\Session::createSessionToken
+ *
+ * Token length: 29
+ * @see \OCA\Settings\Controller\AuthSettingsController::generateRandomDeviceToken
+ * @see \OCA\Registration\Service\RegistrationService::generateAppPassword
+ */
+ if (strlen($tokenId) < self::TOKEN_MIN_LENGTH) {
+ throw new InvalidTokenException('Token is too short for a generated token, should be the password during basic auth');
+ }
+
$tokenHash = $this->hashToken($tokenId);
if (isset($this->cache[$tokenHash])) {
@@ -122,7 +165,7 @@ class PublicKeyTokenProvider implements IProvider {
$token = $this->cache[$tokenHash];
} else {
try {
- $token = $this->mapper->getToken($this->hashToken($tokenId));
+ $token = $this->mapper->getToken($tokenHash);
$this->cache[$token->getToken()] = $token;
} catch (DoesNotExistException $ex) {
try {
@@ -289,10 +332,11 @@ class PublicKeyTokenProvider implements IProvider {
// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($token->getUID());
+ $hashedPassword = $this->hashPassword($password);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
- $t->setPasswordHash($this->hashPassword($password));
+ $t->setPasswordHash($hashedPassword);
$this->updateToken($t);
}
}
@@ -481,6 +525,13 @@ class PublicKeyTokenProvider implements IProvider {
$this->updateToken($t);
}
}
+
+ // If password hashes are different we update them all to be equal so
+ // that the next execution only needs to verify once
+ if (count($hashNeedsUpdate) > 1) {
+ $newPasswordHash = $this->hashPassword($password);
+ $this->mapper->updateHashesForUser($uid, $newPasswordHash);
+ }
}
private function logOpensslError() {
diff --git a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
index 4e6c7ea623f..95e49cdf860 100644
--- a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
+++ b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
@@ -62,21 +62,21 @@ class FileReferenceProvider implements IReferenceProvider {
}
private function getFilesAppLinkId(string $referenceText): ?int {
- $start = $this->urlGenerator->getAbsoluteURL('/apps/files');
- $startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/files');
+ $start = $this->urlGenerator->getAbsoluteURL('/apps/files/');
+ $startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/files/');
$fileId = null;
if (mb_strpos($referenceText, $start) === 0) {
$parts = parse_url($referenceText);
- parse_str($parts['query'], $query);
+ parse_str($parts['query'] ?? '', $query);
$fileId = isset($query['fileid']) ? (int)$query['fileid'] : $fileId;
$fileId = isset($query['openfile']) ? (int)$query['openfile'] : $fileId;
}
if (mb_strpos($referenceText, $startIndex) === 0) {
$parts = parse_url($referenceText);
- parse_str($parts['query'], $query);
+ parse_str($parts['query'] ?? '', $query);
$fileId = isset($query['fileid']) ? (int)$query['fileid'] : $fileId;
$fileId = isset($query['openfile']) ? (int)$query['openfile'] : $fileId;
}
diff --git a/lib/private/Config.php b/lib/private/Config.php
index ba3b8c6fe4d..7308a3769df 100644
--- a/lib/private/Config.php
+++ b/lib/private/Config.php
@@ -285,6 +285,13 @@ class Config {
'This can usually be fixed by giving the webserver write access to the config directory.');
}
+ // Never write file back if disk space should be too low
+ $df = disk_free_space($this->configDir);
+ $size = strlen($content) + 10240;
+ if ($df !== false && (int)$df < $size) {
+ throw new \Exception($this->configDir . " does not have enough space for writing the config file! Not writing it back!");
+ }
+
// Try to acquire a file lock
if (!flock($filePointer, LOCK_EX)) {
throw new \Exception(sprintf('Could not acquire an exclusive lock on the config file %s', $this->configFilePath));
diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php
index a0a14cd9cbd..3a75e924d24 100644
--- a/lib/private/Contacts/ContactsMenu/ContactsStore.php
+++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php
@@ -287,7 +287,13 @@ class ContactsStore implements IContactsStore {
if (isset($contact['UID'])) {
$uid = $contact['UID'];
$entry->setId($uid);
- $avatar = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $uid, 'size' => 64]);
+ if (isset($contact['isLocalSystemBook'])) {
+ $avatar = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $uid, 'size' => 64]);
+ } elseif (isset($contact['FN'])) {
+ $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $contact['FN'], 'size' => 64]);
+ } else {
+ $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $uid, 'size' => 64]);
+ }
$entry->setAvatar($avatar);
}
diff --git a/lib/private/DB/BacktraceDebugStack.php b/lib/private/DB/BacktraceDebugStack.php
index be37e5a35da..6a19be89225 100644
--- a/lib/private/DB/BacktraceDebugStack.php
+++ b/lib/private/DB/BacktraceDebugStack.php
@@ -30,5 +30,6 @@ class BacktraceDebugStack extends DebugStack {
parent::startQuery($sql, $params, $types);
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$this->queries[$this->currentQuery]['backtrace'] = $backtrace;
+ $this->queries[$this->currentQuery]['start'] = $this->start;
}
}
diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php
index 0e08d9d0e83..ae6481e45bb 100644
--- a/lib/private/Files/Config/MountProviderCollection.php
+++ b/lib/private/Files/Config/MountProviderCollection.php
@@ -26,6 +26,7 @@ namespace OC\Files\Config;
use OC\Hooks\Emitter;
use OC\Hooks\EmitterTrait;
+use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\IHomeMountProvider;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IMountProviderCollection;
@@ -65,13 +66,29 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
/** @var callable[] */
private $mountFilters = [];
+ private IEventLogger $eventLogger;
+
/**
* @param \OCP\Files\Storage\IStorageFactory $loader
* @param IUserMountCache $mountCache
*/
- public function __construct(IStorageFactory $loader, IUserMountCache $mountCache) {
+ public function __construct(
+ IStorageFactory $loader,
+ IUserMountCache $mountCache,
+ IEventLogger $eventLogger
+ ) {
$this->loader = $loader;
$this->mountCache = $mountCache;
+ $this->eventLogger = $eventLogger;
+ }
+
+ private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
+ $class = str_replace('\\', '_', get_class($provider));
+ $uid = $user->getUID();
+ $this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
+ $mounts = $provider->getMountsForUser($user, $loader) ?? [];
+ $this->eventLogger->end('fs:setup:provider:' . $class);
+ return $mounts;
}
/**
@@ -82,11 +99,8 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
private function getUserMountsForProviders(IUser $user, array $providers): array {
$loader = $this->loader;
$mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
- return $provider->getMountsForUser($user, $loader);
+ return $this->getMountsFromProvider($provider, $user, $loader);
}, $providers);
- $mounts = array_filter($mounts, function ($result) {
- return is_array($result);
- });
$mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
return array_merge($mounts, $providerMounts);
}, []);
@@ -121,24 +135,22 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
return (get_class($provider) === 'OCA\Files_Sharing\MountProvider');
});
foreach ($firstProviders as $provider) {
- $mounts = $provider->getMountsForUser($user, $this->loader);
- if (is_array($mounts)) {
- $firstMounts = array_merge($firstMounts, $mounts);
- }
+ $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
+ $firstMounts = array_merge($firstMounts, $mounts);
}
$firstMounts = $this->filterMounts($user, $firstMounts);
array_walk($firstMounts, [$mountManager, 'addMount']);
$lateMounts = [];
foreach ($lastProviders as $provider) {
- $mounts = $provider->getMountsForUser($user, $this->loader);
- if (is_array($mounts)) {
- $lateMounts = array_merge($lateMounts, $mounts);
- }
+ $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
+ $lateMounts = array_merge($lateMounts, $mounts);
}
$lateMounts = $this->filterMounts($user, $lateMounts);
+ $this->eventLogger->start("fs:setup:add-mounts", "Add mounts to the filesystem");
array_walk($lateMounts, [$mountManager, 'addMount']);
+ $this->eventLogger->end("fs:setup:add-mounts");
return array_merge($lateMounts, $firstMounts);
}
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index 3540b563742..fe677c5ea52 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -31,6 +31,7 @@ namespace OC\Files\Config;
use OCP\Cache\CappedMemoryCache;
use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
@@ -56,19 +57,27 @@ class UserMountCache implements IUserMountCache {
private LoggerInterface $logger;
/** @var CappedMemoryCache<array> */
private CappedMemoryCache $cacheInfoCache;
+ private IEventLogger $eventLogger;
/**
* UserMountCache constructor.
*/
- public function __construct(IDBConnection $connection, IUserManager $userManager, LoggerInterface $logger) {
+ public function __construct(
+ IDBConnection $connection,
+ IUserManager $userManager,
+ LoggerInterface $logger,
+ IEventLogger $eventLogger
+ ) {
$this->connection = $connection;
$this->userManager = $userManager;
$this->logger = $logger;
+ $this->eventLogger = $eventLogger;
$this->cacheInfoCache = new CappedMemoryCache();
$this->mountsForUsers = new CappedMemoryCache();
}
public function registerMounts(IUser $user, array $mounts, array $mountProviderClasses = null) {
+ $this->eventLogger->start('fs:setup:user:register', 'Registering mounts for user');
// filter out non-proper storages coming from unit tests
$mounts = array_filter($mounts, function (IMountPoint $mount) {
return $mount instanceof SharedMount || ($mount->getStorage() && $mount->getStorage()->getCache());
@@ -134,6 +143,7 @@ class UserMountCache implements IUserMountCache {
foreach ($changedMounts as $mount) {
$this->updateCachedMount($mount);
}
+ $this->eventLogger->end('fs:setup:user:register');
}
/**
diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php
index 9ba0e504058..805cce658a6 100644
--- a/lib/private/Files/Mount/Manager.php
+++ b/lib/private/Files/Mount/Manager.php
@@ -184,8 +184,13 @@ class Manager implements IMountManager {
* @return IMountPoint[]
*/
public function findByNumericId(int $id): array {
- $storageId = \OC\Files\Cache\Storage::getStorageId($id);
- return $this->findByStorageId($storageId);
+ $result = [];
+ foreach ($this->mounts as $mount) {
+ if ($mount->getNumericStorageId() === $id) {
+ $result[] = $mount;
+ }
+ }
+ return $result;
}
/**
diff --git a/lib/private/Files/Mount/MountPoint.php b/lib/private/Files/Mount/MountPoint.php
index 49f7e560ad3..20e08120080 100644
--- a/lib/private/Files/Mount/MountPoint.php
+++ b/lib/private/Files/Mount/MountPoint.php
@@ -44,6 +44,7 @@ class MountPoint implements IMountPoint {
protected $storage = null;
protected $class;
protected $storageId;
+ protected $numericStorageId = null;
protected $rootId = null;
/**
@@ -195,19 +196,15 @@ class MountPoint implements IMountPoint {
}
/**
- * @return string
+ * @return string|null
*/
public function getStorageId() {
if (!$this->storageId) {
- if (is_null($this->storage)) {
- $storage = $this->createStorage(); //FIXME: start using exceptions
- if (is_null($storage)) {
- return null;
- }
-
- $this->storage = $storage;
+ $storage = $this->getStorage();
+ if (is_null($storage)) {
+ return null;
}
- $this->storageId = $this->storage->getId();
+ $this->storageId = $storage->getId();
if (strlen($this->storageId) > 64) {
$this->storageId = md5($this->storageId);
}
@@ -219,7 +216,14 @@ class MountPoint implements IMountPoint {
* @return int
*/
public function getNumericStorageId() {
- return $this->getStorage()->getStorageCache()->getNumericId();
+ if (is_null($this->numericStorageId)) {
+ $storage = $this->getStorage();
+ if (is_null($storage)) {
+ return -1;
+ }
+ $this->numericStorageId = $storage->getStorageCache()->getNumericId();
+ }
+ return $this->numericStorageId;
}
/**
diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php
index d8a6741dc6e..475930b361a 100644
--- a/lib/private/Files/Node/File.php
+++ b/lib/private/Files/Node/File.php
@@ -37,7 +37,7 @@ class File extends Node implements \OCP\Files\File {
* Creates a Folder that represents a non-existing path
*
* @param string $path path
- * @return string non-existing node class
+ * @return NonExistingFile non-existing node
*/
protected function createNonExistingNode($path) {
return new NonExistingFile($this->root, $this->view, $path);
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index bf9ae3c148d..90aed642a2d 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -54,7 +54,7 @@ class Folder extends Node implements \OCP\Files\Folder {
* Creates a Folder that represents a non-existing path
*
* @param string $path path
- * @return string non-existing node class
+ * @return NonExistingFolder non-existing node
*/
protected function createNonExistingNode($path) {
return new NonExistingFolder($this->root, $this->view, $path);
@@ -98,7 +98,7 @@ class Folder extends Node implements \OCP\Files\Folder {
* @throws \OCP\Files\NotFoundException
*/
public function getDirectoryListing() {
- $folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo());
+ $folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false));
return array_map(function (FileInfo $info) {
if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
@@ -114,7 +114,7 @@ class Folder extends Node implements \OCP\Files\Folder {
* @param FileInfo $info
* @return File|Folder
*/
- protected function createNode($path, FileInfo $info = null) {
+ protected function createNode($path, FileInfo $info = null, bool $infoHasSubMountsIncluded = true) {
if (is_null($info)) {
$isDir = $this->view->is_dir($path);
} else {
@@ -122,7 +122,7 @@ class Folder extends Node implements \OCP\Files\Folder {
}
$parent = dirname($path) === $this->getPath() ? $this : null;
if ($isDir) {
- return new Folder($this->root, $this->view, $path, $info, $parent);
+ return new Folder($this->root, $this->view, $path, $info, $parent, $infoHasSubMountsIncluded);
} else {
return new File($this->root, $this->view, $path, $info, $parent);
}
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php
index 6c4e0b0f908..2f88cc3a15a 100644
--- a/lib/private/Files/Node/Node.php
+++ b/lib/private/Files/Node/Node.php
@@ -56,35 +56,35 @@ class Node implements \OCP\Files\Node {
*/
protected $path;
- /**
- * @var \OCP\Files\FileInfo
- */
- protected $fileInfo;
+ protected ?FileInfo $fileInfo;
/**
* @var Node|null
*/
protected $parent;
+ private bool $infoHasSubMountsIncluded;
+
/**
* @param \OC\Files\View $view
* @param \OCP\Files\IRootFolder $root
* @param string $path
* @param FileInfo $fileInfo
*/
- public function __construct($root, $view, $path, $fileInfo = null, ?Node $parent = null) {
+ public function __construct($root, $view, $path, $fileInfo = null, ?Node $parent = null, bool $infoHasSubMountsIncluded = true) {
$this->view = $view;
$this->root = $root;
$this->path = $path;
$this->fileInfo = $fileInfo;
$this->parent = $parent;
+ $this->infoHasSubMountsIncluded = $infoHasSubMountsIncluded;
}
/**
* Creates a Node of the same type that represents a non-existing path
*
* @param string $path path
- * @return string non-existing node class
+ * @return Node non-existing node
* @throws \Exception
*/
protected function createNonExistingNode($path) {
@@ -98,17 +98,23 @@ class Node implements \OCP\Files\Node {
* @throws InvalidPathException
* @throws NotFoundException
*/
- public function getFileInfo() {
+ public function getFileInfo(bool $includeMountPoint = true) {
if (!$this->fileInfo) {
if (!Filesystem::isValidPath($this->path)) {
throw new InvalidPathException();
}
- $fileInfo = $this->view->getFileInfo($this->path);
+ $fileInfo = $this->view->getFileInfo($this->path, $includeMountPoint);
+ $this->infoHasSubMountsIncluded = $includeMountPoint;
if ($fileInfo instanceof FileInfo) {
$this->fileInfo = $fileInfo;
} else {
throw new NotFoundException();
}
+ } elseif ($includeMountPoint && !$this->infoHasSubMountsIncluded && $this instanceof Folder) {
+ if ($this->fileInfo instanceof \OC\Files\FileInfo) {
+ $this->view->addSubMounts($this->fileInfo);
+ }
+ $this->infoHasSubMountsIncluded = true;
}
return $this->fileInfo;
}
@@ -179,7 +185,7 @@ class Node implements \OCP\Files\Node {
* @return string
*/
public function getInternalPath() {
- return $this->getFileInfo()->getInternalPath();
+ return $this->getFileInfo(false)->getInternalPath();
}
/**
@@ -188,7 +194,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function getId() {
- return $this->getFileInfo()->getId();
+ return $this->getFileInfo(false)->getId() ?? -1;
}
/**
@@ -232,7 +238,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function getPermissions() {
- return $this->getFileInfo()->getPermissions();
+ return $this->getFileInfo(false)->getPermissions();
}
/**
@@ -241,7 +247,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function isReadable() {
- return $this->getFileInfo()->isReadable();
+ return $this->getFileInfo(false)->isReadable();
}
/**
@@ -250,7 +256,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function isUpdateable() {
- return $this->getFileInfo()->isUpdateable();
+ return $this->getFileInfo(false)->isUpdateable();
}
/**
@@ -259,7 +265,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function isDeletable() {
- return $this->getFileInfo()->isDeletable();
+ return $this->getFileInfo(false)->isDeletable();
}
/**
@@ -268,7 +274,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function isShareable() {
- return $this->getFileInfo()->isShareable();
+ return $this->getFileInfo(false)->isShareable();
}
/**
@@ -277,7 +283,7 @@ class Node implements \OCP\Files\Node {
* @throws NotFoundException
*/
public function isCreatable() {
- return $this->getFileInfo()->isCreatable();
+ return $this->getFileInfo(false)->isCreatable();
}
/**
@@ -328,42 +334,42 @@ class Node implements \OCP\Files\Node {
}
public function isMounted() {
- return $this->getFileInfo()->isMounted();
+ return $this->getFileInfo(false)->isMounted();
}
public function isShared() {
- return $this->getFileInfo()->isShared();
+ return $this->getFileInfo(false)->isShared();
}
public function getMimeType() {
- return $this->getFileInfo()->getMimetype();
+ return $this->getFileInfo(false)->getMimetype();
}
public function getMimePart() {
- return $this->getFileInfo()->getMimePart();
+ return $this->getFileInfo(false)->getMimePart();
}
public function getType() {
- return $this->getFileInfo()->getType();
+ return $this->getFileInfo(false)->getType();
}
public function isEncrypted() {
- return $this->getFileInfo()->isEncrypted();
+ return $this->getFileInfo(false)->isEncrypted();
}
public function getMountPoint() {
- return $this->getFileInfo()->getMountPoint();
+ return $this->getFileInfo(false)->getMountPoint();
}
public function getOwner() {
- return $this->getFileInfo()->getOwner();
+ return $this->getFileInfo(false)->getOwner();
}
public function getChecksum() {
}
public function getExtension(): string {
- return $this->getFileInfo()->getExtension();
+ return $this->getFileInfo(false)->getExtension();
}
/**
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index 8d0a65d2a68..29cdbb987c3 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -202,9 +202,9 @@ class Root extends Folder implements IRootFolder {
$path = $this->normalizePath($path);
if ($this->isValidPath($path)) {
$fullPath = $this->getFullPath($path);
- $fileInfo = $this->view->getFileInfo($fullPath);
+ $fileInfo = $this->view->getFileInfo($fullPath, false);
if ($fileInfo) {
- return $this->createNode($fullPath, $fileInfo);
+ return $this->createNode($fullPath, $fileInfo, false);
} else {
throw new NotFoundException($path);
}
diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php
index 979e4ce966a..4cbd0328d76 100644
--- a/lib/private/Files/SetupManager.php
+++ b/lib/private/Files/SetupManager.php
@@ -212,6 +212,8 @@ class SetupManager {
}
$this->setupUsersComplete[] = $user->getUID();
+ $this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user');
+
if (!isset($this->setupUserMountProviders[$user->getUID()])) {
$this->setupUserMountProviders[$user->getUID()] = [];
}
@@ -226,6 +228,7 @@ class SetupManager {
});
});
$this->afterUserFullySetup($user, $previouslySetupProviders);
+ $this->eventLogger->end('fs:setup:user:full');
}
/**
@@ -237,6 +240,8 @@ class SetupManager {
}
$this->setupUsers[] = $user->getUID();
+ $this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user');
+
$this->setupBuiltinWrappers();
$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
@@ -250,14 +255,18 @@ class SetupManager {
Filesystem::initInternal($userDir);
if ($this->lockdownManager->canAccessFilesystem()) {
+ $this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user');
// home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers
$homeMount = $this->mountProviderCollection->getHomeMountForUser($user);
$this->mountManager->addMount($homeMount);
if ($homeMount->getStorageRootId() === -1) {
+ $this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user');
$homeMount->getStorage()->mkdir('');
$homeMount->getStorage()->getScanner()->scan('');
+ $this->eventLogger->end('fs:setup:user:home:scan');
}
+ $this->eventLogger->end('fs:setup:user:home');
} else {
$this->mountManager->addMount(new MountPoint(
new NullStorage([]),
@@ -271,12 +280,15 @@ class SetupManager {
}
$this->listenForNewMountProviders();
+
+ $this->eventLogger->end('fs:setup:user:onetime');
}
/**
* Final housekeeping after a user has been fully setup
*/
private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void {
+ $this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup');
$userRoot = '/' . $user->getUID() . '/';
$mounts = $this->mountManager->getAll();
$mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) {
@@ -296,6 +308,7 @@ class SetupManager {
$this->cache->set($user->getUID(), true, $cacheDuration);
$this->fullSetupRequired[$user->getUID()] = false;
}
+ $this->eventLogger->end('fs:setup:user:full:post');
}
/**
@@ -312,17 +325,17 @@ class SetupManager {
$this->oneTimeUserSetup($user);
}
- $this->eventLogger->start('setup_fs', 'Setup filesystem');
-
if ($this->lockdownManager->canAccessFilesystem()) {
$mountCallback();
}
+ $this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook');
\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]);
+ $this->eventLogger->end('fs:setup:user:post-init-mountpoint');
$userDir = '/' . $user->getUID() . '/files';
+ $this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook');
OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]);
-
- $this->eventLogger->end('setup_fs');
+ $this->eventLogger->end('fs:setup:user:setup-hook');
}
/**
@@ -335,7 +348,7 @@ class SetupManager {
}
$this->rootSetup = true;
- $this->eventLogger->start('setup_root_fs', 'Setup root filesystem');
+ $this->eventLogger->start('fs:setup:root', 'Setup root filesystem');
$this->setupBuiltinWrappers();
@@ -344,7 +357,7 @@ class SetupManager {
$this->mountManager->addMount($rootMountProvider);
}
- $this->eventLogger->end('setup_root_fs');
+ $this->eventLogger->end('fs:setup:root');
}
/**
@@ -413,6 +426,9 @@ class SetupManager {
$this->oneTimeUserSetup($user);
}
+ $this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user");
+ $this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path");
+
$mounts = [];
if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
$currentProviders[] = $cachedMount->getMountProvider();
@@ -421,13 +437,16 @@ class SetupManager {
$mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]);
} else {
$this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup");
+ $this->eventLogger->end('fs:setup:user:path:find');
$this->setupForUser($user);
+ $this->eventLogger->end('fs:setup:user:path');
return;
}
}
if ($includeChildren) {
$subCachedMounts = $this->userMountCache->getMountsInPath($user, $path);
+ $this->eventLogger->end('fs:setup:user:path:find');
$needsFullSetup = array_reduce($subCachedMounts, function (bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) {
return $needsFullSetup || $cachedMountInfo->getMountProvider() === '';
@@ -436,6 +455,7 @@ class SetupManager {
if ($needsFullSetup) {
$this->logger->debug("mount has no provider set, performing full setup");
$this->setupForUser($user);
+ $this->eventLogger->end('fs:setup:user:path');
return;
} else {
foreach ($subCachedMounts as $cachedMount) {
@@ -446,6 +466,8 @@ class SetupManager {
}
}
}
+ } else {
+ $this->eventLogger->end('fs:setup:user:path:find');
}
if (count($mounts)) {
@@ -456,6 +478,7 @@ class SetupManager {
} elseif (!$this->isSetupStarted($user)) {
$this->oneTimeUserSetup($user);
}
+ $this->eventLogger->end('fs:setup:user:path');
}
private function fullSetupRequired(IUser $user): bool {
@@ -488,6 +511,8 @@ class SetupManager {
return;
}
+ $this->eventLogger->start('fs:setup:user:providers', "Setup filesystem for " . implode(', ', $providers));
+
// home providers are always used
$providers = array_filter($providers, function (string $provider) {
return !is_subclass_of($provider, IHomeMountProvider::class);
@@ -504,6 +529,7 @@ class SetupManager {
if (!$this->isSetupStarted($user)) {
$this->oneTimeUserSetup($user);
}
+ $this->eventLogger->end('fs:setup:user:providers');
return;
} else {
$this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
@@ -514,6 +540,7 @@ class SetupManager {
$this->setupForUserWith($user, function () use ($mounts) {
array_walk($mounts, [$this->mountManager, 'addMount']);
});
+ $this->eventLogger->end('fs:setup:user:providers');
}
public function tearDown() {
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index f79a992c773..456f804ee56 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1412,11 +1412,7 @@ class View {
if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
//add the sizes of other mount points to the folder
$extOnly = ($includeMountPoints === 'ext');
- $mounts = Filesystem::getMountManager()->findIn($path);
- $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
- $subStorage = $mount->getStorage();
- return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
- }));
+ $this->addSubMounts($info, $extOnly);
}
}
@@ -1429,6 +1425,17 @@ class View {
}
/**
+ * Extend a FileInfo that was previously requested with `$includeMountPoints = false` to include the sub mounts
+ */
+ public function addSubMounts(FileInfo $info, $extOnly = false): void {
+ $mounts = Filesystem::getMountManager()->findIn($info->getPath());
+ $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
+ $subStorage = $mount->getStorage();
+ return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
+ }));
+ }
+
+ /**
* get the content of a directory
*
* @param string $directory path under datadirectory
diff --git a/lib/private/Memcache/WithLocalCache.php b/lib/private/Memcache/WithLocalCache.php
new file mode 100644
index 00000000000..5b7ccc10e39
--- /dev/null
+++ b/lib/private/Memcache/WithLocalCache.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace OC\Memcache;
+
+use OCP\Cache\CappedMemoryCache;
+use OCP\ICache;
+
+/**
+ * Wrap a cache instance with an extra later of local, in-memory caching
+ */
+class WithLocalCache implements ICache {
+ private ICache $inner;
+ private CappedMemoryCache $cached;
+
+ public function __construct(ICache $inner, int $localCapacity = 512) {
+ $this->inner = $inner;
+ $this->cached = new CappedMemoryCache($localCapacity);
+ }
+
+ public function get($key) {
+ if (isset($this->cached[$key])) {
+ return $this->cached[$key];
+ } else {
+ $value = $this->inner->get($key);
+ if (!is_null($value)) {
+ $this->cached[$key] = $value;
+ }
+ return $value;
+ }
+ }
+
+ public function set($key, $value, $ttl = 0) {
+ $this->cached[$key] = $value;
+ return $this->inner->set($key, $value, $ttl);
+ }
+
+ public function hasKey($key) {
+ return isset($this->cached[$key]) || $this->inner->hasKey($key);
+ }
+
+ public function remove($key) {
+ unset($this->cached[$key]);
+ return $this->inner->remove($key);
+ }
+
+ public function clear($prefix = '') {
+ $this->cached->clear();
+ return $this->inner->clear($prefix);
+ }
+
+ public static function isAvailable(): bool {
+ return false;
+ }
+}
diff --git a/lib/private/Preview/IMagickSupport.php b/lib/private/Preview/IMagickSupport.php
new file mode 100644
index 00000000000..e22ae93ab94
--- /dev/null
+++ b/lib/private/Preview/IMagickSupport.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace OC\Preview;
+
+use OCP\ICache;
+use OCP\ICacheFactory;
+
+class IMagickSupport {
+ private ICache $cache;
+ private ?\Imagick $imagick;
+
+ public function __construct(ICacheFactory $cacheFactory) {
+ $this->cache = $cacheFactory->createLocal('imagick');
+
+ if (extension_loaded('imagick')) {
+ $this->imagick = new \Imagick();
+ } else {
+ $this->imagick = null;
+ }
+ }
+
+ public function hasExtension(): bool {
+ return !is_null($this->imagick);
+ }
+
+ public function supportsFormat(string $format): bool {
+ if (is_null($this->imagick)) {
+ return false;
+ }
+
+ $cached = $this->cache->get($format);
+ if (!is_null($cached)) {
+ return $cached;
+ }
+
+ $formatSupported = count($this->imagick->queryFormats($format)) === 1;
+ $this->cache->set($format, $cached);
+ return $formatSupported;
+ }
+}
diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php
index 367f0c1c057..dd6b6ba8ee1 100644
--- a/lib/private/PreviewManager.php
+++ b/lib/private/PreviewManager.php
@@ -33,6 +33,7 @@ namespace OC;
use OC\AppFramework\Bootstrap\Coordinator;
use OC\Preview\Generator;
use OC\Preview\GeneratorHelper;
+use OC\Preview\IMagickSupport;
use OCP\AppFramework\QueryException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
@@ -73,6 +74,7 @@ class PreviewManager implements IPreview {
private array $loadedBootstrapProviders = [];
private IServerContainer $container;
private IBinaryFinder $binaryFinder;
+ private IMagickSupport $imagickSupport;
public function __construct(
IConfig $config,
@@ -84,7 +86,8 @@ class PreviewManager implements IPreview {
?string $userId,
Coordinator $bootstrapCoordinator,
IServerContainer $container,
- IBinaryFinder $binaryFinder
+ IBinaryFinder $binaryFinder,
+ IMagickSupport $imagickSupport
) {
$this->config = $config;
$this->rootFolder = $rootFolder;
@@ -96,6 +99,7 @@ class PreviewManager implements IPreview {
$this->bootstrapCoordinator = $bootstrapCoordinator;
$this->container = $container;
$this->binaryFinder = $binaryFinder;
+ $this->imagickSupport = $imagickSupport;
}
/**
@@ -368,9 +372,7 @@ class PreviewManager implements IPreview {
$this->registerCoreProvider(Preview\Imaginary::class, Preview\Imaginary::supportedMimeTypes());
// SVG, Office and Bitmap require imagick
- if (extension_loaded('imagick')) {
- $checkImagick = new \Imagick();
-
+ if ($this->imagickSupport->hasExtension()) {
$imagickProviders = [
'SVG' => ['mimetype' => '/image\/svg\+xml/', 'class' => Preview\SVG::class],
'TIFF' => ['mimetype' => '/image\/tiff/', 'class' => Preview\TIFF::class],
@@ -390,12 +392,12 @@ class PreviewManager implements IPreview {
continue;
}
- if (count($checkImagick->queryFormats($queryFormat)) === 1) {
+ if ($this->imagickSupport->supportsFormat($queryFormat)) {
$this->registerCoreProvider($class, $provider['mimetype']);
}
}
- if (count($checkImagick->queryFormats('PDF')) === 1) {
+ if ($this->imagickSupport->supportsFormat('PDF')) {
// Office requires openoffice or libreoffice
$officeBinary = $this->config->getSystemValue('preview_libreoffice_path', null);
if (!is_string($officeBinary)) {
diff --git a/lib/private/Route/CachingRouter.php b/lib/private/Route/CachingRouter.php
index f65060e710b..69fb3c986c9 100644
--- a/lib/private/Route/CachingRouter.php
+++ b/lib/private/Route/CachingRouter.php
@@ -24,20 +24,27 @@
*/
namespace OC\Route;
+use OCP\Diagnostics\IEventLogger;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use OCP\IRequest;
+use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
class CachingRouter extends Router {
- /**
- * @var \OCP\ICache
- */
- protected $cache;
+ protected ICache $cache;
- /**
- * @param \OCP\ICache $cache
- */
- public function __construct($cache, LoggerInterface $logger) {
- $this->cache = $cache;
- parent::__construct($logger);
+ public function __construct(
+ ICacheFactory $cacheFactory,
+ LoggerInterface $logger,
+ IRequest $request,
+ IConfig $config,
+ IEventLogger $eventLogger,
+ ContainerInterface $container
+ ) {
+ $this->cache = $cacheFactory->createLocal('route');
+ parent::__construct($logger, $request, $config, $eventLogger, $container);
}
/**
diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php
index 7e1acd49800..e2a092d861e 100644
--- a/lib/private/Route/Router.php
+++ b/lib/private/Route/Router.php
@@ -34,8 +34,12 @@ namespace OC\Route;
use OC\AppFramework\Routing\RouteParser;
use OCP\AppFramework\App;
+use OCP\Diagnostics\IEventLogger;
+use OCP\IConfig;
+use OCP\IRequest;
use OCP\Route\IRouter;
use OCP\Util;
+use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
@@ -64,11 +68,21 @@ class Router implements IRouter {
protected LoggerInterface $logger;
/** @var RequestContext */
protected $context;
-
- public function __construct(LoggerInterface $logger) {
+ private IEventLogger $eventLogger;
+ private IConfig $config;
+ private ContainerInterface $container;
+
+ public function __construct(
+ LoggerInterface $logger,
+ IRequest $request,
+ IConfig $config,
+ IEventLogger $eventLogger,
+ ContainerInterface $container
+ ) {
$this->logger = $logger;
+ $this->config = $config;
$baseUrl = \OC::$WEBROOT;
- if (!(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
+ if (!($config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
$baseUrl .= '/index.php';
}
if (!\OC::$CLI && isset($_SERVER['REQUEST_METHOD'])) {
@@ -76,12 +90,13 @@ class Router implements IRouter {
} else {
$method = 'GET';
}
- $request = \OC::$server->getRequest();
$host = $request->getServerHost();
$schema = $request->getServerProtocol();
$this->context = new RequestContext($baseUrl, $method, $host, $schema);
// TODO cache
$this->root = $this->getCollection('root');
+ $this->eventLogger = $eventLogger;
+ $this->container = $container;
}
/**
@@ -134,7 +149,7 @@ class Router implements IRouter {
$routingFiles = [];
}
}
- \OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes');
+ $this->eventLogger->start('route:load:' . $requestedApp, 'Loading Routes for ' . $requestedApp);
foreach ($routingFiles as $app => $file) {
if (!isset($this->loadedApps[$app])) {
if (!\OC_App::isAppLoaded($app)) {
@@ -170,7 +185,7 @@ class Router implements IRouter {
$collection->addPrefix('/ocs');
$this->root->addCollection($collection);
}
- \OC::$server->getEventLogger()->end('loadroutes' . $requestedApp);
+ $this->eventLogger->end('route:load:' . $requestedApp);
}
/**
@@ -231,6 +246,7 @@ class Router implements IRouter {
* @return array
*/
public function findMatchingRoute(string $url): array {
+ $this->eventLogger->start('route:match', 'Match route');
if (substr($url, 0, 6) === '/apps/') {
// empty string / 'apps' / $app / rest of the route
[, , $app,] = explode('/', $url, 4);
@@ -249,7 +265,7 @@ class Router implements IRouter {
$this->loadRoutes('settings');
} elseif (substr($url, 0, 6) === '/core/') {
\OC::$REQUESTEDAPP = $url;
- if (!\OC::$server->getConfig()->getSystemValueBool('maintenance') && !Util::needUpgrade()) {
+ if (!$this->config->getSystemValueBool('maintenance') && !Util::needUpgrade()) {
\OC_App::loadApps();
}
$this->loadRoutes('core');
@@ -276,6 +292,7 @@ class Router implements IRouter {
}
}
+ $this->eventLogger->end('route:match');
return $parameters;
}
@@ -289,7 +306,7 @@ class Router implements IRouter {
public function match($url) {
$parameters = $this->findMatchingRoute($url);
- \OC::$server->getEventLogger()->start('run_route', 'Run route');
+ $this->eventLogger->start('route:run', 'Run route');
if (isset($parameters['caller'])) {
$caller = $parameters['caller'];
unset($parameters['caller']);
@@ -303,13 +320,15 @@ class Router implements IRouter {
}
unset($parameters['action']);
unset($parameters['caller']);
+ $this->eventLogger->start('route:run:call', 'Run callable route');
call_user_func($action, $parameters);
+ $this->eventLogger->end('route:run:call');
} elseif (isset($parameters['file'])) {
include $parameters['file'];
} else {
throw new \Exception('no action available');
}
- \OC::$server->getEventLogger()->end('run_route');
+ $this->eventLogger->end('route:run');
}
/**
@@ -434,7 +453,7 @@ class Router implements IRouter {
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
if (class_exists($applicationClassName)) {
- $application = \OC::$server->query($applicationClassName);
+ $application = $this->container->get($applicationClassName);
} else {
$application = new App($appName);
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index bd33cdf58bd..35f63686457 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -86,6 +86,7 @@ use OC\EventDispatcher\SymfonyAdapter;
use OC\Federation\CloudFederationFactory;
use OC\Federation\CloudFederationProviderManager;
use OC\Federation\CloudIdManager;
+use OC\Files\Config\MountProviderCollection;
use OC\Files\Config\UserMountCache;
use OC\Files\Config\UserMountCacheListener;
use OC\Files\Lock\LockManager;
@@ -126,9 +127,11 @@ use OC\Metadata\MetadataManager;
use OC\Notification\Manager;
use OC\OCS\DiscoveryService;
use OC\Preview\GeneratorHelper;
+use OC\Preview\IMagickSupport;
use OC\Remote\Api\ApiFactory;
use OC\Remote\InstanceFactory;
use OC\RichObjectStrings\Validator;
+use OC\Route\CachingRouter;
use OC\Route\Router;
use OC\Security\Bruteforce\Throttler;
use OC\Security\CertificateManager;
@@ -336,7 +339,8 @@ class Server extends ServerContainer implements IServerContainer {
$c->get(ISession::class)->get('user_id'),
$c->get(Coordinator::class),
$c->get(IServerContainer::class),
- $c->get(IBinaryFinder::class)
+ $c->get(IBinaryFinder::class),
+ $c->get(IMagickSupport::class)
);
});
/** @deprecated 19.0.0 */
@@ -819,11 +823,10 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService(Router::class, function (Server $c) {
$cacheFactory = $c->get(ICacheFactory::class);
- $logger = $c->get(LoggerInterface::class);
if ($cacheFactory->isLocalCacheAvailable()) {
- $router = new \OC\Route\CachingRouter($cacheFactory->createLocal('route'), $logger);
+ $router = $c->resolve(CachingRouter::class);
} else {
- $router = new \OC\Route\Router($logger);
+ $router = $c->resolve(Router::class);
}
return $router;
});
@@ -946,11 +949,7 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerDeprecatedAlias('DateTimeFormatter', IDateTimeFormatter::class);
$this->registerService(IUserMountCache::class, function (ContainerInterface $c) {
- $mountCache = new UserMountCache(
- $c->get(IDBConnection::class),
- $c->get(IUserManager::class),
- $c->get(LoggerInterface::class)
- );
+ $mountCache = $c->get(UserMountCache::class);
$listener = new UserMountCacheListener($mountCache);
$listener->listen($c->get(IUserManager::class));
return $mountCache;
@@ -959,9 +958,10 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerDeprecatedAlias('UserMountCache', IUserMountCache::class);
$this->registerService(IMountProviderCollection::class, function (ContainerInterface $c) {
- $loader = \OC\Files\Filesystem::getLoader();
+ $loader = $c->get(IStorageFactory::class);
$mountCache = $c->get(IUserMountCache::class);
- $manager = new \OC\Files\Config\MountProviderCollection($loader, $mountCache);
+ $eventLogger = $c->get(IEventLogger::class);
+ $manager = new MountProviderCollection($loader, $mountCache, $eventLogger);
// builtin providers
diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php
index 5a4cd32e5df..d127443944f 100644
--- a/lib/private/TemplateLayout.php
+++ b/lib/private/TemplateLayout.php
@@ -130,7 +130,7 @@ class TemplateLayout extends \OC_Template {
$navigation = $this->navigationManager->getAll();
$this->assign('navigation', $navigation);
$settingsNavigation = $this->navigationManager->getAll('settings');
- $this->assign('settingsnavigation', $settingsNavigation);
+ $this->initialState->provideInitialState('core', 'settingsNavEntries', $settingsNavigation);
foreach ($navigation as $entry) {
if ($entry['active']) {
@@ -268,7 +268,7 @@ class TemplateLayout extends \OC_Template {
$this->assign('cssfiles', []);
$this->assign('printcssfiles', []);
- $this->assign('versionHash', self::$versionHash);
+ $this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
foreach ($cssFiles as $info) {
$web = $info[1];
$file = $info[2];
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index 937d825ed77..859ebd2a604 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -34,6 +34,7 @@
namespace OC\User;
use OC\Hooks\PublicEmitter;
+use OC\Memcache\WithLocalCache;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\HintException;
@@ -104,7 +105,7 @@ class Manager extends PublicEmitter implements IUserManager {
IEventDispatcher $eventDispatcher) {
$this->config = $config;
$this->dispatcher = $oldDispatcher;
- $this->cache = $cacheFactory->createDistributed('user_backend_map');
+ $this->cache = new WithLocalCache($cacheFactory->createDistributed('user_backend_map'));
$cachedUsers = &$this->cachedUsers;
$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
/** @var \OC\User\User $user */
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index c4c2f089767..7f51d81d21b 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -158,12 +158,17 @@ class OC_App {
* @param string $app
* @throws Exception
*/
- public static function loadApp(string $app) {
+ public static function loadApp(string $app): void {
+ if (isset(self::$loadedApps[$app])) {
+ return;
+ }
self::$loadedApps[$app] = true;
$appPath = self::getAppPath($app);
if ($appPath === false) {
return;
}
+ $eventLogger = \OC::$server->get(\OCP\Diagnostics\IEventLogger::class);
+ $eventLogger->start("bootstrap:load_app:$app", "Load $app");
// in case someone calls loadApp() directly
self::registerAutoloading($app, $appPath);
@@ -174,12 +179,12 @@ class OC_App {
$hasAppPhpFile = is_file($appPath . '/appinfo/app.php');
- \OC::$server->getEventLogger()->start('bootstrap:load_app_' . $app, 'Load app: ' . $app);
if ($isBootable && $hasAppPhpFile) {
\OC::$server->getLogger()->error('/appinfo/app.php is not loaded when \OCP\AppFramework\Bootstrap\IBootstrap on the application class is used. Migrate everything from app.php to the Application class.', [
'app' => $app,
]);
} elseif ($hasAppPhpFile) {
+ $eventLogger->start("bootstrap:load_app:$app:app.php", "Load legacy app.php app $app");
\OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [
'app' => $app,
]);
@@ -202,11 +207,12 @@ class OC_App {
]);
}
}
+ $eventLogger->end("bootstrap:load_app:$app:app.php");
}
- \OC::$server->getEventLogger()->end('bootstrap:load_app_' . $app);
$coordinator->bootApp($app);
+ $eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it");
$info = self::getAppInfo($app);
if (!empty($info['activity']['filters'])) {
foreach ($info['activity']['filters'] as $filter) {
@@ -261,6 +267,10 @@ class OC_App {
}
}
}
+
+ $eventLogger->end("bootstrap:load_app:$app:info");
+
+ $eventLogger->end("bootstrap:load_app:$app");
}
/**
diff --git a/lib/private/legacy/OC_Response.php b/lib/private/legacy/OC_Response.php
index e4525fe9e10..9440feae3cd 100644
--- a/lib/private/legacy/OC_Response.php
+++ b/lib/private/legacy/OC_Response.php
@@ -99,7 +99,7 @@ class OC_Response {
header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE
header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains
header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
- header('X-Robots-Tag: none'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
+ header('X-Robots-Tag: noindex, nofollow'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters
}
}
diff --git a/lib/public/AppFramework/Controller.php b/lib/public/AppFramework/Controller.php
index 89cfd2e55fc..e8500d5ae1a 100644
--- a/lib/public/AppFramework/Controller.php
+++ b/lib/public/AppFramework/Controller.php
@@ -91,6 +91,9 @@ abstract class Controller {
if ($data->getLastModified() !== null) {
$response->setLastModified($data->getLastModified());
}
+ if ($data->isThrottled()) {
+ $response->throttle($data->getThrottleMetadata());
+ }
return $response;
}
diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php
index 4db6caa556c..152f8c4a3c5 100644
--- a/lib/public/AppFramework/Http/Response.php
+++ b/lib/public/AppFramework/Http/Response.php
@@ -257,7 +257,7 @@ class Response {
$this->headers['Content-Security-Policy'] = $this->getContentSecurityPolicy()->buildPolicy();
$this->headers['Feature-Policy'] = $this->getFeaturePolicy()->buildPolicy();
- $this->headers['X-Robots-Tag'] = 'none';
+ $this->headers['X-Robots-Tag'] = 'noindex, nofollow';
if ($this->ETag) {
$mergeWith['ETag'] = '"' . $this->ETag . '"';
diff --git a/lib/public/Files/Mount/IMountPoint.php b/lib/public/Files/Mount/IMountPoint.php
index b8e7ec9118f..1272550d737 100644
--- a/lib/public/Files/Mount/IMountPoint.php
+++ b/lib/public/Files/Mount/IMountPoint.php
@@ -55,7 +55,7 @@ interface IMountPoint {
/**
* Get the id of the storages
*
- * @return string
+ * @return string|null
* @since 8.0.0
*/
public function getStorageId();
@@ -63,7 +63,7 @@ interface IMountPoint {
/**
* Get the id of the storages
*
- * @return int
+ * @return int|null
* @since 9.1.0
*/
public function getNumericStorageId();