Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15417164
D20738.id49447.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
21 KB
Referenced Files
None
Subscribers
None
D20738.id49447.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 21, 3:47 PM (1 d, 22 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7696697
Default Alt Text
D20738.id49447.diff (21 KB)
Attached To
Mode
D20738: Add credential rotation and statuses (disabled, unsubscribed) to Phortune external email
Attached
Detach File
Event Timeline
Log In to Comment