aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/admin_audit/l10n/pt_BR.js2
-rw-r--r--apps/admin_audit/l10n/pt_BR.json2
-rw-r--r--apps/dashboard/l10n/da.js34
-rw-r--r--apps/dashboard/l10n/da.json32
-rw-r--r--apps/dav/lib/CardDAV/Converter.php4
-rw-r--r--apps/dav/tests/unit/CardDAV/ConverterTest.php14
-rw-r--r--apps/dav/tests/unit/CardDAV/SyncServiceTest.php14
-rw-r--r--apps/federation/l10n/hu.js12
-rw-r--r--apps/federation/l10n/hu.json12
-rw-r--r--apps/files/l10n/es.js6
-rw-r--r--apps/files/l10n/es.json6
-rw-r--r--apps/files_sharing/l10n/de.js1
-rw-r--r--apps/files_sharing/l10n/de.json1
-rw-r--r--apps/files_sharing/l10n/es.js1
-rw-r--r--apps/files_sharing/l10n/es.json1
-rw-r--r--apps/files_sharing/lib/Controller/ShareController.php2
-rw-r--r--apps/files_sharing/tests/Controller/ShareControllerTest.php8
-rw-r--r--apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php2
-rw-r--r--apps/provisioning_api/composer/composer/autoload_classmap.php1
-rw-r--r--apps/provisioning_api/composer/composer/autoload_static.php1
-rw-r--r--apps/provisioning_api/lib/AppInfo/Application.php2
-rw-r--r--apps/provisioning_api/lib/Capabilities.php62
-rw-r--r--apps/provisioning_api/lib/Controller/AUserData.php35
-rw-r--r--apps/provisioning_api/lib/Controller/UsersController.php68
-rw-r--r--apps/provisioning_api/tests/CapabilitiesTest.php92
-rw-r--r--apps/provisioning_api/tests/Controller/UsersControllerTest.php212
-rw-r--r--apps/settings/js/federationscopemenu.js43
-rw-r--r--apps/settings/js/federationsettingsview.js39
-rw-r--r--apps/settings/js/settings/personalInfo.js6
-rw-r--r--apps/settings/l10n/cs.js8
-rw-r--r--apps/settings/l10n/cs.json8
-rw-r--r--apps/settings/l10n/de.js2
-rw-r--r--apps/settings/l10n/de.json2
-rw-r--r--apps/settings/lib/Controller/UsersController.php15
-rw-r--r--apps/settings/lib/Settings/Personal/PersonalInfo.php43
-rw-r--r--apps/settings/templates/settings/personal/personal.info.php38
-rw-r--r--apps/settings/tests/Controller/UsersControllerTest.php29
-rw-r--r--apps/sharebymail/l10n/cs.js2
-rw-r--r--apps/sharebymail/l10n/cs.json2
-rw-r--r--apps/sharebymail/l10n/de.js1
-rw-r--r--apps/sharebymail/l10n/de.json1
-rw-r--r--apps/updatenotification/l10n/cs.js2
-rw-r--r--apps/updatenotification/l10n/cs.json2
-rw-r--r--apps/user_ldap/l10n/de_DE.js8
-rw-r--r--apps/user_ldap/l10n/de_DE.json8
-rw-r--r--build/integration/features/bootstrap/Provisioning.php6
-rw-r--r--build/integration/features/provisioning-v1.feature77
-rw-r--r--build/psalm-baseline.xml33
-rw-r--r--core/l10n/cs.js10
-rw-r--r--core/l10n/cs.json10
-rw-r--r--core/l10n/de.js2
-rw-r--r--core/l10n/de.json2
-rw-r--r--lib/composer/composer/autoload_classmap.php1
-rw-r--r--lib/composer/composer/autoload_static.php1
-rw-r--r--lib/l10n/cs.js4
-rw-r--r--lib/l10n/cs.json4
-rw-r--r--lib/l10n/de_DE.js14
-rw-r--r--lib/l10n/de_DE.json14
-rw-r--r--lib/l10n/es.js1
-rw-r--r--lib/l10n/es.json1
-rw-r--r--lib/private/Accounts/AccountManager.php56
-rw-r--r--lib/private/Accounts/AccountProperty.php22
-rw-r--r--lib/private/Avatar/AvatarManager.php44
-rw-r--r--lib/private/Avatar/PlaceholderAvatar.php183
-rw-r--r--lib/private/Avatar/UserAvatar.php1
-rw-r--r--lib/private/Files/Node/Root.php2
-rw-r--r--lib/private/KnownUser/KnownUserService.php4
-rw-r--r--lib/private/Server.php6
-rw-r--r--lib/public/Accounts/IAccountManager.php49
-rw-r--r--tests/lib/Accounts/AccountManagerTest.php135
-rw-r--r--tests/lib/Accounts/AccountPropertyTest.php49
-rw-r--r--tests/lib/Accounts/AccountTest.php36
-rw-r--r--tests/lib/Avatar/AvatarManagerTest.php135
73 files changed, 1427 insertions, 361 deletions
diff --git a/apps/admin_audit/l10n/pt_BR.js b/apps/admin_audit/l10n/pt_BR.js
index 535d25025c7..03736d2b309 100644
--- a/apps/admin_audit/l10n/pt_BR.js
+++ b/apps/admin_audit/l10n/pt_BR.js
@@ -1,7 +1,7 @@
OC.L10N.register(
"admin_audit",
{
- "Auditing / Logging" : "Auditoria/Registro",
+ "Auditing / Logging" : "Auditoria / Registro",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fornece recursos de registro para Nextcloud, como registros de acesso a arquivos ou outras ações confidenciais."
},
"nplurals=2; plural=(n > 1);");
diff --git a/apps/admin_audit/l10n/pt_BR.json b/apps/admin_audit/l10n/pt_BR.json
index 0c6a29620e7..392c961c5c4 100644
--- a/apps/admin_audit/l10n/pt_BR.json
+++ b/apps/admin_audit/l10n/pt_BR.json
@@ -1,5 +1,5 @@
{ "translations": {
- "Auditing / Logging" : "Auditoria/Registro",
+ "Auditing / Logging" : "Auditoria / Registro",
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Fornece recursos de registro para Nextcloud, como registros de acesso a arquivos ou outras ações confidenciais."
},"pluralForm" :"nplurals=2; plural=(n > 1);"
} \ No newline at end of file
diff --git a/apps/dashboard/l10n/da.js b/apps/dashboard/l10n/da.js
new file mode 100644
index 00000000000..0c5cc013be1
--- /dev/null
+++ b/apps/dashboard/l10n/da.js
@@ -0,0 +1,34 @@
+OC.L10N.register(
+ "dashboard",
+ {
+ "Dashboard" : "Dashboard",
+ "Dashboard app" : "Dashboard app",
+ "Show something" : "Vis noget",
+ "Customize" : "Tilpas",
+ "Edit widgets" : "Redigér widgets",
+ "Get more widgets from the app store" : "Hent flere widgets fra app store",
+ "Change background image" : "Ændre baggrund",
+ "Weather service" : "Vejret",
+ "For your privacy, the weather data is requested by your Nextcloud server on your behalf so the weather service receives no personal information." : "Af hensyn til dit privatliv, er det din Nextcloud-server der henter vejr-data og udbyderen modtager således ingen oplysninger om dig.",
+ "Weather data from Met.no" : "Vejr-data leveres af Met.no",
+ "geocoding with Nominatim" : "Geocoding med Nominatim",
+ "elevation data from OpenTopoData" : "Højde-data fra OpenTopoData",
+ "Weather" : "Vejr",
+ "Status" : "Status",
+ "Good morning" : "God morgen",
+ "Good morning, {name}" : "God morgen {name}",
+ "Good afternoon" : "God eftermiddag",
+ "Good afternoon, {name}" : "God eftermiddag {name}",
+ "Good evening" : "God aften",
+ "Good evening, {name}" : "God aften {name}",
+ "Hello" : "Hej",
+ "Hello, {name}" : "Hej {name}",
+ "Pick from Files" : "Vælg fra Filer",
+ "Default images" : "Standardbilleder",
+ "Plain background" : "Standard baggrund",
+ "Insert from {productName}" : "Indsæt fra {productName}",
+ "Good night, {name}" : "Godnat {name}",
+ "Good night" : "Godnat",
+ "Pick from files" : "Vælg fra Filer"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/dashboard/l10n/da.json b/apps/dashboard/l10n/da.json
new file mode 100644
index 00000000000..622ad2929d2
--- /dev/null
+++ b/apps/dashboard/l10n/da.json
@@ -0,0 +1,32 @@
+{ "translations": {
+ "Dashboard" : "Dashboard",
+ "Dashboard app" : "Dashboard app",
+ "Show something" : "Vis noget",
+ "Customize" : "Tilpas",
+ "Edit widgets" : "Redigér widgets",
+ "Get more widgets from the app store" : "Hent flere widgets fra app store",
+ "Change background image" : "Ændre baggrund",
+ "Weather service" : "Vejret",
+ "For your privacy, the weather data is requested by your Nextcloud server on your behalf so the weather service receives no personal information." : "Af hensyn til dit privatliv, er det din Nextcloud-server der henter vejr-data og udbyderen modtager således ingen oplysninger om dig.",
+ "Weather data from Met.no" : "Vejr-data leveres af Met.no",
+ "geocoding with Nominatim" : "Geocoding med Nominatim",
+ "elevation data from OpenTopoData" : "Højde-data fra OpenTopoData",
+ "Weather" : "Vejr",
+ "Status" : "Status",
+ "Good morning" : "God morgen",
+ "Good morning, {name}" : "God morgen {name}",
+ "Good afternoon" : "God eftermiddag",
+ "Good afternoon, {name}" : "God eftermiddag {name}",
+ "Good evening" : "God aften",
+ "Good evening, {name}" : "God aften {name}",
+ "Hello" : "Hej",
+ "Hello, {name}" : "Hej {name}",
+ "Pick from Files" : "Vælg fra Filer",
+ "Default images" : "Standardbilleder",
+ "Plain background" : "Standard baggrund",
+ "Insert from {productName}" : "Indsæt fra {productName}",
+ "Good night, {name}" : "Godnat {name}",
+ "Good night" : "Godnat",
+ "Pick from files" : "Vælg fra Filer"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/dav/lib/CardDAV/Converter.php b/apps/dav/lib/CardDAV/Converter.php
index 59e5401d058..95ac43aba36 100644
--- a/apps/dav/lib/CardDAV/Converter.php
+++ b/apps/dav/lib/CardDAV/Converter.php
@@ -71,8 +71,8 @@ class Converter {
foreach ($userData as $property => $value) {
$shareWithTrustedServers =
- $value['scope'] === AccountManager::VISIBILITY_CONTACTS_ONLY ||
- $value['scope'] === AccountManager::VISIBILITY_PUBLIC;
+ $value['scope'] === AccountManager::SCOPE_FEDERATED ||
+ $value['scope'] === AccountManager::SCOPE_PUBLISHED;
$emptyValue = !isset($value['value']) || $value['value'] === '';
diff --git a/apps/dav/tests/unit/CardDAV/ConverterTest.php b/apps/dav/tests/unit/CardDAV/ConverterTest.php
index aef5cf8ef1c..43344451437 100644
--- a/apps/dav/tests/unit/CardDAV/ConverterTest.php
+++ b/apps/dav/tests/unit/CardDAV/ConverterTest.php
@@ -53,36 +53,36 @@ class ConverterTest extends TestCase {
IAccountManager::PROPERTY_DISPLAYNAME =>
[
'value' => $user->getDisplayName(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => AccountManager::SCOPE_FEDERATED,
],
IAccountManager::PROPERTY_ADDRESS =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
IAccountManager::PROPERTY_WEBSITE =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
IAccountManager::PROPERTY_EMAIL =>
[
'value' => $user->getEMailAddress(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => AccountManager::SCOPE_FEDERATED,
],
IAccountManager::PROPERTY_AVATAR =>
[
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
+ 'scope' => AccountManager::SCOPE_FEDERATED
],
IAccountManager::PROPERTY_PHONE =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
IAccountManager::PROPERTY_TWITTER =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
]
);
diff --git a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
index eb8186807c6..724670bc986 100644
--- a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
+++ b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
@@ -136,36 +136,36 @@ class SyncServiceTest extends TestCase {
IAccountManager::PROPERTY_DISPLAYNAME =>
[
'value' => $user->getDisplayName(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => AccountManager::SCOPE_FEDERATED,
],
IAccountManager::PROPERTY_ADDRESS =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
IAccountManager::PROPERTY_WEBSITE =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
IAccountManager::PROPERTY_EMAIL =>
[
'value' => $user->getEMailAddress(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => AccountManager::SCOPE_FEDERATED,
],
IAccountManager::PROPERTY_AVATAR =>
[
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
+ 'scope' => AccountManager::SCOPE_FEDERATED
],
IAccountManager::PROPERTY_PHONE =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
IAccountManager::PROPERTY_TWITTER =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
],
]
);
diff --git a/apps/federation/l10n/hu.js b/apps/federation/l10n/hu.js
index 535e71b3937..126f3a5ef55 100644
--- a/apps/federation/l10n/hu.js
+++ b/apps/federation/l10n/hu.js
@@ -3,16 +3,16 @@ OC.L10N.register(
{
"Added to the list of trusted servers" : "Hozzáadva a megbízható kiszolgálók listájához",
"Server is already in the list of trusted servers." : "A kiszolgáló már szerepel a megbízható kiszolgálók között.",
- "No server to federate with found" : "Nem található egyesíthető kiszolgáló",
+ "No server to federate with found" : "Nem található olyan kiszolgáló, amellyel föderálni lehetne",
"Could not add server" : "A kiszolgáló nem adható hozzá",
- "Federation" : "Egyesítés",
- "Federation allows you to connect with other trusted servers to exchange the user directory." : "Az egyesítés lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cserélhessenek.",
- "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Az egyesítés lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók az egyesített megosztáshoz.",
+ "Federation" : "Föderáció",
+ "Federation allows you to connect with other trusted servers to exchange the user directory." : "A föderáció lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cserélhessenek.",
+ "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "A föderáció lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók a föderált megosztásnál.",
"Trusted servers" : "Megbízható kiszolgálók",
- "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing. It is not necessary to add a server as trusted server in order to create a federated share." : "Az egyesítés lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók az egyesített megosztáshoz. Nem szükséges egy kiszolgálót megbízhatóként hozzáadni ahhoz, hogy egyesített megosztást hozzon létre.",
+ "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing. It is not necessary to add a server as trusted server in order to create a federated share." : "A föderáció lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók a föderált megosztásnál. Nem szükséges egy kiszolgálót megbízhatóként hozzáadni ahhoz, hogy föderált megosztást hozzon létre.",
"Add server automatically once a federated share was created successfully" : "Kiszolgáló automatikus hozzáadása, ha az egyesített megosztás létrehozása sikeres",
"+ Add trusted server" : "+ Megbízható kiszolgáló hozzáadása",
- "Trusted server" : "Megbízható kiszolgál",
+ "Trusted server" : "Megbízható kiszolgáló",
"Add" : "Hozzáadás"
},
"nplurals=2; plural=(n != 1);");
diff --git a/apps/federation/l10n/hu.json b/apps/federation/l10n/hu.json
index 76f9bfd3d2a..5898af48fc3 100644
--- a/apps/federation/l10n/hu.json
+++ b/apps/federation/l10n/hu.json
@@ -1,16 +1,16 @@
{ "translations": {
"Added to the list of trusted servers" : "Hozzáadva a megbízható kiszolgálók listájához",
"Server is already in the list of trusted servers." : "A kiszolgáló már szerepel a megbízható kiszolgálók között.",
- "No server to federate with found" : "Nem található egyesíthető kiszolgáló",
+ "No server to federate with found" : "Nem található olyan kiszolgáló, amellyel föderálni lehetne",
"Could not add server" : "A kiszolgáló nem adható hozzá",
- "Federation" : "Egyesítés",
- "Federation allows you to connect with other trusted servers to exchange the user directory." : "Az egyesítés lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cserélhessenek.",
- "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "Az egyesítés lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók az egyesített megosztáshoz.",
+ "Federation" : "Föderáció",
+ "Federation allows you to connect with other trusted servers to exchange the user directory." : "A föderáció lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cserélhessenek.",
+ "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing." : "A föderáció lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók a föderált megosztásnál.",
"Trusted servers" : "Megbízható kiszolgálók",
- "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing. It is not necessary to add a server as trusted server in order to create a federated share." : "Az egyesítés lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók az egyesített megosztáshoz. Nem szükséges egy kiszolgálót megbízhatóként hozzáadni ahhoz, hogy egyesített megosztást hozzon létre.",
+ "Federation allows you to connect with other trusted servers to exchange the user directory. For example this will be used to auto-complete external users for federated sharing. It is not necessary to add a server as trusted server in order to create a federated share." : "A föderáció lehetővé teszi a más megbízható kiszolgálókhoz kapcsolódást, hogy a kiszolgálók felhasználójegyzéket cseréljenek. Például ennek segítségével lesznek automatikusan kiegészítve a külső felhasználók a föderált megosztásnál. Nem szükséges egy kiszolgálót megbízhatóként hozzáadni ahhoz, hogy föderált megosztást hozzon létre.",
"Add server automatically once a federated share was created successfully" : "Kiszolgáló automatikus hozzáadása, ha az egyesített megosztás létrehozása sikeres",
"+ Add trusted server" : "+ Megbízható kiszolgáló hozzáadása",
- "Trusted server" : "Megbízható kiszolgál",
+ "Trusted server" : "Megbízható kiszolgáló",
"Add" : "Hozzáadás"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js
index 5dd5012f681..35d077e2e6e 100644
--- a/apps/files/l10n/es.js
+++ b/apps/files/l10n/es.js
@@ -130,7 +130,13 @@ OC.L10N.register(
"{user} deleted an encrypted file in {file}" : "{user} ha borrado un archivo cifrado en {file}",
"You restored {file}" : "Has restaurado {file}",
"{user} restored {file}" : "{user} restauró {file}",
+ "You renamed {oldfile} (hidden) to {newfile} (hidden)" : "Has renombrado {oldfile} (oculto) a {newfile} (oculto)",
+ "You renamed {oldfile} (hidden) to {newfile}" : "Has renombrado {oldfile} (oculto) a {newfile}",
+ "You renamed {oldfile} to {newfile} (hidden)" : "Has renombrado {oldfile} a {newfile} (oculto)",
"You renamed {oldfile} to {newfile}" : "Has renombrado {oldfile} a {newfile}",
+ "{user} renamed {oldfile} (hidden) to {newfile} (hidden)" : "{user} renombro {oldfile} (oculto) a {newfile} (oculto)",
+ "{user} renamed {oldfile} (hidden) to {newfile}" : "{user} renombro {oldfile} (oculto) a {newfile}",
+ "{user} renamed {oldfile} to {newfile} (hidden)" : "{user} renombro {oldfile} a {newfile} (oculto)",
"{user} renamed {oldfile} to {newfile}" : "{user} ha renombrado {oldfile} a {newfile}",
"You moved {oldfile} to {newfile}" : "Has movido {oldfile} a {newfile}",
"{user} moved {oldfile} to {newfile}" : "{user} movió {oldfile} a {newfile}",
diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json
index 138a7d51b2c..53dcaeeba1e 100644
--- a/apps/files/l10n/es.json
+++ b/apps/files/l10n/es.json
@@ -128,7 +128,13 @@
"{user} deleted an encrypted file in {file}" : "{user} ha borrado un archivo cifrado en {file}",
"You restored {file}" : "Has restaurado {file}",
"{user} restored {file}" : "{user} restauró {file}",
+ "You renamed {oldfile} (hidden) to {newfile} (hidden)" : "Has renombrado {oldfile} (oculto) a {newfile} (oculto)",
+ "You renamed {oldfile} (hidden) to {newfile}" : "Has renombrado {oldfile} (oculto) a {newfile}",
+ "You renamed {oldfile} to {newfile} (hidden)" : "Has renombrado {oldfile} a {newfile} (oculto)",
"You renamed {oldfile} to {newfile}" : "Has renombrado {oldfile} a {newfile}",
+ "{user} renamed {oldfile} (hidden) to {newfile} (hidden)" : "{user} renombro {oldfile} (oculto) a {newfile} (oculto)",
+ "{user} renamed {oldfile} (hidden) to {newfile}" : "{user} renombro {oldfile} (oculto) a {newfile}",
+ "{user} renamed {oldfile} to {newfile} (hidden)" : "{user} renombro {oldfile} a {newfile} (oculto)",
"{user} renamed {oldfile} to {newfile}" : "{user} ha renombrado {oldfile} a {newfile}",
"You moved {oldfile} to {newfile}" : "Has movido {oldfile} a {newfile}",
"{user} moved {oldfile} to {newfile}" : "{user} movió {oldfile} a {newfile}",
diff --git a/apps/files_sharing/l10n/de.js b/apps/files_sharing/l10n/de.js
index 8e352ddbc42..8933be8a41d 100644
--- a/apps/files_sharing/l10n/de.js
+++ b/apps/files_sharing/l10n/de.js
@@ -183,6 +183,7 @@ OC.L10N.register(
"Create a new share link" : "Neuen Freigabe-Link erstellen",
"{shareWith} by {initiator}" : "{shareWith} von {initiator}",
"Shared via link by {initiator}" : "Geteilt mittels Link von {initiator}",
+ "Mail share ({label})" : "Mail teilen ({label})",
"Share link ({label})" : "Link teilen ({label})",
"Share link" : "Link teilen",
"Error, please enter proper password and/or expiration date" : "Fehler. Bitte gebe das richtige Passwort und/oder Ablaufdatum ein",
diff --git a/apps/files_sharing/l10n/de.json b/apps/files_sharing/l10n/de.json
index 13763081c56..7f498ad56a7 100644
--- a/apps/files_sharing/l10n/de.json
+++ b/apps/files_sharing/l10n/de.json
@@ -181,6 +181,7 @@
"Create a new share link" : "Neuen Freigabe-Link erstellen",
"{shareWith} by {initiator}" : "{shareWith} von {initiator}",
"Shared via link by {initiator}" : "Geteilt mittels Link von {initiator}",
+ "Mail share ({label})" : "Mail teilen ({label})",
"Share link ({label})" : "Link teilen ({label})",
"Share link" : "Link teilen",
"Error, please enter proper password and/or expiration date" : "Fehler. Bitte gebe das richtige Passwort und/oder Ablaufdatum ein",
diff --git a/apps/files_sharing/l10n/es.js b/apps/files_sharing/l10n/es.js
index a97dafd827b..3842cb08817 100644
--- a/apps/files_sharing/l10n/es.js
+++ b/apps/files_sharing/l10n/es.js
@@ -183,6 +183,7 @@ OC.L10N.register(
"Create a new share link" : "Crear un nuevo enlace compartido",
"{shareWith} by {initiator}" : "{shareWith} por {initiator}",
"Shared via link by {initiator}" : "Compartido vía enlace por {initiator}",
+ "Mail share ({label})" : "Compartir correo ({label})",
"Share link ({label})" : "Compartir enlace ({label})",
"Share link" : "Compartir enlace",
"Error, please enter proper password and/or expiration date" : "Error, por favor, introduce la contraseña y/o fecha de caducidad adecuada",
diff --git a/apps/files_sharing/l10n/es.json b/apps/files_sharing/l10n/es.json
index dd8c4f4c30d..8bf368c2c2a 100644
--- a/apps/files_sharing/l10n/es.json
+++ b/apps/files_sharing/l10n/es.json
@@ -181,6 +181,7 @@
"Create a new share link" : "Crear un nuevo enlace compartido",
"{shareWith} by {initiator}" : "{shareWith} por {initiator}",
"Shared via link by {initiator}" : "Compartido vía enlace por {initiator}",
+ "Mail share ({label})" : "Compartir correo ({label})",
"Share link ({label})" : "Compartir enlace ({label})",
"Share link" : "Compartir enlace",
"Error, please enter proper password and/or expiration date" : "Error, por favor, introduce la contraseña y/o fecha de caducidad adecuada",
diff --git a/apps/files_sharing/lib/Controller/ShareController.php b/apps/files_sharing/lib/Controller/ShareController.php
index 31f13ee2756..7e83ffaa7dc 100644
--- a/apps/files_sharing/lib/Controller/ShareController.php
+++ b/apps/files_sharing/lib/Controller/ShareController.php
@@ -343,7 +343,7 @@ class ShareController extends AuthPublicShareController {
$ownerAccount = $this->accountManager->getAccount($owner);
$ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
- if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
+ if ($ownerName->getScope() === IAccountManager::SCOPE_PUBLISHED) {
$shareTmpl['owner'] = $owner->getUID();
$shareTmpl['shareOwner'] = $owner->getDisplayName();
}
diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php
index 270f38a1148..e00d6bc8c66 100644
--- a/apps/files_sharing/tests/Controller/ShareControllerTest.php
+++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php
@@ -234,7 +234,7 @@ class ShareControllerTest extends \Test\TestCase {
$accountName = $this->createMock(IAccountProperty::class);
$accountName->method('getScope')
- ->willReturn(IAccountManager::VISIBILITY_PUBLIC);
+ ->willReturn(IAccountManager::SCOPE_PUBLISHED);
$account = $this->createMock(IAccount::class);
$account->method('getProperty')
->with(IAccountManager::PROPERTY_DISPLAYNAME)
@@ -381,7 +381,7 @@ class ShareControllerTest extends \Test\TestCase {
$accountName = $this->createMock(IAccountProperty::class);
$accountName->method('getScope')
- ->willReturn(IAccountManager::VISIBILITY_PRIVATE);
+ ->willReturn(IAccountManager::SCOPE_LOCAL);
$account = $this->createMock(IAccount::class);
$account->method('getProperty')
->with(IAccountManager::PROPERTY_DISPLAYNAME)
@@ -528,7 +528,7 @@ class ShareControllerTest extends \Test\TestCase {
$accountName = $this->createMock(IAccountProperty::class);
$accountName->method('getScope')
- ->willReturn(IAccountManager::VISIBILITY_PUBLIC);
+ ->willReturn(IAccountManager::SCOPE_PUBLISHED);
$account = $this->createMock(IAccount::class);
$account->method('getProperty')
->with(IAccountManager::PROPERTY_DISPLAYNAME)
@@ -688,7 +688,7 @@ class ShareControllerTest extends \Test\TestCase {
$accountName = $this->createMock(IAccountProperty::class);
$accountName->method('getScope')
- ->willReturn(IAccountManager::VISIBILITY_PUBLIC);
+ ->willReturn(IAccountManager::SCOPE_PUBLISHED);
$account = $this->createMock(IAccount::class);
$account->method('getProperty')
->with(IAccountManager::PROPERTY_DISPLAYNAME)
diff --git a/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php b/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php
index 889fcfd6277..c462eeedb43 100644
--- a/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php
+++ b/apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php
@@ -193,7 +193,7 @@ class RetryJob extends Job {
$publicData = [];
foreach ($account->getProperties() as $property) {
- if ($property->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
+ if ($property->getScope() === IAccountManager::SCOPE_PUBLISHED) {
$publicData[$property->getName()] = $property->getValue();
}
}
diff --git a/apps/provisioning_api/composer/composer/autoload_classmap.php b/apps/provisioning_api/composer/composer/autoload_classmap.php
index e94a97c1949..22927806e65 100644
--- a/apps/provisioning_api/composer/composer/autoload_classmap.php
+++ b/apps/provisioning_api/composer/composer/autoload_classmap.php
@@ -8,6 +8,7 @@ $baseDir = $vendorDir;
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'OCA\\Provisioning_API\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
+ 'OCA\\Provisioning_API\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
'OCA\\Provisioning_API\\Controller\\AUserData' => $baseDir . '/../lib/Controller/AUserData.php',
'OCA\\Provisioning_API\\Controller\\AppConfigController' => $baseDir . '/../lib/Controller/AppConfigController.php',
'OCA\\Provisioning_API\\Controller\\AppsController' => $baseDir . '/../lib/Controller/AppsController.php',
diff --git a/apps/provisioning_api/composer/composer/autoload_static.php b/apps/provisioning_api/composer/composer/autoload_static.php
index b982f203211..f5a4b73f4f8 100644
--- a/apps/provisioning_api/composer/composer/autoload_static.php
+++ b/apps/provisioning_api/composer/composer/autoload_static.php
@@ -23,6 +23,7 @@ class ComposerStaticInitProvisioning_API
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'OCA\\Provisioning_API\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
+ 'OCA\\Provisioning_API\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
'OCA\\Provisioning_API\\Controller\\AUserData' => __DIR__ . '/..' . '/../lib/Controller/AUserData.php',
'OCA\\Provisioning_API\\Controller\\AppConfigController' => __DIR__ . '/..' . '/../lib/Controller/AppConfigController.php',
'OCA\\Provisioning_API\\Controller\\AppsController' => __DIR__ . '/..' . '/../lib/Controller/AppsController.php',
diff --git a/apps/provisioning_api/lib/AppInfo/Application.php b/apps/provisioning_api/lib/AppInfo/Application.php
index 7ec21c3329e..af6b2b33711 100644
--- a/apps/provisioning_api/lib/AppInfo/Application.php
+++ b/apps/provisioning_api/lib/AppInfo/Application.php
@@ -29,6 +29,7 @@
namespace OCA\Provisioning_API\AppInfo;
use OC\Group\Manager as GroupManager;
+use OCA\Provisioning_API\Capabilities;
use OCA\Provisioning_API\Listener\UserDeletedListener;
use OCA\Provisioning_API\Middleware\ProvisioningApiMiddleware;
use OCA\Settings\Mailer\NewUserMailHelper;
@@ -92,6 +93,7 @@ class Application extends App implements IBootstrap {
);
});
$context->registerMiddleware(ProvisioningApiMiddleware::class);
+ $context->registerCapability(Capabilities::class);
}
public function boot(IBootContext $context): void {
diff --git a/apps/provisioning_api/lib/Capabilities.php b/apps/provisioning_api/lib/Capabilities.php
new file mode 100644
index 00000000000..d355e4db4c2
--- /dev/null
+++ b/apps/provisioning_api/lib/Capabilities.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @copyright Copyright (c) 2021 Vincent Petry <vincent@nextcloud.com>
+ *
+ * @author Vincent Petry <vincent@nextcloud.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Provisioning_API;
+
+use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCP\App\IAppManager;
+use OCP\Capabilities\ICapability;
+
+class Capabilities implements ICapability {
+
+ /** @var IAppManager */
+ private $appManager;
+
+ public function __construct(IAppManager $appManager) {
+ $this->appManager = $appManager;
+ }
+
+ /**
+ * Function an app uses to return the capabilities
+ *
+ * @return array Array containing the apps capabilities
+ */
+ public function getCapabilities() {
+ $federationScopesEnabled = false;
+
+ $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
+ if ($federatedFileSharingEnabled) {
+ /** @var FederatedShareProvider $shareProvider */
+ $shareProvider = \OC::$server->query(FederatedShareProvider::class);
+ $federationScopesEnabled = $shareProvider->isLookupServerUploadEnabled();
+ }
+
+ return [
+ 'provisioning_api' => [
+ 'version' => $this->appManager->getAppVersion('provisioning_api'),
+ 'AccountPropertyScopesVersion' => 2,
+ 'AccountPropertyScopesFederationEnabled' => $federationScopesEnabled,
+ ]
+ ];
+ }
+}
diff --git a/apps/provisioning_api/lib/Controller/AUserData.php b/apps/provisioning_api/lib/Controller/AUserData.php
index 5e6af27cf72..c26c4f9e2d0 100644
--- a/apps/provisioning_api/lib/Controller/AUserData.php
+++ b/apps/provisioning_api/lib/Controller/AUserData.php
@@ -51,6 +51,7 @@ use OCP\User\Backend\ISetDisplayNameBackend;
use OCP\User\Backend\ISetPasswordBackend;
abstract class AUserData extends OCSController {
+ public const SCOPE_SUFFIX = 'Scope';
/** @var IUserManager */
protected $userManager;
@@ -87,12 +88,13 @@ abstract class AUserData extends OCSController {
* creates a array with all user data
*
* @param string $userId
+ * @param bool $includeScopes
* @return array
* @throws NotFoundException
* @throws OCSException
* @throws OCSNotFoundException
*/
- protected function getUserData(string $userId): array {
+ protected function getUserData(string $userId, bool $includeScopes = false): array {
$currentLoggedInUser = $this->userSession->getUser();
$data = [];
@@ -115,7 +117,7 @@ abstract class AUserData extends OCSController {
}
// Get groups data
- $userAccount = $this->accountManager->getUser($targetUserObject);
+ $userAccount = $this->accountManager->getAccount($targetUserObject);
$groups = $this->groupManager->getUserGroups($targetUserObject);
$gids = [];
foreach ($groups as $group) {
@@ -137,12 +139,33 @@ abstract class AUserData extends OCSController {
$data['backend'] = $targetUserObject->getBackendClassName();
$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
+
+ if ($includeScopes) {
+ $data[IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope();
+ }
+
$data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
+ if ($includeScopes) {
+ $data[IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope();
+ }
$data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
- $data[IAccountManager::PROPERTY_PHONE] = $userAccount[IAccountManager::PROPERTY_PHONE]['value'];
- $data[IAccountManager::PROPERTY_ADDRESS] = $userAccount[IAccountManager::PROPERTY_ADDRESS]['value'];
- $data[IAccountManager::PROPERTY_WEBSITE] = $userAccount[IAccountManager::PROPERTY_WEBSITE]['value'];
- $data[IAccountManager::PROPERTY_TWITTER] = $userAccount[IAccountManager::PROPERTY_TWITTER]['value'];
+ if ($includeScopes) {
+ $data[IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX] = $userAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope();
+ }
+
+ foreach ([
+ IAccountManager::PROPERTY_PHONE,
+ IAccountManager::PROPERTY_ADDRESS,
+ IAccountManager::PROPERTY_WEBSITE,
+ IAccountManager::PROPERTY_TWITTER,
+ ] as $propertyName) {
+ $property = $userAccount->getProperty($propertyName);
+ $data[$propertyName] = $property->getValue();
+ if ($includeScopes) {
+ $data[$propertyName . self::SCOPE_SUFFIX] = $property->getScope();
+ }
+ }
+
$data['groups'] = $gids;
$data['language'] = $this->l10nFactory->getUserLanguage($targetUserObject);
$data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale');
diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php
index d2f9b9e91c2..0019472c884 100644
--- a/apps/provisioning_api/lib/Controller/UsersController.php
+++ b/apps/provisioning_api/lib/Controller/UsersController.php
@@ -50,7 +50,6 @@ use OC\Accounts\AccountManager;
use OC\Authentication\Token\RemoteWipe;
use OC\HintException;
use OC\KnownUser\KnownUserService;
-use OCA\Provisioning_API\FederatedShareProviderFactory;
use OCA\Settings\Mailer\NewUserMailHelper;
use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
@@ -85,8 +84,6 @@ class UsersController extends AUserData {
protected $l10nFactory;
/** @var NewUserMailHelper */
private $newUserMailHelper;
- /** @var FederatedShareProviderFactory */
- private $federatedShareProviderFactory;
/** @var ISecureRandom */
private $secureRandom;
/** @var RemoteWipe */
@@ -108,7 +105,6 @@ class UsersController extends AUserData {
LoggerInterface $logger,
IFactory $l10nFactory,
NewUserMailHelper $newUserMailHelper,
- FederatedShareProviderFactory $federatedShareProviderFactory,
ISecureRandom $secureRandom,
RemoteWipe $remoteWipe,
KnownUserService $knownUserService,
@@ -127,7 +123,6 @@ class UsersController extends AUserData {
$this->logger = $logger;
$this->l10nFactory = $l10nFactory;
$this->newUserMailHelper = $newUserMailHelper;
- $this->federatedShareProviderFactory = $federatedShareProviderFactory;
$this->secureRandom = $secureRandom;
$this->remoteWipe = $remoteWipe;
$this->knownUserService = $knownUserService;
@@ -488,7 +483,13 @@ class UsersController extends AUserData {
* @throws OCSException
*/
public function getUser(string $userId): DataResponse {
- $data = $this->getUserData($userId);
+ $includeScopes = false;
+ $currentUser = $this->userSession->getUser();
+ if ($currentUser && $currentUser->getUID() === $userId) {
+ $includeScopes = true;
+ }
+
+ $data = $this->getUserData($userId, $includeScopes);
// getUserData returns empty array if not enough permissions
if (empty($data)) {
throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
@@ -508,7 +509,7 @@ class UsersController extends AUserData {
public function getCurrentUser(): DataResponse {
$user = $this->userSession->getUser();
if ($user) {
- $data = $this->getUserData($user->getUID());
+ $data = $this->getUserData($user->getUID(), true);
// rename "displayname" to "display-name" only for this call to keep
// the API stable.
$data['display-name'] = $data['displayname'];
@@ -532,15 +533,10 @@ class UsersController extends AUserData {
$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
}
- if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
- $shareProvider = $this->federatedShareProviderFactory->get();
- if ($shareProvider->isLookupServerUploadEnabled()) {
- $permittedFields[] = IAccountManager::PROPERTY_PHONE;
- $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
- $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
- $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
- }
- }
+ $permittedFields[] = IAccountManager::PROPERTY_PHONE;
+ $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+ $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+ $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
return new DataResponse($permittedFields);
}
@@ -575,6 +571,9 @@ class UsersController extends AUserData {
$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
}
+ $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
+ $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
+
$permittedFields[] = 'password';
if ($this->config->getSystemValue('force_language', false) === false ||
$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
@@ -586,15 +585,16 @@ class UsersController extends AUserData {
$permittedFields[] = 'locale';
}
- if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
- $shareProvider = $this->federatedShareProviderFactory->get();
- if ($shareProvider->isLookupServerUploadEnabled()) {
- $permittedFields[] = IAccountManager::PROPERTY_PHONE;
- $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
- $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
- $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
- }
- }
+ $permittedFields[] = IAccountManager::PROPERTY_PHONE;
+ $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+ $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+ $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
+ $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
+ $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
+ $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
+ $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
+
+ $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
// If admin they can edit their own quota
if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
@@ -699,6 +699,24 @@ class UsersController extends AUserData {
}
}
break;
+ case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
+ case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
+ case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
+ case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
+ case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
+ case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
+ case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
+ $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
+ $userAccount = $this->accountManager->getUser($targetUser);
+ if ($userAccount[$propertyName]['scope'] !== $value) {
+ $userAccount[$propertyName]['scope'] = $value;
+ try {
+ $this->accountManager->updateUser($targetUser, $userAccount, true);
+ } catch (\InvalidArgumentException $e) {
+ throw new OCSException('Invalid ' . $e->getMessage(), 102);
+ }
+ }
+ break;
default:
throw new OCSException('', 103);
}
diff --git a/apps/provisioning_api/tests/CapabilitiesTest.php b/apps/provisioning_api/tests/CapabilitiesTest.php
new file mode 100644
index 00000000000..89a4215d273
--- /dev/null
+++ b/apps/provisioning_api/tests/CapabilitiesTest.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @copyright Copyright (c) 2021 Vincent Petry <vincent@nextcloud.com>
+ *
+ * @author Vincent Petry <vincent@nextcloud.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Provisioning_API\Tests\unit;
+
+use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCA\Provisioning_API\Capabilities;
+use OCP\App\IAppManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+/**
+ * Capabilities test for provisioning API.
+ *
+ * Note: group DB needed because of usage of overwriteService()
+ *
+ * @package OCA\Provisioning_API\Tests
+ * @group DB
+ */
+class CapabilitiesTest extends TestCase {
+
+ /** @var Capabilities */
+ protected $capabilities;
+
+ /** @var IAppManager|MockObject */
+ protected $appManager;
+
+ public function setUp(): void {
+ parent::setUp();
+ $this->appManager = $this->createMock(IAppManager::class);
+ $this->capabilities = new Capabilities($this->appManager);
+
+ $this->appManager->expects($this->once())
+ ->method('getAppVersion')
+ ->with('provisioning_api')
+ ->willReturn('1.12');
+ }
+
+ public function getCapabilitiesProvider() {
+ return [
+ [false, false, false],
+ [true, false, false],
+ [true, true, true],
+ ];
+ }
+
+ /**
+ * @dataProvider getCapabilitiesProvider
+ */
+ public function testGetCapabilities($federationAppEnabled, $lookupServerEnabled, $expectedFederationScopesEnabled) {
+ $this->appManager->expects($this->once())
+ ->method('isEnabledForUser')
+ ->with('federatedfilesharing')
+ ->willReturn($federationAppEnabled);
+
+ $federatedShareProvider = $this->createMock(FederatedShareProvider::class);
+ $this->overwriteService(FederatedShareProvider::class, $federatedShareProvider);
+
+ $federatedShareProvider->expects($this->any())
+ ->method('isLookupServerUploadEnabled')
+ ->willReturn($lookupServerEnabled);
+
+ $expected = [
+ 'provisioning_api' => [
+ 'version' => '1.12',
+ 'AccountPropertyScopesVersion' => 2,
+ 'AccountPropertyScopesFederationEnabled' => $expectedFederationScopesEnabled,
+ ],
+ ];
+ $this->assertSame($expected, $this->capabilities->getCapabilities());
+ }
+}
diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php
index d65e3d07913..4754c5a468d 100644
--- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php
+++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php
@@ -46,11 +46,11 @@ use OC\Authentication\Token\RemoteWipe;
use OC\Group\Manager;
use OC\KnownUser\KnownUserService;
use OC\SubAdmin;
-use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Provisioning_API\Controller\UsersController;
-use OCA\Provisioning_API\FederatedShareProviderFactory;
use OCA\Settings\Mailer\NewUserMailHelper;
+use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
+use OCP\Accounts\IAccountProperty;
use OCP\App\IAppManager;
use OCP\AppFramework\Http\DataResponse;
use OCP\EventDispatcher\IEventDispatcher;
@@ -97,8 +97,6 @@ class UsersControllerTest extends TestCase {
private $l10nFactory;
/** @var NewUserMailHelper|MockObject */
private $newUserMailHelper;
- /** @var FederatedShareProviderFactory|MockObject */
- private $federatedShareProviderFactory;
/** @var ISecureRandom|MockObject */
private $secureRandom;
/** @var RemoteWipe|MockObject */
@@ -122,7 +120,6 @@ class UsersControllerTest extends TestCase {
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->l10nFactory = $this->createMock(IFactory::class);
$this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
- $this->federatedShareProviderFactory = $this->createMock(FederatedShareProviderFactory::class);
$this->secureRandom = $this->createMock(ISecureRandom::class);
$this->remoteWipe = $this->createMock(RemoteWipe::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
@@ -142,7 +139,6 @@ class UsersControllerTest extends TestCase {
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
- $this->federatedShareProviderFactory,
$this->secureRandom,
$this->remoteWipe,
$this->knownUserService,
@@ -407,7 +403,6 @@ class UsersControllerTest extends TestCase {
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
- $this->federatedShareProviderFactory,
$this->secureRandom,
$this->remoteWipe,
$this->knownUserService,
@@ -934,7 +929,6 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$this->userSession
- ->expects($this->once())
->method('getUser')
->willReturn($loggedInUser);
$this->userManager
@@ -1004,16 +998,13 @@ class UsersControllerTest extends TestCase {
$group->expects($this->at(3))
->method('getGID')
->willReturn('group3');
- $this->accountManager->expects($this->any())->method('getUser')
- ->with($targetUser)
- ->willReturn(
- [
- IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
- IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
- IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
- IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
- ]
- );
+
+ $this->mockAccount($targetUser, [
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+ IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+ ]);
$this->config
->expects($this->at(0))
->method('getUserValue')
@@ -1173,16 +1164,13 @@ class UsersControllerTest extends TestCase {
$targetUser
->method('getUID')
->willReturn('UID');
- $this->accountManager->expects($this->any())->method('getUser')
- ->with($targetUser)
- ->willReturn(
- [
- IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
- IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
- IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
- IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
- ]
- );
+
+ $this->mockAccount($targetUser, [
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+ IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+ ]);
$this->l10nFactory
->expects($this->once())
@@ -1225,14 +1213,13 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
- ->expects($this->exactly(2))
+ ->expects($this->exactly(3))
->method('getUID')
->willReturn('subadmin');
$targetUser = $this->getMockBuilder(IUser::class)
->disableOriginalConstructor()
->getMock();
$this->userSession
- ->expects($this->once())
->method('getUser')
->willReturn($loggedInUser);
$this->userManager
@@ -1344,16 +1331,12 @@ class UsersControllerTest extends TestCase {
->expects($this->once())
->method('getBackend')
->willReturn($backend);
- $this->accountManager->expects($this->any())->method('getUser')
- ->with($targetUser)
- ->willReturn(
- [
- IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
- IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
- IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
- IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
- ]
- );
+ $this->mockAccount($targetUser, [
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+ IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+ ]);
$this->l10nFactory
->expects($this->once())
@@ -1538,6 +1521,91 @@ class UsersControllerTest extends TestCase {
$this->api->editUser('UserToEdit', 'email', 'demo.org');
}
+ public function selfEditChangePropertyProvider() {
+ return [
+ [IAccountManager::PROPERTY_TWITTER, '@oldtwitter', '@newtwitter'],
+ [IAccountManager::PROPERTY_PHONE, '1234', '12345'],
+ [IAccountManager::PROPERTY_ADDRESS, 'Something street 2', 'Another street 3'],
+ [IAccountManager::PROPERTY_WEBSITE, 'https://examplesite1', 'https://examplesite2'],
+ ];
+ }
+
+ /**
+ * @dataProvider selfEditChangePropertyProvider
+ */
+ public function testEditUserRegularUserSelfEditChangeProperty($propertyName, $oldValue, $newValue) {
+ $loggedInUser = $this->getMockBuilder(IUser::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $loggedInUser
+ ->expects($this->any())
+ ->method('getUID')
+ ->willReturn('UID');
+ $this->userSession
+ ->expects($this->once())
+ ->method('getUser')
+ ->willReturn($loggedInUser);
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('UserToEdit')
+ ->willReturn($loggedInUser);
+
+ $this->accountManager->expects($this->once())
+ ->method('getUser')
+ ->with($loggedInUser)
+ ->willReturn([$propertyName => ['value' => $oldValue, 'scope' => IAccountManager::SCOPE_LOCAL]]);
+ $this->accountManager->expects($this->once())
+ ->method('updateUser')
+ ->with($loggedInUser, [$propertyName => ['value' => $newValue, 'scope' => IAccountManager::SCOPE_LOCAL]], true);
+
+ $this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName, $newValue)->getData());
+ }
+
+ public function selfEditChangePropertyScopeProvider() {
+ return [
+ [IAccountManager::PROPERTY_AVATAR, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::PROPERTY_DISPLAYNAME, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::PROPERTY_EMAIL, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::PROPERTY_TWITTER, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::PROPERTY_PHONE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::PROPERTY_ADDRESS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::PROPERTY_WEBSITE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
+ ];
+ }
+
+ /**
+ * @dataProvider selfEditChangePropertyProvider
+ */
+ public function testEditUserRegularUserSelfEditChangePropertyScope($propertyName, $oldScope, $newScope) {
+ $loggedInUser = $this->getMockBuilder(IUser::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $loggedInUser
+ ->expects($this->any())
+ ->method('getUID')
+ ->willReturn('UID');
+ $this->userSession
+ ->expects($this->once())
+ ->method('getUser')
+ ->willReturn($loggedInUser);
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('UserToEdit')
+ ->willReturn($loggedInUser);
+
+ $this->accountManager->expects($this->once())
+ ->method('getUser')
+ ->with($loggedInUser)
+ ->willReturn([$propertyName => ['value' => 'somevalue', 'scope' => $oldScope]]);
+ $this->accountManager->expects($this->once())
+ ->method('updateUser')
+ ->with($loggedInUser, [$propertyName => ['value' => 'somevalue', 'scope' => $newScope]], true);
+
+ $this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName . 'Scope', $newScope)->getData());
+ }
+
public function testEditUserRegularUserSelfEditChangePassword() {
$loggedInUser = $this->getMockBuilder(IUser::class)
->disableOriginalConstructor()
@@ -3247,7 +3315,6 @@ class UsersControllerTest extends TestCase {
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
- $this->federatedShareProviderFactory,
$this->secureRandom,
$this->remoteWipe,
$this->knownUserService,
@@ -3256,7 +3323,7 @@ class UsersControllerTest extends TestCase {
->setMethods(['getUserData'])
->getMock();
- $api->expects($this->once())->method('getUserData')->with('UID')
+ $api->expects($this->once())->method('getUserData')->with('UID', true)
->willReturn(
[
'id' => 'UID',
@@ -3297,8 +3364,15 @@ class UsersControllerTest extends TestCase {
$this->api->getCurrentUser();
}
-
public function testGetUser() {
+ $loggedInUser = $this->createMock(IUser::class);
+ $loggedInUser
+ ->method('getUID')
+ ->willReturn('currentuser');
+ $this->userSession
+ ->method('getUser')
+ ->willReturn($loggedInUser);
+
/** @var UsersController | MockObject $api */
$api = $this->getMockBuilder(UsersController::class)
->setConstructorArgs([
@@ -3314,7 +3388,6 @@ class UsersControllerTest extends TestCase {
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
- $this->federatedShareProviderFactory,
$this->secureRandom,
$this->remoteWipe,
$this->knownUserService,
@@ -3335,11 +3408,16 @@ class UsersControllerTest extends TestCase {
'displayname' => 'Demo User'
];
- $api->expects($this->once())->method('getUserData')
- ->with('uid')
+ $api->expects($this->at(0))->method('getUserData')
+ ->with('uid', false)
+ ->willReturn($expected);
+ $api->expects($this->at(1))->method('getUserData')
+ ->with('currentuser', true)
->willReturn($expected);
$this->assertSame($expected, $api->getUser('uid')->getData());
+
+ $this->assertSame($expected, $api->getUser('currentuser')->getData());
}
@@ -3639,18 +3717,13 @@ class UsersControllerTest extends TestCase {
public function dataGetEditableFields() {
return [
- [false, false, []],
- [false, true, [
+ [false, [
IAccountManager::PROPERTY_PHONE,
IAccountManager::PROPERTY_ADDRESS,
IAccountManager::PROPERTY_WEBSITE,
IAccountManager::PROPERTY_TWITTER,
]],
- [ true, false, [
- IAccountManager::PROPERTY_DISPLAYNAME,
- IAccountManager::PROPERTY_EMAIL,
- ]],
- [ true, true ,[
+ [ true, [
IAccountManager::PROPERTY_DISPLAYNAME,
IAccountManager::PROPERTY_EMAIL,
IAccountManager::PROPERTY_PHONE,
@@ -3665,29 +3738,36 @@ class UsersControllerTest extends TestCase {
* @dataProvider dataGetEditableFields
*
* @param bool $allowedToChangeDisplayName
- * @param bool $federatedSharingEnabled
* @param array $expected
*/
- public function testGetEditableFields(bool $allowedToChangeDisplayName, bool $federatedSharingEnabled, array $expected) {
+ public function testGetEditableFields(bool $allowedToChangeDisplayName, array $expected) {
$this->config
->method('getSystemValue')
->with(
$this->equalTo('allow_user_to_change_display_name'),
$this->anything()
)->willReturn($allowedToChangeDisplayName);
- $this->appManager
- ->method('isEnabledForUser')
- ->with($this->equalTo('federatedfilesharing'))
- ->willReturn($federatedSharingEnabled);
-
- $shareprovider = $this->createMock(FederatedShareProvider::class);
- $shareprovider->method('isLookupServerUploadEnabled')->willReturn(true);
-
- $this->federatedShareProviderFactory
- ->method('get')
- ->willReturn($shareprovider);
$expectedResp = new DataResponse($expected);
$this->assertEquals($expectedResp, $this->api->getEditableFields());
}
+
+ private function mockAccount($targetUser, $accountProperties) {
+ $mockedProperties = [];
+
+ foreach ($accountProperties as $propertyName => $data) {
+ $mockedProperty = $this->createMock(IAccountProperty::class);
+ $mockedProperty->method('getValue')->willReturn($data['value'] ?? '');
+ $mockedProperty->method('getScope')->willReturn($data['scope'] ?? '');
+ $mockedProperties[] = [$propertyName, $mockedProperty];
+ }
+
+ $account = $this->createMock(IAccount::class);
+ $account->method('getProperty')
+ ->will($this->returnValueMap($mockedProperties));
+
+ $this->accountManager->expects($this->any())->method('getAccount')
+ ->with($targetUser)
+ ->willReturn($account);
+ }
}
diff --git a/apps/settings/js/federationscopemenu.js b/apps/settings/js/federationscopemenu.js
index 170aec15a85..617a8e3d412 100644
--- a/apps/settings/js/federationscopemenu.js
+++ b/apps/settings/js/federationscopemenu.js
@@ -15,6 +15,8 @@
* Construct a new FederationScopeMenu instance
* @constructs FederationScopeMenu
* @memberof OC.Settings
+ * @param {object} options
+ * @param {array.<string>} [options.excludedScopes] array of excluded scopes
*/
var FederationScopeMenu = OC.Backbone.View.extend({
tagName: 'div',
@@ -26,27 +28,40 @@
this.field = options.field;
this._scopes = [
{
- name: 'private',
+ name: 'v2-private',
displayName: t('settings', 'Private'),
+ tooltip: t('settings', "Don't show via public link"),
+ iconClass: 'icon-password',
+ active: false
+ },
+ {
+ name: 'v2-local',
+ displayName: t('settings', 'Local'),
tooltip: t('settings', "Don't synchronize to servers"),
iconClass: 'icon-password',
active: false
},
{
- name: 'contacts',
- displayName: t('settings', 'Trusted'),
+ name: 'v2-federated',
+ displayName: t('settings', 'Federated'),
tooltip: t('settings', 'Only synchronize to trusted servers'),
iconClass: 'icon-contacts-dark',
active: false
},
{
- name: 'public',
- displayName: t('settings', 'Public'),
+ name: 'v2-published',
+ displayName: t('settings', 'Published'),
tooltip: t('settings', 'Synchronize to trusted servers and the global and public address book'),
iconClass: 'icon-link',
active: false
}
];
+
+ if (options.excludedScopes && options.excludedScopes.length) {
+ this._scopes = this._scopes.filter(function(scopeEntry) {
+ return options.excludedScopes.indexOf(scopeEntry.name) === -1;
+ })
+ }
},
/**
@@ -102,19 +117,11 @@
var currentlyActiveValue = $('#'+context.target.closest('form').id).find('input[type="hidden"]')[0].value;
for(var i in this._scopes) {
- this._scopes[i].active = false;
- }
-
- switch (currentlyActiveValue) {
- case 'private':
- this._scopes[0].active = true;
- break;
- case 'contacts':
- this._scopes[1].active = true;
- break;
- case 'public':
- this._scopes[2].active = true;
- break;
+ if (this._scopes[i].name === currentlyActiveValue) {
+ this._scopes[i].active = true;
+ } else {
+ this._scopes[i].active = false;
+ }
}
this.render();
diff --git a/apps/settings/js/federationsettingsview.js b/apps/settings/js/federationsettingsview.js
index 9cefaf132f2..cf7f4648905 100644
--- a/apps/settings/js/federationsettingsview.js
+++ b/apps/settings/js/federationsettingsview.js
@@ -10,6 +10,13 @@
(function(_, $, OC) {
'use strict';
+ /**
+ * Construct a new FederationScopeMenu instance
+ * @constructs FederationScopeMenu
+ * @memberof OC.Settings
+ * @param {object} options
+ * @param {bool} [options.lookupServerUploadEnabled=false] whether uploading to the lookup server is enabled
+ */
var FederationSettingsView = OC.Backbone.View.extend({
_inputFields: undefined,
@@ -24,6 +31,7 @@
} else {
this._config = new OC.Settings.UserSettings();
}
+ this.showFederationScopes = !!options.showFederationScopes;
this._inputFields = [
'displayname',
@@ -61,9 +69,31 @@
render: function() {
var self = this;
+ var fieldsWithV2Private = [
+ 'avatar',
+ 'phone',
+ 'twitter',
+ 'website',
+ 'address',
+ ];
+
_.each(this._inputFields, function(field) {
var $icon = self.$('#' + field + 'form h3 > .federation-menu');
- var scopeMenu = new OC.Settings.FederationScopeMenu({field: field});
+ var excludedScopes = []
+
+ if (fieldsWithV2Private.indexOf(field) === -1) {
+ excludedScopes.push('v2-private');
+ }
+
+ if (!self.showFederationScopes) {
+ excludedScopes.push('v2-federated');
+ excludedScopes.push('v2-published');
+ }
+
+ var scopeMenu = new OC.Settings.FederationScopeMenu({
+ field: field,
+ excludedScopes: excludedScopes,
+ });
self.listenTo(scopeMenu, 'select:scope', function(scope) {
self._onScopeChanged(field, scope);
@@ -207,15 +237,16 @@
$icon.addClass('hidden');
switch (scope) {
- case 'private':
+ case 'v2-private':
+ case 'v2-local':
$icon.addClass('icon-password');
$icon.removeClass('hidden');
break;
- case 'contacts':
+ case 'v2-federated':
$icon.addClass('icon-contacts-dark');
$icon.removeClass('hidden');
break;
- case 'public':
+ case 'v2-published':
$icon.addClass('icon-link');
$icon.removeClass('hidden');
break;
diff --git a/apps/settings/js/settings/personalInfo.js b/apps/settings/js/settings/personalInfo.js
index a6055fd7a94..e71f4840123 100644
--- a/apps/settings/js/settings/personalInfo.js
+++ b/apps/settings/js/settings/personalInfo.js
@@ -199,10 +199,12 @@ window.addEventListener('DOMContentLoaded', function () {
});
+ var settingsEl = $('#personal-settings')
var userSettings = new OC.Settings.UserSettings();
var federationSettingsView = new OC.Settings.FederationSettingsView({
- el: '#personal-settings',
- config: userSettings
+ el: settingsEl,
+ config: userSettings,
+ showFederationScopes: !!settingsEl.data('lookup-server-upload-enabled'),
});
userSettings.on("sync", function() {
diff --git a/apps/settings/l10n/cs.js b/apps/settings/l10n/cs.js
index b1f994db1fe..04dcb0777c8 100644
--- a/apps/settings/l10n/cs.js
+++ b/apps/settings/l10n/cs.js
@@ -155,7 +155,7 @@ OC.L10N.register(
"User documentation" : "Dokumentace pro uživatele",
"Admin documentation" : "Dokumentace pro správce",
"Developer documentation" : "Dokumentace pro vývojáře",
- "This app is supported via your current Nextcloud subscription." : "Tato aplikace je podporována prostřednictvím vašeho stávajícího předplatného Nextcloud.",
+ "This app is supported via your current Nextcloud subscription." : "Tato aplikace je podporována prostřednictvím vašeho stávajícího předplatného podpory Nextcloud.",
"Supported" : "Podporováno",
"Featured apps are developed by and within the community. They offer central functionality and are ready for production use." : "Doporučované aplikace jsou vyvíjeny komunitou. Jsou propojeny se zbytkem Nextcloud a připraveny na produkční nasazení.",
"Featured" : "Doporučené",
@@ -317,14 +317,14 @@ OC.L10N.register(
"From address" : "Adresa odesílatele",
"mail" : "e-mail",
"Authentication method" : "Metoda ověření",
- "Authentication required" : "Vyžadováno ověření",
+ "Authentication required" : "Vyžadováno ověření se",
"Server address" : "Adresa serveru",
"Port" : "Port",
"Credentials" : "Přihlašovací údaje",
"SMTP Username" : "SMTP uživatelské jméno ",
"SMTP Password" : "Heslo pro SMTP",
"Save" : "Uložit",
- "Test email settings" : "Test nastavení e-mailu",
+ "Test email settings" : "Vyzkoušet nastavení e-mailu",
"Send email" : "Odeslat e-mail",
"Security & setup warnings" : "Varování ohledně zabezpečení a nastavení",
"It's important for the security and performance of your instance that everything is configured correctly. To help you with that we are doing some automatic checks. Please see the linked documentation for more information." : "Pro zabezpečení a optimální výkon instance Nextcloud je důležité, aby vše bylo správně nastaveno. Jako pomoc, instance samotná automaticky ověřuje některá nastavení. Další informace naleznete v odkazované dokumentaci.",
@@ -339,7 +339,7 @@ OC.L10N.register(
"Server-side encryption" : "Šifrování na straně serveru",
"Server-side encryption makes it possible to encrypt files which are uploaded to this server. This comes with limitations like a performance penalty, so enable this only if needed." : "Šifrování na straně serveru umožňuje zašifrovat soubory, které jsou na tento server nahrávány. To přináší omezení jako třeba snížení výkonu, takže toto zapněte jen pokud je opravdu potřeba.",
"Enable server-side encryption" : "Šifrovat na straně serveru",
- "Please read carefully before activating server-side encryption: " : "Pročtěte důkladně před zapnutím šifrování dat na serveru:",
+ "Please read carefully before activating server-side encryption: " : "Důkladně si pročtěte a až teprve poté případně zapínejte šifrování dat na straně serveru: ",
"Once encryption is enabled, all files uploaded to the server from that point forward will be encrypted at rest on the server. It will only be possible to disable encryption at a later date if the active encryption module supports that function, and all pre-conditions (e.g. setting a recover key) are met." : "Poté co je zapnuto šifrování, jsou od toho bodu všechny nahrávané soubory šifrovány serverem. Vypnout šifrování bude možné pouze později, až bude šifrovací modul tuto možnost podporovat a po splnění všech nutných podmínek (tzn. nastavení klíčů pro obnovení).",
"Encryption alone does not guarantee security of the system. Please see documentation for more information about how the encryption app works, and the supported use cases." : "Šifrování samotné ještě nezaručuje bezpečnost systému. Pokud se chcete dozvědět víc o tom, jak aplikace pro šifrování funguje a jaké jsou podporované případy použití, naleznete to v dokumentaci.",
"Be aware that encryption always increases the file size." : "Mějte na paměti, že šifrování vždy navýší velikost souboru.",
diff --git a/apps/settings/l10n/cs.json b/apps/settings/l10n/cs.json
index 0a562904f4a..919836fcf28 100644
--- a/apps/settings/l10n/cs.json
+++ b/apps/settings/l10n/cs.json
@@ -153,7 +153,7 @@
"User documentation" : "Dokumentace pro uživatele",
"Admin documentation" : "Dokumentace pro správce",
"Developer documentation" : "Dokumentace pro vývojáře",
- "This app is supported via your current Nextcloud subscription." : "Tato aplikace je podporována prostřednictvím vašeho stávajícího předplatného Nextcloud.",
+ "This app is supported via your current Nextcloud subscription." : "Tato aplikace je podporována prostřednictvím vašeho stávajícího předplatného podpory Nextcloud.",
"Supported" : "Podporováno",
"Featured apps are developed by and within the community. They offer central functionality and are ready for production use." : "Doporučované aplikace jsou vyvíjeny komunitou. Jsou propojeny se zbytkem Nextcloud a připraveny na produkční nasazení.",
"Featured" : "Doporučené",
@@ -315,14 +315,14 @@
"From address" : "Adresa odesílatele",
"mail" : "e-mail",
"Authentication method" : "Metoda ověření",
- "Authentication required" : "Vyžadováno ověření",
+ "Authentication required" : "Vyžadováno ověření se",
"Server address" : "Adresa serveru",
"Port" : "Port",
"Credentials" : "Přihlašovací údaje",
"SMTP Username" : "SMTP uživatelské jméno ",
"SMTP Password" : "Heslo pro SMTP",
"Save" : "Uložit",
- "Test email settings" : "Test nastavení e-mailu",
+ "Test email settings" : "Vyzkoušet nastavení e-mailu",
"Send email" : "Odeslat e-mail",
"Security & setup warnings" : "Varování ohledně zabezpečení a nastavení",
"It's important for the security and performance of your instance that everything is configured correctly. To help you with that we are doing some automatic checks. Please see the linked documentation for more information." : "Pro zabezpečení a optimální výkon instance Nextcloud je důležité, aby vše bylo správně nastaveno. Jako pomoc, instance samotná automaticky ověřuje některá nastavení. Další informace naleznete v odkazované dokumentaci.",
@@ -337,7 +337,7 @@
"Server-side encryption" : "Šifrování na straně serveru",
"Server-side encryption makes it possible to encrypt files which are uploaded to this server. This comes with limitations like a performance penalty, so enable this only if needed." : "Šifrování na straně serveru umožňuje zašifrovat soubory, které jsou na tento server nahrávány. To přináší omezení jako třeba snížení výkonu, takže toto zapněte jen pokud je opravdu potřeba.",
"Enable server-side encryption" : "Šifrovat na straně serveru",
- "Please read carefully before activating server-side encryption: " : "Pročtěte důkladně před zapnutím šifrování dat na serveru:",
+ "Please read carefully before activating server-side encryption: " : "Důkladně si pročtěte a až teprve poté případně zapínejte šifrování dat na straně serveru: ",
"Once encryption is enabled, all files uploaded to the server from that point forward will be encrypted at rest on the server. It will only be possible to disable encryption at a later date if the active encryption module supports that function, and all pre-conditions (e.g. setting a recover key) are met." : "Poté co je zapnuto šifrování, jsou od toho bodu všechny nahrávané soubory šifrovány serverem. Vypnout šifrování bude možné pouze později, až bude šifrovací modul tuto možnost podporovat a po splnění všech nutných podmínek (tzn. nastavení klíčů pro obnovení).",
"Encryption alone does not guarantee security of the system. Please see documentation for more information about how the encryption app works, and the supported use cases." : "Šifrování samotné ještě nezaručuje bezpečnost systému. Pokud se chcete dozvědět víc o tom, jak aplikace pro šifrování funguje a jaké jsou podporované případy použití, naleznete to v dokumentaci.",
"Be aware that encryption always increases the file size." : "Mějte na paměti, že šifrování vždy navýší velikost souboru.",
diff --git a/apps/settings/l10n/de.js b/apps/settings/l10n/de.js
index 6a5dbcf3680..2282b21ce3c 100644
--- a/apps/settings/l10n/de.js
+++ b/apps/settings/l10n/de.js
@@ -370,9 +370,11 @@ OC.L10N.register(
"Expire after " : "Ablauf nach ",
"days" : "Tagen",
"Enforce expiration date" : "Ablaufdatum erzwingen",
+ "Allow users to share via link and emails" : "Benutzern erlauben, Inhalte üver Links und E-Mails zu teilen",
"Allow public uploads" : "Öffentliches Hochladen erlauben",
"Always ask for a password" : "Immer nach einem Passwort fragen",
"Enforce password protection" : "Passwortschutz erzwingen",
+ "Set default expiration date" : "Standardmäßiges Ablaufdatum setzen",
"Allow resharing" : "Weiterverteilen erlauben",
"Allow sharing with groups" : "Teilen mit Gruppen erlauben",
"Restrict users to only share with users in their groups" : "Benutzer auf das Teilen innerhalb ihrer Gruppen beschränken",
diff --git a/apps/settings/l10n/de.json b/apps/settings/l10n/de.json
index bffb27c9ae9..38bd0a90795 100644
--- a/apps/settings/l10n/de.json
+++ b/apps/settings/l10n/de.json
@@ -368,9 +368,11 @@
"Expire after " : "Ablauf nach ",
"days" : "Tagen",
"Enforce expiration date" : "Ablaufdatum erzwingen",
+ "Allow users to share via link and emails" : "Benutzern erlauben, Inhalte üver Links und E-Mails zu teilen",
"Allow public uploads" : "Öffentliches Hochladen erlauben",
"Always ask for a password" : "Immer nach einem Passwort fragen",
"Enforce password protection" : "Passwortschutz erzwingen",
+ "Set default expiration date" : "Standardmäßiges Ablaufdatum setzen",
"Allow resharing" : "Weiterverteilen erlauben",
"Allow sharing with groups" : "Teilen mit Gruppen erlauben",
"Restrict users to only share with users in their groups" : "Benutzer auf das Teilen innerhalb ihrer Gruppen beschränken",
diff --git a/apps/settings/lib/Controller/UsersController.php b/apps/settings/lib/Controller/UsersController.php
index 46de0b4cd96..a9b72571de6 100644
--- a/apps/settings/lib/Controller/UsersController.php
+++ b/apps/settings/lib/Controller/UsersController.php
@@ -46,7 +46,6 @@ use OC\KnownUser\KnownUserService;
use OC\L10N\Factory;
use OC\Security\IdentityProof\Manager;
use OC\User\Manager as UserManager;
-use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCA\Settings\Events\BeforeTemplateRenderedEvent;
use OCA\User_LDAP\User_Proxy;
@@ -401,15 +400,11 @@ class UsersController extends Controller {
$data[IAccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
$data[IAccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
}
- if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
- $shareProvider = \OC::$server->query(FederatedShareProvider::class);
- if ($shareProvider->isLookupServerUploadEnabled()) {
- $data[IAccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
- $data[IAccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
- $data[IAccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
- $data[IAccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
- }
- }
+ $data[IAccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
+ $data[IAccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
+ $data[IAccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
+ $data[IAccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
+
try {
$data = $this->saveUserSettings($user, $data);
if ($beforeData[IAccountManager::PROPERTY_PHONE]['value'] !== $data[IAccountManager::PROPERTY_PHONE]['value']) {
diff --git a/apps/settings/lib/Settings/Personal/PersonalInfo.php b/apps/settings/lib/Settings/Personal/PersonalInfo.php
index a853846fadd..7a0253d2be4 100644
--- a/apps/settings/lib/Settings/Personal/PersonalInfo.php
+++ b/apps/settings/lib/Settings/Personal/PersonalInfo.php
@@ -37,6 +37,7 @@ namespace OCA\Settings\Settings\Personal;
use OC\Accounts\AccountManager;
use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\AppFramework\Http\TemplateResponse;
@@ -96,7 +97,7 @@ class PersonalInfo implements ISettings {
$uid = \OC_User::getUser();
$user = $this->userManager->get($uid);
- $userData = $this->accountManager->getUser($user);
+ $account = $this->accountManager->getAccount($user);
// make sure FS is setup before querying storage related stuff...
\OC_Util::setupFS($user->getUID());
@@ -110,7 +111,7 @@ class PersonalInfo implements ISettings {
$languageParameters = $this->getLanguages($user);
$localeParameters = $this->getLocales($user);
- $messageParameters = $this->getMessageParameters($userData);
+ $messageParameters = $this->getMessageParameters($account);
$parameters = [
'total_space' => $totalSpace,
@@ -119,23 +120,23 @@ class PersonalInfo implements ISettings {
'quota' => $storageInfo['quota'],
'avatarChangeSupported' => $user->canChangeAvatar(),
'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
- 'avatarScope' => $userData[IAccountManager::PROPERTY_AVATAR]['scope'],
+ 'avatarScope' => $account->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope(),
'displayNameChangeSupported' => $user->canChangeDisplayName(),
- 'displayName' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
- 'displayNameScope' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
- 'email' => $userData[IAccountManager::PROPERTY_EMAIL]['value'],
- 'emailScope' => $userData[IAccountManager::PROPERTY_EMAIL]['scope'],
- 'emailVerification' => $userData[IAccountManager::PROPERTY_EMAIL]['verified'],
- 'phone' => $userData[IAccountManager::PROPERTY_PHONE]['value'],
- 'phoneScope' => $userData[IAccountManager::PROPERTY_PHONE]['scope'],
- 'address' => $userData[IAccountManager::PROPERTY_ADDRESS]['value'],
- 'addressScope' => $userData[IAccountManager::PROPERTY_ADDRESS]['scope'],
- 'website' => $userData[IAccountManager::PROPERTY_WEBSITE]['value'],
- 'websiteScope' => $userData[IAccountManager::PROPERTY_WEBSITE]['scope'],
- 'websiteVerification' => $userData[IAccountManager::PROPERTY_WEBSITE]['verified'],
- 'twitter' => $userData[IAccountManager::PROPERTY_TWITTER]['value'],
- 'twitterScope' => $userData[IAccountManager::PROPERTY_TWITTER]['scope'],
- 'twitterVerification' => $userData[IAccountManager::PROPERTY_TWITTER]['verified'],
+ 'displayName' => $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getValue(),
+ 'displayNameScope' => $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope(),
+ 'email' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
+ 'emailScope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
+ 'emailVerification' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(),
+ 'phone' => $account->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(),
+ 'phoneScope' => $account->getProperty(IAccountManager::PROPERTY_PHONE)->getScope(),
+ 'address' => $account->getProperty(IAccountManager::PROPERTY_ADDRESS)->getValue(),
+ 'addressScope' => $account->getProperty(IAccountManager::PROPERTY_ADDRESS)->getScope(),
+ 'website' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getValue(),
+ 'websiteScope' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getScope(),
+ 'websiteVerification' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getVerified(),
+ 'twitter' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getValue(),
+ 'twitterScope' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(),
+ 'twitterVerification' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getVerified(),
'groups' => $this->getGroups($user),
] + $messageParameters + $languageParameters + $localeParameters;
@@ -263,14 +264,14 @@ class PersonalInfo implements ISettings {
}
/**
- * @param array $userData
+ * @param IAccount $account
* @return array
*/
- private function getMessageParameters(array $userData): array {
+ private function getMessageParameters(IAccount $account): array {
$needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
$messageParameters = [];
foreach ($needVerifyMessage as $property) {
- switch ($userData[$property]['verified']) {
+ switch ($account->getProperty($property)->getVerified()) {
case AccountManager::VERIFIED:
$message = $this->l->t('Verifying');
break;
diff --git a/apps/settings/templates/settings/personal/personal.info.php b/apps/settings/templates/settings/personal/personal.info.php
index 84198b3c0c4..f2e3a51aad7 100644
--- a/apps/settings/templates/settings/personal/personal.info.php
+++ b/apps/settings/templates/settings/personal/personal.info.php
@@ -34,8 +34,8 @@ script('settings', [
]);
?>
-<div id="personal-settings">
- <div id="personal-settings-avatar-container" class="personal-settings-container">
+<div id="personal-settings" data-lookup-server-upload-enabled="<?php p($_['lookupServerUploadEnabled'] ? 'true' : 'false') ?>">
+<div id="personal-settings-avatar-container" class="personal-settings-container">
<div>
<form id="avatarform" class="section" method="post" action="<?php p(\OC::$server->getURLGenerator()->linkToRoute('core.avatar.postAvatar')); ?>">
<h3>
@@ -68,9 +68,7 @@ script('settings', [
</div>
<span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
- <?php if ($_['lookupServerUploadEnabled']) { ?>
<input type="hidden" id="avatarscope" value="<?php p($_['avatarScope']) ?>">
- <?php } ?>
</form>
</div>
<div class="personal-settings-setting-box personal-settings-group-box section">
@@ -125,7 +123,7 @@ script('settings', [
<span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
<?php if ($_['lookupServerUploadEnabled']) { ?>
- <input type="hidden" id="displaynamescope" value="<?php p($_['displayNameScope']) ?>">
+ <input type="hidden" id="displaynamescope" value="<?php p($_['displayNameScope']) ?>">
<?php } ?>
</form>
</div>
@@ -179,7 +177,6 @@ script('settings', [
<?php } ?>
</form>
</div>
- <?php if (!empty($_['phone']) || $_['lookupServerUploadEnabled']) { ?>
<div class="personal-settings-setting-box">
<form id="phoneform" class="section">
<h3>
@@ -190,21 +187,15 @@ script('settings', [
</span>
</div>
</h3>
- <input type="tel" id="phone" name="phone" <?php if (!$_['lookupServerUploadEnabled']) {
- print_unescaped('disabled="1"');
- } ?>
+ <input type="tel" id="phone" name="phone"
value="<?php p($_['phone']) ?>"
placeholder="<?php p($l->t('Your phone number')); ?>"
autocomplete="on" autocapitalize="none" autocorrect="off" />
<span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
- <?php if ($_['lookupServerUploadEnabled']) { ?>
<input type="hidden" id="phonescope" value="<?php p($_['phoneScope']) ?>">
- <?php } ?>
</form>
</div>
- <?php } ?>
- <?php if (!empty($_['address']) || $_['lookupServerUploadEnabled']) { ?>
<div class="personal-settings-setting-box">
<form id="addressform" class="section">
<h3>
@@ -215,21 +206,15 @@ script('settings', [
</span>
</div>
</h3>
- <input type="text" id="address" name="address" <?php if (!$_['lookupServerUploadEnabled']) {
- print_unescaped('disabled="1"');
- } ?>
+ <input type="text" id="address" name="address"
placeholder="<?php p($l->t('Your postal address')); ?>"
value="<?php p($_['address']) ?>"
autocomplete="on" autocapitalize="none" autocorrect="off" />
<span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
- <?php if ($_['lookupServerUploadEnabled']) { ?>
<input type="hidden" id="addressscope" value="<?php p($_['addressScope']) ?>">
- <?php } ?>
</form>
</div>
- <?php } ?>
- <?php if (!empty($_['website']) || $_['lookupServerUploadEnabled']) { ?>
<div class="personal-settings-setting-box">
<form id="websiteform" class="section">
<h3>
@@ -273,19 +258,12 @@ script('settings', [
<input type="url" name="website" id="website" value="<?php p($_['website']); ?>"
placeholder="<?php p($l->t('Link https://…')); ?>"
autocomplete="on" autocapitalize="none" autocorrect="off"
- <?php if (!$_['lookupServerUploadEnabled']) {
- print_unescaped('disabled="1"');
- } ?>
/>
<span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
- <?php if ($_['lookupServerUploadEnabled']) { ?>
<input type="hidden" id="websitescope" value="<?php p($_['websiteScope']) ?>">
- <?php } ?>
</form>
</div>
- <?php } ?>
- <?php if (!empty($_['twitter']) || $_['lookupServerUploadEnabled']) { ?>
<div class="personal-settings-setting-box">
<form id="twitterform" class="section">
<h3>
@@ -329,18 +307,12 @@ script('settings', [
<input type="text" name="twitter" id="twitter" value="<?php p($_['twitter']); ?>"
placeholder="<?php p($l->t('Twitter handle @…')); ?>"
autocomplete="on" autocapitalize="none" autocorrect="off"
- <?php if (!$_['lookupServerUploadEnabled']) {
- print_unescaped('disabled="1"');
- } ?>
/>
<span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
- <?php if ($_['lookupServerUploadEnabled']) { ?>
<input type="hidden" id="twitterscope" value="<?php p($_['twitterScope']) ?>">
- <?php } ?>
</form>
</div>
- <?php } ?>
</div>
<div class="profile-settings-container">
diff --git a/apps/settings/tests/Controller/UsersControllerTest.php b/apps/settings/tests/Controller/UsersControllerTest.php
index b14e8d00d60..f9652053de8 100644
--- a/apps/settings/tests/Controller/UsersControllerTest.php
+++ b/apps/settings/tests/Controller/UsersControllerTest.php
@@ -190,6 +190,7 @@ class UsersControllerTest extends \Test\TestCase {
public function testSetUserSettings($email, $validEmail, $expectedStatus) {
$controller = $this->getController(false, ['saveUserSettings']);
$user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('johndoe');
$this->userSession->method('getUser')->willReturn($user);
@@ -208,41 +209,41 @@ class UsersControllerTest extends \Test\TestCase {
IAccountManager::PROPERTY_DISPLAYNAME =>
[
'value' => 'Display name',
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => AccountManager::SCOPE_FEDERATED,
'verified' => AccountManager::NOT_VERIFIED,
],
IAccountManager::PROPERTY_ADDRESS =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
'verified' => AccountManager::NOT_VERIFIED,
],
IAccountManager::PROPERTY_WEBSITE =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
'verified' => AccountManager::NOT_VERIFIED,
],
IAccountManager::PROPERTY_EMAIL =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => AccountManager::SCOPE_FEDERATED,
'verified' => AccountManager::NOT_VERIFIED,
],
IAccountManager::PROPERTY_AVATAR =>
[
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
+ 'scope' => AccountManager::SCOPE_FEDERATED
],
IAccountManager::PROPERTY_PHONE =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
'verified' => AccountManager::NOT_VERIFIED,
],
IAccountManager::PROPERTY_TWITTER =>
[
'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
+ 'scope' => AccountManager::SCOPE_LOCAL,
'verified' => AccountManager::NOT_VERIFIED,
],
]);
@@ -255,19 +256,19 @@ class UsersControllerTest extends \Test\TestCase {
}
$result = $controller->setUserSettings(//
- AccountManager::VISIBILITY_CONTACTS_ONLY,
+ AccountManager::SCOPE_FEDERATED,
'displayName',
- AccountManager::VISIBILITY_CONTACTS_ONLY,
+ AccountManager::SCOPE_FEDERATED,
'47658468',
- AccountManager::VISIBILITY_CONTACTS_ONLY,
+ AccountManager::SCOPE_FEDERATED,
$email,
- AccountManager::VISIBILITY_CONTACTS_ONLY,
+ AccountManager::SCOPE_FEDERATED,
'nextcloud.com',
- AccountManager::VISIBILITY_CONTACTS_ONLY,
+ AccountManager::SCOPE_FEDERATED,
'street and city',
- AccountManager::VISIBILITY_CONTACTS_ONLY,
+ AccountManager::SCOPE_FEDERATED,
'@nextclouders',
- AccountManager::VISIBILITY_CONTACTS_ONLY
+ AccountManager::SCOPE_FEDERATED
);
$this->assertSame($expectedStatus, $result->getStatus());
diff --git a/apps/sharebymail/l10n/cs.js b/apps/sharebymail/l10n/cs.js
index 45a3aee8666..4eea5e47357 100644
--- a/apps/sharebymail/l10n/cs.js
+++ b/apps/sharebymail/l10n/cs.js
@@ -41,7 +41,7 @@ OC.L10N.register(
"%1$s shared »%2$s« with you and wants to add" : "%1$s sdílí „%2$s“ a dodává",
"»%s« added a note to a file shared with you" : "„%s“ dodává poznámku k nasdílenému souboru",
"You just shared »%1$s« with %2$s. The share was already sent to the recipient. Due to the security policies defined by the administrator of %3$s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient." : "Právě jste s „%1$s“ nasdílel(a) %2$s. Sdílení bylo již příjemci zasláno. Kvůli bezpečnostní politice nastavené administrátorem %3$s musí být každé sdílení chráněno heslem a toto heslo nemůže být příjemci zasláno přímo. Kvůli tomu ho budete muset ručně přeposlat.",
- "Password to access »%1$s« shared by you with %2$s" : "Heslo pro přístup k „%1$s“ sdílené vámi s %2$s",
+ "Password to access »%1$s« shared by you with %2$s" : "Heslo pro přístup k „%1$s“ nasdílenému vámi pro %2$s",
"This is the password:" : "Toto je heslo:",
"You can choose a different password at any time in the share dialog." : "V dialogu sdílení můžete kdykoliv vybrat jiné heslo.",
"Could not find share" : "Sdílení se nedaří nalézt",
diff --git a/apps/sharebymail/l10n/cs.json b/apps/sharebymail/l10n/cs.json
index f0574d8ba4e..d29dcab483a 100644
--- a/apps/sharebymail/l10n/cs.json
+++ b/apps/sharebymail/l10n/cs.json
@@ -39,7 +39,7 @@
"%1$s shared »%2$s« with you and wants to add" : "%1$s sdílí „%2$s“ a dodává",
"»%s« added a note to a file shared with you" : "„%s“ dodává poznámku k nasdílenému souboru",
"You just shared »%1$s« with %2$s. The share was already sent to the recipient. Due to the security policies defined by the administrator of %3$s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient." : "Právě jste s „%1$s“ nasdílel(a) %2$s. Sdílení bylo již příjemci zasláno. Kvůli bezpečnostní politice nastavené administrátorem %3$s musí být každé sdílení chráněno heslem a toto heslo nemůže být příjemci zasláno přímo. Kvůli tomu ho budete muset ručně přeposlat.",
- "Password to access »%1$s« shared by you with %2$s" : "Heslo pro přístup k „%1$s“ sdílené vámi s %2$s",
+ "Password to access »%1$s« shared by you with %2$s" : "Heslo pro přístup k „%1$s“ nasdílenému vámi pro %2$s",
"This is the password:" : "Toto je heslo:",
"You can choose a different password at any time in the share dialog." : "V dialogu sdílení můžete kdykoliv vybrat jiné heslo.",
"Could not find share" : "Sdílení se nedaří nalézt",
diff --git a/apps/sharebymail/l10n/de.js b/apps/sharebymail/l10n/de.js
index 9eb2fdcf75a..f7ed2b1b692 100644
--- a/apps/sharebymail/l10n/de.js
+++ b/apps/sharebymail/l10n/de.js
@@ -24,6 +24,7 @@ OC.L10N.register(
"Password to access {file} was sent to {email}" : "Passwort für den Zugriff auf {file} wurde an {email} versandt ",
"Password to access %1$s was sent to you" : " Passwort für den Zugriff auf %1$s wurde an Dich versandt ",
"Password to access {file} was sent to you" : " Passwort für den Zugriff auf {file} wurde an Dich versandt ",
+ "Sharing %1$s failed, because this item is already shared with user %2$s" : "Freigabe von %1$s fehlgeschlagen, da dieses Element bereits für den Benutzer freigegeben ist %2$s",
"We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "Automatisch erzeugtes Passwort kann nicht versandt werden. Bitte gebe in Deinen persönlichen Einstellungen eine gültige E-Mail-Adresse ein und versuche es erneut.",
"Failed to send share by email" : "Fehler beim Senden der Freigabe per E-Mail",
"%1$s shared »%2$s« with you" : "%1$s hat »%2$s« mit Dir geteilt",
diff --git a/apps/sharebymail/l10n/de.json b/apps/sharebymail/l10n/de.json
index 315febd3118..6c9e2d1a99e 100644
--- a/apps/sharebymail/l10n/de.json
+++ b/apps/sharebymail/l10n/de.json
@@ -22,6 +22,7 @@
"Password to access {file} was sent to {email}" : "Passwort für den Zugriff auf {file} wurde an {email} versandt ",
"Password to access %1$s was sent to you" : " Passwort für den Zugriff auf %1$s wurde an Dich versandt ",
"Password to access {file} was sent to you" : " Passwort für den Zugriff auf {file} wurde an Dich versandt ",
+ "Sharing %1$s failed, because this item is already shared with user %2$s" : "Freigabe von %1$s fehlgeschlagen, da dieses Element bereits für den Benutzer freigegeben ist %2$s",
"We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "Automatisch erzeugtes Passwort kann nicht versandt werden. Bitte gebe in Deinen persönlichen Einstellungen eine gültige E-Mail-Adresse ein und versuche es erneut.",
"Failed to send share by email" : "Fehler beim Senden der Freigabe per E-Mail",
"%1$s shared »%2$s« with you" : "%1$s hat »%2$s« mit Dir geteilt",
diff --git a/apps/updatenotification/l10n/cs.js b/apps/updatenotification/l10n/cs.js
index 9d1d877ad74..fcb0ec5c469 100644
--- a/apps/updatenotification/l10n/cs.js
+++ b/apps/updatenotification/l10n/cs.js
@@ -38,7 +38,7 @@ OC.L10N.register(
"<strong>All</strong> apps have a compatible version for this Nextcloud version available" : "<strong>Všechny</strong> aplikace mají k dispozici kompatibilní verzi s verzí tohoto Nextcloudu",
"View changelog" : "Zobrazit souhrn změn",
"Enterprise" : "Podnikové",
- "For enterprise use. Provides always the latest patch level, but will not update to the next major release immediately. That update happens once Nextcloud GmbH has done additional hardening and testing for large-scale and mission-critical deployments. This channel is only available to customers and provides the Nextcloud Enterprise package." : "Pro podnikové nasazení. Poskytuje vždy nejnovější opravy, ale nebude hned aktualizováno na další hlavní vydání. K tomu dojde až Nextcloud GmbH dokončí další zodolnění a testování pro velká a kritická nasazení. Tento kanál je k dispozici pouze zákazníkům a poskytuje balíček Nextcloud Enterprise.",
+ "For enterprise use. Provides always the latest patch level, but will not update to the next major release immediately. That update happens once Nextcloud GmbH has done additional hardening and testing for large-scale and mission-critical deployments. This channel is only available to customers and provides the Nextcloud Enterprise package." : "Pro podnikové nasazení. Poskytuje vždy nejnovější opravy, ale nebude hned aktualizováno na další hlavní vydání. K tomu dochází až když Nextcloud GmbH dokončí další zodolnění a testování pro rozsáhlá a kritická nasazení. Tento kanál je k dispozici pouze zákazníkům a poskytuje balíček Nextcloud Enterprise.",
"Stable" : "Stabilní",
"The most recent stable version. It is suited for regular use and will always update to the latest major version." : "Nejnovější stabilní verze. Je vhodná pro běžné používání a vždy ji lze aktualizovat na nejnovější hlavní verzi",
"Beta" : "Vývojové",
diff --git a/apps/updatenotification/l10n/cs.json b/apps/updatenotification/l10n/cs.json
index 0ffca7e75dd..a6edbdb751a 100644
--- a/apps/updatenotification/l10n/cs.json
+++ b/apps/updatenotification/l10n/cs.json
@@ -36,7 +36,7 @@
"<strong>All</strong> apps have a compatible version for this Nextcloud version available" : "<strong>Všechny</strong> aplikace mají k dispozici kompatibilní verzi s verzí tohoto Nextcloudu",
"View changelog" : "Zobrazit souhrn změn",
"Enterprise" : "Podnikové",
- "For enterprise use. Provides always the latest patch level, but will not update to the next major release immediately. That update happens once Nextcloud GmbH has done additional hardening and testing for large-scale and mission-critical deployments. This channel is only available to customers and provides the Nextcloud Enterprise package." : "Pro podnikové nasazení. Poskytuje vždy nejnovější opravy, ale nebude hned aktualizováno na další hlavní vydání. K tomu dojde až Nextcloud GmbH dokončí další zodolnění a testování pro velká a kritická nasazení. Tento kanál je k dispozici pouze zákazníkům a poskytuje balíček Nextcloud Enterprise.",
+ "For enterprise use. Provides always the latest patch level, but will not update to the next major release immediately. That update happens once Nextcloud GmbH has done additional hardening and testing for large-scale and mission-critical deployments. This channel is only available to customers and provides the Nextcloud Enterprise package." : "Pro podnikové nasazení. Poskytuje vždy nejnovější opravy, ale nebude hned aktualizováno na další hlavní vydání. K tomu dochází až když Nextcloud GmbH dokončí další zodolnění a testování pro rozsáhlá a kritická nasazení. Tento kanál je k dispozici pouze zákazníkům a poskytuje balíček Nextcloud Enterprise.",
"Stable" : "Stabilní",
"The most recent stable version. It is suited for regular use and will always update to the latest major version." : "Nejnovější stabilní verze. Je vhodná pro běžné používání a vždy ji lze aktualizovat na nejnovější hlavní verzi",
"Beta" : "Vývojové",
diff --git a/apps/user_ldap/l10n/de_DE.js b/apps/user_ldap/l10n/de_DE.js
index 5d979cd9c2b..0e2642a8d7d 100644
--- a/apps/user_ldap/l10n/de_DE.js
+++ b/apps/user_ldap/l10n/de_DE.js
@@ -56,9 +56,9 @@ OC.L10N.register(
"Password change rejected. Hint: " : "Passwortändertung verweigert. Hinweis:",
"Please login with the new password" : "Bitte mit dem neuen Passwort anmelden",
"LDAP User backend" : "LDAP Benutzer-Backend",
- "Your password will expire tomorrow." : "Ihr Passwort läuft morgen ab",
- "Your password will expire today." : "Ihr Passwort läuft heute ab",
- "_Your password will expire within %n day._::_Your password will expire within %n days._" : ["Ihr Passwort läuft in %n Tage ab","Ihr Passwort läuft in %n Tagen ab"],
+ "Your password will expire tomorrow." : "Ihr Passwort läuft morgen ab.",
+ "Your password will expire today." : "Ihr Passwort läuft heute ab.",
+ "_Your password will expire within %n day._::_Your password will expire within %n days._" : ["Ihr Passwort läuft in %n Tag ab.","Ihr Passwort läuft in %n Tagen ab."],
"LDAP / AD integration" : "LDAP/AD-Integration",
"_%s group found_::_%s groups found_" : ["%s Gruppe gefunden","%s Gruppen gefunden"],
"_%s user found_::_%s users found_" : ["%s Benutzer gefunden","%s Benutzer gefunden"],
@@ -120,7 +120,7 @@ OC.L10N.register(
"Please try again or contact your administrator." : "Bitte versuchen Sie es noch einmal oder kontaktieren Sie Ihren Administrator.",
"Current password" : "Aktuelles Passwort",
"New password" : "Neues Passwort",
- "Renew password" : "Bitte erneuern Sie Ihr Passwort",
+ "Renew password" : "Passwort erneuern",
"Wrong password." : "Falsches Passwort.",
"Cancel" : "Abbrechen",
"Server" : "Server",
diff --git a/apps/user_ldap/l10n/de_DE.json b/apps/user_ldap/l10n/de_DE.json
index c38fff0c180..7dd3d04b10d 100644
--- a/apps/user_ldap/l10n/de_DE.json
+++ b/apps/user_ldap/l10n/de_DE.json
@@ -54,9 +54,9 @@
"Password change rejected. Hint: " : "Passwortändertung verweigert. Hinweis:",
"Please login with the new password" : "Bitte mit dem neuen Passwort anmelden",
"LDAP User backend" : "LDAP Benutzer-Backend",
- "Your password will expire tomorrow." : "Ihr Passwort läuft morgen ab",
- "Your password will expire today." : "Ihr Passwort läuft heute ab",
- "_Your password will expire within %n day._::_Your password will expire within %n days._" : ["Ihr Passwort läuft in %n Tage ab","Ihr Passwort läuft in %n Tagen ab"],
+ "Your password will expire tomorrow." : "Ihr Passwort läuft morgen ab.",
+ "Your password will expire today." : "Ihr Passwort läuft heute ab.",
+ "_Your password will expire within %n day._::_Your password will expire within %n days._" : ["Ihr Passwort läuft in %n Tag ab.","Ihr Passwort läuft in %n Tagen ab."],
"LDAP / AD integration" : "LDAP/AD-Integration",
"_%s group found_::_%s groups found_" : ["%s Gruppe gefunden","%s Gruppen gefunden"],
"_%s user found_::_%s users found_" : ["%s Benutzer gefunden","%s Benutzer gefunden"],
@@ -118,7 +118,7 @@
"Please try again or contact your administrator." : "Bitte versuchen Sie es noch einmal oder kontaktieren Sie Ihren Administrator.",
"Current password" : "Aktuelles Passwort",
"New password" : "Neues Passwort",
- "Renew password" : "Bitte erneuern Sie Ihr Passwort",
+ "Renew password" : "Passwort erneuern",
"Wrong password." : "Falsches Passwort.",
"Cancel" : "Abbrechen",
"Server" : "Server",
diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php
index 0ec19f27c60..cbe11403ba8 100644
--- a/build/integration/features/bootstrap/Provisioning.php
+++ b/build/integration/features/bootstrap/Provisioning.php
@@ -157,7 +157,11 @@ trait Provisioning {
$fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user";
$client = new Client();
$options = [];
- $options['auth'] = $this->adminUser;
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$this->currentUser, $this->regularUser];
+ }
$options['headers'] = [
'OCS-APIREQUEST' => 'true',
];
diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature
index 717aa04e4bd..03aaad4b857 100644
--- a/build/integration/features/provisioning-v1.feature
+++ b/build/integration/features/provisioning-v1.feature
@@ -103,6 +103,82 @@ Feature: provisioning
| website | https://nextcloud.com |
| twitter | Nextcloud |
+ Scenario: Edit a user account properties scopes
+ Given user "brand-new-user" exists
+ And As an "brand-new-user"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | phoneScope |
+ | value | v2-private |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | twitterScope |
+ | value | v2-local |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | addressScope |
+ | value | v2-federated |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | emailScope |
+ | value | v2-published |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | websiteScope |
+ | value | public |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | displaynameScope |
+ | value | contacts |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | avatarScope |
+ | value | private |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ Then user "brand-new-user" has
+ | id | brand-new-user |
+ | phoneScope | v2-private |
+ | twitterScope | v2-local |
+ | addressScope | v2-federated |
+ | emailScope | v2-published |
+ | websiteScope | v2-published |
+ | displaynameScope | v2-federated |
+ | avatarScope | v2-local |
+
+ Scenario: Edit a user account properties scopes with invalid or unsupported value
+ Given user "brand-new-user" exists
+ And As an "brand-new-user"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | phoneScope |
+ | value | invalid |
+ Then the OCS status code should be "102"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | displaynameScope |
+ | value | v2-private |
+ Then the OCS status code should be "102"
+ And the HTTP status code should be "200"
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | emailScope |
+ | value | v2-private |
+ Then the OCS status code should be "102"
+ And the HTTP status code should be "200"
+
+ Scenario: An admin cannot edit user account property scopes
+ Given As an "admin"
+ And user "brand-new-user" exists
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | phoneScope |
+ | value | v2-private |
+ Then the OCS status code should be "997"
+ And the HTTP status code should be "401"
+
Scenario: Search by phone number
Given As an "admin"
And user "phone-user" exists
@@ -612,4 +688,3 @@ Feature: provisioning
And As an "user0"
When sending "GET" with exact url to "/index.php/apps/files"
And the HTTP status code should be "403"
-
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 1a28e2e885e..feabe34ecfa 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -2981,6 +2981,14 @@
<code>InMemoryFile</code>
</ImplementedReturnTypeMismatch>
</file>
+ <file src="lib/private/Avatar/PlaceholderAvatar.php">
+ <ImplementedReturnTypeMismatch occurrences="1">
+ <code>ISimpleFile</code>
+ </ImplementedReturnTypeMismatch>
+ <InvalidScalarArgument occurrences="1">
+ <code>$data</code>
+ </InvalidScalarArgument>
+ </file>
<file src="lib/private/Avatar/UserAvatar.php">
<ImplementedReturnTypeMismatch occurrences="1">
<code>ISimpleFile</code>
@@ -3495,12 +3503,10 @@
<InvalidNullableReturnType occurrences="1">
<code>string</code>
</InvalidNullableReturnType>
- <InvalidReturnStatement occurrences="2">
- <code>$this-&gt;root-&gt;get($this-&gt;getFullPath($path))</code>
+ <InvalidReturnStatement occurrences="1">
<code>new NonExistingFolder($this-&gt;root, $this-&gt;view, $path)</code>
</InvalidReturnStatement>
- <InvalidReturnType occurrences="2">
- <code>\OC\Files\Node\Node</code>
+ <InvalidReturnType occurrences="1">
<code>string</code>
</InvalidReturnType>
<MoreSpecificImplementedParamType occurrences="1">
@@ -3579,15 +3585,7 @@
<InvalidNullableReturnType occurrences="1">
<code>int</code>
</InvalidNullableReturnType>
- <InvalidReturnStatement occurrences="3">
- <code>$targetNode</code>
- <code>$targetNode</code>
- <code>$this-&gt;root-&gt;get($newPath)</code>
- </InvalidReturnStatement>
- <InvalidReturnType occurrences="4">
- <code>Node</code>
- <code>\OC\Files\Node\Node</code>
- <code>\OC\Files\Node\Node</code>
+ <InvalidReturnType occurrences="1">
<code>getChecksum</code>
</InvalidReturnType>
<NullableReturnStatement occurrences="1">
@@ -3601,9 +3599,8 @@
</UndefinedInterfaceMethod>
</file>
<file src="lib/private/Files/Node/Root.php">
- <ImplementedReturnTypeMismatch occurrences="2">
+ <ImplementedReturnTypeMismatch occurrences="1">
<code>Node</code>
- <code>string</code>
</ImplementedReturnTypeMismatch>
<InvalidNullableReturnType occurrences="7">
<code>\OC\Files\Mount\MountPoint</code>
@@ -3614,12 +3611,6 @@
<code>int</code>
<code>string</code>
</InvalidNullableReturnType>
- <InvalidReturnStatement occurrences="1">
- <code>$this-&gt;createNode($fullPath, $fileInfo)</code>
- </InvalidReturnStatement>
- <InvalidReturnType occurrences="1">
- <code>string</code>
- </InvalidReturnType>
<NullableReturnStatement occurrences="7">
<code>$this-&gt;mountManager-&gt;find($mountPoint)</code>
<code>$this-&gt;user</code>
diff --git a/core/l10n/cs.js b/core/l10n/cs.js
index ffefc77a25b..6f24c6cc145 100644
--- a/core/l10n/cs.js
+++ b/core/l10n/cs.js
@@ -11,7 +11,7 @@ OC.L10N.register(
"Invalid image" : "Neplatný obrázek",
"An error occurred. Please contact your admin." : "Došlo k chybě. Obraťte se na svého správce.",
"No temporary profile picture available, try again" : "Dočasný profilový obrázek není k dispozici, zkuste to znovu",
- "No crop data provided" : "Nebyla poskytnuta data pro oříznutí obrázku",
+ "No crop data provided" : "Nezadáno, jak má být obrázek oříznut",
"No valid crop data provided" : "Nebyla poskytnuta platná data pro oříznutí obrázku",
"Crop is not square" : "Ořez není čtvercový",
"State token does not match" : "Stavový token neodpovídá",
@@ -52,7 +52,7 @@ OC.L10N.register(
"Set log level to debug" : "Nastavit úroveň podrobnosti záznamu událostí na stupeň ladění (debug)",
"Reset log level" : "Vrátit podrobnost záznamu událostí na výchozí úroveň",
"Starting code integrity check" : "Spouští se kontrola neporušenosti kódu",
- "Finished code integrity check" : "Kontrola neporušenosti kódu dokončena",
+ "Finished code integrity check" : "Kontrola neporušenosti zdrojových kódů aplikace dokončena",
"%s (incompatible)" : "%s (nekompatibilní)",
"The following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s",
"Already up to date" : "Už je aktuální",
@@ -121,7 +121,7 @@ OC.L10N.register(
"An error occurred." : "Došlo k chybě.",
"Please reload the page." : "Načtěte stránku znovu.",
"The update was unsuccessful. For more information <a href=\"{url}\">check our forum post</a> covering this issue." : "Aktualizace nebyla úspěšná. Další informace <a href=\"{url}\">se dočtete v příspěvku na diskuzním fóru</a>, pojednávajícím o tomto problému.",
- "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "Aktualizace nebyla úspěšná. Nahlaste problém <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">komunitě kolem Nextcloud</a>",
+ "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "Aktualizace nebyla úspěšná. Prosím nahlaste tento problém <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">komunitě projektu Nextcloud</a>",
"Continue to {productName}" : "Pokračujte k {productName}",
"_The update was successful. Redirecting you to {productName} in %n second._::_The update was successful. Redirecting you to {productName} in %n seconds._" : ["Aktualizace proběhla úspěšně. Za %n vteřinu budete přesměřováni na {productName}","Aktualizace proběhla úspěšně. Za %n vteřiny budete přesměřováni na {productName}","Aktualizace proběhla úspěšně. Za %n vteřin budete přesměřováni na {productName}","Aktualizace proběhla úspěšně. Za %n vteřin budete přesměřováni na {productName}"],
"Log in" : "Přihlásit",
@@ -129,7 +129,7 @@ OC.L10N.register(
"Server side authentication failed!" : "Ověření se na straně serveru se nezdařilo!",
"Please contact your administrator." : "Obraťte se na svého správce.",
"An internal error occurred." : "Došlo k vnitřní chybě.",
- "Please try again or contact your administrator." : "Zkuste to znovu nebo se obraťte na svého správce.",
+ "Please try again or contact your administrator." : "Zkuste to znovu nebo se obraťte na správce.",
"Username or email" : "Uživatelské jméno nebo e-mailová adresa",
"Password" : "Heslo",
"Wrong username or password." : "Nesprávné uživatelské jméno nebo heslo.",
@@ -363,7 +363,7 @@ OC.L10N.register(
"This page will refresh itself when the instance is available again." : "Tato stránka se automaticky znovu načte, jakmile bude tato instance opět dostupná.",
"Contact your system administrator if this message persists or appeared unexpectedly." : "Pokud se tato zpráva objevuje opakovaně nebo nečekaně, obraťte se správce systému.",
"Checking whether the database schema can be updated (this can take a long time depending on the database size)" : "Probíhá zjišťování, zda je možné aktualizovat schéma databáze (v závislosti na velikosti databáze to může zabrat delší dobu)",
- "Checked database schema update" : "Aktualizace schéma databáze byla ověřena",
+ "Checked database schema update" : "Ověřeno, že schéma databáze bylo zaktualizováno",
"Checking updates of apps" : "Kontrola aktualizace aplikací",
"Checked database schema update for apps" : "Aktualizace schéma databáze pro aplikace byla ověřena",
"Following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s",
diff --git a/core/l10n/cs.json b/core/l10n/cs.json
index 821e1fb6081..85bd206d32c 100644
--- a/core/l10n/cs.json
+++ b/core/l10n/cs.json
@@ -9,7 +9,7 @@
"Invalid image" : "Neplatný obrázek",
"An error occurred. Please contact your admin." : "Došlo k chybě. Obraťte se na svého správce.",
"No temporary profile picture available, try again" : "Dočasný profilový obrázek není k dispozici, zkuste to znovu",
- "No crop data provided" : "Nebyla poskytnuta data pro oříznutí obrázku",
+ "No crop data provided" : "Nezadáno, jak má být obrázek oříznut",
"No valid crop data provided" : "Nebyla poskytnuta platná data pro oříznutí obrázku",
"Crop is not square" : "Ořez není čtvercový",
"State token does not match" : "Stavový token neodpovídá",
@@ -50,7 +50,7 @@
"Set log level to debug" : "Nastavit úroveň podrobnosti záznamu událostí na stupeň ladění (debug)",
"Reset log level" : "Vrátit podrobnost záznamu událostí na výchozí úroveň",
"Starting code integrity check" : "Spouští se kontrola neporušenosti kódu",
- "Finished code integrity check" : "Kontrola neporušenosti kódu dokončena",
+ "Finished code integrity check" : "Kontrola neporušenosti zdrojových kódů aplikace dokončena",
"%s (incompatible)" : "%s (nekompatibilní)",
"The following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s",
"Already up to date" : "Už je aktuální",
@@ -119,7 +119,7 @@
"An error occurred." : "Došlo k chybě.",
"Please reload the page." : "Načtěte stránku znovu.",
"The update was unsuccessful. For more information <a href=\"{url}\">check our forum post</a> covering this issue." : "Aktualizace nebyla úspěšná. Další informace <a href=\"{url}\">se dočtete v příspěvku na diskuzním fóru</a>, pojednávajícím o tomto problému.",
- "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "Aktualizace nebyla úspěšná. Nahlaste problém <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">komunitě kolem Nextcloud</a>",
+ "The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "Aktualizace nebyla úspěšná. Prosím nahlaste tento problém <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">komunitě projektu Nextcloud</a>",
"Continue to {productName}" : "Pokračujte k {productName}",
"_The update was successful. Redirecting you to {productName} in %n second._::_The update was successful. Redirecting you to {productName} in %n seconds._" : ["Aktualizace proběhla úspěšně. Za %n vteřinu budete přesměřováni na {productName}","Aktualizace proběhla úspěšně. Za %n vteřiny budete přesměřováni na {productName}","Aktualizace proběhla úspěšně. Za %n vteřin budete přesměřováni na {productName}","Aktualizace proběhla úspěšně. Za %n vteřin budete přesměřováni na {productName}"],
"Log in" : "Přihlásit",
@@ -127,7 +127,7 @@
"Server side authentication failed!" : "Ověření se na straně serveru se nezdařilo!",
"Please contact your administrator." : "Obraťte se na svého správce.",
"An internal error occurred." : "Došlo k vnitřní chybě.",
- "Please try again or contact your administrator." : "Zkuste to znovu nebo se obraťte na svého správce.",
+ "Please try again or contact your administrator." : "Zkuste to znovu nebo se obraťte na správce.",
"Username or email" : "Uživatelské jméno nebo e-mailová adresa",
"Password" : "Heslo",
"Wrong username or password." : "Nesprávné uživatelské jméno nebo heslo.",
@@ -361,7 +361,7 @@
"This page will refresh itself when the instance is available again." : "Tato stránka se automaticky znovu načte, jakmile bude tato instance opět dostupná.",
"Contact your system administrator if this message persists or appeared unexpectedly." : "Pokud se tato zpráva objevuje opakovaně nebo nečekaně, obraťte se správce systému.",
"Checking whether the database schema can be updated (this can take a long time depending on the database size)" : "Probíhá zjišťování, zda je možné aktualizovat schéma databáze (v závislosti na velikosti databáze to může zabrat delší dobu)",
- "Checked database schema update" : "Aktualizace schéma databáze byla ověřena",
+ "Checked database schema update" : "Ověřeno, že schéma databáze bylo zaktualizováno",
"Checking updates of apps" : "Kontrola aktualizace aplikací",
"Checked database schema update for apps" : "Aktualizace schéma databáze pro aplikace byla ověřena",
"Following apps have been disabled: %s" : "Následující aplikace byly vypnuty: %s",
diff --git a/core/l10n/de.js b/core/l10n/de.js
index 82950426f28..2a91646bcc2 100644
--- a/core/l10n/de.js
+++ b/core/l10n/de.js
@@ -122,6 +122,8 @@ OC.L10N.register(
"Please reload the page." : "Bitte die Seite neu laden.",
"The update was unsuccessful. For more information <a href=\"{url}\">check our forum post</a> covering this issue." : "Das Update war nicht erfolgreich. Für mehr Informationen <a href=\"{url}\">lese bitte diesen Forenbeitrag</a> zu diesem Thema.",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "Das Update ist fehlgeschlagen. Bitte melde dieses Problem an die <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud Community</a>.",
+ "Continue to {productName}" : "Fortfahren zu {productName}",
+ "_The update was successful. Redirecting you to {productName} in %n second._::_The update was successful. Redirecting you to {productName} in %n seconds._" : ["Die Aktualisierung war erfolgreich. Du wirst in %n Sekunden zu {productName} weitergeleitet.","Die Aktualisierung war erfolgreich. Du wirst in %n Sekunden zu {productName} weitergeleitet."],
"Log in" : "Anmelden",
"Logging in …" : "Melde an…",
"Server side authentication failed!" : "Serverseitige Authentifizierung fehlgeschlagen!",
diff --git a/core/l10n/de.json b/core/l10n/de.json
index b062c559c55..dc077b977d0 100644
--- a/core/l10n/de.json
+++ b/core/l10n/de.json
@@ -120,6 +120,8 @@
"Please reload the page." : "Bitte die Seite neu laden.",
"The update was unsuccessful. For more information <a href=\"{url}\">check our forum post</a> covering this issue." : "Das Update war nicht erfolgreich. Für mehr Informationen <a href=\"{url}\">lese bitte diesen Forenbeitrag</a> zu diesem Thema.",
"The update was unsuccessful. Please report this issue to the <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud community</a>." : "Das Update ist fehlgeschlagen. Bitte melde dieses Problem an die <a href=\"https://github.com/nextcloud/server/issues\" target=\"_blank\">Nextcloud Community</a>.",
+ "Continue to {productName}" : "Fortfahren zu {productName}",
+ "_The update was successful. Redirecting you to {productName} in %n second._::_The update was successful. Redirecting you to {productName} in %n seconds._" : ["Die Aktualisierung war erfolgreich. Du wirst in %n Sekunden zu {productName} weitergeleitet.","Die Aktualisierung war erfolgreich. Du wirst in %n Sekunden zu {productName} weitergeleitet."],
"Log in" : "Anmelden",
"Logging in …" : "Melde an…",
"Server side authentication failed!" : "Serverseitige Authentifizierung fehlgeschlagen!",
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index bf07152256d..9f4973e50f0 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -734,6 +734,7 @@ return array(
'OC\\Avatar\\Avatar' => $baseDir . '/lib/private/Avatar/Avatar.php',
'OC\\Avatar\\AvatarManager' => $baseDir . '/lib/private/Avatar/AvatarManager.php',
'OC\\Avatar\\GuestAvatar' => $baseDir . '/lib/private/Avatar/GuestAvatar.php',
+ 'OC\\Avatar\\PlaceholderAvatar' => $baseDir . '/lib/private/Avatar/PlaceholderAvatar.php',
'OC\\Avatar\\UserAvatar' => $baseDir . '/lib/private/Avatar/UserAvatar.php',
'OC\\BackgroundJob\\Job' => $baseDir . '/lib/private/BackgroundJob/Job.php',
'OC\\BackgroundJob\\JobList' => $baseDir . '/lib/private/BackgroundJob/JobList.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index e5fe0d55bfd..9fcfb40b9ba 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -763,6 +763,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Avatar\\Avatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/Avatar.php',
'OC\\Avatar\\AvatarManager' => __DIR__ . '/../../..' . '/lib/private/Avatar/AvatarManager.php',
'OC\\Avatar\\GuestAvatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/GuestAvatar.php',
+ 'OC\\Avatar\\PlaceholderAvatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/PlaceholderAvatar.php',
'OC\\Avatar\\UserAvatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/UserAvatar.php',
'OC\\BackgroundJob\\Job' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Job.php',
'OC\\BackgroundJob\\JobList' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/JobList.php',
diff --git a/lib/l10n/cs.js b/lib/l10n/cs.js
index f1d757a88ff..408857ab127 100644
--- a/lib/l10n/cs.js
+++ b/lib/l10n/cs.js
@@ -202,11 +202,11 @@ OC.L10N.register(
"Please install one of these locales on your system and restart your webserver." : "Do svého systému nainstalujte alespoň jeden z těchto jazyků a restartujte webový server.",
"PHP module %s not installed." : "PHP modul %s není nainstalován.",
"Please ask your server administrator to install the module." : "Požádejte správce serveru, který využíváte o instalaci tohoto modulu.",
- "PHP setting \"%s\" is not set to \"%s\"." : "Hodnota PHP nastavení „%s“ není nastavená na „%s“.",
+ "PHP setting \"%s\" is not set to \"%s\"." : "Hodnota v konfiguraci PHP „%s“ není nastavená na „%s“.",
"Adjusting this setting in php.ini will make Nextcloud run again" : "Úprava tohoto nastavení v php.ini umožní Nextcloud opět zprovoznit",
"mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload je nastaven na „%s“ namísto očekávané hodnoty „0“",
"To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pro nápravu nastavte v souboru php.ini parametr <code>mbstring.func_overload</code> na <code>0</code>",
- "libxml2 2.7.0 is at least required. Currently %s is installed." : "Je požadována verze softwarové knihovny libxml2 minimálně 2.7.0. Nyní je nainstalována verze %s.",
+ "libxml2 2.7.0 is at least required. Currently %s is installed." : "Je zapotřebí verze softwarové knihovny libxml2 přinejmenším 2.7.0. Nyní je nainstalována verze %s.",
"To fix this issue update your libxml2 version and restart your web server." : "Tento problém opravíte instalací novější verze knihovny libxml2 a restartem webového serveru.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP je patrně nastaveno tak, aby odstraňovalo bloky komentářů. Toto bude mít za následek znepřístupnění mnoha důležitých aplikací.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Toto je pravděpodobně způsobeno aplikacemi pro urychlení načítání jako jsou Zend OPcache nebo eAccelerator.",
diff --git a/lib/l10n/cs.json b/lib/l10n/cs.json
index c92a4a4e1d6..892ee97a6c8 100644
--- a/lib/l10n/cs.json
+++ b/lib/l10n/cs.json
@@ -200,11 +200,11 @@
"Please install one of these locales on your system and restart your webserver." : "Do svého systému nainstalujte alespoň jeden z těchto jazyků a restartujte webový server.",
"PHP module %s not installed." : "PHP modul %s není nainstalován.",
"Please ask your server administrator to install the module." : "Požádejte správce serveru, který využíváte o instalaci tohoto modulu.",
- "PHP setting \"%s\" is not set to \"%s\"." : "Hodnota PHP nastavení „%s“ není nastavená na „%s“.",
+ "PHP setting \"%s\" is not set to \"%s\"." : "Hodnota v konfiguraci PHP „%s“ není nastavená na „%s“.",
"Adjusting this setting in php.ini will make Nextcloud run again" : "Úprava tohoto nastavení v php.ini umožní Nextcloud opět zprovoznit",
"mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload je nastaven na „%s“ namísto očekávané hodnoty „0“",
"To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Pro nápravu nastavte v souboru php.ini parametr <code>mbstring.func_overload</code> na <code>0</code>",
- "libxml2 2.7.0 is at least required. Currently %s is installed." : "Je požadována verze softwarové knihovny libxml2 minimálně 2.7.0. Nyní je nainstalována verze %s.",
+ "libxml2 2.7.0 is at least required. Currently %s is installed." : "Je zapotřebí verze softwarové knihovny libxml2 přinejmenším 2.7.0. Nyní je nainstalována verze %s.",
"To fix this issue update your libxml2 version and restart your web server." : "Tento problém opravíte instalací novější verze knihovny libxml2 a restartem webového serveru.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP je patrně nastaveno tak, aby odstraňovalo bloky komentářů. Toto bude mít za následek znepřístupnění mnoha důležitých aplikací.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Toto je pravděpodobně způsobeno aplikacemi pro urychlení načítání jako jsou Zend OPcache nebo eAccelerator.",
diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js
index d41d375d8e1..4bb2342e6c8 100644
--- a/lib/l10n/de_DE.js
+++ b/lib/l10n/de_DE.js
@@ -53,21 +53,21 @@ OC.L10N.register(
"today" : "Heute",
"tomorrow" : "Morgen",
"yesterday" : "Gestern",
- "_in %n day_::_in %n days_" : ["in %n Tag","in %n Tagen"],
+ "_in %n day_::_in %n days_" : ["In %n Tag","In %n Tagen"],
"_%n day ago_::_%n days ago_" : ["Vor %n Tag","Vor %n Tagen"],
"next month" : "Nächsten Monat",
"last month" : "Letzten Monat",
- "_in %n month_::_in %n months_" : ["in %n Monat","in %n Monaten"],
+ "_in %n month_::_in %n months_" : ["In %n Monat","In %n Monaten"],
"_%n month ago_::_%n months ago_" : ["Vor %n Monat","Vor %n Monaten"],
- "next year" : "nächstes Jahr",
+ "next year" : "Nächstes Jahr",
"last year" : "Letztes Jahr",
- "_in %n year_::_in %n years_" : ["in %n Jahr","in %n Jahren"],
+ "_in %n year_::_in %n years_" : ["In %n Jahr","In %n Jahren"],
"_%n year ago_::_%n years ago_" : ["Vor %n Jahr","Vor %n Jahren"],
- "_in %n hour_::_in %n hours_" : ["in %n Stunde","in %n Stunden"],
+ "_in %n hour_::_in %n hours_" : ["In %n Stunde","In %n Stunden"],
"_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"],
- "_in %n minute_::_in %n minutes_" : ["in %n Minute","in %n Minuten"],
+ "_in %n minute_::_in %n minutes_" : ["In %n Minute","In %n Minuten"],
"_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"],
- "in a few seconds" : "in wenigen Sekunden",
+ "in a few seconds" : "In wenigen Sekunden",
"seconds ago" : "Gerade eben",
"Empty file" : "Leere Datei",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte aktivieren Sie es in Ihren Einstellungen oder kontaktieren Sie Ihren Administrator.",
diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json
index 7193030931a..8ea41565c2c 100644
--- a/lib/l10n/de_DE.json
+++ b/lib/l10n/de_DE.json
@@ -51,21 +51,21 @@
"today" : "Heute",
"tomorrow" : "Morgen",
"yesterday" : "Gestern",
- "_in %n day_::_in %n days_" : ["in %n Tag","in %n Tagen"],
+ "_in %n day_::_in %n days_" : ["In %n Tag","In %n Tagen"],
"_%n day ago_::_%n days ago_" : ["Vor %n Tag","Vor %n Tagen"],
"next month" : "Nächsten Monat",
"last month" : "Letzten Monat",
- "_in %n month_::_in %n months_" : ["in %n Monat","in %n Monaten"],
+ "_in %n month_::_in %n months_" : ["In %n Monat","In %n Monaten"],
"_%n month ago_::_%n months ago_" : ["Vor %n Monat","Vor %n Monaten"],
- "next year" : "nächstes Jahr",
+ "next year" : "Nächstes Jahr",
"last year" : "Letztes Jahr",
- "_in %n year_::_in %n years_" : ["in %n Jahr","in %n Jahren"],
+ "_in %n year_::_in %n years_" : ["In %n Jahr","In %n Jahren"],
"_%n year ago_::_%n years ago_" : ["Vor %n Jahr","Vor %n Jahren"],
- "_in %n hour_::_in %n hours_" : ["in %n Stunde","in %n Stunden"],
+ "_in %n hour_::_in %n hours_" : ["In %n Stunde","In %n Stunden"],
"_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"],
- "_in %n minute_::_in %n minutes_" : ["in %n Minute","in %n Minuten"],
+ "_in %n minute_::_in %n minutes_" : ["In %n Minute","In %n Minuten"],
"_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"],
- "in a few seconds" : "in wenigen Sekunden",
+ "in a few seconds" : "In wenigen Sekunden",
"seconds ago" : "Gerade eben",
"Empty file" : "Leere Datei",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte aktivieren Sie es in Ihren Einstellungen oder kontaktieren Sie Ihren Administrator.",
diff --git a/lib/l10n/es.js b/lib/l10n/es.js
index 7eb8fef6640..fd5f5947f84 100644
--- a/lib/l10n/es.js
+++ b/lib/l10n/es.js
@@ -121,6 +121,7 @@ OC.L10N.register(
"Expiration date is in the past" : "Ha pasado la fecha de caducidad",
"Can’t set expiration date more than %s days in the future" : "No se puede establecer la fecha de expiración a más de %s días en el futuro",
"Sharing is only allowed with group members" : "Sólo está permitido compartir a los integrantes del grupo",
+ "Sharing %s failed, because this item is already shared with user %s" : "No se pudo compartir %s, porque este elemento ya está compartido con el usuario %s",
"%1$s shared »%2$s« with you" : "%1$s ha compartido «%2$s» contigo",
"%1$s shared »%2$s« with you." : "%1$s ha compartido «%2$s» contigo.",
"Click the button below to open it." : "Haz clic en el botón de abajo para abrirlo.",
diff --git a/lib/l10n/es.json b/lib/l10n/es.json
index 0ad202345a5..ca88baaf650 100644
--- a/lib/l10n/es.json
+++ b/lib/l10n/es.json
@@ -119,6 +119,7 @@
"Expiration date is in the past" : "Ha pasado la fecha de caducidad",
"Can’t set expiration date more than %s days in the future" : "No se puede establecer la fecha de expiración a más de %s días en el futuro",
"Sharing is only allowed with group members" : "Sólo está permitido compartir a los integrantes del grupo",
+ "Sharing %s failed, because this item is already shared with user %s" : "No se pudo compartir %s, porque este elemento ya está compartido con el usuario %s",
"%1$s shared »%2$s« with you" : "%1$s ha compartido «%2$s» contigo",
"%1$s shared »%2$s« with you." : "%1$s ha compartido «%2$s» contigo.",
"Click the button below to open it." : "Haz clic en el botón de abajo para abrirlo.",
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index c5a0f21319e..6198f8dbddd 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -144,6 +144,42 @@ class AccountManager implements IAccountManager {
}
}
+ $allowedScopes = [
+ self::SCOPE_PRIVATE,
+ self::SCOPE_LOCAL,
+ self::SCOPE_FEDERATED,
+ self::SCOPE_PUBLISHED,
+ self::VISIBILITY_PRIVATE,
+ self::VISIBILITY_CONTACTS_ONLY,
+ self::VISIBILITY_PUBLIC,
+ ];
+
+ // validate and convert scope values
+ foreach ($data as $propertyName => $propertyData) {
+ if (isset($propertyData['scope'])) {
+ if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) {
+ throw new \InvalidArgumentException('scope');
+ }
+
+ if (
+ $propertyData['scope'] === self::SCOPE_PRIVATE
+ && ($propertyName === self::PROPERTY_DISPLAYNAME || $propertyName === self::PROPERTY_EMAIL)
+ ) {
+ if ($throwOnData) {
+ // v2-private is not available for these fields
+ throw new \InvalidArgumentException('scope');
+ } else {
+ // default to local
+ $data[$propertyName]['scope'] = self::SCOPE_LOCAL;
+ }
+ } else {
+ // migrate scope values to the new format
+ // invalid scopes are mapped to a default value
+ $data[$propertyName]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']);
+ }
+ }
+ }
+
if (empty($userData)) {
$this->insertNewUser($user, $data);
} elseif ($userData !== $data) {
@@ -198,6 +234,8 @@ class AccountManager implements IAccountManager {
*
* @param IUser $user
* @return array
+ *
+ * @deprecated use getAccount instead to make sure migrated properties work correctly
*/
public function getUser(IUser $user) {
$uid = $user->getUID();
@@ -405,7 +443,7 @@ class AccountManager implements IAccountManager {
}
$query->setParameter('name', $propertyName)
- ->setParameter('value', $property['value']);
+ ->setParameter('value', $property['value'] ?? '');
$query->execute();
}
}
@@ -421,41 +459,41 @@ class AccountManager implements IAccountManager {
self::PROPERTY_DISPLAYNAME =>
[
'value' => $user->getDisplayName(),
- 'scope' => self::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => self::SCOPE_FEDERATED,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_ADDRESS =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_WEBSITE =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_EMAIL =>
[
'value' => $user->getEMailAddress(),
- 'scope' => self::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => self::SCOPE_FEDERATED,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_AVATAR =>
[
- 'scope' => self::VISIBILITY_CONTACTS_ONLY
+ 'scope' => self::SCOPE_FEDERATED
],
self::PROPERTY_PHONE =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
self::PROPERTY_TWITTER =>
[
'value' => '',
- 'scope' => self::VISIBILITY_PRIVATE,
+ 'scope' => self::SCOPE_LOCAL,
'verified' => self::NOT_VERIFIED,
],
];
@@ -464,7 +502,7 @@ class AccountManager implements IAccountManager {
private function parseAccountData(IUser $user, $data): Account {
$account = new Account($user);
foreach ($data as $property => $accountData) {
- $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::VISIBILITY_PRIVATE, $accountData['verified'] ?? self::NOT_VERIFIED);
+ $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
}
return $account;
}
diff --git a/lib/private/Accounts/AccountProperty.php b/lib/private/Accounts/AccountProperty.php
index 97f9b1c356f..850f39df9e3 100644
--- a/lib/private/Accounts/AccountProperty.php
+++ b/lib/private/Accounts/AccountProperty.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
namespace OC\Accounts;
+use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
class AccountProperty implements IAccountProperty {
@@ -42,7 +43,7 @@ class AccountProperty implements IAccountProperty {
public function __construct(string $name, string $value, string $scope, string $verified) {
$this->name = $name;
$this->value = $value;
- $this->scope = $scope;
+ $this->scope = $this->mapScopeToV2($scope);
$this->verified = $verified;
}
@@ -77,7 +78,7 @@ class AccountProperty implements IAccountProperty {
* @return IAccountProperty
*/
public function setScope(string $scope): IAccountProperty {
- $this->scope = $scope;
+ $this->scope = $this->mapScopeToV2($scope);
return $this;
}
@@ -127,6 +128,23 @@ class AccountProperty implements IAccountProperty {
return $this->scope;
}
+ public static function mapScopeToV2($scope) {
+ if (strpos($scope, 'v2-') === 0) {
+ return $scope;
+ }
+
+ switch ($scope) {
+ case IAccountManager::VISIBILITY_PRIVATE:
+ return IAccountManager::SCOPE_LOCAL;
+ case IAccountManager::VISIBILITY_CONTACTS_ONLY:
+ return IAccountManager::SCOPE_FEDERATED;
+ case IAccountManager::VISIBILITY_PUBLIC:
+ return IAccountManager::SCOPE_PUBLISHED;
+ }
+
+ return IAccountManager::SCOPE_LOCAL;
+ }
+
/**
* Get the verification status of a property
*
diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php
index 5102396224d..04d3a721022 100644
--- a/lib/private/Avatar/AvatarManager.php
+++ b/lib/private/Avatar/AvatarManager.php
@@ -34,8 +34,10 @@ declare(strict_types=1);
namespace OC\Avatar;
+use OC\KnownUser\KnownUserService;
use OC\User\Manager;
use OC\User\NoUserException;
+use OCP\Accounts\IAccountManager;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
@@ -44,12 +46,16 @@ use OCP\IAvatarManager;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
+use OCP\IUserSession;
/**
* This class implements methods to access Avatar functionality
*/
class AvatarManager implements IAvatarManager {
+ /** @var IUserSession */
+ private $userSession;
+
/** @var Manager */
private $userManager;
@@ -65,6 +71,12 @@ class AvatarManager implements IAvatarManager {
/** @var IConfig */
private $config;
+ /** @var IAccountManager */
+ private $accountManager;
+
+ /** @var KnownUserService */
+ private $knownUserService;
+
/**
* AvatarManager constructor.
*
@@ -73,18 +85,26 @@ class AvatarManager implements IAvatarManager {
* @param IL10N $l
* @param ILogger $logger
* @param IConfig $config
+ * @param IUserSession $userSession
*/
public function __construct(
+ IUserSession $userSession,
Manager $userManager,
IAppData $appData,
IL10N $l,
ILogger $logger,
- IConfig $config) {
+ IConfig $config,
+ IAccountManager $accountManager,
+ KnownUserService $knownUserService
+ ) {
+ $this->userSession = $userSession;
$this->userManager = $userManager;
$this->appData = $appData;
$this->l = $l;
$this->logger = $logger;
$this->config = $config;
+ $this->accountManager = $accountManager;
+ $this->knownUserService = $knownUserService;
}
/**
@@ -104,12 +124,34 @@ class AvatarManager implements IAvatarManager {
// sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below)
$userId = $user->getUID();
+ $requestingUser = null;
+ if ($this->userSession !== null) {
+ $requestingUser = $this->userSession->getUser();
+ }
+
try {
$folder = $this->appData->getFolder($userId);
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($userId);
}
+ $account = $this->accountManager->getAccount($user);
+ $avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR);
+ $avatarScope = $avatarProperties->getScope();
+
+ if (
+ // v2-private scope hides the avatar from public access and from unknown users
+ $avatarScope === IAccountManager::SCOPE_PRIVATE
+ && (
+ // accessing from public link
+ $requestingUser === null
+ // logged in, but unknown to user
+ || !$this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)
+ )) {
+ // use a placeholder avatar which caches the generated images
+ return new PlaceholderAvatar($folder, $user, $this->logger);
+ }
+
return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
}
diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php
new file mode 100644
index 00000000000..5883fe531a3
--- /dev/null
+++ b/lib/private/Avatar/PlaceholderAvatar.php
@@ -0,0 +1,183 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author Joas Schilling <coding@schilljs.com>
+ * @author Michael Weimann <mail@michael-weimann.eu>
+ *
+ * @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\Avatar;
+
+use OC\NotSquareException;
+use OC\User\User;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\ISimpleFile;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\IConfig;
+use OCP\IImage;
+use OCP\IL10N;
+use OCP\ILogger;
+
+/**
+ * This class represents a registered user's placeholder avatar.
+ *
+ * It generates an image based on the user's initials and caches it on storage
+ * for faster retrieval, unlike the GuestAvatar.
+ */
+class PlaceholderAvatar extends Avatar {
+ /** @var ISimpleFolder */
+ private $folder;
+
+ /** @var User */
+ private $user;
+
+ /**
+ * UserAvatar constructor.
+ *
+ * @param IConfig $config The configuration
+ * @param ISimpleFolder $folder The avatar files folder
+ * @param IL10N $l The localization helper
+ * @param User $user The user this class manages the avatar for
+ * @param ILogger $logger The logger
+ */
+ public function __construct(
+ ISimpleFolder $folder,
+ $user,
+ ILogger $logger) {
+ parent::__construct($logger);
+
+ $this->folder = $folder;
+ $this->user = $user;
+ }
+
+ /**
+ * Check if an avatar exists for the user
+ *
+ * @return bool
+ */
+ public function exists() {
+ return true;
+ }
+
+ /**
+ * Sets the users avatar.
+ *
+ * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar
+ * @throws \Exception if the provided file is not a jpg or png image
+ * @throws \Exception if the provided image is not valid
+ * @throws NotSquareException if the image is not square
+ * @return void
+ */
+ public function set($data) {
+ // unimplemented for placeholder avatars
+ }
+
+ /**
+ * Removes the users avatar.
+ */
+ public function remove(bool $silent = false) {
+ $avatars = $this->folder->getDirectoryListing();
+
+ foreach ($avatars as $avatar) {
+ $avatar->delete();
+ }
+ }
+
+ /**
+ * Returns the avatar for an user.
+ *
+ * If there is no avatar file yet, one is generated.
+ *
+ * @param int $size
+ * @return ISimpleFile
+ * @throws NotFoundException
+ * @throws \OCP\Files\NotPermittedException
+ * @throws \OCP\PreConditionNotMetException
+ */
+ public function getFile($size) {
+ $size = (int) $size;
+
+ $ext = 'png';
+
+ if ($size === -1) {
+ $path = 'avatar-placeholder.' . $ext;
+ } else {
+ $path = 'avatar-placeholder.' . $size . '.' . $ext;
+ }
+
+ try {
+ $file = $this->folder->getFile($path);
+ } catch (NotFoundException $e) {
+ if ($size <= 0) {
+ throw new NotFoundException;
+ }
+
+ if (!$data = $this->generateAvatarFromSvg($size)) {
+ $data = $this->generateAvatar($this->getDisplayName(), $size);
+ }
+
+ try {
+ $file = $this->folder->newFile($path);
+ $file->putContent($data);
+ } catch (NotPermittedException $e) {
+ $this->logger->error('Failed to save avatar placeholder for ' . $this->user->getUID());
+ throw new NotFoundException();
+ }
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the user display name.
+ *
+ * @return string
+ */
+ public function getDisplayName(): string {
+ return $this->user->getDisplayName();
+ }
+
+ /**
+ * Handles user changes.
+ *
+ * @param string $feature The changed feature
+ * @param mixed $oldValue The previous value
+ * @param mixed $newValue The new value
+ * @throws NotPermittedException
+ * @throws \OCP\PreConditionNotMetException
+ */
+ public function userChanged($feature, $oldValue, $newValue) {
+ $this->remove();
+ }
+
+ /**
+ * Check if the avatar of a user is a custom uploaded one
+ *
+ * @return bool
+ */
+ public function isCustomAvatar(): bool {
+ return false;
+ }
+}
diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php
index f7ace429f7d..f47809425ed 100644
--- a/lib/private/Avatar/UserAvatar.php
+++ b/lib/private/Avatar/UserAvatar.php
@@ -270,6 +270,7 @@ class UserAvatar extends Avatar {
throw new NotFoundException;
}
+ // TODO: rework to integrate with the PlaceholderAvatar in a compatible way
if ($this->folder->fileExists('generated')) {
if (!$data = $this->generateAvatarFromSvg($size)) {
$data = $this->generateAvatar($this->getDisplayName(), $size);
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index fba8a24b40f..8d8c91f66dc 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -192,7 +192,7 @@ class Root extends Folder implements IRootFolder {
* @param string $path
* @throws \OCP\Files\NotFoundException
* @throws \OCP\Files\NotPermittedException
- * @return string
+ * @return Node
*/
public function get($path) {
$path = $this->normalizePath($path);
diff --git a/lib/private/KnownUser/KnownUserService.php b/lib/private/KnownUser/KnownUserService.php
index 96af21c836f..1f300a9f8e4 100644
--- a/lib/private/KnownUser/KnownUserService.php
+++ b/lib/private/KnownUser/KnownUserService.php
@@ -74,6 +74,10 @@ class KnownUserService {
* @return bool
*/
public function isKnownToUser(string $knownTo, string $contactUserId): bool {
+ if ($knownTo === $contactUserId) {
+ return true;
+ }
+
if (!isset($this->knownUsers[$knownTo])) {
$entities = $this->mapper->getKnownUsers($knownTo);
$this->knownUsers[$knownTo] = [];
diff --git a/lib/private/Server.php b/lib/private/Server.php
index c0d6afbaaf6..26c76125e56 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -104,6 +104,7 @@ use OC\IntegrityCheck\Checker;
use OC\IntegrityCheck\Helpers\AppLocator;
use OC\IntegrityCheck\Helpers\EnvironmentHelper;
use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OC\KnownUser\KnownUserService;
use OC\Lock\DBLockingProvider;
use OC\Lock\MemcacheLockingProvider;
use OC\Lock\NoopLockingProvider;
@@ -720,11 +721,14 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService(AvatarManager::class, function (Server $c) {
return new AvatarManager(
+ $c->get(IUserSession::class),
$c->get(\OC\User\Manager::class),
$c->getAppDataDir('avatar'),
$c->getL10N('lib'),
$c->get(ILogger::class),
- $c->get(\OCP\IConfig::class)
+ $c->get(\OCP\IConfig::class),
+ $c->get(IAccountManager::class),
+ $c->get(KnownUserService::class)
);
});
$this->registerAlias(IAvatarManager::class, AvatarManager::class);
diff --git a/lib/public/Accounts/IAccountManager.php b/lib/public/Accounts/IAccountManager.php
index ae70d8963b4..e88fd32f674 100644
--- a/lib/public/Accounts/IAccountManager.php
+++ b/lib/public/Accounts/IAccountManager.php
@@ -38,11 +38,54 @@ use OCP\IUser;
*/
interface IAccountManager {
- /** nobody can see my account details */
+ /**
+ * Contact details visible locally only
+ *
+ * @since 21.0.1
+ */
+ public const SCOPE_PRIVATE = 'v2-private';
+
+ /**
+ * Contact details visible locally and through public link access on local instance
+ *
+ * @since 21.0.1
+ */
+ public const SCOPE_LOCAL = 'v2-local';
+
+ /**
+ * Contact details visible locally, through public link access and on trusted federated servers.
+ *
+ * @since 21.0.1
+ */
+ public const SCOPE_FEDERATED = 'v2-federated';
+
+ /**
+ * Contact details visible locally, through public link access, on trusted federated servers
+ * and published to the public lookup server.
+ *
+ * @since 21.0.1
+ */
+ public const SCOPE_PUBLISHED = 'v2-published';
+
+ /**
+ * Contact details only visible locally
+ *
+ * @deprecated 21.0.1
+ */
public const VISIBILITY_PRIVATE = 'private';
- /** only contacts, especially trusted servers can see my contact details */
+
+ /**
+ * Contact details visible on trusted federated servers.
+ *
+ * @deprecated 21.0.1
+ */
public const VISIBILITY_CONTACTS_ONLY = 'contacts';
- /** every body ca see my contact detail, will be published to the lookup server */
+
+ /**
+ * Contact details visible on trusted federated servers and in the public lookup server.
+ *
+ * @deprecated 21.0.1
+ */
public const VISIBILITY_PUBLIC = 'public';
public const PROPERTY_AVATAR = 'avatar';
diff --git a/tests/lib/Accounts/AccountManagerTest.php b/tests/lib/Accounts/AccountManagerTest.php
index fcd1a78add7..27ebed69793 100644
--- a/tests/lib/Accounts/AccountManagerTest.php
+++ b/tests/lib/Accounts/AccountManagerTest.php
@@ -153,6 +153,129 @@ class AccountManagerTest extends TestCase {
];
}
+ public function updateUserSetScopeProvider() {
+ return [
+ // regular scope switching
+ [
+ [
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_AVATAR => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'some street', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://example.org', 'scope' => IAccountManager::SCOPE_PRIVATE],
+ ],
+ [
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PRIVATE],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'some street', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://example.org', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ ],
+ [
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PRIVATE],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'some street', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://example.org', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ ],
+ ],
+ // legacy scope mapping, the given visibility values get converted to scopes
+ [
+ [
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://example.org', 'scope' => IAccountManager::SCOPE_PRIVATE],
+ ],
+ [
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::VISIBILITY_PUBLIC],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::VISIBILITY_CONTACTS_ONLY],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://example.org', 'scope' => IAccountManager::VISIBILITY_PRIVATE],
+ ],
+ [
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://example.org', 'scope' => IAccountManager::SCOPE_LOCAL],
+ ],
+ ],
+ // invalid or unsupported scope values get converted to SCOPE_LOCAL
+ [
+ [
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PUBLISHED],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_FEDERATED],
+ ],
+ [
+ // SCOPE_PRIVATE is not allowed for display name and email
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_PRIVATE],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_PRIVATE],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => 'invalid'],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => ''],
+ ],
+ [
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_LOCAL],
+ IAccountManager::PROPERTY_PHONE => ['value' => '+491601231212', 'scope' => IAccountManager::SCOPE_LOCAL],
+ ],
+ // don't throw but fall back
+ false, false,
+ ],
+ // invalid or unsupported scope values throw an exception when passing $throwOnData=true
+ [
+ [IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_PUBLISHED]],
+ [IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'Display Name', 'scope' => IAccountManager::SCOPE_PRIVATE]],
+ null,
+ // throw exception
+ true, true,
+ ],
+ [
+ [IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_PUBLISHED]],
+ [IAccountManager::PROPERTY_EMAIL => ['value' => 'test@example.org', 'scope' => IAccountManager::SCOPE_PRIVATE]],
+ null,
+ // throw exception
+ true, true,
+ ],
+ [
+ [IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => IAccountManager::SCOPE_PUBLISHED]],
+ [IAccountManager::PROPERTY_TWITTER => ['value' => '@sometwitter', 'scope' => 'invalid']],
+ null,
+ // throw exception
+ true, true,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider updateUserSetScopeProvider
+ */
+ public function testUpdateUserSetScope($oldData, $newData, $savedData, $throwOnData = true, $expectedThrow = false) {
+ $accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser', 'updateVerifyStatus', 'checkEmailVerification']);
+ /** @var IUser $user */
+ $user = $this->createMock(IUser::class);
+
+ $accountManager->expects($this->once())->method('getUser')->with($user)->willReturn($oldData);
+
+ if ($expectedThrow) {
+ $accountManager->expects($this->never())->method('updateExistingUser');
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('scope');
+ } else {
+ $accountManager->expects($this->once())->method('checkEmailVerification')
+ ->with($oldData, $savedData, $user)->willReturn($savedData);
+ $accountManager->expects($this->once())->method('updateVerifyStatus')
+ ->with($oldData, $savedData)->willReturn($savedData);
+ $accountManager->expects($this->once())->method('updateExistingUser')
+ ->with($user, $savedData);
+ $accountManager->expects($this->never())->method('insertNewUser');
+ }
+
+ $accountManager->updateUser($user, $newData, $throwOnData);
+ }
/**
* @dataProvider dataTestGetUser
@@ -278,26 +401,26 @@ class AccountManagerTest extends TestCase {
IAccountManager::PROPERTY_TWITTER =>
[
'value' => '@twitterhandle',
- 'scope' => IAccountManager::VISIBILITY_PRIVATE,
+ 'scope' => IAccountManager::SCOPE_LOCAL,
'verified' => IAccountManager::NOT_VERIFIED,
],
IAccountManager::PROPERTY_EMAIL =>
[
'value' => 'test@example.com',
- 'scope' => IAccountManager::VISIBILITY_PUBLIC,
+ 'scope' => IAccountManager::SCOPE_PUBLISHED,
'verified' => IAccountManager::VERIFICATION_IN_PROGRESS,
],
IAccountManager::PROPERTY_WEBSITE =>
[
'value' => 'https://example.com',
- 'scope' => IAccountManager::VISIBILITY_CONTACTS_ONLY,
+ 'scope' => IAccountManager::SCOPE_FEDERATED,
'verified' => IAccountManager::VERIFIED,
],
];
$expected = new Account($user);
- $expected->setProperty(IAccountManager::PROPERTY_TWITTER, '@twitterhandle', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::NOT_VERIFIED);
- $expected->setProperty(IAccountManager::PROPERTY_EMAIL, 'test@example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::VERIFICATION_IN_PROGRESS);
- $expected->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_CONTACTS_ONLY, IAccountManager::VERIFIED);
+ $expected->setProperty(IAccountManager::PROPERTY_TWITTER, '@twitterhandle', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
+ $expected->setProperty(IAccountManager::PROPERTY_EMAIL, 'test@example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS);
+ $expected->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_FEDERATED, IAccountManager::VERIFIED);
$accountManager->expects($this->once())
->method('getUser')
diff --git a/tests/lib/Accounts/AccountPropertyTest.php b/tests/lib/Accounts/AccountPropertyTest.php
index afd807a44b4..f99abc21f83 100644
--- a/tests/lib/Accounts/AccountPropertyTest.php
+++ b/tests/lib/Accounts/AccountPropertyTest.php
@@ -37,12 +37,12 @@ class AccountPropertyTest extends TestCase {
$accountProperty = new AccountProperty(
IAccountManager::PROPERTY_WEBSITE,
'https://example.com',
- IAccountManager::VISIBILITY_PUBLIC,
+ IAccountManager::SCOPE_PUBLISHED,
IAccountManager::VERIFIED
);
$this->assertEquals(IAccountManager::PROPERTY_WEBSITE, $accountProperty->getName());
$this->assertEquals('https://example.com', $accountProperty->getValue());
- $this->assertEquals(IAccountManager::VISIBILITY_PUBLIC, $accountProperty->getScope());
+ $this->assertEquals(IAccountManager::SCOPE_PUBLISHED, $accountProperty->getScope());
$this->assertEquals(IAccountManager::VERIFIED, $accountProperty->getVerified());
}
@@ -50,7 +50,7 @@ class AccountPropertyTest extends TestCase {
$accountProperty = new AccountProperty(
IAccountManager::PROPERTY_WEBSITE,
'https://example.com',
- IAccountManager::VISIBILITY_PUBLIC,
+ IAccountManager::SCOPE_PUBLISHED,
IAccountManager::VERIFIED
);
$actualReturn = $accountProperty->setValue('https://example.org');
@@ -62,19 +62,48 @@ class AccountPropertyTest extends TestCase {
$accountProperty = new AccountProperty(
IAccountManager::PROPERTY_WEBSITE,
'https://example.com',
- IAccountManager::VISIBILITY_PUBLIC,
+ IAccountManager::SCOPE_PUBLISHED,
IAccountManager::VERIFIED
);
- $actualReturn = $accountProperty->setScope(IAccountManager::VISIBILITY_PRIVATE);
- $this->assertEquals(IAccountManager::VISIBILITY_PRIVATE, $accountProperty->getScope());
- $this->assertEquals(IAccountManager::VISIBILITY_PRIVATE, $actualReturn->getScope());
+ $actualReturn = $accountProperty->setScope(IAccountManager::SCOPE_LOCAL);
+ $this->assertEquals(IAccountManager::SCOPE_LOCAL, $accountProperty->getScope());
+ $this->assertEquals(IAccountManager::SCOPE_LOCAL, $actualReturn->getScope());
+ }
+
+ public function scopesProvider() {
+ return [
+ // current values
+ [IAccountManager::SCOPE_PRIVATE, IAccountManager::SCOPE_PRIVATE],
+ [IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_LOCAL],
+ [IAccountManager::SCOPE_PUBLISHED, IAccountManager::SCOPE_PUBLISHED],
+ // legacy values
+ [IAccountManager::VISIBILITY_PRIVATE, IAccountManager::SCOPE_LOCAL],
+ [IAccountManager::VISIBILITY_CONTACTS_ONLY, IAccountManager::SCOPE_FEDERATED],
+ [IAccountManager::VISIBILITY_PUBLIC, IAccountManager::SCOPE_PUBLISHED],
+ // fallback
+ ['', IAccountManager::SCOPE_LOCAL],
+ ['unknown', IAccountManager::SCOPE_LOCAL],
+ ];
+ }
+
+ /**
+ * @dataProvider scopesProvider
+ */
+ public function testSetScopeMapping($storedScope, $returnedScope) {
+ $accountProperty = new AccountProperty(
+ IAccountManager::PROPERTY_WEBSITE,
+ 'https://example.com',
+ $storedScope,
+ IAccountManager::VERIFIED
+ );
+ $this->assertEquals($returnedScope, $accountProperty->getScope());
}
public function testSetVerified() {
$accountProperty = new AccountProperty(
IAccountManager::PROPERTY_WEBSITE,
'https://example.com',
- IAccountManager::VISIBILITY_PUBLIC,
+ IAccountManager::SCOPE_PUBLISHED,
IAccountManager::VERIFIED
);
$actualReturn = $accountProperty->setVerified(IAccountManager::NOT_VERIFIED);
@@ -86,13 +115,13 @@ class AccountPropertyTest extends TestCase {
$accountProperty = new AccountProperty(
IAccountManager::PROPERTY_WEBSITE,
'https://example.com',
- IAccountManager::VISIBILITY_PUBLIC,
+ IAccountManager::SCOPE_PUBLISHED,
IAccountManager::VERIFIED
);
$this->assertEquals([
'name' => IAccountManager::PROPERTY_WEBSITE,
'value' => 'https://example.com',
- 'scope' => IAccountManager::VISIBILITY_PUBLIC,
+ 'scope' => IAccountManager::SCOPE_PUBLISHED,
'verified' => IAccountManager::VERIFIED
], $accountProperty->jsonSerialize());
}
diff --git a/tests/lib/Accounts/AccountTest.php b/tests/lib/Accounts/AccountTest.php
index 11b13637bd0..8afcc44afd1 100644
--- a/tests/lib/Accounts/AccountTest.php
+++ b/tests/lib/Accounts/AccountTest.php
@@ -43,21 +43,21 @@ class AccountTest extends TestCase {
public function testSetProperty() {
$user = $this->createMock(IUser::class);
- $property = new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED);
+ $property = new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
$account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
$this->assertEquals($property, $account->getProperty(IAccountManager::PROPERTY_WEBSITE));
}
public function testGetProperties() {
$user = $this->createMock(IUser::class);
$properties = [
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::VERIFIED)
+ IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED),
+ IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED)
];
$account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED);
$this->assertEquals($properties, $account->getProperties());
}
@@ -65,14 +65,14 @@ class AccountTest extends TestCase {
public function testGetFilteredProperties() {
$user = $this->createMock(IUser::class);
$properties = [
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::VERIFIED),
- IAccountManager::PROPERTY_PHONE => new AccountProperty(IAccountManager::PROPERTY_PHONE, '123456', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::VERIFIED),
+ IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED),
+ IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED),
+ IAccountManager::PROPERTY_PHONE => new AccountProperty(IAccountManager::PROPERTY_PHONE, '123456', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED),
];
$account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_PHONE, '123456', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_PHONE, '123456', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED);
$this->assertEquals(
@@ -80,7 +80,7 @@ class AccountTest extends TestCase {
IAccountManager::PROPERTY_WEBSITE => $properties[IAccountManager::PROPERTY_WEBSITE],
IAccountManager::PROPERTY_PHONE => $properties[IAccountManager::PROPERTY_PHONE],
],
- $account->getFilteredProperties(IAccountManager::VISIBILITY_PUBLIC)
+ $account->getFilteredProperties(IAccountManager::SCOPE_PUBLISHED)
);
$this->assertEquals(
[
@@ -91,19 +91,19 @@ class AccountTest extends TestCase {
);
$this->assertEquals(
[IAccountManager::PROPERTY_PHONE => $properties[IAccountManager::PROPERTY_PHONE]],
- $account->getFilteredProperties(IAccountManager::VISIBILITY_PUBLIC, IAccountManager::VERIFIED)
+ $account->getFilteredProperties(IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED)
);
}
public function testJsonSerialize() {
$user = $this->createMock(IUser::class);
$properties = [
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::VERIFIED)
+ IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED),
+ IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED)
];
$account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::VISIBILITY_PUBLIC, IAccountManager::NOT_VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::VISIBILITY_PRIVATE, IAccountManager::VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
+ $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED);
$this->assertEquals($properties, $account->jsonSerialize());
}
diff --git a/tests/lib/Avatar/AvatarManagerTest.php b/tests/lib/Avatar/AvatarManagerTest.php
index 5a061cd10e9..d3bc60efb59 100644
--- a/tests/lib/Avatar/AvatarManagerTest.php
+++ b/tests/lib/Avatar/AvatarManagerTest.php
@@ -25,19 +25,27 @@
namespace Test\Avatar;
use OC\Avatar\AvatarManager;
+use OC\Avatar\PlaceholderAvatar;
use OC\Avatar\UserAvatar;
+use OC\KnownUser\KnownUserService;
use OC\User\Manager;
+use OCP\Accounts\IAccount;
+use OCP\Accounts\IAccountManager;
+use OCP\Accounts\IAccountProperty;
use OCP\Files\IAppData;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IUser;
+use OCP\IUserSession;
/**
* Class AvatarManagerTest
*/
class AvatarManagerTest extends \Test\TestCase {
+ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
+ private $userSession;
/** @var Manager|\PHPUnit\Framework\MockObject\MockObject */
private $userManager;
/** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */
@@ -48,28 +56,37 @@ class AvatarManagerTest extends \Test\TestCase {
private $logger;
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $config;
+ /** @var IAccountManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $accountManager;
/** @var AvatarManager | \PHPUnit\Framework\MockObject\MockObject */
private $avatarManager;
+ /** @var KnownUserService | \PHPUnit\Framework\MockObject\MockObject */
+ private $knownUserService;
protected function setUp(): void {
parent::setUp();
+ $this->userSession = $this->createMock(IUserSession::class);
$this->userManager = $this->createMock(Manager::class);
$this->appData = $this->createMock(IAppData::class);
$this->l10n = $this->createMock(IL10N::class);
$this->logger = $this->createMock(ILogger::class);
$this->config = $this->createMock(IConfig::class);
+ $this->accountManager = $this->createMock(IAccountManager::class);
+ $this->knownUserService = $this->createMock(KnownUserService::class);
$this->avatarManager = new AvatarManager(
+ $this->userSession,
$this->userManager,
$this->appData,
$this->l10n,
$this->logger,
- $this->config
+ $this->config,
+ $this->accountManager,
+ $this->knownUserService
);
}
-
public function testGetAvatarInvalidUser() {
$this->expectException(\Exception::class);
$this->expectExceptionMessage('user does not exist');
@@ -83,17 +100,45 @@ class AvatarManagerTest extends \Test\TestCase {
$this->avatarManager->getAvatar('invalidUser');
}
- public function testGetAvatarValidUser() {
+ public function testGetAvatarForSelf() {
$user = $this->createMock(IUser::class);
$user
- ->expects($this->once())
+ ->expects($this->any())
->method('getUID')
->willReturn('valid-user');
+
+ // requesting user
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($user);
+
$this->userManager
->expects($this->once())
->method('get')
->with('valid-user')
->willReturn($user);
+
+ $account = $this->createMock(IAccount::class);
+ $this->accountManager->expects($this->once())
+ ->method('getAccount')
+ ->with($user)
+ ->willReturn($account);
+
+ $property = $this->createMock(IAccountProperty::class);
+ $account->expects($this->once())
+ ->method('getProperty')
+ ->with(IAccountManager::PROPERTY_AVATAR)
+ ->willReturn($property);
+
+ $property->expects($this->once())
+ ->method('getScope')
+ ->willReturn(IAccountManager::SCOPE_PRIVATE);
+
+ $this->knownUserService->expects($this->any())
+ ->method('isKnownToUser')
+ ->with('valid-user', 'valid-user')
+ ->willReturn(true);
+
$folder = $this->createMock(ISimpleFolder::class);
$this->appData
->expects($this->once())
@@ -126,4 +171,86 @@ class AvatarManagerTest extends \Test\TestCase {
$expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
$this->assertEquals($expected, $this->avatarManager->getAvatar('vaLid-USER'));
}
+
+ public function knownUnknownProvider() {
+ return [
+ [IAccountManager::SCOPE_LOCAL, false, false, false],
+ [IAccountManager::SCOPE_LOCAL, true, false, false],
+
+ // public access cannot see real avatar
+ [IAccountManager::SCOPE_PRIVATE, true, false, true],
+ // unknown users cannot see real avatar
+ [IAccountManager::SCOPE_PRIVATE, false, false, true],
+ // known users can see real avatar
+ [IAccountManager::SCOPE_PRIVATE, false, true, false],
+ ];
+ }
+
+ /**
+ * @dataProvider knownUnknownProvider
+ */
+ public function testGetAvatarScopes($avatarScope, $isPublicCall, $isKnownUser, $expectedPlaceholder) {
+ if ($isPublicCall) {
+ $requestingUser = null;
+ } else {
+ $requestingUser = $this->createMock(IUser::class);
+ $requestingUser->method('getUID')->willReturn('requesting-user');
+ }
+
+ // requesting user
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($requestingUser);
+
+ $user = $this->createMock(IUser::class);
+ $user
+ ->expects($this->once())
+ ->method('getUID')
+ ->willReturn('valid-user');
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('valid-user')
+ ->willReturn($user);
+
+ $account = $this->createMock(IAccount::class);
+ $this->accountManager->expects($this->once())
+ ->method('getAccount')
+ ->with($user)
+ ->willReturn($account);
+
+ $property = $this->createMock(IAccountProperty::class);
+ $account->expects($this->once())
+ ->method('getProperty')
+ ->with(IAccountManager::PROPERTY_AVATAR)
+ ->willReturn($property);
+
+ $property->expects($this->once())
+ ->method('getScope')
+ ->willReturn($avatarScope);
+
+ $folder = $this->createMock(ISimpleFolder::class);
+ $this->appData
+ ->expects($this->once())
+ ->method('getFolder')
+ ->with('valid-user')
+ ->willReturn($folder);
+
+ if (!$isPublicCall) {
+ $this->knownUserService->expects($this->any())
+ ->method('isKnownToUser')
+ ->with('requesting-user', 'valid-user')
+ ->willReturn($isKnownUser);
+ } else {
+ $this->knownUserService->expects($this->never())
+ ->method('isKnownToUser');
+ }
+
+ if ($expectedPlaceholder) {
+ $expected = new PlaceholderAvatar($folder, $user, $this->createMock(ILogger::class));
+ } else {
+ $expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
+ }
+ $this->assertEquals($expected, $this->avatarManager->getAvatar('valid-user'));
+ }
}