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,12 +2201,14 @@ 'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php', 'PhabricatorAuthContactNumber' => 'applications/auth/storage/PhabricatorAuthContactNumber.php', 'PhabricatorAuthContactNumberController' => 'applications/auth/controller/contact/PhabricatorAuthContactNumberController.php', + 'PhabricatorAuthContactNumberDisableController' => 'applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.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', + 'PhabricatorAuthContactNumberStatusTransaction' => 'applications/auth/xaction/PhabricatorAuthContactNumberStatusTransaction.php', 'PhabricatorAuthContactNumberTransaction' => 'applications/auth/storage/PhabricatorAuthContactNumberTransaction.php', 'PhabricatorAuthContactNumberTransactionQuery' => 'applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php', 'PhabricatorAuthContactNumberTransactionType' => 'applications/auth/xaction/PhabricatorAuthContactNumberTransactionType.php', @@ -7904,12 +7906,14 @@ 'PhabricatorDestructibleInterface', ), 'PhabricatorAuthContactNumberController' => 'PhabricatorAuthController', + 'PhabricatorAuthContactNumberDisableController' => 'PhabricatorAuthContactNumberController', 'PhabricatorAuthContactNumberEditController' => 'PhabricatorAuthContactNumberController', 'PhabricatorAuthContactNumberEditEngine' => 'PhabricatorEditEngine', 'PhabricatorAuthContactNumberEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorAuthContactNumberNumberTransaction' => 'PhabricatorAuthContactNumberTransactionType', 'PhabricatorAuthContactNumberPHIDType' => 'PhabricatorPHIDType', 'PhabricatorAuthContactNumberQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorAuthContactNumberStatusTransaction' => 'PhabricatorAuthContactNumberTransactionType', 'PhabricatorAuthContactNumberTransaction' => 'PhabricatorModularTransaction', 'PhabricatorAuthContactNumberTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorAuthContactNumberTransactionType' => 'PhabricatorModularTransactionType', 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 @@ -111,6 +111,8 @@ 'PhabricatorAuthContactNumberEditController', '(?P[1-9]\d*)/' => 'PhabricatorAuthContactNumberViewController', + '(?Pdisable|enable)/(?P[1-9]\d*)/' => + 'PhabricatorAuthContactNumberDisableController', ), ), diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php @@ -0,0 +1,87 @@ +getViewer(); + $id = $request->getURIData('id'); + + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$number) { + return new Aphront404Response(); + } + + $is_disable = ($request->getURIData('action') == 'disable'); + $id = $number->getID(); + $cancel_uri = $number->getURI(); + + if ($request->isFormPost()) { + $xactions = array(); + + if ($is_disable) { + $new_status = PhabricatorAuthContactNumber::STATUS_DISABLED; + } else { + $new_status = PhabricatorAuthContactNumber::STATUS_ACTIVE; + } + + $xactions[] = id(new PhabricatorAuthContactNumberTransaction()) + ->setTransactionType( + PhabricatorAuthContactNumberStatusTransaction::TRANSACTIONTYPE) + ->setNewValue($new_status); + + $editor = id(new PhabricatorAuthContactNumberEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + try { + $editor->applyTransactions($number, $xactions); + } catch (PhabricatorApplicationTransactionValidationException $ex) { + // This happens when you enable a number which collides with another + // number. + return $this->newDialog() + ->setTitle(pht('Changing Status Failed')) + ->setValidationException($ex) + ->addCancelButton($cancel_uri); + } + + return id(new AphrontRedirectResponse())->setURI($cancel_uri); + } + + $number_display = phutil_tag( + 'strong', + array(), + $number->getDisplayName()); + + if ($is_disable) { + $title = pht('Disable Contact Number'); + $body = pht( + 'Disable the contact number %s?', + $number_display); + $button = pht('Disable Number'); + } else { + $title = pht('Enable Contact Number'); + $body = pht( + 'Enable the contact number %s?', + $number_display); + $button = pht('Enable Number'); + } + + return $this->newDialog() + ->setTitle($title) + ->appendParagraph($body) + ->addSubmitButton($button) + ->addCancelButton($cancel_uri); + } + +} diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php @@ -54,6 +54,10 @@ ->setHeader($number->getObjectName()) ->setPolicyObject($number); + if ($number->isDisabled()) { + $view->setStatus('fa-ban', 'red', pht('Disabled')); + } + return $view; } @@ -92,6 +96,24 @@ ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); + + if ($number->isDisabled()) { + $disable_uri = $this->getApplicationURI("contact/enable/{$id}/"); + $disable_name = pht('Enable Contact Number'); + $disable_icon = 'fa-check'; + } else { + $disable_uri = $this->getApplicationURI("contact/disable/{$id}/"); + $disable_name = pht('Disable Contact Number'); + $disable_icon = 'fa-ban'; + } + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName($disable_name) + ->setIcon($disable_icon) + ->setHref($disable_uri) + ->setWorkflow(true)); + return $curtain; } diff --git a/src/applications/auth/storage/PhabricatorAuthContactNumber.php b/src/applications/auth/storage/PhabricatorAuthContactNumber.php --- a/src/applications/auth/storage/PhabricatorAuthContactNumber.php +++ b/src/applications/auth/storage/PhabricatorAuthContactNumber.php @@ -93,10 +93,32 @@ } public function save() { - $this->uniqueKey = $this->newUniqueKey(); + // We require that active contact numbers be unique, but it's okay to + // disable a number and then reuse it somewhere else. + if ($this->isDisabled()) { + $this->uniqueKey = null; + } else { + $this->uniqueKey = $this->newUniqueKey(); + } + return parent::save(); } + public static function getStatusNameMap() { + return ipull(self::getStatusPropertyMap(), 'name'); + } + + private static function getStatusPropertyMap() { + return array( + self::STATUS_ACTIVE => array( + 'name' => pht('Active'), + ), + self::STATUS_DISABLED => array( + 'name' => pht('Disabled'), + ), + ); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/auth/xaction/PhabricatorAuthContactNumberStatusTransaction.php b/src/applications/auth/xaction/PhabricatorAuthContactNumberStatusTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/xaction/PhabricatorAuthContactNumberStatusTransaction.php @@ -0,0 +1,59 @@ +getStatus(); + } + + public function applyInternalEffects($object, $value) { + $object->setStatus($value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new === PhabricatorAuthContactNumber::STATUS_DISABLED) { + return pht( + '%s disabled this contact number.', + $this->renderAuthor()); + } else { + return pht( + '%s enabled this contact number.', + $this->renderAuthor()); + } + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $map = PhabricatorAuthContactNumber::getStatusNameMap(); + + foreach ($xactions as $xaction) { + $new_value = $xaction->getNewValue(); + + if (!isset($map[$new_value])) { + $errors[] = $this->newInvalidError( + pht( + 'Status ("%s") is not a valid contact number status. Valid '. + 'status constants are: %s.', + $new_value, + implode(', ', array_keys($map))), + $xaction); + continue; + } + + // NOTE: Enabling a contact number may cause us to collide with another + // active contact number. However, there might also be a transaction in + // this group that changes the number itself. Since we can't easily + // predict if we'll collide or not, just let the duplicate key logic + // handle it when we do. + } + + return $errors; + } + +}