Page MenuHomePhabricator

D20738.diff
No OneTemporary

D20738.diff

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
@@ -5237,7 +5237,11 @@
'PhortuneAccountEmailEditor' => 'applications/phortune/editor/PhortuneAccountEmailEditor.php',
'PhortuneAccountEmailPHIDType' => 'applications/phortune/phid/PhortuneAccountEmailPHIDType.php',
'PhortuneAccountEmailQuery' => 'applications/phortune/query/PhortuneAccountEmailQuery.php',
+ 'PhortuneAccountEmailRotateController' => 'applications/phortune/controller/account/PhortuneAccountEmailRotateController.php',
+ 'PhortuneAccountEmailRotateTransaction' => 'applications/phortune/xaction/PhortuneAccountEmailRotateTransaction.php',
'PhortuneAccountEmailStatus' => 'applications/phortune/constants/PhortuneAccountEmailStatus.php',
+ 'PhortuneAccountEmailStatusController' => 'applications/phortune/controller/account/PhortuneAccountEmailStatusController.php',
+ 'PhortuneAccountEmailStatusTransaction' => 'applications/phortune/xaction/PhortuneAccountEmailStatusTransaction.php',
'PhortuneAccountEmailTransaction' => 'applications/phortune/storage/PhortuneAccountEmailTransaction.php',
'PhortuneAccountEmailTransactionQuery' => 'applications/phortune/query/PhortuneAccountEmailTransactionQuery.php',
'PhortuneAccountEmailTransactionType' => 'applications/phortune/xaction/PhortuneAccountEmailTransactionType.php',
@@ -5296,6 +5300,7 @@
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneExternalController' => 'applications/phortune/controller/external/PhortuneExternalController.php',
'PhortuneExternalOverviewController' => 'applications/phortune/controller/external/PhortuneExternalOverviewController.php',
+ 'PhortuneExternalUnsubscribeController' => 'applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php',
'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
@@ -11815,7 +11820,11 @@
'PhortuneAccountEmailEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneAccountEmailPHIDType' => 'PhabricatorPHIDType',
'PhortuneAccountEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhortuneAccountEmailRotateController' => 'PhortuneAccountController',
+ 'PhortuneAccountEmailRotateTransaction' => 'PhortuneAccountEmailTransactionType',
'PhortuneAccountEmailStatus' => 'Phobject',
+ 'PhortuneAccountEmailStatusController' => 'PhortuneAccountController',
+ 'PhortuneAccountEmailStatusTransaction' => 'PhortuneAccountEmailTransactionType',
'PhortuneAccountEmailTransaction' => 'PhabricatorModularTransaction',
'PhortuneAccountEmailTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneAccountEmailTransactionType' => 'PhabricatorModularTransactionType',
@@ -11883,6 +11892,7 @@
'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneExternalController' => 'PhortuneController',
'PhortuneExternalOverviewController' => 'PhortuneExternalController',
+ 'PhortuneExternalUnsubscribeController' => 'PhortuneExternalController',
'PhortuneInvoiceView' => 'AphrontTagView',
'PhortuneLandingController' => 'PhortuneController',
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
--- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php
+++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
@@ -87,7 +87,12 @@
),
'addresses/' => array(
'' => 'PhortuneAccountEmailAddressesController',
- '(?P<id>\d+)/' => 'PhortuneAccountEmailViewController',
+ '(?P<addressID>\d+)/' => array(
+ '' => 'PhortuneAccountEmailViewController',
+ 'rotate/' => 'PhortuneAccountEmailRotateController',
+ '(?P<action>disable|enable)/'
+ => 'PhortuneAccountEmailStatusController',
+ ),
$this->getEditRoutePattern('edit/')
=> 'PhortuneAccountEmailEditController',
),
@@ -106,6 +111,7 @@
),
'external/(?P<addressKey>[^/]+)/(?P<accessKey>[^/]+)/' => array(
'' => 'PhortuneExternalOverviewController',
+ 'unsubscribe/' => 'PhortuneExternalUnsubscribeController',
),
'merchant/' => array(
$this->getQueryRoutePattern()
diff --git a/src/applications/phortune/controller/account/PhortuneAccountEmailRotateController.php b/src/applications/phortune/controller/account/PhortuneAccountEmailRotateController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/controller/account/PhortuneAccountEmailRotateController.php
@@ -0,0 +1,62 @@
+<?php
+
+final class PhortuneAccountEmailRotateController
+ extends PhortuneAccountController {
+
+ protected function shouldRequireAccountEditCapability() {
+ return true;
+ }
+
+ protected function handleAccountRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
+ $account = $this->getAccount();
+
+ $address = id(new PhortuneAccountEmailQuery())
+ ->setViewer($viewer)
+ ->withAccountPHIDs(array($account->getPHID()))
+ ->withIDs(array($request->getURIData('addressID')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$address) {
+ return new Aphront404Response();
+ }
+
+ $address_uri = $address->getURI();
+
+ if ($request->isFormOrHisecPost()) {
+ $xactions = array();
+
+ $xactions[] = $address->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhortuneAccountEmailRotateTransaction::TRANSACTIONTYPE)
+ ->setNewValue(true);
+
+ $address->getApplicationTransactionEditor()
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnMissingFields(true)
+ ->setContinueOnNoEffect(true)
+ ->setCancelURI($address_uri)
+ ->applyTransactions($address, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($address_uri);
+ }
+
+ return $this->newDialog()
+ ->setTitle(pht('Rotate Access Key'))
+ ->appendParagraph(
+ pht(
+ 'Rotate the access key for email address %s?',
+ phutil_tag('strong', array(), $address->getAddress())))
+ ->appendParagraph(
+ pht(
+ 'Existing access links which have been sent to this email address '.
+ 'will stop working.'))
+ ->addSubmitButton(pht('Rotate Access Key'))
+ ->addCancelButton($address_uri);
+ }
+}
diff --git a/src/applications/phortune/controller/account/PhortuneAccountEmailStatusController.php b/src/applications/phortune/controller/account/PhortuneAccountEmailStatusController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/controller/account/PhortuneAccountEmailStatusController.php
@@ -0,0 +1,137 @@
+<?php
+
+final class PhortuneAccountEmailStatusController
+ extends PhortuneAccountController {
+
+ protected function shouldRequireAccountEditCapability() {
+ return true;
+ }
+
+ protected function handleAccountRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
+ $account = $this->getAccount();
+
+ $address = id(new PhortuneAccountEmailQuery())
+ ->setViewer($viewer)
+ ->withAccountPHIDs(array($account->getPHID()))
+ ->withIDs(array($request->getURIData('addressID')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$address) {
+ return new Aphront404Response();
+ }
+
+ $address_uri = $address->getURI();
+
+ $is_enable = false;
+ $is_disable = false;
+
+ $old_status = $address->getStatus();
+ switch ($request->getURIData('action')) {
+ case 'enable':
+ if ($old_status === PhortuneAccountEmailStatus::STATUS_ACTIVE) {
+ return $this->newDialog()
+ ->setTitle(pht('Already Enabled'))
+ ->appendParagraph(
+ pht(
+ 'You can not enable this address because it is already '.
+ 'active.'))
+ ->addCancelButton($address_uri);
+ }
+
+ if ($old_status === PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED) {
+ return $this->newDialog()
+ ->setTitle(pht('Permanently Unsubscribed'))
+ ->appendParagraph(
+ pht(
+ 'You can not enable this address because it has been '.
+ 'permanently unsubscribed.'))
+ ->addCancelButton($address_uri);
+ }
+
+ $new_status = PhortuneAccountEmailStatus::STATUS_ACTIVE;
+ $is_enable = true;
+ break;
+ case 'disable':
+ if ($old_status === PhortuneAccountEmailStatus::STATUS_DISABLED) {
+ return $this->newDialog()
+ ->setTitle(pht('Already Disabled'))
+ ->appendParagraph(
+ pht(
+ 'You can not disabled this address because it is already '.
+ 'disabled.'))
+ ->addCancelButton($address_uri);
+ }
+
+ if ($old_status === PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED) {
+ return $this->newDialog()
+ ->setTitle(pht('Permanently Unsubscribed'))
+ ->appendParagraph(
+ pht(
+ 'You can not disable this address because it has been '.
+ 'permanently unsubscribed.'))
+ ->addCancelButton($address_uri);
+ }
+
+ $new_status = PhortuneAccountEmailStatus::STATUS_DISABLED;
+ $is_disable = true;
+ break;
+ default:
+ return new Aphront404Response();
+ }
+
+ if ($request->isFormOrHisecPost()) {
+ $xactions = array();
+
+ $xactions[] = $address->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhortuneAccountEmailStatusTransaction::TRANSACTIONTYPE)
+ ->setNewValue($new_status);
+
+ $address->getApplicationTransactionEditor()
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnMissingFields(true)
+ ->setContinueOnNoEffect(true)
+ ->setCancelURI($address_uri)
+ ->applyTransactions($address, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($address_uri);
+ }
+
+ $dialog = $this->newDialog();
+
+ $body = array();
+
+ if ($is_disable) {
+ $title = pht('Disable Address');
+
+ $body[] = pht(
+ 'This address will no longer receive email, and access links will '.
+ 'no longer function.');
+
+ $submit = pht('Disable Address');
+ } else {
+ $title = pht('Enable Address');
+
+ $body[] = pht(
+ 'This address will receive email again, and existing links '.
+ 'to access order history will work again.');
+
+ $submit = pht('Enable Address');
+ }
+
+ foreach ($body as $graph) {
+ $dialog->appendParagraph($graph);
+ }
+
+ return $dialog
+ ->setTitle($title)
+ ->addCancelButton($address_uri)
+ ->addSubmitButton($submit);
+ }
+}
diff --git a/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php b/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php
--- a/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php
+++ b/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php
@@ -14,7 +14,7 @@
$address = id(new PhortuneAccountEmailQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
- ->withIDs(array($request->getURIData('id')))
+ ->withIDs(array($request->getURIData('addressID')))
->executeOne();
if (!$address) {
return new Aphront404Response();
@@ -83,6 +83,56 @@
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
+ switch ($address->getStatus()) {
+ case PhortuneAccountEmailStatus::STATUS_ACTIVE:
+ $disable_name = pht('Disable Address');
+ $disable_icon = 'fa-times';
+ $can_disable = true;
+ $disable_action = 'disable';
+ break;
+ case PhortuneAccountEmailStatus::STATUS_DISABLED:
+ $disable_name = pht('Enable Address');
+ $disable_icon = 'fa-check';
+ $can_disable = true;
+ $disable_action = 'enable';
+ break;
+ case PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED:
+ $disable_name = pht('Disable Address');
+ $disable_icon = 'fa-times';
+ $can_disable = false;
+ $disable_action = 'disable';
+ break;
+ }
+
+ $disable_uri = $this->getApplicationURI(
+ urisprintf(
+ 'account/%d/addresses/%d/%s/',
+ $account->getID(),
+ $address->getID(),
+ $disable_action));
+
+ $curtain->addAction(
+ id(new PhabricatorActionView())
+ ->setName($disable_name)
+ ->setIcon($disable_icon)
+ ->setHref($disable_uri)
+ ->setDisabled(!$can_disable)
+ ->setWorkflow(true));
+
+ $rotate_uri = $this->getApplicationURI(
+ urisprintf(
+ 'account/%d/addresses/%d/rotate/',
+ $account->getID(),
+ $address->getID()));
+
+ $curtain->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Rotate Access Key'))
+ ->setIcon('fa-refresh')
+ ->setHref($rotate_uri)
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true));
+
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Show External View'))
@@ -100,7 +150,23 @@
$view = id(new PHUIPropertyListView())
->setUser($viewer);
+ $access_key = $address->getAccessKey();
+
+ // This is not a meaningful security barrier: the full plaintext of the
+ // access key is visible on the page in the link target of the "Show
+ // External View" action. It's just here to make it clear "Rotate Access
+ // Key" actually does something.
+
+ $prefix_length = 4;
+ $visible_part = substr($access_key, 0, $prefix_length);
+ $masked_part = str_repeat(
+ "\xE2\x80\xA2",
+ strlen($access_key) - $prefix_length);
+ $access_display = $visible_part.$masked_part;
+ $access_display = phutil_tag('tt', array(), $access_display);
+
$view->addProperty(pht('Email Address'), $address->getAddress());
+ $view->addProperty(pht('Access Key'), $access_display);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Email Address Details'))
diff --git a/src/applications/phortune/controller/external/PhortuneExternalController.php b/src/applications/phortune/controller/external/PhortuneExternalController.php
--- a/src/applications/phortune/controller/external/PhortuneExternalController.php
+++ b/src/applications/phortune/controller/external/PhortuneExternalController.php
@@ -83,7 +83,29 @@
return $dialog;
}
- // TODO: Test that status is good.
+ switch ($email->getStatus()) {
+ case PhortuneAccountEmailStatus::STATUS_ACTIVE:
+ break;
+ case PhortuneAccountEmailStatus::STATUS_DISABLED:
+ return $this->newDialog()
+ ->setTitle(pht('Address Disabled'))
+ ->appendParagraph(
+ pht(
+ 'This email address (%s) has been disabled and no longer has '.
+ 'access to this payment account.',
+ $email_display));
+ case PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED:
+ return $this->newDialog()
+ ->setTitle(pht('Permanently Unsubscribed'))
+ ->appendParagraph(
+ pht(
+ 'This email address (%s) has been permanently unsubscribed '.
+ 'and no longer has access to this payment account.',
+ $email_display));
+ break;
+ default:
+ return new Aphront404Response();
+ }
$this->email = $email;
diff --git a/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php b/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
--- a/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
+++ b/src/applications/phortune/controller/external/PhortuneExternalOverviewController.php
@@ -12,7 +12,14 @@
->setBorder(true);
$header = id(new PHUIHeaderView())
- ->setHeader(pht('Invoices and Receipts: %s', $account->getName()));
+ ->setHeader(pht('Invoices and Receipts: %s', $account->getName()))
+ ->addActionLink(
+ id(new PHUIButtonView())
+ ->setTag('a')
+ ->setIcon('fa-times')
+ ->setText(pht('Unsubscribe'))
+ ->setHref($email->getUnsubscribeURI())
+ ->setWorkflow(true));
$external_view = $this->newExternalView();
$invoices_view = $this->newInvoicesView();
diff --git a/src/applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php b/src/applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php
@@ -0,0 +1,67 @@
+<?php
+
+final class PhortuneExternalUnsubscribeController
+ extends PhortuneExternalController {
+
+ protected function handleExternalRequest(AphrontRequest $request) {
+ $xviewer = $this->getExternalViewer();
+ $email = $this->getAccountEmail();
+ $account = $email->getAccount();
+
+ $email_uri = $email->getExternalURI();
+
+ if ($request->isFormOrHisecPost()) {
+ $xactions = array();
+
+ $xactions[] = $email->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhortuneAccountEmailStatusTransaction::TRANSACTIONTYPE)
+ ->setNewValue(PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED);
+
+ $email->getApplicationTransactionEditor()
+ ->setActor($xviewer)
+ ->setActingAsPHID($email->getPHID())
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnMissingFields(true)
+ ->setContinueOnNoEffect(true)
+ ->setCancelURI($email_uri)
+ ->applyTransactions($email, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($email_uri);
+ }
+
+ $email_display = phutil_tag(
+ 'strong',
+ array(),
+ $email->getAddress());
+
+ $account_display = phutil_tag(
+ 'strong',
+ array(),
+ $account->getName());
+
+ $submit = pht(
+ 'Permanently Unsubscribe (%s)',
+ $email->getAddress());
+
+ return $this->newDialog()
+ ->setTitle(pht('Permanently Unsubscribe'))
+ ->appendParagraph(
+ pht(
+ 'Permanently unsubscribe this email address (%s) from this '.
+ 'payment account (%s)?',
+ $email_display,
+ $account_display))
+ ->appendParagraph(
+ pht(
+ 'You will no longer receive email and access links will no longer '.
+ 'function.'))
+ ->appendParagraph(
+ pht(
+ 'This action is permanent and can not be undone.'))
+ ->addCancelButton($email_uri)
+ ->addSubmitButton($submit);
+
+ }
+
+}
diff --git a/src/applications/phortune/storage/PhortuneAccountEmail.php b/src/applications/phortune/storage/PhortuneAccountEmail.php
--- a/src/applications/phortune/storage/PhortuneAccountEmail.php
+++ b/src/applications/phortune/storage/PhortuneAccountEmail.php
@@ -85,6 +85,13 @@
$this->getAccessKey());
}
+ public function getUnsubscribeURI() {
+ return urisprintf(
+ '/phortune/external/%s/%s/unsubscribe/',
+ $this->getAddressKey(),
+ $this->getAccessKey());
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
diff --git a/src/applications/phortune/xaction/PhortuneAccountEmailRotateTransaction.php b/src/applications/phortune/xaction/PhortuneAccountEmailRotateTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/xaction/PhortuneAccountEmailRotateTransaction.php
@@ -0,0 +1,23 @@
+<?php
+
+final class PhortuneAccountEmailRotateTransaction
+ extends PhortuneAccountEmailTransactionType {
+
+ const TRANSACTIONTYPE = 'rotate';
+
+ public function generateOldValue($object) {
+ return false;
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $access_key = Filesystem::readRandomCharacters(16);
+ $object->setAccessKey($access_key);
+ }
+
+ public function getTitle() {
+ return pht(
+ '%s rotated the access key for this email address.',
+ $this->renderAuthor());
+ }
+
+}
diff --git a/src/applications/phortune/xaction/PhortuneAccountEmailStatusTransaction.php b/src/applications/phortune/xaction/PhortuneAccountEmailStatusTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/xaction/PhortuneAccountEmailStatusTransaction.php
@@ -0,0 +1,23 @@
+<?php
+
+final class PhortuneAccountEmailStatusTransaction
+ extends PhortuneAccountEmailTransactionType {
+
+ const TRANSACTIONTYPE = 'status';
+
+ public function generateOldValue($object) {
+ return $object->getStatus();
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setStatus($value);
+ }
+
+ public function getTitle() {
+ return pht(
+ '%s changed the status for this address to %s.',
+ $this->renderAuthor(),
+ $this->renderNewValue());
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 21, 12:17 PM (1 d, 20 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7696697
Default Alt Text
D20738.diff (21 KB)

Event Timeline