Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14839944
D19988.id47722.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
32 KB
Referenced Files
None
Subscribers
None
D19988.id47722.diff
View Options
diff --git a/resources/sql/autopatches/20190116.contact.01.number.sql b/resources/sql/autopatches/20190116.contact.01.number.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20190116.contact.01.number.sql
@@ -0,0 +1,10 @@
+CREATE TABLE {$NAMESPACE}_auth.auth_contactnumber (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ objectPHID VARBINARY(64) NOT NULL,
+ contactNumber VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
+ status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
+ properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20190116.contact.02.xaction.sql b/resources/sql/autopatches/20190116.contact.02.xaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20190116.contact.02.xaction.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_auth.auth_contactnumbertransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ objectPHID VARBINARY(64) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ commentPHID VARBINARY(64) DEFAULT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) NOT NULL,
+ oldValue LONGTEXT NOT NULL,
+ newValue LONGTEXT NOT NULL,
+ contentSource LONGTEXT NOT NULL,
+ metadata LONGTEXT NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ KEY `key_object` (`objectPHID`)
+) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2201,6 +2201,18 @@
'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php',
'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php',
'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php',
+ 'PhabricatorAuthContactNumber' => 'applications/auth/storage/PhabricatorAuthContactNumber.php',
+ 'PhabricatorAuthContactNumberController' => 'applications/auth/controller/contact/PhabricatorAuthContactNumberController.php',
+ 'PhabricatorAuthContactNumberEditController' => 'applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php',
+ 'PhabricatorAuthContactNumberEditEngine' => 'applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php',
+ 'PhabricatorAuthContactNumberEditor' => 'applications/auth/editor/PhabricatorAuthContactNumberEditor.php',
+ 'PhabricatorAuthContactNumberNumberTransaction' => 'applications/auth/xaction/PhabricatorAuthContactNumberNumberTransaction.php',
+ 'PhabricatorAuthContactNumberPHIDType' => 'applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php',
+ 'PhabricatorAuthContactNumberQuery' => 'applications/auth/query/PhabricatorAuthContactNumberQuery.php',
+ 'PhabricatorAuthContactNumberTransaction' => 'applications/auth/storage/PhabricatorAuthContactNumberTransaction.php',
+ 'PhabricatorAuthContactNumberTransactionQuery' => 'applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php',
+ 'PhabricatorAuthContactNumberTransactionType' => 'applications/auth/xaction/PhabricatorAuthContactNumberTransactionType.php',
+ 'PhabricatorAuthContactNumberViewController' => 'applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php',
'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php',
'PhabricatorAuthDAO' => 'applications/auth/storage/PhabricatorAuthDAO.php',
'PhabricatorAuthDisableController' => 'applications/auth/controller/config/PhabricatorAuthDisableController.php',
@@ -2723,6 +2735,7 @@
'PhabricatorConpherenceWidgetVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceWidgetVisibleSetting.php',
'PhabricatorConsoleApplication' => 'applications/console/application/PhabricatorConsoleApplication.php',
'PhabricatorConsoleContentSource' => 'infrastructure/contentsource/PhabricatorConsoleContentSource.php',
+ 'PhabricatorContactNumbersSettingsPanel' => 'applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php',
'PhabricatorContentSource' => 'infrastructure/contentsource/PhabricatorContentSource.php',
'PhabricatorContentSourceModule' => 'infrastructure/contentsource/PhabricatorContentSourceModule.php',
'PhabricatorContentSourceView' => 'infrastructure/contentsource/PhabricatorContentSourceView.php',
@@ -3855,6 +3868,7 @@
'PhabricatorPholioConfigOptions' => 'applications/pholio/config/PhabricatorPholioConfigOptions.php',
'PhabricatorPholioMockTestDataGenerator' => 'applications/pholio/lipsum/PhabricatorPholioMockTestDataGenerator.php',
'PhabricatorPhoneNumber' => 'applications/metamta/message/PhabricatorPhoneNumber.php',
+ 'PhabricatorPhoneNumberTestCase' => 'applications/metamta/message/__tests__/PhabricatorPhoneNumberTestCase.php',
'PhabricatorPhortuneApplication' => 'applications/phortune/application/PhabricatorPhortuneApplication.php',
'PhabricatorPhortuneContentSource' => 'applications/phortune/contentsource/PhabricatorPhortuneContentSource.php',
'PhabricatorPhortuneManagementInvoiceWorkflow' => 'applications/phortune/management/PhabricatorPhortuneManagementInvoiceWorkflow.php',
@@ -7869,6 +7883,23 @@
'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController',
+ 'PhabricatorAuthContactNumber' => array(
+ 'PhabricatorAuthDAO',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorDestructibleInterface',
+ ),
+ 'PhabricatorAuthContactNumberController' => 'PhabricatorAuthController',
+ 'PhabricatorAuthContactNumberEditController' => 'PhabricatorAuthContactNumberController',
+ 'PhabricatorAuthContactNumberEditEngine' => 'PhabricatorEditEngine',
+ 'PhabricatorAuthContactNumberEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhabricatorAuthContactNumberNumberTransaction' => 'PhabricatorAuthContactNumberTransactionType',
+ 'PhabricatorAuthContactNumberPHIDType' => 'PhabricatorPHIDType',
+ 'PhabricatorAuthContactNumberQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorAuthContactNumberTransaction' => 'PhabricatorModularTransaction',
+ 'PhabricatorAuthContactNumberTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhabricatorAuthContactNumberTransactionType' => 'PhabricatorModularTransactionType',
+ 'PhabricatorAuthContactNumberViewController' => 'PhabricatorAuthContactNumberController',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorAuthDAO' => 'PhabricatorLiskDAO',
'PhabricatorAuthDisableController' => 'PhabricatorAuthProviderConfigController',
@@ -8487,6 +8518,7 @@
'PhabricatorConpherenceWidgetVisibleSetting' => 'PhabricatorInternalSetting',
'PhabricatorConsoleApplication' => 'PhabricatorApplication',
'PhabricatorConsoleContentSource' => 'PhabricatorContentSource',
+ 'PhabricatorContactNumbersSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorContentSource' => 'Phobject',
'PhabricatorContentSourceModule' => 'PhabricatorConfigModule',
'PhabricatorContentSourceView' => 'AphrontView',
@@ -9780,6 +9812,7 @@
'PhabricatorPholioConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPholioMockTestDataGenerator' => 'PhabricatorTestDataGenerator',
'PhabricatorPhoneNumber' => 'Phobject',
+ 'PhabricatorPhoneNumberTestCase' => 'PhabricatorTestCase',
'PhabricatorPhortuneApplication' => 'PhabricatorApplication',
'PhabricatorPhortuneContentSource' => 'PhabricatorContentSource',
'PhabricatorPhortuneManagementInvoiceWorkflow' => 'PhabricatorPhortuneManagementWorkflow',
diff --git a/src/applications/auth/application/PhabricatorAuthApplication.php b/src/applications/auth/application/PhabricatorAuthApplication.php
--- a/src/applications/auth/application/PhabricatorAuthApplication.php
+++ b/src/applications/auth/application/PhabricatorAuthApplication.php
@@ -94,6 +94,13 @@
'(?P<id>[1-9]\d*)/' =>
'PhabricatorAuthFactorProviderViewController',
),
+
+ 'contact/' => array(
+ $this->getEditRoutePattern('edit/') =>
+ 'PhabricatorAuthContactNumberEditController',
+ '(?P<id>[1-9]\d*)/' =>
+ 'PhabricatorAuthContactNumberViewController',
+ ),
),
'/oauth/(?P<provider>\w+)/login/'
diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php
@@ -0,0 +1,16 @@
+<?php
+
+abstract class PhabricatorAuthContactNumberController
+ extends PhabricatorAuthController {
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $crumbs->addTextCrumb(
+ pht('Contact Numbers'),
+ pht('/settings/panel/contact/'));
+
+ return $crumbs;
+ }
+
+}
diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php
@@ -0,0 +1,12 @@
+<?php
+
+final class PhabricatorAuthContactNumberEditController
+ extends PhabricatorAuthContactNumberController {
+
+ public function handleRequest(AphrontRequest $request) {
+ return id(new PhabricatorAuthContactNumberEditEngine())
+ ->setController($this)
+ ->buildResponse();
+ }
+
+}
diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php
@@ -0,0 +1,98 @@
+<?php
+
+final class PhabricatorAuthContactNumberViewController
+ extends PhabricatorAuthContactNumberController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
+
+ $number = id(new PhabricatorAuthContactNumberQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($request->getURIData('id')))
+ ->executeOne();
+ if (!$number) {
+ return new Aphront404Response();
+ }
+
+ $crumbs = $this->buildApplicationCrumbs()
+ ->addTextCrumb($number->getObjectName())
+ ->setBorder(true);
+
+ $header = $this->buildHeaderView($number);
+ $properties = $this->buildPropertiesView($number);
+ $curtain = $this->buildCurtain($number);
+
+ $timeline = $this->buildTransactionTimeline(
+ $number,
+ new PhabricatorAuthContactNumberTransactionQuery());
+ $timeline->setShouldTerminate(true);
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setCurtain($curtain)
+ ->setMainColumn(
+ array(
+ $timeline,
+ ))
+ ->addPropertySection(pht('Details'), $properties);
+
+ return $this->newPage()
+ ->setTitle($number->getDisplayName())
+ ->setCrumbs($crumbs)
+ ->setPageObjectPHIDs(
+ array(
+ $number->getPHID(),
+ ))
+ ->appendChild($view);
+ }
+
+ private function buildHeaderView(PhabricatorAuthContactNumber $number) {
+ $viewer = $this->getViewer();
+
+ $view = id(new PHUIHeaderView())
+ ->setViewer($viewer)
+ ->setHeader($number->getObjectName())
+ ->setPolicyObject($number);
+
+ return $view;
+ }
+
+ private function buildPropertiesView(
+ PhabricatorAuthContactNumber $number) {
+ $viewer = $this->getViewer();
+
+ $view = id(new PHUIPropertyListView())
+ ->setViewer($viewer);
+
+ $view->addProperty(
+ pht('Owner'),
+ $viewer->renderHandle($number->getObjectPHID()));
+
+ $view->addProperty(pht('Contact Number'), $number->getDisplayName());
+
+ return $view;
+ }
+
+ private function buildCurtain(PhabricatorAuthContactNumber $number) {
+ $viewer = $this->getViewer();
+ $id = $number->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $number,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $curtain = $this->newCurtainView($number);
+
+ $curtain->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Contact Number'))
+ ->setIcon('fa-pencil')
+ ->setHref($this->getApplicationURI("contact/edit/{$id}/"))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit));
+
+ return $curtain;
+ }
+
+}
diff --git a/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php b/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php
@@ -0,0 +1,86 @@
+<?php
+
+final class PhabricatorAuthContactNumberEditEngine
+ extends PhabricatorEditEngine {
+
+ const ENGINECONST = 'auth.contact';
+
+ public function isEngineConfigurable() {
+ return false;
+ }
+
+ public function getEngineName() {
+ return pht('Contact Numbers');
+ }
+
+ public function getSummaryHeader() {
+ return pht('Edit Contact Numbers');
+ }
+
+ public function getSummaryText() {
+ return pht('This engine is used to edit contact numbers.');
+ }
+
+ public function getEngineApplicationClass() {
+ return 'PhabricatorAuthApplication';
+ }
+
+ protected function newEditableObject() {
+ $viewer = $this->getViewer();
+ return PhabricatorAuthContactNumber::initializeNewContactNumber($viewer);
+ }
+
+ protected function newObjectQuery() {
+ return new PhabricatorAuthContactNumberQuery();
+ }
+
+ protected function getObjectCreateTitleText($object) {
+ return pht('Create Contact Number');
+ }
+
+ protected function getObjectCreateButtonText($object) {
+ return pht('Create Contact Number');
+ }
+
+ protected function getObjectEditTitleText($object) {
+ return pht('Edit Contact Number');
+ }
+
+ protected function getObjectEditShortText($object) {
+ return $object->getObjectName();
+ }
+
+ protected function getObjectCreateShortText() {
+ return pht('Create Contact Number');
+ }
+
+ protected function getObjectName() {
+ return pht('Contact Number');
+ }
+
+ protected function getEditorURI() {
+ return '/auth/contact/edit/';
+ }
+
+ protected function getObjectCreateCancelURI($object) {
+ return '/settings/panel/contact/';
+ }
+
+ protected function getObjectViewURI($object) {
+ return $object->getURI();
+ }
+
+ protected function buildCustomEditFields($object) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('contactNumber')
+ ->setTransactionType(
+ PhabricatorAuthContactNumberNumberTransaction::TRANSACTIONTYPE)
+ ->setLabel(pht('Contact Number'))
+ ->setDescription(pht('The contact number.'))
+ ->setValue($object->getContactNumber())
+ ->setIsRequired(true),
+ );
+ }
+
+}
diff --git a/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php b/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php
@@ -0,0 +1,38 @@
+<?php
+
+final class PhabricatorAuthContactNumberEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorAuthApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Contact Numbers');
+ }
+
+ public function getCreateObjectTitle($author, $object) {
+ return pht('%s created this contact number.', $author);
+ }
+
+ public function getCreateObjectTitleForFeed($author, $object) {
+ return pht('%s created %s.', $author, $object);
+ }
+
+ protected function didCatchDuplicateKeyException(
+ PhabricatorLiskDAO $object,
+ array $xactions,
+ Exception $ex) {
+
+ $errors = array();
+ $errors[] = new PhabricatorApplicationTransactionValidationError(
+ PhabricatorAuthContactNumberNumberTransaction::TRANSACTIONTYPE,
+ pht('Duplicate'),
+ pht('This contact number is already in use.'),
+ null);
+
+ throw new PhabricatorApplicationTransactionValidationException($errors);
+ }
+
+
+}
diff --git a/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php b/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php
@@ -0,0 +1,38 @@
+<?php
+
+final class PhabricatorAuthContactNumberPHIDType
+ extends PhabricatorPHIDType {
+
+ const TYPECONST = 'CTNM';
+
+ public function getTypeName() {
+ return pht('Contact Number');
+ }
+
+ public function newObject() {
+ return new PhabricatorAuthContactNumber();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorAuthApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PhabricatorAuthContactNumberQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $contact_number = $objects[$phid];
+ }
+ }
+
+}
diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php
@@ -0,0 +1,90 @@
+<?php
+
+final class PhabricatorAuthContactNumberQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $objectPHIDs;
+ private $statuses;
+ private $contactNumbers;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withObjectPHIDs(array $object_phids) {
+ $this->objectPHIDs = $object_phids;
+ return $this;
+ }
+
+ public function withStatuses(array $statuses) {
+ $this->statuses = $statuses;
+ return $this;
+ }
+
+ public function withContactNumbers(array $contact_numbers) {
+ $this->contactNumbers = $contact_numbers;
+ return $this;
+ }
+
+ public function newResultObject() {
+ return new PhabricatorAuthContactNumber();
+ }
+
+ protected function loadPage() {
+ return $this->loadStandardPage($this->newResultObject());
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->objectPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'objectPHID IN (%Ls)',
+ $this->objectPHIDs);
+ }
+
+ if ($this->statuses !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'status IN (%Ls)',
+ $this->statuses);
+ }
+
+ if ($this->contactNumbers !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'contactNumber IN (%Ls)',
+ $this->contactNumbers);
+ }
+
+ return $where;
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorAuthApplication';
+ }
+
+}
diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorAuthContactNumberTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhabricatorAuthContactNumberTransaction();
+ }
+
+}
diff --git a/src/applications/auth/storage/PhabricatorAuthContactNumber.php b/src/applications/auth/storage/PhabricatorAuthContactNumber.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/storage/PhabricatorAuthContactNumber.php
@@ -0,0 +1,117 @@
+<?php
+
+
+final class PhabricatorAuthContactNumber
+ extends PhabricatorAuthDAO
+ implements
+ PhabricatorApplicationTransactionInterface,
+ PhabricatorPolicyInterface,
+ PhabricatorDestructibleInterface {
+
+ protected $objectPHID;
+ protected $contactNumber;
+ protected $status;
+ protected $properties = array();
+
+ const STATUS_ACTIVE = 'active';
+ const STATUS_DISABLED = 'disabled';
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_SERIALIZATION => array(
+ 'properties' => self::SERIALIZATION_JSON,
+ ),
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'contactNumber' => 'text255',
+ 'status' => 'text32',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_number' => array(
+ 'columns' => array('contactNumber'),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public static function initializeNewContactNumber($object) {
+ return id(new self())
+ ->setStatus(self::STATUS_ACTIVE)
+ ->setObjectPHID($object->getPHID());
+ }
+
+ public function getPHIDType() {
+ return PhabricatorAuthContactNumberPHIDType::TYPECONST;
+ }
+
+ public function getURI() {
+ return urisprintf('/auth/contact/%s/', $this->getID());
+ }
+
+ public function getObjectName() {
+ return pht('Contact Number %d', $this->getID());
+ }
+
+ public function getDisplayName() {
+ return $this->getContactNumber();
+ }
+
+ public function isDisabled() {
+ return ($this->getStatus() === self::STATUS_DISABLED);
+ }
+
+ public function newIconView() {
+ if ($this->isDisabled()) {
+ return id(new PHUIIconView())
+ ->setIcon('fa-ban', 'grey')
+ ->setTooltip(pht('Disabled'));
+ }
+
+ return id(new PHUIIconView())
+ ->setIcon('fa-mobile', 'green')
+ ->setTooltip(pht('Active Phone Number'));
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getObjectPHID();
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return false;
+ }
+
+
+/* -( PhabricatorDestructibleInterface )----------------------------------- */
+
+
+ public function destroyObjectPermanently(
+ PhabricatorDestructionEngine $engine) {
+ $this->delete();
+ }
+
+
+/* -( PhabricatorApplicationTransactionInterface )------------------------- */
+
+
+ public function getApplicationTransactionEditor() {
+ return new PhabricatorAuthContactNumberEditor();
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PhabricatorAuthContactNumberTransaction();
+ }
+
+
+}
diff --git a/src/applications/auth/storage/PhabricatorAuthContactNumberTransaction.php b/src/applications/auth/storage/PhabricatorAuthContactNumberTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/storage/PhabricatorAuthContactNumberTransaction.php
@@ -0,0 +1,18 @@
+<?php
+
+final class PhabricatorAuthContactNumberTransaction
+ extends PhabricatorModularTransaction {
+
+ public function getApplicationName() {
+ return 'auth';
+ }
+
+ public function getApplicationTransactionType() {
+ return PhabricatorAuthContactNumberPHIDType::TYPECONST;
+ }
+
+ public function getBaseTransactionClass() {
+ return 'PhabricatorAuthContactNumberTransactionType';
+ }
+
+}
diff --git a/src/applications/auth/xaction/PhabricatorAuthContactNumberNumberTransaction.php b/src/applications/auth/xaction/PhabricatorAuthContactNumberNumberTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/xaction/PhabricatorAuthContactNumberNumberTransaction.php
@@ -0,0 +1,87 @@
+<?php
+
+final class PhabricatorAuthContactNumberNumberTransaction
+ extends PhabricatorAuthContactNumberTransactionType {
+
+ const TRANSACTIONTYPE = 'number';
+
+ public function generateOldValue($object) {
+ return $object->getContactNumber();
+ }
+
+ public function generateNewValue($object, $value) {
+ $number = new PhabricatorPhoneNumber($value);
+ return $number->toE164();
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setContactNumber($value);
+ }
+
+ public function getTitle() {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ return pht(
+ '%s changed this contact number from %s to %s.',
+ $this->renderAuthor(),
+ $this->renderOldValue(),
+ $this->renderNewValue());
+ }
+
+ public function validateTransactions($object, array $xactions) {
+ $errors = array();
+
+ $current_value = $object->getContactNumber();
+ if ($this->isEmptyTextTransaction($current_value, $xactions)) {
+ $errors[] = $this->newRequiredError(
+ pht('Contact numbers must have a contact number.'));
+ return $errors;
+ }
+
+ $max_length = $object->getColumnMaximumByteLength('contactNumber');
+ foreach ($xactions as $xaction) {
+ $new_value = $xaction->getNewValue();
+ $new_length = strlen($new_value);
+ if ($new_length > $max_length) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Contact numbers can not be longer than %s characters.',
+ new PhutilNumber($max_length)),
+ $xaction);
+ continue;
+ }
+
+ try {
+ new PhabricatorPhoneNumber($new_value);
+ } catch (Exception $ex) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Contact number is invalid: %s',
+ $ex->getMessage()),
+ $xaction);
+ continue;
+ }
+
+ $normal_value = $this->generateNewValue($object, $new_value);
+
+ $other = id(new PhabricatorAuthContactNumberQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withContactNumbers(array($normal_value))
+ ->executeOne();
+
+ if ($other) {
+ if ($other->getID() !== $object->getID()) {
+ $errors[] = $this->newInvalidError(
+ pht('Contact number is already in use.'),
+ $xaction);
+ continue;
+ }
+ }
+
+ }
+
+ return $errors;
+ }
+
+}
diff --git a/src/applications/auth/xaction/PhabricatorAuthContactNumberTransactionType.php b/src/applications/auth/xaction/PhabricatorAuthContactNumberTransactionType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/xaction/PhabricatorAuthContactNumberTransactionType.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class PhabricatorAuthContactNumberTransactionType
+ extends PhabricatorModularTransactionType {}
diff --git a/src/applications/metamta/message/PhabricatorPhoneNumber.php b/src/applications/metamta/message/PhabricatorPhoneNumber.php
--- a/src/applications/metamta/message/PhabricatorPhoneNumber.php
+++ b/src/applications/metamta/message/PhabricatorPhoneNumber.php
@@ -8,13 +8,23 @@
public function __construct($raw_number) {
$number = preg_replace('/[^\d]+/', '', $raw_number);
- if (!preg_match('/^[1-9]\d{1,14}\z/', $number)) {
+ if (!preg_match('/^[1-9]\d{9,14}\z/', $number)) {
throw new Exception(
pht(
- 'Phone number ("%s") is not in a recognized format.',
+ 'Phone number ("%s") is not in a recognized format: expected a '.
+ 'US number like "(555) 555-5555", or an international number '.
+ 'like "+55 5555 555555".',
$raw_number));
}
+ // If the number didn't start with "+" and has has 10 digits, assume it is
+ // a US number with no country code prefix, like "(555) 555-5555".
+ if (!preg_match('/^[+]/', $raw_number)) {
+ if (strlen($number) === 10) {
+ $number = '1'.$number;
+ }
+ }
+
$this->number = $number;
}
diff --git a/src/applications/metamta/message/__tests__/PhabricatorPhoneNumberTestCase.php b/src/applications/metamta/message/__tests__/PhabricatorPhoneNumberTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/applications/metamta/message/__tests__/PhabricatorPhoneNumberTestCase.php
@@ -0,0 +1,37 @@
+<?php
+
+final class PhabricatorPhoneNumberTestCase
+ extends PhabricatorTestCase {
+
+ public function testNumberNormalization() {
+ $map = array(
+ '+15555555555' => '+15555555555',
+ '+1 (555) 555-5555' => '+15555555555',
+ '(555) 555-5555' => '+15555555555',
+
+ '' => false,
+ '1-800-CALL-SAUL' => false,
+ );
+
+ foreach ($map as $input => $expect) {
+ $caught = null;
+ try {
+ $actual = id(new PhabricatorPhoneNumber($input))
+ ->toE164();
+ } catch (Exception $ex) {
+ $caught = $ex;
+ }
+
+ $this->assertEqual(
+ (bool)$caught,
+ ($expect === false),
+ pht('Exception raised by: %s', $input));
+
+ if ($expect !== false) {
+ $this->assertEqual($expect, $actual, pht('E164 of: %s', $input));
+ }
+ }
+
+ }
+
+}
diff --git a/src/applications/search/view/PhabricatorSearchResultView.php b/src/applications/search/view/PhabricatorSearchResultView.php
--- a/src/applications/search/view/PhabricatorSearchResultView.php
+++ b/src/applications/search/view/PhabricatorSearchResultView.php
@@ -126,7 +126,7 @@
}
// Go through the string one display glyph at a time. If a glyph starts
- // on a highlighted byte position, turn on highlighting for the nubmer
+ // on a highlighted byte position, turn on highlighting for the number
// of matching bytes. If a query searches for "e" and the document contains
// an "e" followed by a bunch of combining marks, this will correctly
// highlight the entire glyph.
diff --git a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
new file mode 100644
--- /dev/null
+++ b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
@@ -0,0 +1,69 @@
+<?php
+
+final class PhabricatorContactNumbersSettingsPanel
+ extends PhabricatorSettingsPanel {
+
+ public function getPanelKey() {
+ return 'contact';
+ }
+
+ public function getPanelName() {
+ return pht('Contact Numbers');
+ }
+
+ public function getPanelGroupKey() {
+ return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
+ }
+
+ public function processRequest(AphrontRequest $request) {
+ $user = $this->getUser();
+ $viewer = $request->getUser();
+
+ $numbers = id(new PhabricatorAuthContactNumberQuery())
+ ->setViewer($viewer)
+ ->withObjectPHIDs(array($user->getPHID()))
+ ->execute();
+
+ $rows = array();
+ foreach ($numbers as $number) {
+ $rows[] = array(
+ $number->newIconView(),
+ phutil_tag(
+ 'a',
+ array(
+ 'href' => $number->getURI(),
+ ),
+ $number->getDisplayName()),
+ phabricator_datetime($number->getDateCreated(), $viewer),
+ );
+ }
+
+ $table = id(new AphrontTableView($rows))
+ ->setNoDataString(
+ pht("You haven't added any contact numbers to your account."))
+ ->setHeaders(
+ array(
+ null,
+ pht('Number'),
+ pht('Created'),
+ ))
+ ->setColumnClasses(
+ array(
+ null,
+ 'wide pri',
+ 'right',
+ ));
+
+ $buttons = array();
+
+ $buttons[] = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setIcon('fa-plus')
+ ->setText(pht('Add Contact Number'))
+ ->setHref('/auth/contact/edit/')
+ ->setColor(PHUIButtonView::GREY);
+
+ return $this->newBox(pht('Contact Numbers'), $table, $buttons);
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 2, 2:16 AM (20 h, 24 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7083429
Default Alt Text
D19988.id47722.diff (32 KB)
Attached To
Mode
D19988: Add "Contact Numbers" so we can send users SMS mesages
Attached
Detach File
Event Timeline
Log In to Comment