Skip to content

Commit

Permalink
Merge pull request #40620 from nextcloud/feat/noid/ocp-for-phonenumbe…
Browse files Browse the repository at this point in the history
…r-util

feat(phonenumber): Add OCP wrapper for PhoneNumber utils library
  • Loading branch information
nickvergessen authored Sep 26, 2023
2 parents 2288e8e + 3b4c306 commit 23a2e34
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 39 deletions.
33 changes: 11 additions & 22 deletions apps/provisioning_api/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@
namespace OCA\Provisioning_API\Controller;

use InvalidArgumentException;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use OC\Authentication\Token\RemoteWipe;
use OC\KnownUser\KnownUserService;
use OC\User\Backend;
Expand All @@ -66,6 +63,7 @@
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IPhoneNumberUtil;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUser;
Expand Down Expand Up @@ -113,7 +111,8 @@ public function __construct(
ISecureRandom $secureRandom,
RemoteWipe $remoteWipe,
KnownUserService $knownUserService,
IEventDispatcher $eventDispatcher
IEventDispatcher $eventDispatcher,
private IPhoneNumberUtil $phoneNumberUtil,
) {
parent::__construct(
$appName,
Expand Down Expand Up @@ -243,9 +242,7 @@ public function getUsersDetails(string $search = '', int $limit = null, int $off
* 400: Invalid location
*/
public function searchByPhoneNumbers(string $location, array $search): DataResponse {
$phoneUtil = PhoneNumberUtil::getInstance();

if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
if ($this->phoneNumberUtil->getCountryCodeForRegion($location) === null) {
// Not a valid region code
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
Expand All @@ -258,26 +255,18 @@ public function searchByPhoneNumbers(string $location, array $search): DataRespo
$normalizedNumberToKey = [];
foreach ($search as $key => $phoneNumbers) {
foreach ($phoneNumbers as $phone) {
try {
$phoneNumber = $phoneUtil->parse($phone, $location);
if ($phoneUtil->isValidNumber($phoneNumber)) {
$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
$normalizedNumberToKey[$normalizedNumber] = (string) $key;
}
} catch (NumberParseException $e) {
$normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $location);
if ($normalizedNumber !== null) {
$normalizedNumberToKey[$normalizedNumber] = (string) $key;
}

if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && str_starts_with($phone, '0')) {
// If the number has a leading zero (no country code),
// we also check the default phone region of the instance,
// when it's different to the user's given region.
try {
$phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
if ($phoneUtil->isValidNumber($phoneNumber)) {
$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
$normalizedNumberToKey[$normalizedNumber] = (string) $key;
}
} catch (NumberParseException $e) {
$normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $defaultPhoneRegion);
if ($normalizedNumber !== null) {
$normalizedNumberToKey[$normalizedNumber] = (string) $key;
}
}
}
Expand Down
19 changes: 14 additions & 5 deletions apps/provisioning_api/tests/Controller/UsersControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
use OC\Authentication\Token\RemoteWipe;
use OC\Group\Manager;
use OC\KnownUser\KnownUserService;
use OC\PhoneNumberUtil;
use OC\SubAdmin;
use OCA\Provisioning_API\Controller\UsersController;
use OCA\Settings\Mailer\NewUserMailHelper;
Expand All @@ -59,6 +60,7 @@
use OCP\IConfig;
use OCP\IGroup;
use OCP\IL10N;
use OCP\IPhoneNumberUtil;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUser;
Expand Down Expand Up @@ -103,8 +105,10 @@ class UsersControllerTest extends TestCase {
private $remoteWipe;
/** @var KnownUserService|MockObject */
private $knownUserService;
/** @var IEventDispatcher */
/** @var IEventDispatcher|MockObject */
private $eventDispatcher;
/** @var IPhoneNumberUtil */
private $phoneNumberUtil;

protected function setUp(): void {
parent::setUp();
Expand All @@ -123,6 +127,7 @@ protected function setUp(): void {
$this->remoteWipe = $this->createMock(RemoteWipe::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->phoneNumberUtil = new PhoneNumberUtil();

$this->api = $this->getMockBuilder(UsersController::class)
->setConstructorArgs([
Expand All @@ -141,8 +146,9 @@ protected function setUp(): void {
$this->remoteWipe,
$this->knownUserService,
$this->eventDispatcher,
$this->phoneNumberUtil,
])
->setMethods(['fillStorageInfo'])
->onlyMethods(['fillStorageInfo'])
->getMock();
}

Expand Down Expand Up @@ -411,8 +417,9 @@ public function testAddUserSuccessfulWithDisplayName() {
$this->remoteWipe,
$this->knownUserService,
$this->eventDispatcher,
$this->phoneNumberUtil,
])
->setMethods(['editUser'])
->onlyMethods(['editUser'])
->getMock();

$this->userManager
Expand Down Expand Up @@ -3693,8 +3700,9 @@ public function testGetCurrentUserLoggedIn() {
$this->remoteWipe,
$this->knownUserService,
$this->eventDispatcher,
$this->phoneNumberUtil,
])
->setMethods(['getUserData'])
->onlyMethods(['getUserData'])
->getMock();

$api->expects($this->once())->method('getUserData')->with('UID', true)
Expand Down Expand Up @@ -3779,8 +3787,9 @@ public function testGetUser() {
$this->remoteWipe,
$this->knownUserService,
$this->eventDispatcher,
$this->phoneNumberUtil,
])
->setMethods(['getUserData'])
->onlyMethods(['getUserData'])
->getMock();

$expected = [
Expand Down
2 changes: 2 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@
'OCP\\IMemcache' => $baseDir . '/lib/public/IMemcache.php',
'OCP\\IMemcacheTTL' => $baseDir . '/lib/public/IMemcacheTTL.php',
'OCP\\INavigationManager' => $baseDir . '/lib/public/INavigationManager.php',
'OCP\\IPhoneNumberUtil' => $baseDir . '/lib/public/IPhoneNumberUtil.php',
'OCP\\IPreview' => $baseDir . '/lib/public/IPreview.php',
'OCP\\IRequest' => $baseDir . '/lib/public/IRequest.php',
'OCP\\IRequestId' => $baseDir . '/lib/public/IRequestId.php',
Expand Down Expand Up @@ -1480,6 +1481,7 @@
'OC\\OCS\\Exception' => $baseDir . '/lib/private/OCS/Exception.php',
'OC\\OCS\\Provider' => $baseDir . '/lib/private/OCS/Provider.php',
'OC\\OCS\\Result' => $baseDir . '/lib/private/OCS/Result.php',
'OC\\PhoneNumberUtil' => $baseDir . '/lib/private/PhoneNumberUtil.php',
'OC\\PreviewManager' => $baseDir . '/lib/private/PreviewManager.php',
'OC\\PreviewNotAvailableException' => $baseDir . '/lib/private/PreviewNotAvailableException.php',
'OC\\Preview\\BMP' => $baseDir . '/lib/private/Preview/BMP.php',
Expand Down
2 changes: 2 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\IMemcache' => __DIR__ . '/../../..' . '/lib/public/IMemcache.php',
'OCP\\IMemcacheTTL' => __DIR__ . '/../../..' . '/lib/public/IMemcacheTTL.php',
'OCP\\INavigationManager' => __DIR__ . '/../../..' . '/lib/public/INavigationManager.php',
'OCP\\IPhoneNumberUtil' => __DIR__ . '/../../..' . '/lib/public/IPhoneNumberUtil.php',
'OCP\\IPreview' => __DIR__ . '/../../..' . '/lib/public/IPreview.php',
'OCP\\IRequest' => __DIR__ . '/../../..' . '/lib/public/IRequest.php',
'OCP\\IRequestId' => __DIR__ . '/../../..' . '/lib/public/IRequestId.php',
Expand Down Expand Up @@ -1513,6 +1514,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\OCS\\Exception' => __DIR__ . '/../../..' . '/lib/private/OCS/Exception.php',
'OC\\OCS\\Provider' => __DIR__ . '/../../..' . '/lib/private/OCS/Provider.php',
'OC\\OCS\\Result' => __DIR__ . '/../../..' . '/lib/private/OCS/Result.php',
'OC\\PhoneNumberUtil' => __DIR__ . '/../../..' . '/lib/private/PhoneNumberUtil.php',
'OC\\PreviewManager' => __DIR__ . '/../../..' . '/lib/private/PreviewManager.php',
'OC\\PreviewNotAvailableException' => __DIR__ . '/../../..' . '/lib/private/PreviewNotAvailableException.php',
'OC\\Preview\\BMP' => __DIR__ . '/../../..' . '/lib/private/Preview/BMP.php',
Expand Down
15 changes: 5 additions & 10 deletions lib/private/Accounts/AccountManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@

use Exception;
use InvalidArgumentException;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use OC\Profile\TProfileHelper;
use OCP\Accounts\UserUpdatedEvent;
use OCP\Cache\CappedMemoryCache;
Expand All @@ -56,6 +53,7 @@
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
use OCP\IPhoneNumberUtil;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\L10N\IFactory;
Expand Down Expand Up @@ -119,6 +117,7 @@ public function __construct(
private IFactory $l10nFactory,
private IURLGenerator $urlGenerator,
private ICrypto $crypto,
private IPhoneNumberUtil $phoneNumberUtil,
) {
$this->internalCache = new CappedMemoryCache();
}
Expand All @@ -139,13 +138,9 @@ protected function parsePhoneNumber(string $input): string {
$defaultRegion = 'EN';
}

$phoneUtil = PhoneNumberUtil::getInstance();
try {
$phoneNumber = $phoneUtil->parse($input, $defaultRegion);
if ($phoneUtil->isValidNumber($phoneNumber)) {
return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
}
} catch (NumberParseException $e) {
$phoneNumber = $this->phoneNumberUtil->convertToStandardFormat($input, $defaultRegion);
if ($phoneNumber !== null) {
return $phoneNumber;
}

throw new InvalidArgumentException(self::PROPERTY_PHONE);
Expand Down
61 changes: 61 additions & 0 deletions lib/private/PhoneNumberUtil.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);
/**
*
* @copyright Copyright (c) 2023 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;

use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberFormat;
use OCP\IPhoneNumberUtil;

/**
* @since 28.0.0
*/
class PhoneNumberUtil implements IPhoneNumberUtil {
/**
* {@inheritDoc}
*/
public function getCountryCodeForRegion(string $regionCode): ?int {
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$countryCode = $phoneUtil->getCountryCodeForRegion($regionCode);
return $countryCode === 0 ? null : $countryCode;
}

/**
* {@inheritDoc}
*/
public function convertToStandardFormat(string $input, ?string $defaultRegion = null): ?string {
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
try {
$phoneNumber = $phoneUtil->parse($input, $defaultRegion);
if ($phoneUtil->isValidNumber($phoneNumber)) {
return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
}
} catch (NumberParseException) {
}

return null;
}
}
3 changes: 3 additions & 0 deletions lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@
use OCP\IL10N;
use OCP\ILogger;
use OCP\INavigationManager;
use OCP\IPhoneNumberUtil;
use OCP\IPreview;
use OCP\IRequest;
use OCP\IRequestId;
Expand Down Expand Up @@ -1423,6 +1424,8 @@ public function __construct($webRoot, \OC\Config $config) {

$this->registerAlias(ILimiter::class, Limiter::class);

$this->registerAlias(IPhoneNumberUtil::class, PhoneNumberUtil::class);

$this->connectDispatcher();
}

Expand Down
57 changes: 57 additions & 0 deletions lib/public/IPhoneNumberUtil.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);
/**
*
* @copyright Copyright (c) 2023 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 OCP;

/**
* @since 28.0.0
*/
interface IPhoneNumberUtil {
/**
* Returns the country code for a specific region
*
* For example, this would be `41` for Switzerland and `49` for Germany.
* Returns null when the region is invalid.
*
* @param string $regionCode Two-letter region code of ISO 3166-1
* @return int|null Null when invalid/unsupported, the phone country code otherwise
* @since 28.0.0
*/
public function getCountryCodeForRegion(string $regionCode): ?int;

/**
* Converts a given input into an E164 formatted phone number
*
* E164 is the international format without any formatting characters or spaces.
* E.g. +41446681800 where +41 is the region code.
*
* @param string $input Input phone number can contain formatting spaces, slashes and dashes
* @param string|null $defaultRegion Two-letter region code of ISO 3166-1
* @return string|null Null when the input is invalid for the given region or requires a region.
* @since 28.0.0
*/
public function convertToStandardFormat(string $input, ?string $defaultRegion = null): ?string;
}
Loading

0 comments on commit 23a2e34

Please sign in to comment.