diff --git a/resources/sql/autopatches/20190802.email.01.storage.sql b/resources/sql/autopatches/20190802.email.01.storage.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20190802.email.01.storage.sql @@ -0,0 +1,12 @@ +CREATE TABLE {$NAMESPACE}_phortune.phortune_accountemail ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + accountPHID VARBINARY(64) NOT NULL, + authorPHID VARBINARY(64) NOT NULL, + address VARCHAR(128) NOT NULL COLLATE {$COLLATE_SORT}, + status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, + addressKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, + accessKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20190802.email.02.xaction.sql b/resources/sql/autopatches/20190802.email.02.xaction.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20190802.email.02.xaction.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_phortune.phortune_accountemailtransaction ( + 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 @@ -5227,6 +5227,19 @@ 'PhortuneAccountEditController' => 'applications/phortune/controller/account/PhortuneAccountEditController.php', 'PhortuneAccountEditEngine' => 'applications/phortune/editor/PhortuneAccountEditEngine.php', 'PhortuneAccountEditor' => 'applications/phortune/editor/PhortuneAccountEditor.php', + 'PhortuneAccountEmail' => 'applications/phortune/storage/PhortuneAccountEmail.php', + 'PhortuneAccountEmailAddressTransaction' => 'applications/phortune/xaction/PhortuneAccountEmailAddressTransaction.php', + 'PhortuneAccountEmailAddressesController' => 'applications/phortune/controller/account/PhortuneAccountEmailAddressesController.php', + 'PhortuneAccountEmailEditController' => 'applications/phortune/controller/account/PhortuneAccountEmailEditController.php', + 'PhortuneAccountEmailEditEngine' => 'applications/phortune/editor/PhortuneAccountEmailEditEngine.php', + 'PhortuneAccountEmailEditor' => 'applications/phortune/editor/PhortuneAccountEmailEditor.php', + 'PhortuneAccountEmailPHIDType' => 'applications/phortune/phid/PhortuneAccountEmailPHIDType.php', + 'PhortuneAccountEmailQuery' => 'applications/phortune/query/PhortuneAccountEmailQuery.php', + 'PhortuneAccountEmailStatus' => 'applications/phortune/constants/PhortuneAccountEmailStatus.php', + 'PhortuneAccountEmailTransaction' => 'applications/phortune/storage/PhortuneAccountEmailTransaction.php', + 'PhortuneAccountEmailTransactionQuery' => 'applications/phortune/query/PhortuneAccountEmailTransactionQuery.php', + 'PhortuneAccountEmailTransactionType' => 'applications/phortune/xaction/PhortuneAccountEmailTransactionType.php', + 'PhortuneAccountEmailViewController' => 'applications/phortune/controller/account/PhortuneAccountEmailViewController.php', 'PhortuneAccountHasMemberEdgeType' => 'applications/phortune/edge/PhortuneAccountHasMemberEdgeType.php', 'PhortuneAccountListController' => 'applications/phortune/controller/account/PhortuneAccountListController.php', 'PhortuneAccountManagersController' => 'applications/phortune/controller/account/PhortuneAccountManagersController.php', @@ -11759,6 +11772,24 @@ 'PhortuneAccountEditController' => 'PhortuneController', 'PhortuneAccountEditEngine' => 'PhabricatorEditEngine', 'PhortuneAccountEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhortuneAccountEmail' => array( + 'PhortuneDAO', + 'PhabricatorApplicationTransactionInterface', + 'PhabricatorPolicyInterface', + 'PhabricatorExtendedPolicyInterface', + ), + 'PhortuneAccountEmailAddressTransaction' => 'PhortuneAccountEmailTransactionType', + 'PhortuneAccountEmailAddressesController' => 'PhortuneAccountProfileController', + 'PhortuneAccountEmailEditController' => 'PhortuneAccountController', + 'PhortuneAccountEmailEditEngine' => 'PhabricatorEditEngine', + 'PhortuneAccountEmailEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhortuneAccountEmailPHIDType' => 'PhabricatorPHIDType', + 'PhortuneAccountEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhortuneAccountEmailStatus' => 'Phobject', + 'PhortuneAccountEmailTransaction' => 'PhabricatorModularTransaction', + 'PhortuneAccountEmailTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhortuneAccountEmailTransactionType' => 'PhabricatorModularTransactionType', + 'PhortuneAccountEmailViewController' => 'PhortuneAccountController', 'PhortuneAccountHasMemberEdgeType' => 'PhabricatorEdgeType', 'PhortuneAccountListController' => 'PhortuneController', 'PhortuneAccountManagersController' => 'PhortuneAccountProfileController', 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 @@ -80,8 +80,18 @@ '' => 'PhortuneAccountManagersController', 'add/' => 'PhortuneAccountAddManagerController', ), + 'addresses/' => array( + '' => 'PhortuneAccountEmailAddressesController', + $this->getEditRoutePattern('edit/') + => 'PhortuneAccountEmailEditController', + ), ), ), + 'address/' => array( + '(?P\d+)/' => 'PhortuneAccountEmailViewController', + $this->getEditRoutePattern('edit/') + => 'PhortuneAccountEmailEditController', + ), 'product/' => array( '' => 'PhortuneProductListController', 'view/(?P\d+)/' => 'PhortuneProductViewController', diff --git a/src/applications/phortune/constants/PhortuneAccountEmailStatus.php b/src/applications/phortune/constants/PhortuneAccountEmailStatus.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/constants/PhortuneAccountEmailStatus.php @@ -0,0 +1,31 @@ + array( + 'name' => pht('Active'), + 'closed' => false, + ), + self::STATUS_DISABLED => array( + 'name' => pht('Disabled'), + 'closed' => true, + ), + self::STATUS_UNSUBSCRIBED => array( + 'name' => pht('Unsubscribed'), + 'closed' => true, + ), + ); + } + +} diff --git a/src/applications/phortune/controller/account/PhortuneAccountController.php b/src/applications/phortune/controller/account/PhortuneAccountController.php --- a/src/applications/phortune/controller/account/PhortuneAccountController.php +++ b/src/applications/phortune/controller/account/PhortuneAccountController.php @@ -9,6 +9,11 @@ return $this->account; } + protected function setAccount(PhortuneAccount $account) { + $this->account = $account; + return $this; + } + protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); @@ -29,7 +34,6 @@ return $this->loadAccountForEdit(); } - protected function loadAccountForEdit() { $viewer = $this->getViewer(); $request = $this->getRequest(); diff --git a/src/applications/phortune/controller/account/PhortuneAccountEmailAddressesController.php b/src/applications/phortune/controller/account/PhortuneAccountEmailAddressesController.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/controller/account/PhortuneAccountEmailAddressesController.php @@ -0,0 +1,90 @@ +loadAccount(); + if ($response) { + return $response; + } + + $account = $this->getAccount(); + $title = $account->getName(); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Email Addresses')); + + $header = $this->buildHeaderView(); + $addresses = $this->buildAddressesSection($account); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter( + array( + $addresses, + )); + + $navigation = $this->buildSideNavView('addresses'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setNavigation($navigation) + ->appendChild($view); + } + + private function buildAddressesSection(PhortuneAccount $account) { + $viewer = $this->getViewer(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $account, + PhabricatorPolicyCapability::CAN_EDIT); + + $id = $account->getID(); + + $add = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Add Address')) + ->setIcon('fa-plus') + ->setWorkflow(!$can_edit) + ->setDisabled(!$can_edit) + ->setHref("/phortune/account/{$id}/addresses/edit/"); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Billing Email Addresses')) + ->addActionLink($add); + + $addresses = id(new PhortuneAccountEmailQuery()) + ->setViewer($viewer) + ->withAccountPHIDs(array($account->getPHID())) + ->execute(); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer) + ->setNoDataString( + pht( + 'There are no billing email addresses associated '. + 'with this account.')); + + $addresses = id(new PhortuneAccountEmailQuery()) + ->setViewer($viewer) + ->withAccountPHIDs(array($account->getPHID())) + ->execute(); + foreach ($addresses as $address) { + $item = id(new PHUIObjectItemView()) + ->setObjectName($address->getObjectName()) + ->setHeader($address->getAddress()) + ->setHref($address->getURI()); + + $list->addItem($item); + } + + return id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setObjectList($list); + } + +} diff --git a/src/applications/phortune/controller/account/PhortuneAccountEmailEditController.php b/src/applications/phortune/controller/account/PhortuneAccountEmailEditController.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/controller/account/PhortuneAccountEmailEditController.php @@ -0,0 +1,28 @@ +setController($this); + + if (!$request->getURIData('id')) { + + if (!$request->getURIData('accountID')) { + return new Aphront404Response(); + } + + $response = $this->loadAccount(); + if ($response) { + return $response; + } + + $account = $this->getAccount(); + + $engine->setAccount($account); + } + + return $engine->buildResponse(); + } +} diff --git a/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php b/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/controller/account/PhortuneAccountEmailViewController.php @@ -0,0 +1,93 @@ +getViewer(); + + $address = id(new PhortuneAccountEmailQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) + ->executeOne(); + if (!$address) { + return new Aphront404Response(); + } + + $account = $address->getAccount(); + $this->setAccount($account); + + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb(pht('Email Addresses'), $account->getEmailAddressesURI()) + ->addTextCrumb($address->getObjectName()) + ->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Account Email: %s', $address->getAddress())); + + $details = $this->newDetailsView($address); + + $timeline = $this->buildTransactionTimeline( + $address, + new PhortuneAccountEmailTransactionQuery()); + $timeline->setShouldTerminate(true); + + $curtain = $this->buildCurtainView($address); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn( + array( + $details, + $timeline, + )); + + return $this->newPage() + ->setTitle($address->getObjectName()) + ->setCrumbs($crumbs) + ->appendChild($view); + + } + + private function buildCurtainView(PhortuneAccountEmail $address) { + $viewer = $this->getViewer(); + $account = $address->getAccount(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $address, + PhabricatorPolicyCapability::CAN_EDIT); + + $edit_uri = $this->getApplicationURI( + urisprintf( + 'address/edit/%d/', + $address->getID())); + + $curtain = $this->newCurtainView($account); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Address')) + ->setIcon('fa-pencil') + ->setHref($edit_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $curtain; + } + + private function newDetailsView(PhortuneAccountEmail $address) { + $viewer = $this->getViewer(); + + $view = id(new PHUIPropertyListView()) + ->setUser($viewer); + + $view->addProperty(pht('Email Address'), $address->getAddress()); + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Email Address Details')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addPropertyList($view); + } +} diff --git a/src/applications/phortune/controller/account/PhortuneAccountProfileController.php b/src/applications/phortune/controller/account/PhortuneAccountProfileController.php --- a/src/applications/phortune/controller/account/PhortuneAccountProfileController.php +++ b/src/applications/phortune/controller/account/PhortuneAccountProfileController.php @@ -82,6 +82,12 @@ $this->getApplicationURI("/account/{$id}/managers/"), 'fa-group'); + $nav->addFilter( + 'addresses', + pht('Email Addresses'), + $this->getApplicationURI("/account/{$id}/addresses/"), + 'fa-envelope-o'); + $nav->selectFilter($filter); return $nav; diff --git a/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php b/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php @@ -0,0 +1,114 @@ +account = $account; + return $this; + } + + public function getAccount() { + return $this->account; + } + + public function getEngineName() { + return pht('Phortune Account Emails'); + } + + public function getEngineApplicationClass() { + return 'PhabricatorPhortuneApplication'; + } + + public function getSummaryHeader() { + return pht('Configure Phortune Account Email Forms'); + } + + public function getSummaryText() { + return pht( + 'Configure creation and editing forms for Phortune Account '. + 'Email Addresses.'); + } + + public function isEngineConfigurable() { + return false; + } + + protected function newEditableObject() { + $viewer = $this->getViewer(); + + $account = $this->getAccount(); + if (!$account) { + $account = new PhortuneAccount(); + } + + return PhortuneAccountEmail::initializeNewAddress( + $account, + $viewer->getPHID()); + } + + protected function newObjectQuery() { + return new PhortuneAccountEmailQuery(); + } + + protected function getObjectCreateTitleText($object) { + return pht('Add Email Address'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Account Email: %s', $object->getAddress()); + } + + protected function getObjectEditShortText($object) { + return pht('%s', $object->getAddress()); + } + + protected function getObjectCreateShortText() { + return pht('Add Email Address'); + } + + protected function getObjectName() { + return pht('Account Email'); + } + + protected function getObjectCreateCancelURI($object) { + return $this->getAccount()->getEmailAddressesURI(); + } + + protected function getEditorURI() { + return $this->getApplication()->getApplicationURI('address/edit/'); + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function buildCustomEditFields($object) { + $viewer = $this->getViewer(); + + if ($this->getIsCreate()) { + $address_field = id(new PhabricatorTextEditField()) + ->setTransactionType( + PhortuneAccountEmailAddressTransaction::TRANSACTIONTYPE) + ->setIsRequired(true); + } else { + $address_field = new PhabricatorStaticEditField(); + } + + $address_field + ->setKey('address') + ->setLabel(pht('Email Address')) + ->setDescription(pht('Email address.')) + ->setConduitTypeDescription(pht('New email address.')) + ->setValue($object->getAddress()); + + return array( + $address_field, + ); + } + +} diff --git a/src/applications/phortune/editor/PhortuneAccountEmailEditor.php b/src/applications/phortune/editor/PhortuneAccountEmailEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/editor/PhortuneAccountEmailEditor.php @@ -0,0 +1,36 @@ +getAddress()), + null); + + throw new PhabricatorApplicationTransactionValidationException($errors); + } + +} diff --git a/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php b/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php @@ -0,0 +1,41 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $email = $objects[$phid]; + + $id = $email->getID(); + + $handle->setName($email->getObjectName()); + } + } + +} diff --git a/src/applications/phortune/query/PhortuneAccountEmailQuery.php b/src/applications/phortune/query/PhortuneAccountEmailQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/query/PhortuneAccountEmailQuery.php @@ -0,0 +1,91 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withAccountPHIDs(array $phids) { + $this->accountPHIDs = $phids; + return $this; + } + + public function newResultObject() { + return new PhortuneAccountEmail(); + } + + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } + + protected function willFilterPage(array $addresses) { + $accounts = id(new PhortuneAccountQuery()) + ->setViewer($this->getViewer()) + ->setParentQuery($this) + ->withPHIDs(mpull($addresses, 'getAccountPHID')) + ->execute(); + $accounts = mpull($accounts, null, 'getPHID'); + + foreach ($addresses as $key => $address) { + $account = idx($accounts, $address->getAccountPHID()); + + if (!$account) { + $this->didRejectResult($addresses[$key]); + unset($addresses[$key]); + continue; + } + + $address->attachAccount($account); + } + + return $addresses; + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'address.id IN (%Ld)', + $this->ids); + } + + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'address.phid IN (%Ls)', + $this->phids); + } + + if ($this->accountPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'address.accountPHID IN (%Ls)', + $this->accountPHIDs); + } + + return $where; + } + + public function getQueryApplicationClass() { + return 'PhabricatorPhortuneApplication'; + } + + protected function getPrimaryTableAlias() { + return 'address'; + } + +} diff --git a/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php b/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php @@ -0,0 +1,10 @@ +getID()); } + public function getEmailAddressesURI() { + return urisprintf( + '/phortune/account/%d/addresses/', + $this->getID()); + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/phortune/storage/PhortuneAccountEmail.php b/src/applications/phortune/storage/PhortuneAccountEmail.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/storage/PhortuneAccountEmail.php @@ -0,0 +1,121 @@ + true, + self::CONFIG_COLUMN_SCHEMA => array( + 'address' => 'sort128', + 'status' => 'text32', + 'addressKey' => 'text32', + 'accessKey' => 'text32', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_account' => array( + 'columns' => array('accountPHID', 'address'), + 'unique' => true, + ), + 'key_address' => array( + 'columns' => array('addressKey'), + ), + ), + ) + parent::getConfiguration(); + } + + public function getPHIDType() { + return PhortuneAccountEmailPHIDType::TYPECONST; + } + + public static function initializeNewAddress( + PhortuneAccount $account, + $author_phid) { + + $address_key = Filesystem::readRandomCharacters(16); + $access_key = Filesystem::readRandomCharacters(16); + $default_status = PhortuneAccountEmailStatus::getDefaultStatusConstant(); + + return id(new self()) + ->setAuthorPHID($author_phid) + ->setAccountPHID($account->getPHID()) + ->setStatus($default_status) + ->attachAccount($account) + ->setAddressKey($address_key) + ->setAccessKey($access_key); + } + + public function attachAccount(PhortuneAccount $account) { + $this->account = $account; + return $this; + } + + public function getAccount() { + return $this->assertAttached($this->account); + } + + public function getObjectName() { + return pht('Account Email %d', $this->getID()); + } + + public function getURI() { + return urisprintf( + '/phortune/address/%d/', + $this->getID()); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + return PhabricatorPolicies::getMostOpenPolicy(); + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + +/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ + + + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { + return array( + array($this->getAccount(), $capability), + ); + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhortuneAccountEmailEditor(); + } + + public function getApplicationTransactionTemplate() { + return new PhortuneAccountEmailTransaction(); + } + +} diff --git a/src/applications/phortune/storage/PhortuneAccountEmailTransaction.php b/src/applications/phortune/storage/PhortuneAccountEmailTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/storage/PhortuneAccountEmailTransaction.php @@ -0,0 +1,18 @@ +getAddress(); + } + + public function applyInternalEffects($object, $value) { + $object->setAddress($value); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('You must provide an email address.')); + } + + $max_length = $object->getColumnMaximumByteLength('address'); + foreach ($xactions as $xaction) { + $old_value = $xaction->getOldValue(); + $new_value = $xaction->getNewValue(); + + $new_length = strlen($new_value); + if ($new_length > $max_length) { + $errors[] = $this->newInvalidError( + pht( + 'The address can be no longer than %s characters.', + new PhutilNumber($max_length)), + $xaction); + continue; + } + + if (!PhabricatorUserEmail::isValidAddress($new_value)) { + $errors[] = $this->newInvalidError( + PhabricatorUserEmail::describeValidAddresses(), + $xaction); + continue; + } + + if ($new_value !== $old_value) { + if (!$this->isNewObject()) { + $errors[] = $this->newInvalidError( + pht( + 'Account email addresses can not be edited once they are '. + 'created. To change the billing address for an account, '. + 'disable the old address and then add a new address.'), + $xaction); + continue; + } + } + + } + + return $errors; + } + +} diff --git a/src/applications/phortune/xaction/PhortuneAccountEmailTransactionType.php b/src/applications/phortune/xaction/PhortuneAccountEmailTransactionType.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/xaction/PhortuneAccountEmailTransactionType.php @@ -0,0 +1,4 @@ +