summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2020-12-08 17:05:38 +0100
committerGitHub <noreply@github.com>2020-12-08 17:05:38 +0100
commit86a3b7e7bf419e2bf1acc687625d0ff8596d39dc (patch)
treee467d21a31f7e78320ee9739ba66dccd4025eb65
parentfda6ffc866cf8c5d3579fe95d1731ab747894002 (diff)
parent13a438b3224f3f42a0f552230f680a243e1af705 (diff)
downloadnextcloud-server-86a3b7e7bf419e2bf1acc687625d0ff8596d39dc.tar.gz
nextcloud-server-86a3b7e7bf419e2bf1acc687625d0ff8596d39dc.zip
Merge pull request #24486 from nextcloud/feature/noid/phone-number-validation
Phone number validation and search
m---------3rdparty0
-rw-r--r--apps/dav/lib/CardDAV/Converter.php19
-rw-r--r--apps/dav/tests/unit/CardDAV/ConverterTest.php17
-rw-r--r--apps/dav/tests/unit/CardDAV/SyncServiceTest.php15
-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/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--build/integration/features/bootstrap/Provisioning.php44
-rw-r--r--build/integration/features/provisioning-v1.feature25
-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--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/private/Accounts/AccountManager.php130
-rw-r--r--lib/private/Accounts/Hooks.php9
-rw-r--r--lib/private/Repair.php4
-rw-r--r--lib/private/Repair/NC21/ValidatePhoneNumber.php89
-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--version.php2
33 files changed, 912 insertions, 264 deletions
diff --git a/3rdparty b/3rdparty
-Subproject e287243b8eeaf72443b841ace3b1788e85a5aac
+Subproject 27a56c5bb9d0ec514a8fb22044fd5f03a51ea2a
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/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/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/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/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/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/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/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/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/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/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';