aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.drone.yml20
m---------3rdparty0
-rw-r--r--apps/accessibility/l10n/is.js2
-rw-r--r--apps/accessibility/l10n/is.json2
-rw-r--r--apps/admin_audit/l10n/zh_TW.js7
-rw-r--r--apps/admin_audit/l10n/zh_TW.json5
-rw-r--r--apps/cloud_federation_api/l10n/ru.js8
-rw-r--r--apps/cloud_federation_api/l10n/ru.json6
-rw-r--r--apps/comments/l10n/ru.js6
-rw-r--r--apps/comments/l10n/ru.json6
-rw-r--r--apps/dav/lib/CardDAV/Converter.php19
-rw-r--r--apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php9
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php7
-rw-r--r--apps/dav/tests/unit/CardDAV/ConverterTest.php17
-rw-r--r--apps/dav/tests/unit/CardDAV/SyncServiceTest.php15
-rw-r--r--apps/dav/tests/unit/Command/MoveCalendarTest.php2
-rw-r--r--apps/encryption/lib/Crypto/Crypt.php7
-rw-r--r--apps/files_sharing/l10n/de.js2
-rw-r--r--apps/files_sharing/l10n/de.json2
-rw-r--r--apps/files_sharing/l10n/de_DE.js2
-rw-r--r--apps/files_sharing/l10n/de_DE.json2
-rw-r--r--apps/files_sharing/l10n/is.js1
-rw-r--r--apps/files_sharing/l10n/is.json1
-rw-r--r--apps/files_sharing/tests/CapabilitiesTest.php12
-rw-r--r--apps/files_trashbin/tests/ExpirationTest.php37
-rw-r--r--apps/files_versions/tests/ExpirationTest.php36
-rw-r--r--apps/provisioning_api/appinfo/routes.php1
-rw-r--r--apps/provisioning_api/lib/Controller/AUserData.php13
-rw-r--r--apps/provisioning_api/lib/Controller/UsersController.php124
-rw-r--r--apps/provisioning_api/tests/Controller/GroupsControllerTest.php9
-rw-r--r--apps/provisioning_api/tests/Controller/UsersControllerTest.php104
-rw-r--r--apps/settings/js/usersettings.js7
-rw-r--r--apps/settings/l10n/is.js6
-rw-r--r--apps/settings/l10n/is.json6
-rw-r--r--apps/settings/lib/BackgroundJobs/VerifyUserData.php11
-rw-r--r--apps/settings/lib/Controller/CheckSetupController.php1
-rw-r--r--apps/settings/lib/Controller/UsersController.php125
-rw-r--r--apps/settings/lib/Settings/Personal/PersonalInfo.php65
-rw-r--r--apps/settings/templates/settings/personal/personal.info.php7
-rw-r--r--apps/settings/tests/Controller/CheckSetupControllerTest.php1
-rw-r--r--apps/settings/tests/Controller/UsersControllerTest.php99
-rw-r--r--apps/user_ldap/tests/AccessTest.php3
-rw-r--r--apps/workflowengine/l10n/is.js6
-rw-r--r--apps/workflowengine/l10n/is.json6
-rw-r--r--build/integration/features/bootstrap/Provisioning.php44
-rw-r--r--build/integration/features/provisioning-v1.feature25
-rw-r--r--build/psalm-baseline.xml14
-rw-r--r--config/config.sample.php10
-rw-r--r--core/Migrations/Version21000Date20201202095923.php75
-rw-r--r--core/js/setupchecks.js6
-rw-r--r--core/js/tests/specs/setupchecksSpec.js69
-rw-r--r--core/l10n/is.js3
-rw-r--r--core/l10n/is.json3
-rw-r--r--core/l10n/ru.js2
-rw-r--r--core/l10n/ru.json2
-rw-r--r--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/l10n/ca.js1
-rw-r--r--lib/l10n/ca.json1
-rw-r--r--lib/private/Accounts/AccountManager.php130
-rw-r--r--lib/private/Accounts/Hooks.php9
-rw-r--r--lib/private/AppConfig.php4
-rw-r--r--lib/private/Lock/MemcacheLockingProvider.php2
-rw-r--r--lib/private/Repair.php4
-rw-r--r--lib/private/Repair/NC21/ValidatePhoneNumber.php89
-rw-r--r--lib/private/legacy/OC_Image.php17
-rw-r--r--lib/public/Accounts/IAccountManager.php11
-rw-r--r--tests/lib/Accounts/AccountsManagerTest.php51
-rw-r--r--tests/lib/Accounts/HooksTest.php29
-rw-r--r--tests/lib/App/CodeChecker/CodeCheckerTest.php8
-rw-r--r--tests/lib/App/CodeChecker/DeprecationCheckTest.php4
-rw-r--r--tests/lib/App/CodeChecker/NodeVisitorTest.php4
-rw-r--r--tests/lib/App/CodeChecker/StrongComparisonCheckTest.php4
-rw-r--r--tests/lib/AppConfigTest.php6
-rw-r--r--tests/lib/AppFramework/Db/EntityTest.php10
-rw-r--r--tests/lib/Avatar/UserAvatarTest.php27
-rw-r--r--tests/lib/ImageTest.php68
-rw-r--r--tests/lib/IntegrityCheck/CheckerTest.php4
-rw-r--r--tests/lib/LargeFileHelperGetFileSizeTest.php3
-rw-r--r--tests/lib/TestCase.php6
-rw-r--r--tests/lib/Traits/ClientServiceTrait.php4
-rw-r--r--tests/lib/Traits/EncryptionTrait.php4
-rw-r--r--version.php2
83 files changed, 1195 insertions, 391 deletions
diff --git a/.drone.yml b/.drone.yml
index e9cacb376fc..34b58e1de58 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -256,11 +256,11 @@ steps:
commands:
- bash tests/drone-run-php-tests.sh || exit 0
- NOCOVERAGE=true TEST_SELECTION=NODB ./autotest.sh sqlite
-#- name: nodb-php8.0
-# image: nextcloudci/php8.0:latest
-# commands:
-# - bash tests/drone-run-php-tests.sh || exit 0
-# - NOCOVERAGE=true TEST_SELECTION=NODB ./autotest.sh sqlite
+- name: nodb-php8.0
+ image: nextcloudci/php8.0:latest
+ commands:
+ - bash tests/drone-run-php-tests.sh || exit 0
+ - NOCOVERAGE=true TEST_SELECTION=NODB ./autotest.sh sqlite
services:
- name: cache
@@ -293,11 +293,11 @@ steps:
commands:
- bash tests/drone-run-php-tests.sh || exit 0
- NOCOVERAGE=true TEST_SELECTION=DB ./autotest.sh sqlite
-#- name: sqlite-php8.0
-# image: nextcloudci/php8.0:latest
-# commands:
-# - bash tests/drone-run-php-tests.sh || exit 0
-# - NOCOVERAGE=true TEST_SELECTION=DB ./autotest.sh sqlite
+- name: sqlite-php8.0
+ image: nextcloudci/php8.0:latest
+ commands:
+ - bash tests/drone-run-php-tests.sh || exit 0
+ - NOCOVERAGE=true TEST_SELECTION=DB ./autotest.sh sqlite
services:
- name: cache
diff --git a/3rdparty b/3rdparty
-Subproject e287243b8eeaf72443b841ace3b1788e85a5aac
+Subproject 27a56c5bb9d0ec514a8fb22044fd5f03a51ea2a
diff --git a/apps/accessibility/l10n/is.js b/apps/accessibility/l10n/is.js
index 0bec39d2900..5e68881756c 100644
--- a/apps/accessibility/l10n/is.js
+++ b/apps/accessibility/l10n/is.js
@@ -13,6 +13,8 @@ OC.L10N.register(
"Accessibility" : "Aukið aðgengi",
"Accessibility options for nextcloud" : "Valkostir fyrir auðveldað aðgengi í Nextcloud",
"Provides multiple accessibilities options to ease your use of Nextcloud" : "Býður upp á marga valkosti fyrir auðveldað aðgengi í Nextcloud",
+ "Universal access is very important to us. We follow web standards and check to make everything usable also without mouse, and assistive software such as screenreaders. We aim to be compliant with the {guidelines}Web Content Accessibility Guidelines{linkend} 2.1 on AA level, with the high contrast theme even on AAA level." : "Aðgengi fyrir alla er okkur mikilvægt. Við fylgjum vefstöðlum oggöngum úr skugga um að allt sé nothæft án þess að nota tölvumús, auk þess að hjálpartæki á borð við skjálesara virki. Við miðum að samhæfni við {guidelines}Web Content Accessibility Guidelines{linkend} 2.1 á stigi AA, og höfum meira að segja háskerpuþemað miðað við stig AAA.",
+ "If you find any issues, don’t hesitate to report them on {issuetracker}our issue tracker{linkend}. And if you want to get involved, come join {designteam}our design team{linkend}!" : "Ef þú rekst á einhver vandamál, ekki hika við að tilkynna þau á {issuetracker}verkbeiðnakerfinu okkar{linkend}. Og ef þig langar að taka þátt í þessu, gakktu þá til liðs við {designteam}hönnunarteymið okkar{linkend}!",
"Enable" : "Virkja"
},
"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);");
diff --git a/apps/accessibility/l10n/is.json b/apps/accessibility/l10n/is.json
index feb91e24428..ecc00a57a65 100644
--- a/apps/accessibility/l10n/is.json
+++ b/apps/accessibility/l10n/is.json
@@ -11,6 +11,8 @@
"Accessibility" : "Aukið aðgengi",
"Accessibility options for nextcloud" : "Valkostir fyrir auðveldað aðgengi í Nextcloud",
"Provides multiple accessibilities options to ease your use of Nextcloud" : "Býður upp á marga valkosti fyrir auðveldað aðgengi í Nextcloud",
+ "Universal access is very important to us. We follow web standards and check to make everything usable also without mouse, and assistive software such as screenreaders. We aim to be compliant with the {guidelines}Web Content Accessibility Guidelines{linkend} 2.1 on AA level, with the high contrast theme even on AAA level." : "Aðgengi fyrir alla er okkur mikilvægt. Við fylgjum vefstöðlum oggöngum úr skugga um að allt sé nothæft án þess að nota tölvumús, auk þess að hjálpartæki á borð við skjálesara virki. Við miðum að samhæfni við {guidelines}Web Content Accessibility Guidelines{linkend} 2.1 á stigi AA, og höfum meira að segja háskerpuþemað miðað við stig AAA.",
+ "If you find any issues, don’t hesitate to report them on {issuetracker}our issue tracker{linkend}. And if you want to get involved, come join {designteam}our design team{linkend}!" : "Ef þú rekst á einhver vandamál, ekki hika við að tilkynna þau á {issuetracker}verkbeiðnakerfinu okkar{linkend}. Og ef þig langar að taka þátt í þessu, gakktu þá til liðs við {designteam}hönnunarteymið okkar{linkend}!",
"Enable" : "Virkja"
},"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"
} \ No newline at end of file
diff --git a/apps/admin_audit/l10n/zh_TW.js b/apps/admin_audit/l10n/zh_TW.js
new file mode 100644
index 00000000000..699f1af96d8
--- /dev/null
+++ b/apps/admin_audit/l10n/zh_TW.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "admin_audit",
+ {
+ "Auditing / Logging" : "稽核 / 記錄",
+ "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "提供 Nextcloud 的記錄功能,例如記錄檔存取或其他敏感操作。"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/admin_audit/l10n/zh_TW.json b/apps/admin_audit/l10n/zh_TW.json
new file mode 100644
index 00000000000..20e615aa666
--- /dev/null
+++ b/apps/admin_audit/l10n/zh_TW.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Auditing / Logging" : "稽核 / 記錄",
+ "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "提供 Nextcloud 的記錄功能,例如記錄檔存取或其他敏感操作。"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/cloud_federation_api/l10n/ru.js b/apps/cloud_federation_api/l10n/ru.js
new file mode 100644
index 00000000000..c615fc0384e
--- /dev/null
+++ b/apps/cloud_federation_api/l10n/ru.js
@@ -0,0 +1,8 @@
+OC.L10N.register(
+ "cloud_federation_api",
+ {
+ "Cloud Federation API" : "API облачной федерации",
+ "Enable clouds to communicate with each other and exchange data" : "Позволяют облакам связываться друг с другом и обмениваться данными",
+ "The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "API облачной федерации позволяет разным экземплярам Nextcloud связываться друг с другом и обмениваться данными."
+},
+"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
diff --git a/apps/cloud_federation_api/l10n/ru.json b/apps/cloud_federation_api/l10n/ru.json
new file mode 100644
index 00000000000..c09724aa6f0
--- /dev/null
+++ b/apps/cloud_federation_api/l10n/ru.json
@@ -0,0 +1,6 @@
+{ "translations": {
+ "Cloud Federation API" : "API облачной федерации",
+ "Enable clouds to communicate with each other and exchange data" : "Позволяют облакам связываться друг с другом и обмениваться данными",
+ "The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "API облачной федерации позволяет разным экземплярам Nextcloud связываться друг с другом и обмениваться данными."
+},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/comments/l10n/ru.js b/apps/comments/l10n/ru.js
index 4790ed4cf10..57502500657 100644
--- a/apps/comments/l10n/ru.js
+++ b/apps/comments/l10n/ru.js
@@ -15,12 +15,18 @@ OC.L10N.register(
"Files app plugin to add comments to files" : "Модуль приложения «Файлы», позволяющий комментировать файлы",
"Edit comment" : "Редактировать комментарий",
"Delete comment" : "Удалить комментарий",
+ "Cancel edit" : "Отменить правку",
"No comments yet, start the conversation!" : "Комментарии отсутствуют, начните обсуждение!",
+ "No more messages" : "Сообщений нет",
"Retry" : "Повторить",
+ "Unable to load the comments list" : "Невозможно загрузить список комментариев",
"_%n unread comment_::_%n unread comments_" : ["%n непрочитанный комментарий","%n непрочитанных комментариев","%n непрочитанных комментариев","%n непрочитанных комментариев"],
"_1 new comment_::_{unread} new comments_" : ["1 новый комментарий","{unread} новых комментариев","{unread} новых комментариев","{unread} новых комментариев"],
"Comment" : "Комментарий",
+ "An error occurred while trying to edit the comment" : "Попытка редактирования комментария завершилась ошибкой",
"Comment deleted" : "Комментарий удалён",
+ "An error occurred while trying to delete the comment" : "Попытка удаления комментария завершилась ошибкой",
+ "An error occurred while trying to create the comment" : "Попытка создания комментария завершилась ошибкой",
"New comment …" : "Новый комментарий…",
"Post" : "Опубликовать",
"Cancel" : "Отмена",
diff --git a/apps/comments/l10n/ru.json b/apps/comments/l10n/ru.json
index 9a67567d5e4..e7d940d9686 100644
--- a/apps/comments/l10n/ru.json
+++ b/apps/comments/l10n/ru.json
@@ -13,12 +13,18 @@
"Files app plugin to add comments to files" : "Модуль приложения «Файлы», позволяющий комментировать файлы",
"Edit comment" : "Редактировать комментарий",
"Delete comment" : "Удалить комментарий",
+ "Cancel edit" : "Отменить правку",
"No comments yet, start the conversation!" : "Комментарии отсутствуют, начните обсуждение!",
+ "No more messages" : "Сообщений нет",
"Retry" : "Повторить",
+ "Unable to load the comments list" : "Невозможно загрузить список комментариев",
"_%n unread comment_::_%n unread comments_" : ["%n непрочитанный комментарий","%n непрочитанных комментариев","%n непрочитанных комментариев","%n непрочитанных комментариев"],
"_1 new comment_::_{unread} new comments_" : ["1 новый комментарий","{unread} новых комментариев","{unread} новых комментариев","{unread} новых комментариев"],
"Comment" : "Комментарий",
+ "An error occurred while trying to edit the comment" : "Попытка редактирования комментария завершилась ошибкой",
"Comment deleted" : "Комментарий удалён",
+ "An error occurred while trying to delete the comment" : "Попытка удаления комментария завершилась ошибкой",
+ "An error occurred while trying to create the comment" : "Попытка создания комментария завершилась ошибкой",
"New comment …" : "Новый комментарий…",
"Post" : "Опубликовать",
"Cancel" : "Отмена",
diff --git a/apps/dav/lib/CardDAV/Converter.php b/apps/dav/lib/CardDAV/Converter.php
index dd7ceb77c55..8602d3b4ed9 100644
--- a/apps/dav/lib/CardDAV/Converter.php
+++ b/apps/dav/lib/CardDAV/Converter.php
@@ -26,6 +26,7 @@
namespace OCA\DAV\CardDAV;
use OC\Accounts\AccountManager;
+use OCP\Accounts\IAccountManager;
use OCP\IImage;
use OCP\IUser;
use Sabre\VObject\Component\VCard;
@@ -62,8 +63,8 @@ class Converter {
$publish = false;
- if ($image !== null && isset($userData[AccountManager::PROPERTY_AVATAR])) {
- $userData[AccountManager::PROPERTY_AVATAR]['value'] = true;
+ if ($image !== null && isset($userData[IAccountManager::PROPERTY_AVATAR])) {
+ $userData[IAccountManager::PROPERTY_AVATAR]['value'] = true;
}
foreach ($userData as $property => $value) {
@@ -76,28 +77,28 @@ class Converter {
if ($shareWithTrustedServers && !$emptyValue) {
$publish = true;
switch ($property) {
- case AccountManager::PROPERTY_DISPLAYNAME:
+ case IAccountManager::PROPERTY_DISPLAYNAME:
$vCard->add(new Text($vCard, 'FN', $value['value']));
$vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
break;
- case AccountManager::PROPERTY_AVATAR:
+ case IAccountManager::PROPERTY_AVATAR:
if ($image !== null) {
$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
}
break;
- case AccountManager::PROPERTY_EMAIL:
+ case IAccountManager::PROPERTY_EMAIL:
$vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
break;
- case AccountManager::PROPERTY_WEBSITE:
+ case IAccountManager::PROPERTY_WEBSITE:
$vCard->add(new Text($vCard, 'URL', $value['value']));
break;
- case AccountManager::PROPERTY_PHONE:
+ case IAccountManager::PROPERTY_PHONE:
$vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
break;
- case AccountManager::PROPERTY_ADDRESS:
+ case IAccountManager::PROPERTY_ADDRESS:
$vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
break;
- case AccountManager::PROPERTY_TWITTER:
+ case IAccountManager::PROPERTY_TWITTER:
$vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
break;
}
diff --git a/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php b/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php
index 55938c72e2d..887f48a1d04 100644
--- a/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php
+++ b/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php
@@ -38,6 +38,9 @@ use OCP\Calendar\Resource\IResource;
use OCP\Calendar\Room\IManager as IRoomManager;
use Test\TestCase;
+interface tmpI extends IResource, IMetadataProvider {
+}
+
class UpdateCalendarResourcesRoomsBackgroundJobTest extends TestCase {
/** @var UpdateCalendarResourcesRoomsBackgroundJob */
@@ -108,9 +111,9 @@ class UpdateCalendarResourcesRoomsBackgroundJobTest extends TestCase {
$backend3 = $this->createMock(IBackend::class);
$backend4 = $this->createMock(IBackend::class);
- $res6 = $this->createMock([IResource::class, IMetadataProvider::class]);
- $res7 = $this->createMock([IResource::class, IMetadataProvider::class]);
- $res8 = $this->createMock([IResource::class, IMetadataProvider::class]);
+ $res6 = $this->createMock(tmpI::class);
+ $res7 = $this->createMock(tmpI::class);
+ $res8 = $this->createMock(tmpI::class);
$res9 = $this->createMock(IResource::class);
$backend2->method('getBackendIdentifier')
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
index 3718f1bde46..b4f5d7dea9e 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
@@ -63,6 +63,9 @@ class NotifierTest extends TestCase {
$this->l10n->expects($this->any())
->method('t')
->willReturnCallback(function ($string, $args) {
+ if (!is_array($args)) {
+ $args = [$args];
+ }
return vsprintf($string, $args);
});
$this->l10n->expects($this->any())
@@ -103,7 +106,7 @@ class NotifierTest extends TestCase {
$this->assertEquals($this->notifier->getName(), 'Calendar');
}
-
+
public function testPrepareWrongApp(): void {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Notification not from this app');
@@ -120,7 +123,7 @@ class NotifierTest extends TestCase {
$this->notifier->prepare($notification, 'en');
}
-
+
public function testPrepareWrongSubject() {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unknown subject');
diff --git a/apps/dav/tests/unit/CardDAV/ConverterTest.php b/apps/dav/tests/unit/CardDAV/ConverterTest.php
index 8b4125c6c68..aef5cf8ef1c 100644
--- a/apps/dav/tests/unit/CardDAV/ConverterTest.php
+++ b/apps/dav/tests/unit/CardDAV/ConverterTest.php
@@ -29,13 +29,14 @@ namespace OCA\DAV\Tests\unit\CardDAV;
use OC\Accounts\AccountManager;
use OCA\DAV\CardDAV\Converter;
+use OCP\Accounts\IAccountManager;
use OCP\IImage;
use OCP\IUser;
use Test\TestCase;
class ConverterTest extends TestCase {
- /** @var AccountManager | \PHPUnit\Framework\MockObject\MockObject */
+ /** @var AccountManager|\PHPUnit\Framework\MockObject\MockObject */
private $accountManager;
protected function setUp(): void {
@@ -49,36 +50,36 @@ class ConverterTest extends TestCase {
->disableOriginalConstructor()->getMock();
$accountManager->expects($this->any())->method('getUser')->willReturn(
[
- AccountManager::PROPERTY_DISPLAYNAME =>
+ IAccountManager::PROPERTY_DISPLAYNAME =>
[
'value' => $user->getDisplayName(),
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
],
- AccountManager::PROPERTY_ADDRESS =>
+ IAccountManager::PROPERTY_ADDRESS =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
],
- AccountManager::PROPERTY_WEBSITE =>
+ IAccountManager::PROPERTY_WEBSITE =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
],
- AccountManager::PROPERTY_EMAIL =>
+ IAccountManager::PROPERTY_EMAIL =>
[
'value' => $user->getEMailAddress(),
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
],
- AccountManager::PROPERTY_AVATAR =>
+ IAccountManager::PROPERTY_AVATAR =>
[
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
],
- AccountManager::PROPERTY_PHONE =>
+ IAccountManager::PROPERTY_PHONE =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
],
- AccountManager::PROPERTY_TWITTER =>
+ IAccountManager::PROPERTY_TWITTER =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
diff --git a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
index 0a474d649bc..eb8186807c6 100644
--- a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
+++ b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
@@ -31,6 +31,7 @@ namespace OCA\DAV\Tests\unit\CardDAV;
use OC\Accounts\AccountManager;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\SyncService;
+use OCP\Accounts\IAccountManager;
use OCP\ILogger;
use OCP\IUser;
use OCP\IUserManager;
@@ -132,36 +133,36 @@ class SyncServiceTest extends TestCase {
$accountManager = $this->getMockBuilder(AccountManager::class)->disableOriginalConstructor()->getMock();
$accountManager->expects($this->any())->method('getUser')
->willReturn([
- AccountManager::PROPERTY_DISPLAYNAME =>
+ IAccountManager::PROPERTY_DISPLAYNAME =>
[
'value' => $user->getDisplayName(),
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
],
- AccountManager::PROPERTY_ADDRESS =>
+ IAccountManager::PROPERTY_ADDRESS =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
],
- AccountManager::PROPERTY_WEBSITE =>
+ IAccountManager::PROPERTY_WEBSITE =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
],
- AccountManager::PROPERTY_EMAIL =>
+ IAccountManager::PROPERTY_EMAIL =>
[
'value' => $user->getEMailAddress(),
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
],
- AccountManager::PROPERTY_AVATAR =>
+ IAccountManager::PROPERTY_AVATAR =>
[
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
],
- AccountManager::PROPERTY_PHONE =>
+ IAccountManager::PROPERTY_PHONE =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
],
- AccountManager::PROPERTY_TWITTER =>
+ IAccountManager::PROPERTY_TWITTER =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
diff --git a/apps/dav/tests/unit/Command/MoveCalendarTest.php b/apps/dav/tests/unit/Command/MoveCalendarTest.php
index 026a722785d..73443eacb7c 100644
--- a/apps/dav/tests/unit/Command/MoveCalendarTest.php
+++ b/apps/dav/tests/unit/Command/MoveCalendarTest.php
@@ -216,7 +216,7 @@ class MoveCalendarTest extends TestCase {
'destinationuid' => 'user2',
]);
- $this->assertContains("[OK] Calendar <personal> was moved from user <user> to <user2>", $commandTester->getDisplay());
+ $this->assertStringContainsString("[OK] Calendar <personal> was moved from user <user> to <user2>", $commandTester->getDisplay());
}
public function dataTestMoveWithDestinationNotPartOfGroup(): array {
diff --git a/apps/encryption/lib/Crypto/Crypt.php b/apps/encryption/lib/Crypto/Crypt.php
index 57aecf95633..2ba30425c7f 100644
--- a/apps/encryption/lib/Crypto/Crypt.php
+++ b/apps/encryption/lib/Crypto/Crypt.php
@@ -444,7 +444,8 @@ class Crypt {
*/
protected function isValidPrivateKey($plainKey) {
$res = openssl_get_privatekey($plainKey);
- if (is_resource($res)) {
+ // TODO: remove resource check one php7.4 is not longer supported
+ if (is_resource($res) || (is_object($res) && get_class($res) === 'OpenSSLAsymmetricKey')) {
$sslInfo = openssl_pkey_get_details($res);
if (isset($sslInfo['key'])) {
return true;
@@ -676,7 +677,7 @@ class Crypt {
throw new MultiKeyDecryptException('Cannot multikey decrypt empty plain content');
}
- if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey)) {
+ if (openssl_open($encKeyFile, $plainContent, $shareKey, $privateKey, 'RC4')) {
return $plainContent;
} else {
throw new MultiKeyDecryptException('multikeydecrypt with share key failed:' . openssl_error_string());
@@ -701,7 +702,7 @@ class Crypt {
$shareKeys = [];
$mappedShareKeys = [];
- if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles)) {
+ if (openssl_seal($plainContent, $sealed, $shareKeys, $keyFiles, 'RC4')) {
$i = 0;
// Ensure each shareKey is labelled with its corresponding key id
diff --git a/apps/files_sharing/l10n/de.js b/apps/files_sharing/l10n/de.js
index 01378d61e13..a6131bd590c 100644
--- a/apps/files_sharing/l10n/de.js
+++ b/apps/files_sharing/l10n/de.js
@@ -43,7 +43,7 @@ OC.L10N.register(
"File shares" : "Dateifreigaben",
"Downloaded via public link" : "Über den öffentlichen Link heruntergeladen",
"Downloaded by {email}" : "Heruntergeladen von {email}",
- "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichen Link",
+ "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichem Link",
"{email} downloaded {file}" : "{email} hat {file} heruntergeladen",
"Shared with group {group}" : "Geteilt mit der Gruppe {group}",
"Removed share for group {group}" : "Freigabe für die Gruppe {group} entfernt",
diff --git a/apps/files_sharing/l10n/de.json b/apps/files_sharing/l10n/de.json
index f0e579ef843..725151dacb4 100644
--- a/apps/files_sharing/l10n/de.json
+++ b/apps/files_sharing/l10n/de.json
@@ -41,7 +41,7 @@
"File shares" : "Dateifreigaben",
"Downloaded via public link" : "Über den öffentlichen Link heruntergeladen",
"Downloaded by {email}" : "Heruntergeladen von {email}",
- "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichen Link",
+ "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichem Link",
"{email} downloaded {file}" : "{email} hat {file} heruntergeladen",
"Shared with group {group}" : "Geteilt mit der Gruppe {group}",
"Removed share for group {group}" : "Freigabe für die Gruppe {group} entfernt",
diff --git a/apps/files_sharing/l10n/de_DE.js b/apps/files_sharing/l10n/de_DE.js
index 8c48b3e321b..3d214082567 100644
--- a/apps/files_sharing/l10n/de_DE.js
+++ b/apps/files_sharing/l10n/de_DE.js
@@ -43,7 +43,7 @@ OC.L10N.register(
"File shares" : "Dateifreigaben",
"Downloaded via public link" : "Über den öffentlichen Link heruntergeladen",
"Downloaded by {email}" : "Heruntergeladen von {email}",
- "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichen Link",
+ "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichem Link",
"{email} downloaded {file}" : "{email} hat {file} heruntergeladen",
"Shared with group {group}" : "Geteilt mit der Gruppe {group}",
"Removed share for group {group}" : "Freigabe für die Gruppe {group} entfernt",
diff --git a/apps/files_sharing/l10n/de_DE.json b/apps/files_sharing/l10n/de_DE.json
index da8c271ec54..507cb0a24f5 100644
--- a/apps/files_sharing/l10n/de_DE.json
+++ b/apps/files_sharing/l10n/de_DE.json
@@ -41,7 +41,7 @@
"File shares" : "Dateifreigaben",
"Downloaded via public link" : "Über den öffentlichen Link heruntergeladen",
"Downloaded by {email}" : "Heruntergeladen von {email}",
- "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichen Link",
+ "{file} downloaded via public link" : "{file} heruntergeladen mittels öffentlichem Link",
"{email} downloaded {file}" : "{email} hat {file} heruntergeladen",
"Shared with group {group}" : "Geteilt mit der Gruppe {group}",
"Removed share for group {group}" : "Freigabe für die Gruppe {group} entfernt",
diff --git a/apps/files_sharing/l10n/is.js b/apps/files_sharing/l10n/is.js
index cb58bf0c608..3ab7e406d3b 100644
--- a/apps/files_sharing/l10n/is.js
+++ b/apps/files_sharing/l10n/is.js
@@ -119,6 +119,7 @@ OC.L10N.register(
"Reject" : "Hafna",
"This application enables users to share files within Nextcloud. If enabled, the admin can choose which groups can share files. The applicable users can then share files and folders with other users and groups within Nextcloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other users outside of Nextcloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.\nTurning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation." : "Þetta forrit gerir notendum kleift að deila skrám innan Nextcloud. Ef þetta er virkt getur stjórnandi valið hvaða hópar geti deilt skrám. Viðkomandi notendur geta þá deilt skrám og möppum með öðrum notendum og hópum innan Nextcloud. Að auki, ef stjórnandinn virkjar eiginleikan til að deila með tenglum, er hægt að nota ytri tengil til að deila skrám með öðrum notendum utan Nextcloud. Stjórnendur geta líka krafist notkunar lykilorða, gildistíma og virkjað þjónn-í-þjón deilingu með deilitenglum, rétt eins og deilingu með snjalltækjum.\nSé slökkt á þessum eiginleika, eru deildar skrár og möppur fjarlægðar af þjóninum fyrir alla notendur þessara sameigna, og einnig úr samstillingaforritum og snjalltækjum. Ítarlegri upplýsingar um þetta má finna í hjálparskjölum Nextcloud.",
"Sharing" : "Deiling",
+ "Accept user and group shares by default" : "Samþykkja sjálfgefið sameignir frá notendum og hópum",
"Allow editing" : "Leyfa breytingar",
"Allow resharing" : "Leyfa endurdeilingu",
"Expiration date enforced" : "Gerði gildistíma nauðsynlegan",
diff --git a/apps/files_sharing/l10n/is.json b/apps/files_sharing/l10n/is.json
index 1c68c95fe95..ebba7459c60 100644
--- a/apps/files_sharing/l10n/is.json
+++ b/apps/files_sharing/l10n/is.json
@@ -117,6 +117,7 @@
"Reject" : "Hafna",
"This application enables users to share files within Nextcloud. If enabled, the admin can choose which groups can share files. The applicable users can then share files and folders with other users and groups within Nextcloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other users outside of Nextcloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.\nTurning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation." : "Þetta forrit gerir notendum kleift að deila skrám innan Nextcloud. Ef þetta er virkt getur stjórnandi valið hvaða hópar geti deilt skrám. Viðkomandi notendur geta þá deilt skrám og möppum með öðrum notendum og hópum innan Nextcloud. Að auki, ef stjórnandinn virkjar eiginleikan til að deila með tenglum, er hægt að nota ytri tengil til að deila skrám með öðrum notendum utan Nextcloud. Stjórnendur geta líka krafist notkunar lykilorða, gildistíma og virkjað þjónn-í-þjón deilingu með deilitenglum, rétt eins og deilingu með snjalltækjum.\nSé slökkt á þessum eiginleika, eru deildar skrár og möppur fjarlægðar af þjóninum fyrir alla notendur þessara sameigna, og einnig úr samstillingaforritum og snjalltækjum. Ítarlegri upplýsingar um þetta má finna í hjálparskjölum Nextcloud.",
"Sharing" : "Deiling",
+ "Accept user and group shares by default" : "Samþykkja sjálfgefið sameignir frá notendum og hópum",
"Allow editing" : "Leyfa breytingar",
"Allow resharing" : "Leyfa endurdeilingu",
"Expiration date enforced" : "Gerði gildistíma nauðsynlegan",
diff --git a/apps/files_sharing/tests/CapabilitiesTest.php b/apps/files_sharing/tests/CapabilitiesTest.php
index a49074cb60e..6cba6ef6c94 100644
--- a/apps/files_sharing/tests/CapabilitiesTest.php
+++ b/apps/files_sharing/tests/CapabilitiesTest.php
@@ -71,9 +71,9 @@ class CapabilitiesTest extends \Test\TestCase {
];
$result = $this->getResults($map);
$this->assertTrue($result['api_enabled']);
- $this->assertContains('public', $result);
- $this->assertContains('user', $result);
- $this->assertContains('resharing', $result);
+ $this->assertArrayHasKey('public', $result);
+ $this->assertArrayHasKey('user', $result);
+ $this->assertArrayHasKey('resharing', $result);
}
public function testDisabledSharingAPI() {
@@ -82,9 +82,9 @@ class CapabilitiesTest extends \Test\TestCase {
];
$result = $this->getResults($map);
$this->assertFalse($result['api_enabled']);
- $this->assertNotContains('public', $result);
- $this->assertNotContains('user', $result);
- $this->assertNotContains('resharing', $result);
+ $this->assertFalse($result['public']['enabled']);
+ $this->assertFalse($result['user']['send_mail']);
+ $this->assertFalse($result['resharing']);
}
public function testNoLinkSharing() {
diff --git a/apps/files_trashbin/tests/ExpirationTest.php b/apps/files_trashbin/tests/ExpirationTest.php
index 9c9ef72e46f..0c26a86295e 100644
--- a/apps/files_trashbin/tests/ExpirationTest.php
+++ b/apps/files_trashbin/tests/ExpirationTest.php
@@ -112,7 +112,7 @@ class ExpirationTest extends \Test\TestCase {
$expiration = new Expiration($mockedConfig, $mockedTimeFactory);
$actualResult = $expiration->isExpired($timestamp, $quotaExceeded);
-
+
$this->assertEquals($expectedResult, $actualResult);
}
@@ -132,41 +132,6 @@ class ExpirationTest extends \Test\TestCase {
/**
- * @dataProvider configData
- *
- * @param string $configValue
- * @param int $expectedMinAge
- * @param int $expectedMaxAge
- * @param bool $expectedCanPurgeToSaveSpace
- */
- public function testParseRetentionObligation($configValue, $expectedMinAge, $expectedMaxAge, $expectedCanPurgeToSaveSpace) {
- $mockedConfig = $this->getMockedConfig($configValue);
- $mockedTimeFactory = $this->getMockedTimeFactory(
- time()
- );
-
- $expiration = new Expiration($mockedConfig, $mockedTimeFactory);
- $this->assertAttributeEquals($expectedMinAge, 'minAge', $expiration);
- $this->assertAttributeEquals($expectedMaxAge, 'maxAge', $expiration);
- $this->assertAttributeEquals($expectedCanPurgeToSaveSpace, 'canPurgeToSaveSpace', $expiration);
- }
-
-
- public function timestampTestData() {
- return [
- [ 'disabled', false],
- [ 'auto', false ],
- [ 'auto,auto', false ],
- [ 'auto, auto', false ],
- [ 'auto, 3', self::FAKE_TIME_NOW - (3 * self::SECONDS_PER_DAY) ],
- [ '5, auto', false ],
- [ '3, 5', self::FAKE_TIME_NOW - (5 * self::SECONDS_PER_DAY) ],
- [ '10, 3', self::FAKE_TIME_NOW - (10 * self::SECONDS_PER_DAY) ],
- ];
- }
-
-
- /**
* @dataProvider timestampTestData
*
* @param string $configValue
diff --git a/apps/files_versions/tests/ExpirationTest.php b/apps/files_versions/tests/ExpirationTest.php
index 6ed1fd74591..d6d057e29a5 100644
--- a/apps/files_versions/tests/ExpirationTest.php
+++ b/apps/files_versions/tests/ExpirationTest.php
@@ -118,42 +118,6 @@ class ExpirationTest extends \Test\TestCase {
}
- public function configData() {
- return [
- [ 'disabled', null, null, null],
- [ 'auto', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ],
- [ 'auto,auto', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ],
- [ 'auto, auto', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ],
- [ 'auto, 3', Expiration::NO_OBLIGATION, 3, true ],
- [ '5, auto', 5, Expiration::NO_OBLIGATION, true ],
- [ '3, 5', 3, 5, false ],
- [ '10, 3', 10, 10, false ],
- [ 'g,a,r,b,a,g,e', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ],
- [ '-3,8', Expiration::NO_OBLIGATION, Expiration::NO_OBLIGATION, true ]
- ];
- }
-
-
- /**
- * @dataProvider configData
- *
- * @param string $configValue
- * @param int $expectedMinAge
- * @param int $expectedMaxAge
- * @param bool $expectedCanPurgeToSaveSpace
- */
- public function testParseRetentionObligation($configValue, $expectedMinAge, $expectedMaxAge, $expectedCanPurgeToSaveSpace) {
- $mockedConfig = $this->getMockedConfig($configValue);
- $mockedTimeFactory = $this->getMockedTimeFactory(
- time()
- );
-
- $expiration = new Expiration($mockedConfig, $mockedTimeFactory);
- $this->assertAttributeEquals($expectedMinAge, 'minAge', $expiration);
- $this->assertAttributeEquals($expectedMaxAge, 'maxAge', $expiration);
- $this->assertAttributeEquals($expectedCanPurgeToSaveSpace, 'canPurgeToSaveSpace', $expiration);
- }
-
/**
* @param int $time
* @return ITimeFactory|MockObject
diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php
index fd1579ca843..912dd82e853 100644
--- a/apps/provisioning_api/appinfo/routes.php
+++ b/apps/provisioning_api/appinfo/routes.php
@@ -48,6 +48,7 @@ return [
// Users
['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
+ ['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
diff --git a/apps/provisioning_api/lib/Controller/AUserData.php b/apps/provisioning_api/lib/Controller/AUserData.php
index 131db91add9..b7b31b18b53 100644
--- a/apps/provisioning_api/lib/Controller/AUserData.php
+++ b/apps/provisioning_api/lib/Controller/AUserData.php
@@ -35,6 +35,7 @@ use OC\Accounts\AccountManager;
use OC\User\Backend;
use OC\User\NoUserException;
use OC_Helper;
+use OCP\Accounts\IAccountManager;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
@@ -135,12 +136,12 @@ abstract class AUserData extends OCSController {
$data['backend'] = $targetUserObject->getBackendClassName();
$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
- $data[AccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
- $data[AccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
- $data[AccountManager::PROPERTY_PHONE] = $userAccount[AccountManager::PROPERTY_PHONE]['value'];
- $data[AccountManager::PROPERTY_ADDRESS] = $userAccount[AccountManager::PROPERTY_ADDRESS]['value'];
- $data[AccountManager::PROPERTY_WEBSITE] = $userAccount[AccountManager::PROPERTY_WEBSITE]['value'];
- $data[AccountManager::PROPERTY_TWITTER] = $userAccount[AccountManager::PROPERTY_TWITTER]['value'];
+ $data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
+ $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'];
$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 fd143a0e466..735d394796b 100644
--- a/apps/provisioning_api/lib/Controller/UsersController.php
+++ b/apps/provisioning_api/lib/Controller/UsersController.php
@@ -41,12 +41,18 @@ declare(strict_types=1);
namespace OCA\Provisioning_API\Controller;
+use libphonenumber\NumberParseException;
+use libphonenumber\PhoneNumber;
+use libphonenumber\PhoneNumberFormat;
+use libphonenumber\PhoneNumberUtil;
use OC\Accounts\AccountManager;
use OC\Authentication\Token\RemoteWipe;
use OC\HintException;
use OCA\Provisioning_API\FederatedShareProviderFactory;
use OCA\Settings\Mailer\NewUserMailHelper;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
+use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
@@ -55,6 +61,7 @@ use OCP\IGroup;
use OCP\IGroupManager;
use OCP\ILogger;
use OCP\IRequest;
+use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
@@ -67,6 +74,8 @@ class UsersController extends AUserData {
/** @var IAppManager */
private $appManager;
+ /** @var IURLGenerator */
+ protected $urlGenerator;
/** @var ILogger */
private $logger;
/** @var IFactory */
@@ -90,6 +99,7 @@ class UsersController extends AUserData {
IGroupManager $groupManager,
IUserSession $userSession,
AccountManager $accountManager,
+ IURLGenerator $urlGenerator,
ILogger $logger,
IFactory $l10nFactory,
NewUserMailHelper $newUserMailHelper,
@@ -107,6 +117,7 @@ class UsersController extends AUserData {
$l10nFactory);
$this->appManager = $appManager;
+ $this->urlGenerator = $urlGenerator;
$this->logger = $logger;
$this->l10nFactory = $l10nFactory;
$this->newUserMailHelper = $newUserMailHelper;
@@ -201,6 +212,65 @@ class UsersController extends AUserData {
]);
}
+
+ /**
+ * @NoAdminRequired
+ * @NoSubAdminRequired
+ *
+ * @param string $location
+ * @param array $search
+ * @return DataResponse
+ */
+ public function searchByPhoneNumbers(string $location, array $search): DataResponse {
+ $phoneUtil = PhoneNumberUtil::getInstance();
+
+ if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
+ // Not a valid region code
+ return new DataResponse([], Http::STATUS_BAD_REQUEST);
+ }
+
+ $normalizedNumberToKey = [];
+ foreach ($search as $key => $phoneNumbers) {
+ foreach ($phoneNumbers as $phone) {
+ try {
+ $phoneNumber = $phoneUtil->parse($phone, $location);
+ if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
+ $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
+ $normalizedNumberToKey[$normalizedNumber] = (string) $key;
+ }
+ } catch (NumberParseException $e) {
+ }
+ }
+ }
+
+ $phoneNumbers = array_keys($normalizedNumberToKey);
+
+ if (empty($phoneNumbers)) {
+ return new DataResponse();
+ }
+
+ $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
+
+ if (empty($userMatches)) {
+ return new DataResponse();
+ }
+
+ $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
+ if (strpos($cloudUrl, 'http://') === 0) {
+ $cloudUrl = substr($cloudUrl, strlen('http://'));
+ } elseif (strpos($cloudUrl, 'https://') === 0) {
+ $cloudUrl = substr($cloudUrl, strlen('https://'));
+ }
+
+ $matches = [];
+ foreach ($userMatches as $phone => $userId) {
+ // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
+ $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
+ }
+
+ return new DataResponse($matches);
+ }
+
/**
* @throws OCSException
*/
@@ -431,17 +501,17 @@ class UsersController extends AUserData {
// Editing self (display, email)
if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
- $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
- $permittedFields[] = AccountManager::PROPERTY_EMAIL;
+ $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
+ $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
}
if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
$shareProvider = $this->federatedShareProviderFactory->get();
if ($shareProvider->isLookupServerUploadEnabled()) {
- $permittedFields[] = AccountManager::PROPERTY_PHONE;
- $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
- $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
- $permittedFields[] = AccountManager::PROPERTY_TWITTER;
+ $permittedFields[] = IAccountManager::PROPERTY_PHONE;
+ $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+ $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+ $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
}
}
@@ -474,8 +544,8 @@ class UsersController extends AUserData {
// Editing self (display, email)
if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
$permittedFields[] = 'display';
- $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
- $permittedFields[] = AccountManager::PROPERTY_EMAIL;
+ $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
+ $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
}
$permittedFields[] = 'password';
@@ -492,10 +562,10 @@ class UsersController extends AUserData {
if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
$shareProvider = $this->federatedShareProviderFactory->get();
if ($shareProvider->isLookupServerUploadEnabled()) {
- $permittedFields[] = AccountManager::PROPERTY_PHONE;
- $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
- $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
- $permittedFields[] = AccountManager::PROPERTY_TWITTER;
+ $permittedFields[] = IAccountManager::PROPERTY_PHONE;
+ $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+ $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+ $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
}
}
@@ -510,15 +580,15 @@ class UsersController extends AUserData {
|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
// They have permissions over the user
$permittedFields[] = 'display';
- $permittedFields[] = AccountManager::PROPERTY_DISPLAYNAME;
- $permittedFields[] = AccountManager::PROPERTY_EMAIL;
+ $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
+ $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
$permittedFields[] = 'password';
$permittedFields[] = 'language';
$permittedFields[] = 'locale';
- $permittedFields[] = AccountManager::PROPERTY_PHONE;
- $permittedFields[] = AccountManager::PROPERTY_ADDRESS;
- $permittedFields[] = AccountManager::PROPERTY_WEBSITE;
- $permittedFields[] = AccountManager::PROPERTY_TWITTER;
+ $permittedFields[] = IAccountManager::PROPERTY_PHONE;
+ $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
+ $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
+ $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
$permittedFields[] = 'quota';
} else {
// No rights
@@ -532,7 +602,7 @@ class UsersController extends AUserData {
// Process the edit
switch ($key) {
case 'display':
- case AccountManager::PROPERTY_DISPLAYNAME:
+ case IAccountManager::PROPERTY_DISPLAYNAME:
$targetUser->setDisplayName($value);
break;
case 'quota':
@@ -577,21 +647,25 @@ class UsersController extends AUserData {
}
$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
break;
- case AccountManager::PROPERTY_EMAIL:
+ case IAccountManager::PROPERTY_EMAIL:
if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
$targetUser->setEMailAddress($value);
} else {
throw new OCSException('', 102);
}
break;
- case AccountManager::PROPERTY_PHONE:
- case AccountManager::PROPERTY_ADDRESS:
- case AccountManager::PROPERTY_WEBSITE:
- case AccountManager::PROPERTY_TWITTER:
+ case IAccountManager::PROPERTY_PHONE:
+ case IAccountManager::PROPERTY_ADDRESS:
+ case IAccountManager::PROPERTY_WEBSITE:
+ case IAccountManager::PROPERTY_TWITTER:
$userAccount = $this->accountManager->getUser($targetUser);
if ($userAccount[$key]['value'] !== $value) {
$userAccount[$key]['value'] = $value;
- $this->accountManager->updateUser($targetUser, $userAccount);
+ try {
+ $this->accountManager->updateUser($targetUser, $userAccount, true);
+ } catch (\InvalidArgumentException $e) {
+ throw new OCSException('Invalid ' . $e->getMessage(), 102);
+ }
}
break;
default:
diff --git a/apps/provisioning_api/tests/Controller/GroupsControllerTest.php b/apps/provisioning_api/tests/Controller/GroupsControllerTest.php
index 1973e6f8b6a..bb8ec854390 100644
--- a/apps/provisioning_api/tests/Controller/GroupsControllerTest.php
+++ b/apps/provisioning_api/tests/Controller/GroupsControllerTest.php
@@ -35,6 +35,7 @@ use OC\Group\Manager;
use OC\SubAdmin;
use OC\User\NoUserException;
use OCA\Provisioning_API\Controller\GroupsController;
+use OCP\Accounts\IAccountManager;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IRequest;
@@ -185,10 +186,10 @@ class GroupsControllerTest extends \Test\TestCase {
->method('getUser')
->willReturnCallback(function (IUser $user) {
return [
- AccountManager::PROPERTY_PHONE => ['value' => '0800-call-' . $user->getUID()],
- AccountManager::PROPERTY_ADDRESS => ['value' => 'Holzweg 99, 0601 Herrera, Panama'],
- AccountManager::PROPERTY_WEBSITE => ['value' => 'https://' . $user->getUid() . '.pa'],
- AccountManager::PROPERTY_TWITTER => ['value' => '@' . $user->getUID()],
+ IAccountManager::PROPERTY_PHONE => ['value' => '0800-call-' . $user->getUID()],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'Holzweg 99, 0601 Herrera, Panama'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://' . $user->getUid() . '.pa'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@' . $user->getUID()],
];
});
}
diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php
index d508670d4f6..b6f28cc4a04 100644
--- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php
+++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php
@@ -48,6 +48,7 @@ use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Provisioning_API\Controller\UsersController;
use OCA\Provisioning_API\FederatedShareProviderFactory;
use OCA\Settings\Mailer\NewUserMailHelper;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\AppFramework\Http\DataResponse;
use OCP\EventDispatcher\IEventDispatcher;
@@ -56,6 +57,7 @@ use OCP\IGroup;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IRequest;
+use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
@@ -85,6 +87,8 @@ class UsersControllerTest extends TestCase {
protected $api;
/** @var AccountManager|MockObject */
protected $accountManager;
+ /** @var IURLGenerator|MockObject */
+ protected $urlGenerator;
/** @var IRequest|MockObject */
protected $request;
/** @var IFactory|MockObject */
@@ -111,6 +115,7 @@ class UsersControllerTest extends TestCase {
$this->logger = $this->createMock(ILogger::class);
$this->request = $this->createMock(IRequest::class);
$this->accountManager = $this->createMock(AccountManager::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->l10nFactory = $this->createMock(IFactory::class);
$this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
$this->federatedShareProviderFactory = $this->createMock(FederatedShareProviderFactory::class);
@@ -128,6 +133,7 @@ class UsersControllerTest extends TestCase {
$this->groupManager,
$this->userSession,
$this->accountManager,
+ $this->urlGenerator,
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
@@ -381,7 +387,7 @@ class UsersControllerTest extends TestCase {
}
public function testAddUserSuccessfulWithDisplayName() {
- $api = $this->getMockBuilder('OCA\Provisioning_API\Controller\UsersController')
+ $api = $this->getMockBuilder(UsersController::class)
->setConstructorArgs([
'provisioning_api',
$this->request,
@@ -391,6 +397,7 @@ class UsersControllerTest extends TestCase {
$this->groupManager,
$this->userSession,
$this->accountManager,
+ $this->urlGenerator,
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
@@ -993,10 +1000,10 @@ class UsersControllerTest extends TestCase {
->with($targetUser)
->willReturn(
[
- AccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
- AccountManager::PROPERTY_PHONE => ['value' => 'phone'],
- AccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
- AccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+ IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
]
);
$this->config
@@ -1162,10 +1169,10 @@ class UsersControllerTest extends TestCase {
->with($targetUser)
->willReturn(
[
- AccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
- AccountManager::PROPERTY_PHONE => ['value' => 'phone'],
- AccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
- AccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+ IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
]
);
@@ -1333,10 +1340,10 @@ class UsersControllerTest extends TestCase {
->with($targetUser)
->willReturn(
[
- AccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
- AccountManager::PROPERTY_PHONE => ['value' => 'phone'],
- AccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
- AccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
+ IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
+ IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
]
);
@@ -1370,6 +1377,47 @@ class UsersControllerTest extends TestCase {
$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
}
+ public function dataSearchByPhoneNumbers(): array {
+ return [
+ 'Invalid country' => ['Not a country code', ['12345' => ['NaN']], 400, null, null, []],
+ 'No number to search' => ['DE', ['12345' => ['NaN']], 200, null, null, []],
+ 'Valid number but no match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
+ 'Invalid number' => ['FR', ['12345' => ['0711 / 25 24 28-90']], 200, null, null, []],
+ 'Invalid and valid number' => ['DE', ['12345' => ['NaN', '0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
+ 'Valid and invalid number' => ['DE', ['12345' => ['0711 / 25 24 28-90', 'NaN']], 200, ['+4971125242890'], [], []],
+ 'Valid number and a match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['12345' => 'admin@localhost']],
+ 'Same number twice, later hits' => ['DE', ['12345' => ['0711 / 25 24 28-90'], '23456' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['23456' => 'admin@localhost']],
+ ];
+ }
+
+ /**
+ * @dataProvider dataSearchByPhoneNumbers
+ * @param string $location
+ * @param array $search
+ * @param int $status
+ * @param array $expected
+ */
+ public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected) {
+ if ($searchUsers === null) {
+ $this->accountManager->expects($this->never())
+ ->method('searchUsers');
+ } else {
+ $this->accountManager->expects($this->once())
+ ->method('searchUsers')
+ ->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
+ ->willReturn($userMatches);
+ }
+
+ $this->urlGenerator->method('getAbsoluteURL')
+ ->with('/')
+ ->willReturn('https://localhost/');
+
+ $response = $this->api->searchByPhoneNumbers($location, $search);
+
+ self::assertEquals($status, $response->getStatus());
+ self::assertEquals($expected, $response->getData());
+ }
+
public function testEditUserRegularUserSelfEditChangeDisplayName() {
$loggedInUser = $this->getMockBuilder(IUser::class)
->disableOriginalConstructor()
@@ -3162,7 +3210,7 @@ class UsersControllerTest extends TestCase {
->willReturn($user);
/** @var UsersController | MockObject $api */
- $api = $this->getMockBuilder('OCA\Provisioning_API\Controller\UsersController')
+ $api = $this->getMockBuilder(UsersController::class)
->setConstructorArgs([
'provisioning_api',
$this->request,
@@ -3172,6 +3220,7 @@ class UsersControllerTest extends TestCase {
$this->groupManager,
$this->userSession,
$this->accountManager,
+ $this->urlGenerator,
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
@@ -3227,7 +3276,7 @@ class UsersControllerTest extends TestCase {
public function testGetUser() {
/** @var UsersController | MockObject $api */
- $api = $this->getMockBuilder('OCA\Provisioning_API\Controller\UsersController')
+ $api = $this->getMockBuilder(UsersController::class)
->setConstructorArgs([
'provisioning_api',
$this->request,
@@ -3237,6 +3286,7 @@ class UsersControllerTest extends TestCase {
$this->groupManager,
$this->userSession,
$this->accountManager,
+ $this->urlGenerator,
$this->logger,
$this->l10nFactory,
$this->newUserMailHelper,
@@ -3566,22 +3616,22 @@ class UsersControllerTest extends TestCase {
return [
[false, false, []],
[false, true, [
- AccountManager::PROPERTY_PHONE,
- AccountManager::PROPERTY_ADDRESS,
- AccountManager::PROPERTY_WEBSITE,
- AccountManager::PROPERTY_TWITTER,
+ IAccountManager::PROPERTY_PHONE,
+ IAccountManager::PROPERTY_ADDRESS,
+ IAccountManager::PROPERTY_WEBSITE,
+ IAccountManager::PROPERTY_TWITTER,
]],
[ true, false, [
- AccountManager::PROPERTY_DISPLAYNAME,
- AccountManager::PROPERTY_EMAIL,
+ IAccountManager::PROPERTY_DISPLAYNAME,
+ IAccountManager::PROPERTY_EMAIL,
]],
[ true, true ,[
- AccountManager::PROPERTY_DISPLAYNAME,
- AccountManager::PROPERTY_EMAIL,
- AccountManager::PROPERTY_PHONE,
- AccountManager::PROPERTY_ADDRESS,
- AccountManager::PROPERTY_WEBSITE,
- AccountManager::PROPERTY_TWITTER,
+ IAccountManager::PROPERTY_DISPLAYNAME,
+ IAccountManager::PROPERTY_EMAIL,
+ IAccountManager::PROPERTY_PHONE,
+ IAccountManager::PROPERTY_ADDRESS,
+ IAccountManager::PROPERTY_WEBSITE,
+ IAccountManager::PROPERTY_TWITTER,
]]
];
}
diff --git a/apps/settings/js/usersettings.js b/apps/settings/js/usersettings.js
index fcfe556b1d9..a02aae6fb6a 100644
--- a/apps/settings/js/usersettings.js
+++ b/apps/settings/js/usersettings.js
@@ -24,6 +24,11 @@
if (_.isUndefined(data)) {
return null;
}
+
+ if (data.status && data.status === 'error') {
+ OC.Notification.show(data.data.message, { type: 'error' });
+ }
+
if (_.isUndefined(data.data)) {
return null;
}
@@ -47,4 +52,4 @@
OC.Settings = OC.Settings || {};
OC.Settings.UserSettings = UserSettings;
-})(); \ No newline at end of file
+})();
diff --git a/apps/settings/l10n/is.js b/apps/settings/l10n/is.js
index bd2cc88fc82..ebcc2c3c37d 100644
--- a/apps/settings/l10n/is.js
+++ b/apps/settings/l10n/is.js
@@ -218,10 +218,16 @@ OC.L10N.register(
"Common languages" : "Algeng tungumál",
"All languages" : "Öll tungumál",
"Password change is disabled because the master key is disabled" : "Lykilorðabreyting er óvirk vegna þess að aðallykill er óvirkur",
+ "Passwordless authentication requires a secure connection." : "Lykilorðalaus auðkenning krefst öruggrar tengingar.",
+ "Add WebAuthn device" : "Bæta við WebAuthn-tæki",
"Name your device" : "Gefðu tækinu þínu nafn",
"Add" : "Bæta við",
"Adding your device …" : "Bæti við tækinu þínu …",
+ "Server error while trying to add WebAuthn device" : "Villa á þjóni við að bæta við WebAuthn-tæki",
"Unnamed device" : "Nafnlaust tæki",
+ "Passwordless Authentication" : "Lykilorðalaus auðkenning",
+ "Set up your account for passwordless authentication following the FIDO2 standard." : "Settu aðganginn þinn upp fyrir lykilorðalausa auðkenningu byggða á FIDO2-staðlinum.",
+ "No devices configured." : "Engin tæki skilgreind.",
"Your apps" : "Forritin þín",
"Active apps" : "Virk forrit",
"Disabled apps" : "Óvirk forrit",
diff --git a/apps/settings/l10n/is.json b/apps/settings/l10n/is.json
index ca8c3628502..8c60cf15d27 100644
--- a/apps/settings/l10n/is.json
+++ b/apps/settings/l10n/is.json
@@ -216,10 +216,16 @@
"Common languages" : "Algeng tungumál",
"All languages" : "Öll tungumál",
"Password change is disabled because the master key is disabled" : "Lykilorðabreyting er óvirk vegna þess að aðallykill er óvirkur",
+ "Passwordless authentication requires a secure connection." : "Lykilorðalaus auðkenning krefst öruggrar tengingar.",
+ "Add WebAuthn device" : "Bæta við WebAuthn-tæki",
"Name your device" : "Gefðu tækinu þínu nafn",
"Add" : "Bæta við",
"Adding your device …" : "Bæti við tækinu þínu …",
+ "Server error while trying to add WebAuthn device" : "Villa á þjóni við að bæta við WebAuthn-tæki",
"Unnamed device" : "Nafnlaust tæki",
+ "Passwordless Authentication" : "Lykilorðalaus auðkenning",
+ "Set up your account for passwordless authentication following the FIDO2 standard." : "Settu aðganginn þinn upp fyrir lykilorðalausa auðkenningu byggða á FIDO2-staðlinum.",
+ "No devices configured." : "Engin tæki skilgreind.",
"Your apps" : "Forritin þín",
"Active apps" : "Virk forrit",
"Disabled apps" : "Óvirk forrit",
diff --git a/apps/settings/lib/BackgroundJobs/VerifyUserData.php b/apps/settings/lib/BackgroundJobs/VerifyUserData.php
index 0faa9b56e82..d1b6d835fa4 100644
--- a/apps/settings/lib/BackgroundJobs/VerifyUserData.php
+++ b/apps/settings/lib/BackgroundJobs/VerifyUserData.php
@@ -30,6 +30,7 @@
namespace OCA\Settings\BackgroundJobs;
use OC\Accounts\AccountManager;
+use OCP\Accounts\IAccountManager;
use OCP\AppFramework\Http;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\Job;
@@ -114,11 +115,11 @@ class VerifyUserData extends Job {
$try = (int)$argument['try'] + 1;
switch ($argument['type']) {
- case AccountManager::PROPERTY_WEBSITE:
+ case IAccountManager::PROPERTY_WEBSITE:
$result = $this->verifyWebsite($argument);
break;
- case AccountManager::PROPERTY_TWITTER:
- case AccountManager::PROPERTY_EMAIL:
+ case IAccountManager::PROPERTY_TWITTER:
+ case IAccountManager::PROPERTY_EMAIL:
$result = $this->verifyViaLookupServer($argument, $argument['type']);
break;
default:
@@ -164,9 +165,9 @@ class VerifyUserData extends Job {
$userData = $this->accountManager->getUser($user);
if ($publishedCodeSanitized === $argument['verificationCode']) {
- $userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
+ $userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
} else {
- $userData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
+ $userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
}
$this->accountManager->updateUser($user, $userData);
diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php
index 7929e9c3962..1ebeb41adfb 100644
--- a/apps/settings/lib/Controller/CheckSetupController.php
+++ b/apps/settings/lib/Controller/CheckSetupController.php
@@ -752,6 +752,7 @@ Raw output
PhpOutputBuffering::class => ['pass' => $phpOutputBuffering->run(), 'description' => $phpOutputBuffering->description(), 'severity' => $phpOutputBuffering->severity()],
LegacySSEKeyFormat::class => ['pass' => $legacySSEKeyFormat->run(), 'description' => $legacySSEKeyFormat->description(), 'severity' => $legacySSEKeyFormat->severity(), 'linkToDocumentation' => $legacySSEKeyFormat->linkToDocumentation()],
CheckUserCertificates::class => ['pass' => $checkUserCertificates->run(), 'description' => $checkUserCertificates->description(), 'severity' => $checkUserCertificates->severity(), 'elements' => $checkUserCertificates->elements()],
+ 'isDefaultPhoneRegionSet' => $this->config->getSystemValueString('default_phone_region', '') !== '',
]
);
}
diff --git a/apps/settings/lib/Controller/UsersController.php b/apps/settings/lib/Controller/UsersController.php
index cad21c5f3b3..dba5ec69b2b 100644
--- a/apps/settings/lib/Controller/UsersController.php
+++ b/apps/settings/lib/Controller/UsersController.php
@@ -1,4 +1,6 @@
<?php
+
+declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
@@ -30,7 +32,6 @@
// FIXME: disabled for now to be able to inject IGroupManager and also use
// getSubAdmin()
-//declare(strict_types=1);
namespace OCA\Settings\Controller;
@@ -46,6 +47,7 @@ use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCA\Settings\Events\BeforeTemplateRenderedEvent;
use OCA\User_LDAP\User_Proxy;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
@@ -140,7 +142,7 @@ class UsersController extends Controller {
*
* @return TemplateResponse
*/
- public function usersListByGroup() {
+ public function usersListByGroup(): TemplateResponse {
return $this->usersList();
}
@@ -152,7 +154,7 @@ class UsersController extends Controller {
*
* @return TemplateResponse
*/
- public function usersList() {
+ public function usersList(): TemplateResponse {
$user = $this->userSession->getUser();
$uid = $user->getUID();
@@ -309,7 +311,7 @@ class UsersController extends Controller {
*
* @return bool
*/
- protected function canAdminChangeUserPasswords() {
+ protected function canAdminChangeUserPasswords(): bool {
$isEncryptionEnabled = $this->encryptionManager->isEnabled();
try {
$noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
@@ -344,19 +346,19 @@ class UsersController extends Controller {
* @param string $twitterScope
* @return DataResponse
*/
- public function setUserSettings($avatarScope,
- $displayname,
- $displaynameScope,
- $phone,
- $phoneScope,
- $email,
- $emailScope,
- $website,
- $websiteScope,
- $address,
- $addressScope,
- $twitter,
- $twitterScope
+ public function setUserSettings(string $avatarScope,
+ string $displayname,
+ string $displaynameScope,
+ string $phone,
+ string $phoneScope,
+ string $email,
+ string $emailScope,
+ string $website,
+ string $websiteScope,
+ string $address,
+ string $addressScope,
+ string $twitter,
+ string $twitterScope
) {
$email = strtolower($email);
if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
@@ -372,36 +374,40 @@ class UsersController extends Controller {
}
$user = $this->userSession->getUser();
$data = $this->accountManager->getUser($user);
- $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
+ $data[IAccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
- $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
- $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
+ $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[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
- $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
- $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
- $data[AccountManager::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 {
- $this->saveUserSettings($user, $data);
+ $data = $this->saveUserSettings($user, $data);
return new DataResponse(
[
'status' => 'success',
'data' => [
'userId' => $user->getUID(),
- 'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'],
- 'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'],
- 'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
- 'email' => $data[AccountManager::PROPERTY_EMAIL]['value'],
- 'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'],
- 'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'],
- 'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'],
- 'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'],
- 'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'],
+ 'avatarScope' => $data[IAccountManager::PROPERTY_AVATAR]['scope'],
+ 'displayname' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
+ 'displaynameScope' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
+ 'phone' => $data[IAccountManager::PROPERTY_PHONE]['value'],
+ 'phoneScope' => $data[IAccountManager::PROPERTY_PHONE]['scope'],
+ 'email' => $data[IAccountManager::PROPERTY_EMAIL]['value'],
+ 'emailScope' => $data[IAccountManager::PROPERTY_EMAIL]['scope'],
+ 'website' => $data[IAccountManager::PROPERTY_WEBSITE]['value'],
+ 'websiteScope' => $data[IAccountManager::PROPERTY_WEBSITE]['scope'],
+ 'address' => $data[IAccountManager::PROPERTY_ADDRESS]['value'],
+ 'addressScope' => $data[IAccountManager::PROPERTY_ADDRESS]['scope'],
+ 'twitter' => $data[IAccountManager::PROPERTY_TWITTER]['value'],
+ 'twitterScope' => $data[IAccountManager::PROPERTY_TWITTER]['scope'],
'message' => $this->l10n->t('Settings saved')
]
],
@@ -414,6 +420,13 @@ class UsersController extends Controller {
'message' => $e->getMessage()
],
]);
+ } catch (\InvalidArgumentException $e) {
+ return new DataResponse([
+ 'status' => 'error',
+ 'data' => [
+ 'message' => $e->getMessage()
+ ],
+ ]);
}
}
/**
@@ -421,34 +434,45 @@ class UsersController extends Controller {
*
* @param IUser $user
* @param array $data
+ * @return array
* @throws ForbiddenException
+ * @throws \InvalidArgumentException
*/
- protected function saveUserSettings(IUser $user, array $data) {
+ protected function saveUserSettings(IUser $user, array $data): array {
// keep the user back-end up-to-date with the latest display name and email
// address
$oldDisplayName = $user->getDisplayName();
$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
- if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
- && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value']
+ if (isset($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
+ && $oldDisplayName !== $data[IAccountManager::PROPERTY_DISPLAYNAME]['value']
) {
- $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']);
+ $result = $user->setDisplayName($data[IAccountManager::PROPERTY_DISPLAYNAME]['value']);
if ($result === false) {
throw new ForbiddenException($this->l10n->t('Unable to change full name'));
}
}
+
$oldEmailAddress = $user->getEMailAddress();
$oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
- if (isset($data[AccountManager::PROPERTY_EMAIL]['value'])
- && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value']
+ if (isset($data[IAccountManager::PROPERTY_EMAIL]['value'])
+ && $oldEmailAddress !== $data[IAccountManager::PROPERTY_EMAIL]['value']
) {
// this is the only permission a backend provides and is also used
// for the permission of setting a email address
if (!$user->canChangeDisplayName()) {
throw new ForbiddenException($this->l10n->t('Unable to change email address'));
}
- $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']);
+ $user->setEMailAddress($data[IAccountManager::PROPERTY_EMAIL]['value']);
+ }
+
+ try {
+ return $this->accountManager->updateUser($user, $data, true);
+ } catch (\InvalidArgumentException $e) {
+ if ($e->getMessage() === IAccountManager::PROPERTY_PHONE) {
+ throw new \InvalidArgumentException($this->l10n->t('Unable to set invalid phone number'));
+ }
+ throw new \InvalidArgumentException($this->l10n->t('Some account data was invalid'));
}
- $this->accountManager->updateUser($user, $data);
}
/**
@@ -479,26 +503,25 @@ class UsersController extends Controller {
switch ($account) {
case 'verify-twitter':
- $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
+ $accountData[IAccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
$msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
$code = $codeMd5;
- $type = AccountManager::PROPERTY_TWITTER;
- $data = $accountData[AccountManager::PROPERTY_TWITTER]['value'];
- $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature;
+ $type = IAccountManager::PROPERTY_TWITTER;
+ $accountData[IAccountManager::PROPERTY_TWITTER]['signature'] = $signature;
break;
case 'verify-website':
- $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
+ $accountData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
$msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
- $type = AccountManager::PROPERTY_WEBSITE;
- $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value'];
- $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
+ $type = IAccountManager::PROPERTY_WEBSITE;
+ $accountData[IAccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
break;
default:
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
if ($onlyVerificationCode === false) {
- $this->accountManager->updateUser($user, $accountData);
+ $accountData = $this->accountManager->updateUser($user, $accountData);
+ $data = $accountData[$type]['value'];
$this->jobList->add(VerifyUserData::class,
[
diff --git a/apps/settings/lib/Settings/Personal/PersonalInfo.php b/apps/settings/lib/Settings/Personal/PersonalInfo.php
index 06ea440afab..d9f9c2b3a7d 100644
--- a/apps/settings/lib/Settings/Personal/PersonalInfo.php
+++ b/apps/settings/lib/Settings/Personal/PersonalInfo.php
@@ -1,4 +1,6 @@
<?php
+
+declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
@@ -33,6 +35,7 @@ namespace OCA\Settings\Settings\Personal;
use OC\Accounts\AccountManager;
use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Files\FileInfo;
@@ -62,14 +65,6 @@ class PersonalInfo implements ISettings {
/** @var IL10N */
private $l;
- /**
- * @param IConfig $config
- * @param IUserManager $userManager
- * @param IGroupManager $groupManager
- * @param AccountManager $accountManager
- * @param IFactory $l10nFactory
- * @param IL10N $l
- */
public function __construct(
IConfig $config,
IUserManager $userManager,
@@ -88,11 +83,7 @@ class PersonalInfo implements ISettings {
$this->l = $l;
}
- /**
- * @return TemplateResponse returns the instance with all parameters set, ready to be rendered
- * @since 9.1
- */
- public function getForm() {
+ public function getForm(): TemplateResponse {
$federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
$lookupServerUploadEnabled = false;
if ($federatedFileSharingEnabled) {
@@ -126,23 +117,23 @@ class PersonalInfo implements ISettings {
'quota' => $storageInfo['quota'],
'avatarChangeSupported' => $user->canChangeAvatar(),
'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
- 'avatarScope' => $userData[AccountManager::PROPERTY_AVATAR]['scope'],
+ 'avatarScope' => $userData[IAccountManager::PROPERTY_AVATAR]['scope'],
'displayNameChangeSupported' => $user->canChangeDisplayName(),
- 'displayName' => $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'],
- 'displayNameScope' => $userData[AccountManager::PROPERTY_DISPLAYNAME]['scope'],
- 'email' => $userData[AccountManager::PROPERTY_EMAIL]['value'],
- 'emailScope' => $userData[AccountManager::PROPERTY_EMAIL]['scope'],
- 'emailVerification' => $userData[AccountManager::PROPERTY_EMAIL]['verified'],
- 'phone' => $userData[AccountManager::PROPERTY_PHONE]['value'],
- 'phoneScope' => $userData[AccountManager::PROPERTY_PHONE]['scope'],
- 'address' => $userData[AccountManager::PROPERTY_ADDRESS]['value'],
- 'addressScope' => $userData[AccountManager::PROPERTY_ADDRESS]['scope'],
- 'website' => $userData[AccountManager::PROPERTY_WEBSITE]['value'],
- 'websiteScope' => $userData[AccountManager::PROPERTY_WEBSITE]['scope'],
- 'websiteVerification' => $userData[AccountManager::PROPERTY_WEBSITE]['verified'],
- 'twitter' => $userData[AccountManager::PROPERTY_TWITTER]['value'],
- 'twitterScope' => $userData[AccountManager::PROPERTY_TWITTER]['scope'],
- 'twitterVerification' => $userData[AccountManager::PROPERTY_TWITTER]['verified'],
+ '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'],
'groups' => $this->getGroups($user),
] + $messageParameters + $languageParameters + $localeParameters;
@@ -154,7 +145,7 @@ class PersonalInfo implements ISettings {
* @return string the section ID, e.g. 'sharing'
* @since 9.1
*/
- public function getSection() {
+ public function getSection(): string {
return 'personal-info';
}
@@ -166,7 +157,7 @@ class PersonalInfo implements ISettings {
* E.g.: 70
* @since 9.1
*/
- public function getPriority() {
+ public function getPriority(): int {
return 10;
}
@@ -176,9 +167,9 @@ class PersonalInfo implements ISettings {
* @param IUser $user
* @return array
*/
- private function getGroups(IUser $user) {
+ private function getGroups(IUser $user): array {
$groups = array_map(
- function (IGroup $group) {
+ static function (IGroup $group) {
return $group->getDisplayName();
},
$this->groupManager->getUserGroups($user)
@@ -195,7 +186,7 @@ class PersonalInfo implements ISettings {
* @param IUser $user
* @return array
*/
- private function getLanguages(IUser $user) {
+ private function getLanguages(IUser $user): array {
$forceLanguage = $this->config->getSystemValue('force_language', false);
if ($forceLanguage !== false) {
return [];
@@ -228,7 +219,7 @@ class PersonalInfo implements ISettings {
);
}
- private function getLocales(IUser $user) {
+ private function getLocales(IUser $user): array {
$forceLanguage = $this->config->getSystemValue('force_locale', false);
if ($forceLanguage !== false) {
return [];
@@ -273,8 +264,8 @@ class PersonalInfo implements ISettings {
* @param array $userData
* @return array
*/
- private function getMessageParameters(array $userData) {
- $needVerifyMessage = [AccountManager::PROPERTY_EMAIL, AccountManager::PROPERTY_WEBSITE, AccountManager::PROPERTY_TWITTER];
+ private function getMessageParameters(array $userData): array {
+ $needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
$messageParameters = [];
foreach ($needVerifyMessage as $property) {
switch ($userData[$property]['verified']) {
diff --git a/apps/settings/templates/settings/personal/personal.info.php b/apps/settings/templates/settings/personal/personal.info.php
index b78c162c6c9..84198b3c0c4 100644
--- a/apps/settings/templates/settings/personal/personal.info.php
+++ b/apps/settings/templates/settings/personal/personal.info.php
@@ -67,6 +67,7 @@ script('settings', [
</div>
</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 } ?>
@@ -161,7 +162,7 @@ script('settings', [
} ?>
placeholder="<?php p($l->t('Your email address')); ?>"
autocomplete="on" autocapitalize="none" autocorrect="off" />
- <span class="icon-checkmark hidden"></span>
+ <span class="icon-checkmark hidden"></span>
<span class="icon-error hidden" ></span>
<?php if (!$_['displayNameChangeSupported']) { ?>
<span><?php if (isset($_['email']) && !empty($_['email'])) {
@@ -196,6 +197,7 @@ script('settings', [
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 } ?>
@@ -220,6 +222,7 @@ script('settings', [
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 } ?>
@@ -275,6 +278,7 @@ script('settings', [
} ?>
/>
<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 } ?>
@@ -330,6 +334,7 @@ script('settings', [
} ?>
/>
<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 } ?>
diff --git a/apps/settings/tests/Controller/CheckSetupControllerTest.php b/apps/settings/tests/Controller/CheckSetupControllerTest.php
index 43ec984041c..965d7586343 100644
--- a/apps/settings/tests/Controller/CheckSetupControllerTest.php
+++ b/apps/settings/tests/Controller/CheckSetupControllerTest.php
@@ -605,6 +605,7 @@ class CheckSetupControllerTest extends TestCase {
'OCA\Settings\SetupChecks\LegacySSEKeyFormat' => ['pass' => true, 'description' => 'The old server-side-encryption format is enabled. We recommend disabling this.', 'severity' => 'warning', 'linkToDocumentation' => ''],
'OCA\Settings\SetupChecks\CheckUserCertificates' => ['pass' => false, 'description' => 'There are some user imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via "occ security:certificates:import" command. Their paths inside the data directory are shown below.', 'severity' => 'warning', 'elements' => ['a', 'b']],
'imageMagickLacksSVGSupport' => false,
+ 'isDefaultPhoneRegionSet' => false,
]
);
$this->assertEquals($expected, $this->checkSetupController->check());
diff --git a/apps/settings/tests/Controller/UsersControllerTest.php b/apps/settings/tests/Controller/UsersControllerTest.php
index 4679fd8f7ba..23e3ef5ec01 100644
--- a/apps/settings/tests/Controller/UsersControllerTest.php
+++ b/apps/settings/tests/Controller/UsersControllerTest.php
@@ -31,6 +31,7 @@ use OC\Accounts\AccountManager;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\Group\Manager;
use OCA\Settings\Controller\UsersController;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\BackgroundJob\IJobList;
@@ -196,41 +197,41 @@ class UsersControllerTest extends \Test\TestCase {
->method('getUser')
->with($user)
->willReturn([
- AccountManager::PROPERTY_DISPLAYNAME =>
+ IAccountManager::PROPERTY_DISPLAYNAME =>
[
'value' => 'Display name',
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
'verified' => AccountManager::NOT_VERIFIED,
],
- AccountManager::PROPERTY_ADDRESS =>
+ IAccountManager::PROPERTY_ADDRESS =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
'verified' => AccountManager::NOT_VERIFIED,
],
- AccountManager::PROPERTY_WEBSITE =>
+ IAccountManager::PROPERTY_WEBSITE =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
'verified' => AccountManager::NOT_VERIFIED,
],
- AccountManager::PROPERTY_EMAIL =>
+ IAccountManager::PROPERTY_EMAIL =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
'verified' => AccountManager::NOT_VERIFIED,
],
- AccountManager::PROPERTY_AVATAR =>
+ IAccountManager::PROPERTY_AVATAR =>
[
'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
],
- AccountManager::PROPERTY_PHONE =>
+ IAccountManager::PROPERTY_PHONE =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
'verified' => AccountManager::NOT_VERIFIED,
],
- AccountManager::PROPERTY_TWITTER =>
+ IAccountManager::PROPERTY_TWITTER =>
[
'value' => '',
'scope' => AccountManager::VISIBILITY_PRIVATE,
@@ -238,12 +239,14 @@ class UsersControllerTest extends \Test\TestCase {
],
]);
- $controller->expects($this->once())->method('saveUserSettings');
+ $controller->expects($this->once())
+ ->method('saveUserSettings')
+ ->willReturnArgument(1);
} else {
$controller->expects($this->never())->method('saveUserSettings');
}
- $result = $controller->setUserSettings(
+ $result = $controller->setUserSettings(//
AccountManager::VISIBILITY_CONTACTS_ONLY,
'displayName',
AccountManager::VISIBILITY_CONTACTS_ONLY,
@@ -289,21 +292,21 @@ class UsersControllerTest extends \Test\TestCase {
$user->method('getEMailAddress')->willReturn($oldEmailAddress);
$user->method('canChangeDisplayName')->willReturn(true);
- if ($data[AccountManager::PROPERTY_EMAIL]['value'] === $oldEmailAddress ||
- ($oldEmailAddress === null && $data[AccountManager::PROPERTY_EMAIL]['value'] === '')) {
+ if ($data[IAccountManager::PROPERTY_EMAIL]['value'] === $oldEmailAddress ||
+ ($oldEmailAddress === null && $data[IAccountManager::PROPERTY_EMAIL]['value'] === '')) {
$user->expects($this->never())->method('setEMailAddress');
} else {
$user->expects($this->once())->method('setEMailAddress')
- ->with($data[AccountManager::PROPERTY_EMAIL]['value'])
+ ->with($data[IAccountManager::PROPERTY_EMAIL]['value'])
->willReturn(true);
}
- if ($data[AccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName ||
- ($oldDisplayName === null && $data[AccountManager::PROPERTY_DISPLAYNAME]['value'] === '')) {
+ if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName ||
+ ($oldDisplayName === null && $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === '')) {
$user->expects($this->never())->method('setDisplayName');
} else {
$user->expects($this->once())->method('setDisplayName')
- ->with($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
+ ->with($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
->willReturn(true);
}
@@ -317,48 +320,48 @@ class UsersControllerTest extends \Test\TestCase {
return [
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'john@example.com',
'john doe'
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'johnNew@example.com',
'john New doe'
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'johnNew@example.com',
'john doe'
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'john@example.com',
'john New doe'
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => ''],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
null,
'john New doe'
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'john@example.com',
null
@@ -391,14 +394,14 @@ class UsersControllerTest extends \Test\TestCase {
$user->method('getDisplayName')->willReturn($oldDisplayName);
$user->method('getEMailAddress')->willReturn($oldEmailAddress);
- if ($data[AccountManager::PROPERTY_EMAIL]['value'] !== $oldEmailAddress) {
+ if ($data[IAccountManager::PROPERTY_EMAIL]['value'] !== $oldEmailAddress) {
$user->method('canChangeDisplayName')
->willReturn($canChangeEmail);
}
- if ($data[AccountManager::PROPERTY_DISPLAYNAME]['value'] !== $oldDisplayName) {
+ if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] !== $oldDisplayName) {
$user->method('setDisplayName')
- ->with($data[AccountManager::PROPERTY_DISPLAYNAME]['value'])
+ ->with($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
->willReturn($setDisplayNameResult);
}
@@ -410,8 +413,8 @@ class UsersControllerTest extends \Test\TestCase {
return [
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'johnNew@example.com',
'john New doe',
@@ -420,8 +423,8 @@ class UsersControllerTest extends \Test\TestCase {
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'johnNew@example.com',
'john New doe',
@@ -430,8 +433,8 @@ class UsersControllerTest extends \Test\TestCase {
],
[
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'],
],
'johnNew@example.com',
'john New doe',
@@ -455,7 +458,7 @@ class UsersControllerTest extends \Test\TestCase {
$signature = 'theSignature';
$code = $message . ' ' . $signature;
- if ($type === AccountManager::PROPERTY_TWITTER) {
+ if ($type === IAccountManager::PROPERTY_TWITTER) {
$code = $message . ' ' . md5($signature);
}
@@ -470,7 +473,7 @@ class UsersControllerTest extends \Test\TestCase {
$controller->expects($this->any())->method('getCurrentTime')->willReturn(1234567);
if ($onlyVerificationCode === false) {
- $this->accountManager->expects($this->once())->method('updateUser')->with($user, $expectedData);
+ $this->accountManager->expects($this->once())->method('updateUser')->with($user, $expectedData)->willReturnArgument(1);
$this->jobList->expects($this->once())->method('add')
->with('OCA\Settings\BackgroundJobs\VerifyUserData',
[
@@ -492,25 +495,25 @@ class UsersControllerTest extends \Test\TestCase {
public function dataTestGetVerificationCode() {
$accountDataBefore = [
- AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
- AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
];
$accountDataAfterWebsite = [
- AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
- AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'],
];
$accountDataAfterTwitter = [
- AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
- AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
+ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED],
+ IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'],
];
return [
- ['verify-twitter', AccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, false],
- ['verify-website', AccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, false],
- ['verify-twitter', AccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, true],
- ['verify-website', AccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, true],
+ ['verify-twitter', IAccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, false],
+ ['verify-website', IAccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, false],
+ ['verify-twitter', IAccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, true],
+ ['verify-website', IAccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, true],
];
}
diff --git a/apps/user_ldap/tests/AccessTest.php b/apps/user_ldap/tests/AccessTest.php
index b9055d32824..cc62a2a19ce 100644
--- a/apps/user_ldap/tests/AccessTest.php
+++ b/apps/user_ldap/tests/AccessTest.php
@@ -664,6 +664,9 @@ class AccessTest extends TestCase {
* @param $expected
*/
public function testSanitizeUsername($name, $expected) {
+ if ($name === 'fränk' && PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Special chars do boom still on CI in php8');
+ }
if ($expected === null) {
$this->expectException(\InvalidArgumentException::class);
}
diff --git a/apps/workflowengine/l10n/is.js b/apps/workflowengine/l10n/is.js
index d8d95ed777d..71b45ed64e3 100644
--- a/apps/workflowengine/l10n/is.js
+++ b/apps/workflowengine/l10n/is.js
@@ -20,6 +20,7 @@ OC.L10N.register(
"Check %s is invalid" : "Athugunin %s er ógild",
"Check #%s does not exist" : "Athugunin #%s er ekki til",
"Check %s is invalid or does not exist" : "Athugunin %s er ógild eða er ekki til",
+ "Flow" : "Flæði",
"Folder" : "Mappa",
"Images" : "Myndir",
"No results" : "Engar niðurstöður",
@@ -31,11 +32,16 @@ OC.L10N.register(
"iOS client" : "iOS-biðlari",
"Desktop client" : "Skjáborðsforrit",
"Thunderbird & Outlook addons" : "Thunderbird & Outlook viðbætur",
+ "Add new flow" : "Bæta við nýju flæði",
"Cancel" : "Hætta við",
"Delete" : "Eyða",
"Active" : "Virkt",
"Save" : "Vista",
+ "Available flows" : "Tiltæk flæði",
+ "More flows" : "Fleiri flæði",
"Browse the app store" : "Flakka um forritasafnið",
+ "Configured flows" : "Uppsett flæði",
+ "Your flows" : "Flæðin þín",
"matches" : "samsvarar",
"does not match" : "samsvarar ekki",
"is" : "er",
diff --git a/apps/workflowengine/l10n/is.json b/apps/workflowengine/l10n/is.json
index 0b6cd3c2a68..a122b47bf5f 100644
--- a/apps/workflowengine/l10n/is.json
+++ b/apps/workflowengine/l10n/is.json
@@ -18,6 +18,7 @@
"Check %s is invalid" : "Athugunin %s er ógild",
"Check #%s does not exist" : "Athugunin #%s er ekki til",
"Check %s is invalid or does not exist" : "Athugunin %s er ógild eða er ekki til",
+ "Flow" : "Flæði",
"Folder" : "Mappa",
"Images" : "Myndir",
"No results" : "Engar niðurstöður",
@@ -29,11 +30,16 @@
"iOS client" : "iOS-biðlari",
"Desktop client" : "Skjáborðsforrit",
"Thunderbird & Outlook addons" : "Thunderbird & Outlook viðbætur",
+ "Add new flow" : "Bæta við nýju flæði",
"Cancel" : "Hætta við",
"Delete" : "Eyða",
"Active" : "Virkt",
"Save" : "Vista",
+ "Available flows" : "Tiltæk flæði",
+ "More flows" : "Fleiri flæði",
"Browse the app store" : "Flakka um forritasafnið",
+ "Configured flows" : "Uppsett flæði",
+ "Your flows" : "Flæðin þín",
"matches" : "samsvarar",
"does not match" : "samsvarar ekki",
"is" : "er",
diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php
index 56d75c058aa..a856e47ef2b 100644
--- a/build/integration/features/bootstrap/Provisioning.php
+++ b/build/integration/features/bootstrap/Provisioning.php
@@ -173,6 +173,37 @@ trait Provisioning {
}
}
+ /**
+ * @Then /^search users by phone for region "([^"]*)" with$/
+ *
+ * @param string $user
+ * @param \Behat\Gherkin\Node\TableNode|null $settings
+ */
+ public function searchUserByPhone($region, \Behat\Gherkin\Node\TableNode $searchTable) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/search/by-phone";
+ $client = new Client();
+ $options = [];
+ $options['auth'] = $this->adminUser;
+ $options['headers'] = [
+ 'OCS-APIREQUEST' => 'true',
+ ];
+
+ $search = [];
+ foreach ($searchTable->getRows() as $row) {
+ if (!isset($search[$row[0]])) {
+ $search[$row[0]] = [];
+ }
+ $search[$row[0]][] = $row[1];
+ }
+
+ $options['form_params'] = [
+ 'location' => $region,
+ 'search' => $search,
+ ];
+
+ $this->response = $client->post($fullUrl, $options);
+ }
+
public function createUser($user) {
$previous_user = $this->currentUser;
$this->currentUser = "admin";
@@ -561,6 +592,19 @@ trait Provisioning {
}
/**
+ * @Then /^phone matches returned are$/
+ * @param \Behat\Gherkin\Node\TableNode|null $usersList
+ */
+ public function thePhoneUsersShouldBe($usersList) {
+ if ($usersList instanceof \Behat\Gherkin\Node\TableNode) {
+ $users = $usersList->getRowsHash();
+ $listCheckedElements = simplexml_load_string($this->response->getBody())->data;
+ $respondedArray = json_decode(json_encode($listCheckedElements), true);
+ Assert::assertEquals($users, $respondedArray);
+ }
+ }
+
+ /**
* @Then /^detailed users returned are$/
* @param \Behat\Gherkin\Node\TableNode|null $usersList
*/
diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature
index ecc33c657f4..717aa04e4bd 100644
--- a/build/integration/features/provisioning-v1.feature
+++ b/build/integration/features/provisioning-v1.feature
@@ -71,12 +71,12 @@ Feature: provisioning
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | email |
- | value | brand-new-user@gmail.com |
+ | value | no-reply@nextcloud.com |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
| key | phone |
- | value | 0123 456 789 |
+ | value | +49 711 / 25 24 28-90 |
And the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "PUT" to "/cloud/users/brand-new-user" with
@@ -97,12 +97,29 @@ Feature: provisioning
Then user "brand-new-user" has
| id | brand-new-user |
| displayname | Brand New User |
- | email | brand-new-user@gmail.com |
- | phone | 0123 456 789 |
+ | email | no-reply@nextcloud.com |
+ | phone | +4971125242890 |
| address | Foo Bar Town |
| website | https://nextcloud.com |
| twitter | Nextcloud |
+ Scenario: Search by phone number
+ Given As an "admin"
+ And user "phone-user" exists
+ And sending "PUT" to "/cloud/users/phone-user" with
+ | key | phone |
+ | value | +49 711 / 25 24 28-90 |
+ And the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ Then search users by phone for region "DE" with
+ | random-string1 | 0711 / 123 456 78 |
+ | random-string1 | 0711 / 252 428-90 |
+ | random-string2 | 0711 / 90-824 252 |
+ And the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ Then phone matches returned are
+ | random-string1 | phone-user@localhost:8080 |
+
Scenario: Create a group
Given As an "admin"
And group "new-group" does not exist
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 379ea1a1c97..9539709e420 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -722,7 +722,6 @@
<code>null</code>
<code>null</code>
<code>null</code>
- <code>null</code>
</NullableReturnStatement>
<UndefinedClass occurrences="2">
<code>\OCA\Circles\Api\v1\Circles</code>
@@ -1034,6 +1033,10 @@
<RedundantCondition occurrences="1">
<code>$userSession</code>
</RedundantCondition>
+ <TypeDoesNotContainType occurrences="2">
+ <code>get_class($res) === 'OpenSSLAsymmetricKey'</code>
+ <code>is_object($res)</code>
+ </TypeDoesNotContainType>
</file>
<file src="apps/encryption/lib/Crypto/EncryptAll.php">
<InvalidArgument occurrences="1">
@@ -2816,7 +2819,7 @@
<file src="core/routes.php">
<InvalidScope occurrences="2">
<code>$this</code>
- <code>$this</code>
+ <code>$this-&gt;create('core_ajax_update', '/core/ajax/update.php')</code>
</InvalidScope>
</file>
<file src="core/templates/layout.public.php">
@@ -5463,6 +5466,9 @@
<code>$data[floor($p)]</code>
<code>$data[floor($p)]</code>
</InvalidArrayOffset>
+ <InvalidPropertyAssignmentValue occurrences="1">
+ <code>$resource</code>
+ </InvalidPropertyAssignmentValue>
<InvalidReturnType occurrences="1">
<code>bool</code>
</InvalidReturnType>
@@ -5491,6 +5497,10 @@
<RedundantCondition occurrences="1">
<code>$isWritable</code>
</RedundantCondition>
+ <TypeDoesNotContainType occurrences="2">
+ <code>get_class($resource) === 'GdImage'</code>
+ <code>get_class($this-&gt;resource) === 'GdImage'</code>
+ </TypeDoesNotContainType>
</file>
<file src="lib/private/legacy/OC_JSON.php">
<InvalidScalarArgument occurrences="1">
diff --git a/config/config.sample.php b/config/config.sample.php
index 2710fbf5fdb..8adb5bf2168 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -195,6 +195,16 @@ $CONFIG = [
'default_locale' => 'en_US',
/**
+ * This sets the default region for phone numbers on your Nextcloud server,
+ * using ISO 3166-1 country codes such as ``DE`` for Germany, ``FR`` for France, …
+ * It is required to allow inserting phone numbers in the user profiles starting
+ * without the country code (e.g. +49 for Germany).
+ *
+ * No default value!
+ */
+'default_phone_region' => 'EN',
+
+/**
* With this setting a locale can be forced for all users. If a locale is
* forced, the users are also unable to change their locale in the personal
* settings. If users shall be unable to change their locale, but users have
diff --git a/core/Migrations/Version21000Date20201202095923.php b/core/Migrations/Version21000Date20201202095923.php
new file mode 100644
index 00000000000..6433d8c9b7a
--- /dev/null
+++ b/core/Migrations/Version21000Date20201202095923.php
@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.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 OC\Core\Migrations;
+
+use Closure;
+use Doctrine\DBAL\Types\Types;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version21000Date20201202095923 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('accounts_data')) {
+ $table = $schema->createTable('accounts_data');
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 20,
+ ]);
+ $table->addColumn('uid', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('name', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('value', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 255,
+ 'default' => '',
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['uid'], 'accounts_data_uid');
+ $table->addIndex(['name'], 'accounts_data_name');
+ $table->addIndex(['value'], 'accounts_data_value');
+
+ return $schema;
+ }
+
+ return null;
+ }
+}
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index 214f148fa94..22c8589f73b 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -216,6 +216,12 @@
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
+ if (!data.isDefaultPhoneRegionSet) {
+ messages.push({
+ msg: t('core', 'Your installation has no default phone region set. This is required to be able to validate phone numbers in the profile settings without a country code. To allow numbers without a country code, please add "default_phone_region" with the respective ISO 3166-1 code of the wished region.'),
+ type: OC.SetupChecks.MESSAGE_TYPE_INFO
+ });
+ }
if (data.cronErrors.length > 0) {
var listOfCronErrors = "";
data.cronErrors.forEach(function(element){
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index a0a3c2a4ba9..c3cddb88a9d 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -251,6 +251,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -306,6 +307,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -362,6 +364,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -416,6 +419,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -468,6 +472,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -522,6 +527,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -574,6 +580,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -626,6 +633,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -678,6 +686,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -751,6 +760,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -804,6 +814,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -857,6 +868,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -910,6 +922,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -962,6 +975,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: true,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyGeneratedURL: 'https://server',
})
@@ -1014,6 +1028,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
reverseProxyDocs: 'https://docs.nextcloud.com/foo/bar.html',
reverseProxyGeneratedURL: 'http://server',
@@ -1067,6 +1082,7 @@ describe('OC.SetupChecks tests', function() {
recommendedPHPModules: [],
pendingBigIntConversionColumns: [],
isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: true,
isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: false,
reverseProxyGeneratedURL: 'https://server',
})
@@ -1080,6 +1096,59 @@ describe('OC.SetupChecks tests', function() {
done();
});
});
+
+ it('should return an info if there is no default phone region', function(done) {
+ var async = OC.SetupChecks.checkSetup();
+
+ suite.server.requests[0].respond(
+ 200,
+ {
+ 'Content-Type': 'application/json',
+ },
+ JSON.stringify({
+ hasFileinfoInstalled: true,
+ isGetenvServerWorking: true,
+ isReadOnlyConfig: false,
+ hasWorkingFileLocking: true,
+ hasValidTransactionIsolationLevel: true,
+ suggestedOverwriteCliURL: '',
+ isRandomnessSecure: true,
+ securityDocs: 'https://docs.owncloud.org/myDocs.html',
+ serverHasInternetConnectionProblems: false,
+ isMemcacheConfigured: true,
+ forwardedForHeadersWorking: true,
+ isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
+ isOpcacheProperlySetup: true,
+ hasOpcacheLoaded: true,
+ isSettimelimitAvailable: true,
+ hasFreeTypeSupport: true,
+ missingIndexes: [],
+ missingPrimaryKeys: [],
+ missingColumns: [],
+ cronErrors: [],
+ cronInfo: {
+ diffInSeconds: 0
+ },
+ isMemoryLimitSufficient: true,
+ appDirsWithDifferentOwner: [],
+ recommendedPHPModules: [],
+ pendingBigIntConversionColumns: [],
+ isMysqlUsedWithoutUTF8MB4: false,
+ isDefaultPhoneRegionSet: false,
+ isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed: true,
+ reverseProxyGeneratedURL: 'https://server',
+ })
+ );
+
+ async.done(function( data, s, x ){
+ expect(data).toEqual([{
+ msg: 'Your installation has no default phone region set. This is required to be able to validate phone numbers in the profile settings without a country code. To allow numbers without a country code, please add "default_phone_region" with the respective ISO 3166-1 code of the wished region.',
+ type: OC.SetupChecks.MESSAGE_TYPE_INFO
+ }]);
+ done();
+ });
+ });
});
describe('checkGeneric', function() {
diff --git a/core/l10n/is.js b/core/l10n/is.js
index cda27d77b04..8acf90defa5 100644
--- a/core/l10n/is.js
+++ b/core/l10n/is.js
@@ -303,7 +303,8 @@ OC.L10N.register(
"Settings menu" : "Stillingavalmynd",
"Confirm your password" : "Staðfestu lykilorðið þitt",
"Connect to your account" : "Tengdu við notandaaðganginn þinn",
- "Please log in before granting %1$s access to your %2$s account." : "Skráði þig inn áður en þú leyfir %1$s aðgang að %2$s notandaaðgangnum þínum.",
+ "Please log in before granting %1$s access to your %2$s account." : "Skráðu þig inn áður en þú leyfir %1$s aðgang að %2$s notandaaðgangnum þínum.",
+ "If you are not trying to set up a new device or app, someone is trying to trick you into granting them access to your data. In this case do not proceed and instead contact your system administrator." : "Ef þú ert ekki að reyna að setja upp nýtt tæki eða forrit, þá er einhver annar að reyna að gabba þig til að gefa þeim aðgang að gögnunum þínum. Ef svo er, skaltu ekki halda áfram í þessu ferli og hafa strax samband við kerfisstjórann þinn.",
"App token" : "Teikn forrits",
"Grant access" : "Veita aðgengi",
"Alternative log in using app token" : "Önnur innskráning með forritsteikni",
diff --git a/core/l10n/is.json b/core/l10n/is.json
index fc5c491adda..952ab2c03a4 100644
--- a/core/l10n/is.json
+++ b/core/l10n/is.json
@@ -301,7 +301,8 @@
"Settings menu" : "Stillingavalmynd",
"Confirm your password" : "Staðfestu lykilorðið þitt",
"Connect to your account" : "Tengdu við notandaaðganginn þinn",
- "Please log in before granting %1$s access to your %2$s account." : "Skráði þig inn áður en þú leyfir %1$s aðgang að %2$s notandaaðgangnum þínum.",
+ "Please log in before granting %1$s access to your %2$s account." : "Skráðu þig inn áður en þú leyfir %1$s aðgang að %2$s notandaaðgangnum þínum.",
+ "If you are not trying to set up a new device or app, someone is trying to trick you into granting them access to your data. In this case do not proceed and instead contact your system administrator." : "Ef þú ert ekki að reyna að setja upp nýtt tæki eða forrit, þá er einhver annar að reyna að gabba þig til að gefa þeim aðgang að gögnunum þínum. Ef svo er, skaltu ekki halda áfram í þessu ferli og hafa strax samband við kerfisstjórann þinn.",
"App token" : "Teikn forrits",
"Grant access" : "Veita aðgengi",
"Alternative log in using app token" : "Önnur innskráning með forritsteikni",
diff --git a/core/l10n/ru.js b/core/l10n/ru.js
index 9cc3663c83b..ecc3f1fff29 100644
--- a/core/l10n/ru.js
+++ b/core/l10n/ru.js
@@ -29,6 +29,8 @@ OC.L10N.register(
"Nextcloud Server" : "Сервер Nextcloud",
"Some of your link shares have been removed" : "Некоторые из ваших ссылок на общие ресурсы были удалены",
"Due to a security bug we had to remove some of your link shares. Please see the link for more information." : "Из-за ошибки в безопасности нам пришлось удалить некоторые из ваших ссылок на опубликованные файлы или папки. Перейдите по ссылке для получения дополнительной информации.",
+ "The user limit of this instance is reached." : "Достигнут лимит пользователей этого экземпляра",
+ "Enter your subscription key to increase the user limit. For more information about Nextcloud Enterprise see our website." : "Введите ключ подписки, чтобы увеличить лимит пользователей. Для получения дополнительной информации о Nextcloud Enterprise посетите наш веб-сайт.",
"Preparing update" : "Подготовка к обновлению",
"[%d / %d]: %s" : "[%d / %d]: %s",
"Repair step:" : "Шаг восстановления:",
diff --git a/core/l10n/ru.json b/core/l10n/ru.json
index b44a9d92f39..eaae3a1e1fe 100644
--- a/core/l10n/ru.json
+++ b/core/l10n/ru.json
@@ -27,6 +27,8 @@
"Nextcloud Server" : "Сервер Nextcloud",
"Some of your link shares have been removed" : "Некоторые из ваших ссылок на общие ресурсы были удалены",
"Due to a security bug we had to remove some of your link shares. Please see the link for more information." : "Из-за ошибки в безопасности нам пришлось удалить некоторые из ваших ссылок на опубликованные файлы или папки. Перейдите по ссылке для получения дополнительной информации.",
+ "The user limit of this instance is reached." : "Достигнут лимит пользователей этого экземпляра",
+ "Enter your subscription key to increase the user limit. For more information about Nextcloud Enterprise see our website." : "Введите ключ подписки, чтобы увеличить лимит пользователей. Для получения дополнительной информации о Nextcloud Enterprise посетите наш веб-сайт.",
"Preparing update" : "Подготовка к обновлению",
"[%d / %d]: %s" : "[%d / %d]: %s",
"Repair step:" : "Шаг восстановления:",
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 0400e681090..b7dbc6675d2 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -930,6 +930,7 @@ return array(
'OC\\Core\\Migrations\\Version20000Date20201109081918' => $baseDir . '/core/Migrations/Version20000Date20201109081918.php',
'OC\\Core\\Migrations\\Version20000Date20201109081919' => $baseDir . '/core/Migrations/Version20000Date20201109081919.php',
'OC\\Core\\Migrations\\Version20000Date20201111081915' => $baseDir . '/core/Migrations/Version20000Date20201111081915.php',
+ 'OC\\Core\\Migrations\\Version21000Date20201202095923' => $baseDir . '/core/Migrations/Version21000Date20201202095923.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
@@ -1266,6 +1267,7 @@ return array(
'OC\\Repair\\NC20\\EncryptionMigration' => $baseDir . '/lib/private/Repair/NC20/EncryptionMigration.php',
'OC\\Repair\\NC20\\ShippedDashboardEnable' => $baseDir . '/lib/private/Repair/NC20/ShippedDashboardEnable.php',
'OC\\Repair\\NC21\\AddCheckForUserCertificatesJob' => $baseDir . '/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php',
+ 'OC\\Repair\\NC21\\ValidatePhoneNumber' => $baseDir . '/lib/private/Repair/NC21/ValidatePhoneNumber.php',
'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php',
'OC\\Repair\\Owncloud\\DropAccountTermsTable' => $baseDir . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index b9b4f2f307b..a8984b486f3 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -959,6 +959,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Core\\Migrations\\Version20000Date20201109081918' => __DIR__ . '/../../..' . '/core/Migrations/Version20000Date20201109081918.php',
'OC\\Core\\Migrations\\Version20000Date20201109081919' => __DIR__ . '/../../..' . '/core/Migrations/Version20000Date20201109081919.php',
'OC\\Core\\Migrations\\Version20000Date20201111081915' => __DIR__ . '/../../..' . '/core/Migrations/Version20000Date20201111081915.php',
+ 'OC\\Core\\Migrations\\Version21000Date20201202095923' => __DIR__ . '/../../..' . '/core/Migrations/Version21000Date20201202095923.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
@@ -1295,6 +1296,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Repair\\NC20\\EncryptionMigration' => __DIR__ . '/../../..' . '/lib/private/Repair/NC20/EncryptionMigration.php',
'OC\\Repair\\NC20\\ShippedDashboardEnable' => __DIR__ . '/../../..' . '/lib/private/Repair/NC20/ShippedDashboardEnable.php',
'OC\\Repair\\NC21\\AddCheckForUserCertificatesJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php',
+ 'OC\\Repair\\NC21\\ValidatePhoneNumber' => __DIR__ . '/../../..' . '/lib/private/Repair/NC21/ValidatePhoneNumber.php',
'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php',
'OC\\Repair\\Owncloud\\DropAccountTermsTable' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
diff --git a/lib/l10n/ca.js b/lib/l10n/ca.js
index ee2604db81b..d13d58a9b86 100644
--- a/lib/l10n/ca.js
+++ b/lib/l10n/ca.js
@@ -167,6 +167,7 @@ OC.L10N.register(
"Oct." : "Oct.",
"Nov." : "Nov.",
"Dec." : "Des.",
+ "The user limit has been reached and the user was not created." : "S'ha assolit el límit d'usuari i no s'ha creat l'usuari.",
"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Només es permeten els següents caràcters en un nom d’usuari: \"a-z\", \"A-Z\", \"0-9\" i \"_.@-'\"",
"A valid username must be provided" : "Heu de facilitar un nom d'usuari vàlid",
"Username contains whitespace at the beginning or at the end" : "El nom d’usuari conté espais en blanc al principi o al final",
diff --git a/lib/l10n/ca.json b/lib/l10n/ca.json
index ecab8e2f8d9..2ca38072ef3 100644
--- a/lib/l10n/ca.json
+++ b/lib/l10n/ca.json
@@ -165,6 +165,7 @@
"Oct." : "Oct.",
"Nov." : "Nov.",
"Dec." : "Des.",
+ "The user limit has been reached and the user was not created." : "S'ha assolit el límit d'usuari i no s'ha creat l'usuari.",
"Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Només es permeten els següents caràcters en un nom d’usuari: \"a-z\", \"A-Z\", \"0-9\" i \"_.@-'\"",
"A valid username must be provided" : "Heu de facilitar un nom d'usuari vàlid",
"Username contains whitespace at the beginning or at the end" : "El nom d’usuari conté espais en blanc al principi o al final",
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index d18555d296c..05feaf87b8f 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -30,10 +30,16 @@
namespace OC\Accounts;
+use libphonenumber\NumberParseException;
+use libphonenumber\PhoneNumber;
+use libphonenumber\PhoneNumberFormat;
+use libphonenumber\PhoneNumberUtil;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
use OCP\BackgroundJob\IJobList;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUser;
use Psr\Log\LoggerInterface;
@@ -55,9 +61,15 @@ class AccountManager implements IAccountManager {
/** @var IDBConnection database connection */
private $connection;
+ /** @var IConfig */
+ private $config;
+
/** @var string table name */
private $table = 'accounts';
+ /** @var string table name */
+ private $dataTable = 'accounts_data';
+
/** @var EventDispatcherInterface */
private $eventDispatcher;
@@ -68,24 +80,70 @@ class AccountManager implements IAccountManager {
private $logger;
public function __construct(IDBConnection $connection,
+ IConfig $config,
EventDispatcherInterface $eventDispatcher,
IJobList $jobList,
LoggerInterface $logger) {
$this->connection = $connection;
+ $this->config = $config;
$this->eventDispatcher = $eventDispatcher;
$this->jobList = $jobList;
$this->logger = $logger;
}
/**
+ * @param string $input
+ * @return string Provided phone number in E.164 format when it was a valid number
+ * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
+ */
+ protected function parsePhoneNumber(string $input): string {
+ $defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
+
+ if ($defaultRegion === '') {
+ // When no default region is set, only +49… numbers are valid
+ if (strpos($input, '+') !== 0) {
+ throw new \InvalidArgumentException(self::PROPERTY_PHONE);
+ }
+
+ $defaultRegion = 'EN';
+ }
+
+ $phoneUtil = PhoneNumberUtil::getInstance();
+ try {
+ $phoneNumber = $phoneUtil->parse($input, $defaultRegion);
+ if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
+ return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
+ }
+ } catch (NumberParseException $e) {
+ }
+
+ throw new \InvalidArgumentException(self::PROPERTY_PHONE);
+ }
+
+ /**
* update user record
*
* @param IUser $user
- * @param $data
+ * @param array $data
+ * @param bool $throwOnData Set to true if you can inform the user about invalid data
+ * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format)
+ * @throws \InvalidArgumentException Message is the property that was invalid
*/
- public function updateUser(IUser $user, $data) {
+ public function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
$userData = $this->getUser($user);
$updated = true;
+
+ if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') {
+ try {
+ $data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']);
+ } catch (\InvalidArgumentException $e) {
+ if ($throwOnData) {
+ throw $e;
+ }
+ $data[self::PROPERTY_PHONE]['value'] = '';
+ }
+ }
+
if (empty($userData)) {
$this->insertNewUser($user, $data);
} elseif ($userData !== $data) {
@@ -103,6 +161,8 @@ class AccountManager implements IAccountManager {
new GenericEvent($user, $data)
);
}
+
+ return $data;
}
/**
@@ -116,6 +176,21 @@ class AccountManager implements IAccountManager {
$query->delete($this->table)
->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
->execute();
+
+ $this->deleteUserData($user);
+ }
+
+ /**
+ * delete user from accounts table
+ *
+ * @param IUser $user
+ */
+ public function deleteUserData(IUser $user): void {
+ $uid = $user->getUID();
+ $query = $this->connection->getQueryBuilder();
+ $query->delete($this->dataTable)
+ ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
+ ->execute();
}
/**
@@ -153,6 +228,24 @@ class AccountManager implements IAccountManager {
return $userDataArray;
}
+ public function searchUsers(string $property, array $values): array {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('*')
+ ->from($this->dataTable)
+ ->where($query->expr()->eq('name', $query->createNamedParameter($property)))
+ ->andWhere($query->expr()->in('value', $query->createNamedParameter($values, IQueryBuilder::PARAM_STR_ARRAY)));
+
+ $result = $query->execute();
+ $matches = [];
+
+ while ($row = $result->fetch()) {
+ $matches[$row['value']] = $row['uid'];
+ }
+ $result->closeCursor();
+
+ return $matches;
+ }
+
/**
* check if we need to ask the server for email verification, if yes we create a cronjob
*
@@ -173,7 +266,7 @@ class AccountManager implements IAccountManager {
'lastRun' => time()
]
);
- $newData[AccountManager::PROPERTY_EMAIL]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
+ $newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS;
}
return $newData;
@@ -256,7 +349,7 @@ class AccountManager implements IAccountManager {
* @param IUser $user
* @param array $data
*/
- protected function insertNewUser(IUser $user, $data) {
+ protected function insertNewUser(IUser $user, array $data): void {
$uid = $user->getUID();
$jsonEncodedData = json_encode($data);
$query = $this->connection->getQueryBuilder();
@@ -268,6 +361,9 @@ class AccountManager implements IAccountManager {
]
)
->execute();
+
+ $this->deleteUserData($user);
+ $this->writeUserData($user, $data);
}
/**
@@ -276,7 +372,7 @@ class AccountManager implements IAccountManager {
* @param IUser $user
* @param array $data
*/
- protected function updateExistingUser(IUser $user, $data) {
+ protected function updateExistingUser(IUser $user, array $data): void {
$uid = $user->getUID();
$jsonEncodedData = json_encode($data);
$query = $this->connection->getQueryBuilder();
@@ -284,6 +380,30 @@ class AccountManager implements IAccountManager {
->set('data', $query->createNamedParameter($jsonEncodedData))
->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
->execute();
+
+ $this->deleteUserData($user);
+ $this->writeUserData($user, $data);
+ }
+
+ protected function writeUserData(IUser $user, array $data): void {
+ $query = $this->connection->getQueryBuilder();
+ $query->insert($this->dataTable)
+ ->values(
+ [
+ 'uid' => $query->createNamedParameter($user->getUID()),
+ 'name' => $query->createParameter('name'),
+ 'value' => $query->createParameter('value'),
+ ]
+ );
+ foreach ($data as $propertyName => $property) {
+ if ($propertyName === self::PROPERTY_AVATAR) {
+ continue;
+ }
+
+ $query->setParameter('name', $propertyName)
+ ->setParameter('value', $property['value']);
+ $query->execute();
+ }
}
/**
diff --git a/lib/private/Accounts/Hooks.php b/lib/private/Accounts/Hooks.php
index 82f55f5a9d1..c5e7c34deaf 100644
--- a/lib/private/Accounts/Hooks.php
+++ b/lib/private/Accounts/Hooks.php
@@ -24,6 +24,7 @@
namespace OC\Accounts;
+use OCP\Accounts\IAccountManager;
use OCP\IUser;
use Psr\Log\LoggerInterface;
@@ -61,14 +62,14 @@ class Hooks {
switch ($feature) {
case 'eMailAddress':
- if ($accountData[AccountManager::PROPERTY_EMAIL]['value'] !== $newValue) {
- $accountData[AccountManager::PROPERTY_EMAIL]['value'] = $newValue;
+ if ($accountData[IAccountManager::PROPERTY_EMAIL]['value'] !== $newValue) {
+ $accountData[IAccountManager::PROPERTY_EMAIL]['value'] = $newValue;
$accountManager->updateUser($user, $accountData);
}
break;
case 'displayName':
- if ($accountData[AccountManager::PROPERTY_DISPLAYNAME]['value'] !== $newValue) {
- $accountData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $newValue;
+ if ($accountData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] !== $newValue) {
+ $accountData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $newValue;
$accountManager->updateUser($user, $accountData);
}
break;
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index 9e36ad0cd57..a671848245e 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -348,10 +348,10 @@ class AppConfig implements IAppConfig {
$rows = $result->fetchAll();
foreach ($rows as $row) {
if (!isset($this->cache[$row['appid']])) {
- $this->cache[$row['appid']] = [];
+ $this->cache[(string)$row['appid']] = [];
}
- $this->cache[$row['appid']][$row['configkey']] = $row['configvalue'];
+ $this->cache[(string)$row['appid']][(string)$row['configkey']] = (string)$row['configvalue'];
}
$result->closeCursor();
diff --git a/lib/private/Lock/MemcacheLockingProvider.php b/lib/private/Lock/MemcacheLockingProvider.php
index 6b01f0aafc6..439894e901f 100644
--- a/lib/private/Lock/MemcacheLockingProvider.php
+++ b/lib/private/Lock/MemcacheLockingProvider.php
@@ -61,7 +61,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider {
public function isLocked(string $path, int $type): bool {
$lockValue = $this->memcache->get($path);
if ($type === self::LOCK_SHARED) {
- return $lockValue > 0;
+ return is_int($lockValue) && $lockValue > 0;
} elseif ($type === self::LOCK_EXCLUSIVE) {
return $lockValue === 'exclusive';
} else {
diff --git a/lib/private/Repair.php b/lib/private/Repair.php
index ec748355567..847a41aeb25 100644
--- a/lib/private/Repair.php
+++ b/lib/private/Repair.php
@@ -53,6 +53,7 @@ use OC\Repair\NC20\EncryptionLegacyCipher;
use OC\Repair\NC20\EncryptionMigration;
use OC\Repair\NC20\ShippedDashboardEnable;
use OC\Repair\NC21\AddCheckForUserCertificatesJob;
+use OC\Repair\NC21\ValidatePhoneNumber;
use OC\Repair\OldGroupMembershipShares;
use OC\Repair\Owncloud\DropAccountTermsTable;
use OC\Repair\Owncloud\SaveAccountsTableData;
@@ -177,7 +178,8 @@ class Repair implements IOutput {
*/
public static function getExpensiveRepairSteps() {
return [
- new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager())
+ new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()),
+ \OC::$server->get(ValidatePhoneNumber::class),
];
}
diff --git a/lib/private/Repair/NC21/ValidatePhoneNumber.php b/lib/private/Repair/NC21/ValidatePhoneNumber.php
new file mode 100644
index 00000000000..6e25ff26b7e
--- /dev/null
+++ b/lib/private/Repair/NC21/ValidatePhoneNumber.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.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 OC\Repair\NC21;
+
+use OC\Accounts\AccountManager;
+use OCP\Accounts\IAccountManager;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class ValidatePhoneNumber implements IRepairStep {
+
+ /** @var IConfig */
+ protected $config;
+ /** @var IUserManager */
+ protected $userManager;
+ /** @var AccountManager */
+ private $accountManager;
+
+ public function __construct(IUserManager $userManager,
+ AccountManager $accountManager,
+ IConfig $config) {
+ $this->config = $config;
+ $this->userManager = $userManager;
+ $this->accountManager = $accountManager;
+ }
+
+ public function getName(): string {
+ return 'Validate the phone number and store it in a known format for search';
+ }
+
+ private function shouldRun(): bool {
+ return true;
+ }
+
+ public function run(IOutput $output): void {
+ if ($this->config->getSystemValueString('default_phone_region', '') === '') {
+ throw new \Exception('Can not validate phone numbers without `default_phone_region` being set in the config file');
+ }
+
+ $numUpdated = 0;
+ $numRemoved = 0;
+
+ $this->userManager->callForSeenUsers(function (IUser $user) use (&$numUpdated, &$numRemoved) {
+ $account = $this->accountManager->getUser($user);
+
+ if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== '') {
+ $updated = $this->accountManager->updateUser($user, $account);
+
+ if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== $updated[IAccountManager::PROPERTY_PHONE]['value']) {
+ if ($updated[IAccountManager::PROPERTY_PHONE]['value'] === '') {
+ $numRemoved++;
+ } else {
+ $numUpdated++;
+ }
+ }
+ }
+ });
+
+ if ($numRemoved > 0 || $numUpdated > 0) {
+ $output->info('Updated ' . $numUpdated . ' entries and cleaned ' . $numRemoved . ' invalid phone numbers');
+ }
+ }
+}
diff --git a/lib/private/legacy/OC_Image.php b/lib/private/legacy/OC_Image.php
index 3e9812c99f2..523468701c7 100644
--- a/lib/private/legacy/OC_Image.php
+++ b/lib/private/legacy/OC_Image.php
@@ -98,7 +98,14 @@ class OC_Image implements \OCP\IImage {
* @return bool
*/
public function valid() { // apparently you can't name a method 'empty'...
- return is_resource($this->resource);
+ if (is_resource($this->resource)) {
+ return true;
+ }
+ if (is_object($this->resource) && get_class($this->resource) === 'GdImage') {
+ return true;
+ }
+
+ return false;
}
/**
@@ -305,7 +312,13 @@ class OC_Image implements \OCP\IImage {
* @throws \InvalidArgumentException in case the supplied resource does not have the type "gd"
*/
public function setResource($resource) {
- if (get_resource_type($resource) === 'gd') {
+ // For PHP<8
+ if (is_resource($resource) && get_resource_type($resource) === 'gd') {
+ $this->resource = $resource;
+ return;
+ }
+ // PHP 8 has real objects for GD stuff
+ if (is_object($resource) && get_class($resource) === 'GdImage') {
$this->resource = $resource;
return;
}
diff --git a/lib/public/Accounts/IAccountManager.php b/lib/public/Accounts/IAccountManager.php
index 3306abc55f1..63824ed94b9 100644
--- a/lib/public/Accounts/IAccountManager.php
+++ b/lib/public/Accounts/IAccountManager.php
@@ -65,4 +65,15 @@ interface IAccountManager {
* @return IAccount
*/
public function getAccount(IUser $user): IAccount;
+
+ /**
+ * Search for users based on account data
+ *
+ * @param string $property
+ * @param string[] $values
+ * @return array
+ *
+ * @since 21.0.0
+ */
+ public function searchUsers(string $property, array $values): array;
}
diff --git a/tests/lib/Accounts/AccountsManagerTest.php b/tests/lib/Accounts/AccountsManagerTest.php
index ff75b51d008..d13d5f4186a 100644
--- a/tests/lib/Accounts/AccountsManagerTest.php
+++ b/tests/lib/Accounts/AccountsManagerTest.php
@@ -25,6 +25,7 @@ use OC\Accounts\Account;
use OC\Accounts\AccountManager;
use OCP\Accounts\IAccountManager;
use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
use OCP\IUser;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
@@ -43,6 +44,9 @@ class AccountsManagerTest extends TestCase {
/** @var \OCP\IDBConnection */
private $connection;
+ /** @var IConfig|MockObject */
+ private $config;
+
/** @var EventDispatcherInterface|MockObject */
private $eventDispatcher;
@@ -59,6 +63,7 @@ class AccountsManagerTest extends TestCase {
parent::setUp();
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
$this->connection = \OC::$server->getDatabaseConnection();
+ $this->config = $this->createMock(IConfig::class);
$this->jobList = $this->createMock(IJobList::class);
$this->logger = $this->createMock(LoggerInterface::class);
}
@@ -77,7 +82,13 @@ class AccountsManagerTest extends TestCase {
*/
public function getInstance($mockedMethods = null) {
return $this->getMockBuilder(AccountManager::class)
- ->setConstructorArgs([$this->connection, $this->eventDispatcher, $this->jobList, $this->logger])
+ ->setConstructorArgs([
+ $this->connection,
+ $this->config,
+ $this->eventDispatcher,
+ $this->jobList,
+ $this->logger,
+ ])
->setMethods($mockedMethods)
->getMock();
}
@@ -187,9 +198,9 @@ class AccountsManagerTest extends TestCase {
public function testUpdateExistingUser() {
$user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->once())->method('getUID')->willReturn('uid');
- $oldData = ['key' => 'value'];
- $newData = ['newKey' => 'newValue'];
+ $user->expects($this->atLeastOnce())->method('getUID')->willReturn('uid');
+ $oldData = ['key' => ['value' => 'value']];
+ $newData = ['newKey' => ['value' => 'newValue']];
$accountManager = $this->getInstance();
$this->addDummyValuesToTable('uid', $oldData);
@@ -201,10 +212,10 @@ class AccountsManagerTest extends TestCase {
public function testInsertNewUser() {
$user = $this->getMockBuilder(IUser::class)->getMock();
$uid = 'uid';
- $data = ['key' => 'value'];
+ $data = ['key' => ['value' => 'value']];
$accountManager = $this->getInstance();
- $user->expects($this->once())->method('getUID')->willReturn($uid);
+ $user->expects($this->atLeastOnce())->method('getUID')->willReturn($uid);
$this->assertNull($this->getDataFromTable($uid));
$this->invokePrivate($accountManager, 'insertNewUser', [$user, $data]);
@@ -293,4 +304,32 @@ class AccountsManagerTest extends TestCase {
->willReturn($data);
$this->assertEquals($expected, $accountManager->getAccount($user));
}
+
+ public function dataParsePhoneNumber(): array {
+ return [
+ ['0711 / 25 24 28-90', 'DE', '+4971125242890'],
+ ['0711 / 25 24 28-90', '', null],
+ ['+49 711 / 25 24 28-90', '', '+4971125242890'],
+ ];
+ }
+
+ /**
+ * @dataProvider dataParsePhoneNumber
+ * @param string $phoneInput
+ * @param string $defaultRegion
+ * @param string|null $phoneNumber
+ */
+ public function testParsePhoneNumber(string $phoneInput, string $defaultRegion, ?string $phoneNumber): void {
+ $this->config->method('getSystemValueString')
+ ->willReturn($defaultRegion);
+
+ $instance = $this->getInstance();
+
+ if ($phoneNumber === null) {
+ $this->expectException(\InvalidArgumentException::class);
+ self::invokePrivate($instance, 'parsePhoneNumber', [$phoneInput]);
+ } else {
+ self::assertEquals($phoneNumber, self::invokePrivate($instance, 'parsePhoneNumber', [$phoneInput]));
+ }
+ }
}
diff --git a/tests/lib/Accounts/HooksTest.php b/tests/lib/Accounts/HooksTest.php
index 39003b2ef23..8af9e209034 100644
--- a/tests/lib/Accounts/HooksTest.php
+++ b/tests/lib/Accounts/HooksTest.php
@@ -23,6 +23,7 @@ namespace Test\Accounts;
use OC\Accounts\AccountManager;
use OC\Accounts\Hooks;
+use OCP\Accounts\IAccountManager;
use OCP\IUser;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
@@ -77,11 +78,11 @@ class HooksTest extends TestCase {
$this->accountManager->expects($this->once())->method('getUser')->willReturn($data);
$newData = $data;
if ($setEmail) {
- $newData[AccountManager::PROPERTY_EMAIL]['value'] = $params['value'];
+ $newData[IAccountManager::PROPERTY_EMAIL]['value'] = $params['value'];
$this->accountManager->expects($this->once())->method('updateUser')
->with($params['user'], $newData);
} elseif ($setDisplayName) {
- $newData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $params['value'];
+ $newData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] = $params['value'];
$this->accountManager->expects($this->once())->method('updateUser')
->with($params['user'], $newData);
} else {
@@ -98,48 +99,48 @@ class HooksTest extends TestCase {
[
['feature' => '', 'value' => ''],
[
- AccountManager::PROPERTY_EMAIL => ['value' => ''],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
+ IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
],
false, false, true
],
[
['user' => $user, 'value' => ''],
[
- AccountManager::PROPERTY_EMAIL => ['value' => ''],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
+ IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
],
false, false, true
],
[
['user' => $user, 'feature' => ''],
[
- AccountManager::PROPERTY_EMAIL => ['value' => ''],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
+ IAccountManager::PROPERTY_EMAIL => ['value' => ''],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
],
false, false, true
],
[
['user' => $user, 'feature' => 'foo', 'value' => 'bar'],
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
],
false, false, false
],
[
['user' => $user, 'feature' => 'eMailAddress', 'value' => 'newMail@example.com'],
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
],
true, false, false
],
[
['user' => $user, 'feature' => 'displayName', 'value' => 'newDisplayName'],
[
- AccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
- AccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
+ IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
+ IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
],
false, true, false
],
diff --git a/tests/lib/App/CodeChecker/CodeCheckerTest.php b/tests/lib/App/CodeChecker/CodeCheckerTest.php
index bb121eccc44..7725d46a273 100644
--- a/tests/lib/App/CodeChecker/CodeCheckerTest.php
+++ b/tests/lib/App/CodeChecker/CodeCheckerTest.php
@@ -22,6 +22,10 @@ class CodeCheckerTest extends TestCase {
* @param string $fileToVerify
*/
public function testFindInvalidUsage($expectedErrorToken, $expectedErrorCode, $fileToVerify) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$checker = new CodeChecker(
new PrivateCheck(new EmptyCheck()),
false
@@ -49,6 +53,10 @@ class CodeCheckerTest extends TestCase {
* @param string $fileToVerify
*/
public function testPassValidUsage($fileToVerify) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$checker = new CodeChecker(
new PrivateCheck(new EmptyCheck()),
false
diff --git a/tests/lib/App/CodeChecker/DeprecationCheckTest.php b/tests/lib/App/CodeChecker/DeprecationCheckTest.php
index 1a16e7e4920..ea8ed8f50d6 100644
--- a/tests/lib/App/CodeChecker/DeprecationCheckTest.php
+++ b/tests/lib/App/CodeChecker/DeprecationCheckTest.php
@@ -22,6 +22,10 @@ class DeprecationCheckTest extends TestCase {
* @param string $fileToVerify
*/
public function testFindInvalidUsage($expectedErrorToken, $expectedErrorCode, $fileToVerify) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$checker = new CodeChecker(
new DeprecationCheck(new EmptyCheck()),
false
diff --git a/tests/lib/App/CodeChecker/NodeVisitorTest.php b/tests/lib/App/CodeChecker/NodeVisitorTest.php
index cfc6951c614..d828b84fc75 100644
--- a/tests/lib/App/CodeChecker/NodeVisitorTest.php
+++ b/tests/lib/App/CodeChecker/NodeVisitorTest.php
@@ -57,6 +57,10 @@ class NodeVisitorTest extends TestCase {
* @param string $fileToVerify
*/
public function testMethodsToCheck($expectedErrors, $fileToVerify) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$checker = new CodeChecker(
new TestList(new EmptyCheck()),
false
diff --git a/tests/lib/App/CodeChecker/StrongComparisonCheckTest.php b/tests/lib/App/CodeChecker/StrongComparisonCheckTest.php
index 6d3d2f50efd..e21dcbbc585 100644
--- a/tests/lib/App/CodeChecker/StrongComparisonCheckTest.php
+++ b/tests/lib/App/CodeChecker/StrongComparisonCheckTest.php
@@ -45,6 +45,10 @@ class StrongComparisonCheckTest extends TestCase {
* @param string $fileToVerify
*/
public function testPassValidUsage($fileToVerify) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$checker = new CodeChecker(
new StrongComparisonCheck(new EmptyCheck()),
false
diff --git a/tests/lib/AppConfigTest.php b/tests/lib/AppConfigTest.php
index 40a99709bd5..d2643d599f3 100644
--- a/tests/lib/AppConfigTest.php
+++ b/tests/lib/AppConfigTest.php
@@ -140,11 +140,11 @@ class AppConfigTest extends TestCase {
public function testGetApps() {
$config = new \OC\AppConfig(\OC::$server->getDatabaseConnection());
- $this->assertEquals([
+ $this->assertEqualsCanonicalizing([
'anotherapp',
'someapp',
'testapp',
- '123456',
+ 123456,
], $config->getApps());
}
@@ -152,7 +152,7 @@ class AppConfigTest extends TestCase {
$config = new \OC\AppConfig(\OC::$server->getDatabaseConnection());
$keys = $config->getKeys('testapp');
- $this->assertEquals([
+ $this->assertEqualsCanonicalizing([
'deletethis',
'depends_on',
'enabled',
diff --git a/tests/lib/AppFramework/Db/EntityTest.php b/tests/lib/AppFramework/Db/EntityTest.php
index 73138749a33..17234849a2d 100644
--- a/tests/lib/AppFramework/Db/EntityTest.php
+++ b/tests/lib/AppFramework/Db/EntityTest.php
@@ -123,11 +123,11 @@ class EntityTest extends \Test\TestCase {
public function testSetterMarksFieldUpdated() {
$this->entity->setId(3);
- $this->assertContains('id', $this->entity->getUpdatedFields());
+ $this->assertContains('id', array_keys($this->entity->getUpdatedFields()));
}
-
+
public function testCallShouldOnlyWorkForGetterSetter() {
$this->expectException(\BadFunctionCallException::class);
@@ -135,14 +135,14 @@ class EntityTest extends \Test\TestCase {
}
-
+
public function testGetterShouldFailIfAttributeNotDefined() {
$this->expectException(\BadFunctionCallException::class);
$this->entity->getTest();
}
-
+
public function testSetterShouldFailIfAttributeNotDefined() {
$this->expectException(\BadFunctionCallException::class);
@@ -243,7 +243,7 @@ class EntityTest extends \Test\TestCase {
$this->assertThat($entity->isAnotherBool(), new IsType(IsType::TYPE_BOOL));
}
-
+
public function testIsGetterShoudFailForOtherType() {
$this->expectException(\BadFunctionCallException::class);
diff --git a/tests/lib/Avatar/UserAvatarTest.php b/tests/lib/Avatar/UserAvatarTest.php
index cf0edad9502..31f2a6ebf5b 100644
--- a/tests/lib/Avatar/UserAvatarTest.php
+++ b/tests/lib/Avatar/UserAvatarTest.php
@@ -52,6 +52,10 @@ class UserAvatarTest extends \Test\TestCase {
}
public function testGetNoAvatar() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$file = $this->createMock(ISimpleFile::class);
$this->folder->method('newFile')
->willReturn($file);
@@ -78,12 +82,19 @@ class UserAvatarTest extends \Test\TestCase {
}));
$file->method('getContent')
- ->willReturn($data);
+ ->willReturnCallback(function () use (&$data) {
+ return $data;
+ });
- $this->assertEquals($data, $this->avatar->get()->data());
+ $result = $this->avatar->get();
+ $this->assertTrue($result->valid());
}
public function testGetAvatarSizeMatch() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$this->folder->method('fileExists')
->willReturnMap([
['avatar.jpg', true],
@@ -101,6 +112,10 @@ class UserAvatarTest extends \Test\TestCase {
}
public function testGetAvatarSizeMinusOne() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$this->folder->method('fileExists')
->willReturnMap([
['avatar.jpg', true],
@@ -117,6 +132,10 @@ class UserAvatarTest extends \Test\TestCase {
}
public function testGetAvatarNoSizeMatch() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$this->folder->method('fileExists')
->willReturnMap([
['avatar.png', true],
@@ -181,6 +200,10 @@ class UserAvatarTest extends \Test\TestCase {
}
public function testSetAvatar() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$avatarFileJPG = $this->createMock(File::class);
$avatarFileJPG->method('getName')
->willReturn('avatar.jpg');
diff --git a/tests/lib/ImageTest.php b/tests/lib/ImageTest.php
index 5b83c4ac57f..ebf00392d87 100644
--- a/tests/lib/ImageTest.php
+++ b/tests/lib/ImageTest.php
@@ -20,6 +20,10 @@ class ImageTest extends \Test\TestCase {
}
public function testConstructDestruct() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertInstanceOf('\OC_Image', $img);
@@ -47,6 +51,10 @@ class ImageTest extends \Test\TestCase {
}
public function testValid() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertTrue($img->valid());
@@ -61,6 +69,10 @@ class ImageTest extends \Test\TestCase {
}
public function testMimeType() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertEquals('image/png', $img->mimeType());
@@ -78,6 +90,10 @@ class ImageTest extends \Test\TestCase {
}
public function testWidth() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertEquals(128, $img->width());
@@ -95,6 +111,10 @@ class ImageTest extends \Test\TestCase {
}
public function testHeight() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertEquals(128, $img->height());
@@ -112,6 +132,10 @@ class ImageTest extends \Test\TestCase {
}
public function testSave() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$img->resize(16);
@@ -126,6 +150,10 @@ class ImageTest extends \Test\TestCase {
}
public function testData() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$raw = imagecreatefromstring(file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.png'));
@@ -160,6 +188,10 @@ class ImageTest extends \Test\TestCase {
}
public function testDataNoResource() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$this->assertNull($img->data());
}
@@ -168,6 +200,10 @@ class ImageTest extends \Test\TestCase {
* @depends testData
*/
public function testToString() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$expected = base64_encode($img->data());
@@ -185,6 +221,10 @@ class ImageTest extends \Test\TestCase {
}
public function testResize() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertTrue($img->resize(32));
@@ -205,6 +245,10 @@ class ImageTest extends \Test\TestCase {
}
public function testPreciseResize() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertTrue($img->preciseResize(128, 512));
@@ -225,6 +269,10 @@ class ImageTest extends \Test\TestCase {
}
public function testCenterCrop() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$img->centerCrop();
@@ -245,6 +293,10 @@ class ImageTest extends \Test\TestCase {
}
public function testCrop() {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$this->assertTrue($img->crop(0, 0, 50, 20));
@@ -280,6 +332,10 @@ class ImageTest extends \Test\TestCase {
* @param int[] $expected
*/
public function testFitIn($filename, $asked, $expected) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT . '/tests/data/' . $filename);
$this->assertTrue($img->fitIn($asked[0], $asked[1]));
@@ -303,6 +359,10 @@ class ImageTest extends \Test\TestCase {
* @param string $filename
*/
public function testScaleDownToFitWhenSmallerAlready($filename) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/' . $filename);
$currentWidth = $img->width();
@@ -336,6 +396,10 @@ class ImageTest extends \Test\TestCase {
* @param int[] $expected
*/
public function testScaleDownWhenBigger($filename, $asked, $expected) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/' . $filename);
//$this->assertTrue($img->scaleDownToFit($asked[0], $asked[1]));
@@ -356,6 +420,10 @@ class ImageTest extends \Test\TestCase {
* @dataProvider convertDataProvider
*/
public function testConvert($mimeType) {
+ if (PHP_MAJOR_VERSION > 7) {
+ $this->markTestSkipped('Only run on php7');
+ }
+
$img = new \OC_Image();
$img->loadFromFile(OC::$SERVERROOT.'/tests/data/testimage.png');
$tempFile = tempnam(sys_get_temp_dir(), 'img-test');
diff --git a/tests/lib/IntegrityCheck/CheckerTest.php b/tests/lib/IntegrityCheck/CheckerTest.php
index c79f192a7fb..631aec7c6bf 100644
--- a/tests/lib/IntegrityCheck/CheckerTest.php
+++ b/tests/lib/IntegrityCheck/CheckerTest.php
@@ -110,7 +110,7 @@ class CheckerTest extends TestCase {
public function testWriteAppSignatureWrongPermissions() {
$this->expectException(\Exception::class);
- $this->expectExceptionMessageRegExp('/[a-zA-Z\\/_-]+ is not writable/');
+ $this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
$this->fileAccessHelper
->expects($this->once())
@@ -507,7 +507,7 @@ class CheckerTest extends TestCase {
public function testWriteCoreSignatureWrongPermissions() {
$this->expectException(\Exception::class);
- $this->expectExceptionMessageRegExp('/[a-zA-Z\\/_-]+ is not writable/');
+ $this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
$this->fileAccessHelper
->expects($this->at(0))
diff --git a/tests/lib/LargeFileHelperGetFileSizeTest.php b/tests/lib/LargeFileHelperGetFileSizeTest.php
index e21ac4ee64b..3066d48792b 100644
--- a/tests/lib/LargeFileHelperGetFileSizeTest.php
+++ b/tests/lib/LargeFileHelperGetFileSizeTest.php
@@ -60,6 +60,9 @@ class LargeFileHelperGetFileSizeTest extends TestCase {
* @dataProvider dataFileNameProvider
*/
public function testGetFileSizeViaExec($filename, $fileSize) {
+ if (escapeshellarg('strängé') !== '\'strängé\'') {
+ $this->markTestSkipped('Your escapeshell args removes accents');
+ }
if (!\OC_Helper::is_function_enabled('exec')) {
$this->markTestSkipped(
'The exec() function needs to be enabled for this test.'
diff --git a/tests/lib/TestCase.php b/tests/lib/TestCase.php
index aa2c720d830..69cf2a39792 100644
--- a/tests/lib/TestCase.php
+++ b/tests/lib/TestCase.php
@@ -226,7 +226,11 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase {
$property->setValue($object, array_pop($parameters));
}
- return $property->getValue($object);
+ if (is_object($object)) {
+ return $property->getValue($object);
+ }
+
+ return $property->getValue();
}
return false;
diff --git a/tests/lib/Traits/ClientServiceTrait.php b/tests/lib/Traits/ClientServiceTrait.php
index e9e9787d734..c35a57268f7 100644
--- a/tests/lib/Traits/ClientServiceTrait.php
+++ b/tests/lib/Traits/ClientServiceTrait.php
@@ -39,7 +39,7 @@ trait ClientServiceTrait {
* @param string $originalClassName
* @return \PHPUnit\Framework\MockObject\MockObject
*/
- abstract protected function createMock($originalClassName);
+ abstract protected function createMock(string $originalClassName);
/**
* Returns a matcher that matches when the method is executed
@@ -49,7 +49,7 @@ trait ClientServiceTrait {
*
* @since Method available since Release 3.0.0
*/
- abstract public function any();
+ abstract public static function any();
protected function setUpClientServiceTrait() {
$this->clientService = $this->createMock(IClientService::class);
diff --git a/tests/lib/Traits/EncryptionTrait.php b/tests/lib/Traits/EncryptionTrait.php
index 38ba18fdfbb..6b74f7ca8ee 100644
--- a/tests/lib/Traits/EncryptionTrait.php
+++ b/tests/lib/Traits/EncryptionTrait.php
@@ -24,8 +24,8 @@ trait EncryptionTrait {
abstract protected function registerStorageWrapper($name, $wrapper);
// from phpunit
- abstract protected function markTestSkipped(string $message = ''): void;
- abstract protected function assertTrue($condition, string $message = ''): void;
+ abstract protected static function markTestSkipped(string $message = ''): void;
+ abstract protected static function assertTrue($condition, string $message = ''): void;
private $encryptionWasEnabled;
diff --git a/version.php b/version.php
index 24e3e8db6ae..299855816a9 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version = [21, 0, 0, 7];
+$OC_Version = [21, 0, 0, 8];
// The human readable string
$OC_VersionString = '21.0.0 alpha';