diff --git a/resources/sql/autopatches/20161025.phortune.contact.1.sql b/resources/sql/autopatches/20161025.phortune.contact.1.sql new file mode 100644 index 0000000000..48bacd1a21 --- /dev/null +++ b/resources/sql/autopatches/20161025.phortune.contact.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phortune.phortune_merchant + ADD contactInfo LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL; diff --git a/src/applications/phortune/controller/PhortuneMerchantEditController.php b/src/applications/phortune/controller/PhortuneMerchantEditController.php index 3a6aad215f..4def6edebf 100644 --- a/src/applications/phortune/controller/PhortuneMerchantEditController.php +++ b/src/applications/phortune/controller/PhortuneMerchantEditController.php @@ -1,183 +1,196 @@ getViewer(); $id = $request->getURIData('id'); if ($id) { $merchant = id(new PhortuneMerchantQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$merchant) { return new Aphront404Response(); } $is_new = false; } else { $this->requireApplicationCapability( PhortuneMerchantCapability::CAPABILITY); $merchant = PhortuneMerchant::initializeNewMerchant($viewer); $merchant->attachMemberPHIDs(array($viewer->getPHID())); $is_new = true; } if ($is_new) { $title = pht('Create Merchant'); $button_text = pht('Create Merchant'); $cancel_uri = $this->getApplicationURI('merchant/'); } else { $title = pht( 'Edit Merchant %d %s', $merchant->getID(), $merchant->getName()); $button_text = pht('Save Changes'); $cancel_uri = $this->getApplicationURI( '/merchant/'.$merchant->getID().'/'); } $e_name = true; $v_name = $merchant->getName(); $v_desc = $merchant->getDescription(); + $v_cont = $merchant->getContactInfo(); $v_members = $merchant->getMemberPHIDs(); $e_members = null; $validation_exception = null; if ($request->isFormPost()) { $v_name = $request->getStr('name'); $v_desc = $request->getStr('desc'); + $v_cont = $request->getStr('cont'); $v_view = $request->getStr('viewPolicy'); $v_edit = $request->getStr('editPolicy'); $v_members = $request->getArr('memberPHIDs'); $type_name = PhortuneMerchantTransaction::TYPE_NAME; $type_desc = PhortuneMerchantTransaction::TYPE_DESCRIPTION; + $type_cont = PhortuneMerchantTransaction::TYPE_CONTACTINFO; $type_edge = PhabricatorTransactions::TYPE_EDGE; $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; $edge_members = PhortuneMerchantHasMemberEdgeType::EDGECONST; $xactions = array(); $xactions[] = id(new PhortuneMerchantTransaction()) ->setTransactionType($type_name) ->setNewValue($v_name); $xactions[] = id(new PhortuneMerchantTransaction()) ->setTransactionType($type_desc) ->setNewValue($v_desc); + $xactions[] = id(new PhortuneMerchantTransaction()) + ->setTransactionType($type_cont) + ->setNewValue($v_cont); + $xactions[] = id(new PhortuneMerchantTransaction()) ->setTransactionType($type_view) ->setNewValue($v_view); $xactions[] = id(new PhortuneMerchantTransaction()) ->setTransactionType($type_edge) ->setMetadataValue('edge:type', $edge_members) ->setNewValue( array( '=' => array_fuse($v_members), )); $editor = id(new PhortuneMerchantEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true); try { $editor->applyTransactions($merchant, $xactions); $id = $merchant->getID(); $merchant_uri = $this->getApplicationURI("merchant/{$id}/"); return id(new AphrontRedirectResponse())->setURI($merchant_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_name = $ex->getShortMessage($type_name); $e_mbmers = $ex->getShortMessage($type_edge); $merchant->setViewPolicy($v_view); } } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($merchant) ->execute(); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendChild( id(new AphrontFormTextControl()) ->setName('name') ->setLabel(pht('Name')) ->setValue($v_name) ->setError($e_name)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setUser($viewer) ->setName('desc') ->setLabel(pht('Description')) ->setValue($v_desc)) + ->appendChild( + id(new PhabricatorRemarkupControl()) + ->setUser($viewer) + ->setName('cont') + ->setLabel(pht('Contact Info')) + ->setValue($v_cont)) ->appendControl( id(new AphrontFormTokenizerControl()) ->setDatasource(new PhabricatorPeopleDatasource()) ->setLabel(pht('Members')) ->setName('memberPHIDs') ->setValue($v_members) ->setError($e_members)) ->appendChild( id(new AphrontFormPolicyControl()) ->setName('viewPolicy') ->setPolicyObject($merchant) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setPolicies($policies)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue($button_text) ->addCancelButton($cancel_uri)); $header = id(new PHUIHeaderView()) ->setHeader($title); $crumbs = $this->buildApplicationCrumbs(); if ($is_new) { $crumbs->addTextCrumb(pht('Create Merchant')); $header->setHeaderIcon('fa-plus-square'); } else { $crumbs->addTextCrumb( pht('Merchant %d', $merchant->getID()), $this->getApplicationURI('/merchant/'.$merchant->getID().'/')); $crumbs->addTextCrumb(pht('Edit')); $header->setHeaderIcon('fa-pencil'); } $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Merchant')) ->setValidationException($validation_exception) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setFooter(array( $box, )); return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->appendChild($view); } } diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php index a0e1100004..cc94da6f4f 100644 --- a/src/applications/phortune/controller/PhortuneMerchantViewController.php +++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php @@ -1,320 +1,318 @@ getViewer(); $id = $request->getURIData('id'); $merchant = id(new PhortuneMerchantQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->executeOne(); if (!$merchant) { return new Aphront404Response(); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($merchant->getName()); $crumbs->setBorder(true); $title = pht( 'Merchant %d %s', $merchant->getID(), $merchant->getName()); $header = id(new PHUIHeaderView()) ->setHeader($merchant->getName()) ->setUser($viewer) ->setPolicyObject($merchant) ->setHeaderIcon('fa-bank'); $providers = id(new PhortunePaymentProviderConfigQuery()) ->setViewer($viewer) ->withMerchantPHIDs(array($merchant->getPHID())) ->execute(); $details = $this->buildDetailsView($merchant, $providers); - $description = $this->buildDescriptionView($merchant); $curtain = $this->buildCurtainView($merchant); $provider_list = $this->buildProviderList( $merchant, $providers); $timeline = $this->buildTransactionTimeline( $merchant, new PhortuneMerchantTransactionQuery()); $timeline->setShouldTerminate(true); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->setMainColumn(array( $details, - $description, $provider_list, $timeline, )); return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->appendChild($view); } private function buildDetailsView( PhortuneMerchant $merchant, array $providers) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($merchant); $status_view = new PHUIStatusListView(); $have_any = false; $any_test = false; foreach ($providers as $provider_config) { $provider = $provider_config->buildProvider(); if ($provider->isEnabled()) { $have_any = true; } if (!$provider->isAcceptingLivePayments()) { $any_test = true; } } if ($have_any) { $status_view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Accepts Payments')) ->setNote(pht('This merchant can accept payments.'))); if ($any_test) { $status_view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow') ->setTarget(pht('Test Mode')) ->setNote(pht('This merchant is accepting test payments.'))); } else { $status_view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Live Mode')) ->setNote(pht('This merchant is accepting live payments.'))); } } else if ($providers) { $status_view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_REJECT, 'red') ->setTarget(pht('No Enabled Providers')) ->setNote( pht( 'All of the payment providers for this merchant are '. 'disabled.'))); } else { $status_view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow') ->setTarget(pht('No Providers')) ->setNote( pht( 'This merchant does not have any payment providers configured '. 'yet, so it can not accept payments. Add a provider.'))); } $view->addProperty(pht('Status'), $status_view); - return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Details')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($view); - } - - private function buildDescriptionView(PhortuneMerchant $merchant) { - $viewer = $this->getViewer(); - $view = id(new PHUIPropertyListView()) - ->setUser($viewer); - $description = $merchant->getDescription(); if (strlen($description)) { $description = new PHUIRemarkupView($viewer, $description); + $view->addSectionHeader( + pht('Description'), + PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent($description); - return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Description')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($view); } - return null; + $contact_info = $merchant->getContactInfo(); + if (strlen($contact_info)) { + $contact_info = new PHUIRemarkupView($viewer, $contact_info); + $view->addSectionHeader( + pht('Contact Info'), + PHUIPropertyListView::ICON_SUMMARY); + $view->addTextContent($contact_info); + } + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Details')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($view); } private function buildCurtainView(PhortuneMerchant $merchant) { $viewer = $this->getRequest()->getUser(); $id = $merchant->getID(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $merchant, PhabricatorPolicyCapability::CAN_EDIT); $curtain = $this->newCurtainView($merchant); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Merchant')) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit) ->setHref($this->getApplicationURI("merchant/edit/{$id}/"))); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('View Orders')) ->setIcon('fa-shopping-cart') ->setHref($this->getApplicationURI("merchant/orders/{$id}/")) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('View Subscriptions')) ->setIcon('fa-moon-o') ->setHref($this->getApplicationURI("merchant/{$id}/subscription/")) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('New Invoice')) ->setIcon('fa-fax') ->setHref($this->getApplicationURI("merchant/{$id}/invoice/new/")) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $member_phids = $merchant->getMemberPHIDs(); $handles = $viewer->loadHandles($member_phids); $member_list = id(new PHUIObjectItemListView()) ->setSimple(true); foreach ($member_phids as $member_phid) { $image_uri = $handles[$member_phid]->getImageURI(); $image_href = $handles[$member_phid]->getURI(); $person = $handles[$member_phid]; $member = id(new PHUIObjectItemView()) ->setImageURI($image_uri) ->setHref($image_href) ->setHeader($person->getFullName()); $member_list->addItem($member); } $curtain->newPanel() ->setHeaderText(pht('Members')) ->appendChild($member_list); return $curtain; } private function buildProviderList( PhortuneMerchant $merchant, array $providers) { $viewer = $this->getRequest()->getUser(); $id = $merchant->getID(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $merchant, PhabricatorPolicyCapability::CAN_EDIT); $provider_list = id(new PHUIObjectItemListView()) ->setFlush(true) ->setNoDataString(pht('This merchant has no payment providers.')); foreach ($providers as $provider_config) { $provider = $provider_config->buildProvider(); $provider_id = $provider_config->getID(); $item = id(new PHUIObjectItemView()) ->setHeader($provider->getName()); if ($provider->isEnabled()) { if ($provider->isAcceptingLivePayments()) { $item->setStatusIcon('fa-check green'); } else { $item->setStatusIcon('fa-warning yellow'); $item->addIcon('fa-exclamation-triangle', pht('Test Mode')); } $item->addAttribute($provider->getConfigureProvidesDescription()); } else { // Don't show disabled providers to users who can't manage the merchant // account. if (!$can_edit) { continue; } $item->setDisabled(true); $item->addAttribute( phutil_tag('em', array(), pht('This payment provider is disabled.'))); } if ($can_edit) { $edit_uri = $this->getApplicationURI( "/provider/edit/{$provider_id}/"); $disable_uri = $this->getApplicationURI( "/provider/disable/{$provider_id}/"); if ($provider->isEnabled()) { $disable_icon = 'fa-times'; $disable_name = pht('Disable'); } else { $disable_icon = 'fa-check'; $disable_name = pht('Enable'); } $item->addAction( id(new PHUIListItemView()) ->setIcon($disable_icon) ->setHref($disable_uri) ->setName($disable_name) ->setWorkflow(true)); $item->addAction( id(new PHUIListItemView()) ->setIcon('fa-pencil') ->setHref($edit_uri) ->setName(pht('Edit'))); } $provider_list->addItem($item); } $add_action = id(new PHUIButtonView()) ->setTag('a') ->setHref($this->getApplicationURI('provider/edit/?merchantID='.$id)) ->setText(pht('Add Payment Provider')) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit) ->setIcon('fa-plus'); $header = id(new PHUIHeaderView()) ->setHeader(pht('Payment Providers')) ->addActionLink($add_action); return id(new PHUIObjectBoxView()) ->setHeader($header) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList($provider_list); } } diff --git a/src/applications/phortune/editor/PhortuneMerchantEditor.php b/src/applications/phortune/editor/PhortuneMerchantEditor.php index 1c659c0d5f..20f329b202 100644 --- a/src/applications/phortune/editor/PhortuneMerchantEditor.php +++ b/src/applications/phortune/editor/PhortuneMerchantEditor.php @@ -1,109 +1,117 @@ getTransactionType()) { case PhortuneMerchantTransaction::TYPE_NAME: return $object->getName(); case PhortuneMerchantTransaction::TYPE_DESCRIPTION: return $object->getDescription(); + case PhortuneMerchantTransaction::TYPE_CONTACTINFO: + return $object->getContactInfo(); } return parent::getCustomTransactionOldValue($object, $xaction); } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhortuneMerchantTransaction::TYPE_NAME: case PhortuneMerchantTransaction::TYPE_DESCRIPTION: + case PhortuneMerchantTransaction::TYPE_CONTACTINFO: return $xaction->getNewValue(); } return parent::getCustomTransactionNewValue($object, $xaction); } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhortuneMerchantTransaction::TYPE_NAME: $object->setName($xaction->getNewValue()); return; case PhortuneMerchantTransaction::TYPE_DESCRIPTION: $object->setDescription($xaction->getNewValue()); return; + case PhortuneMerchantTransaction::TYPE_CONTACTINFO: + $object->setContactInfo($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhortuneMerchantTransaction::TYPE_NAME: case PhortuneMerchantTransaction::TYPE_DESCRIPTION: + case PhortuneMerchantTransaction::TYPE_CONTACTINFO: return; } return parent::applyCustomExternalTransaction($object, $xaction); } protected function validateTransaction( PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhortuneMerchantTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( $object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Required'), pht('Merchant name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; } } diff --git a/src/applications/phortune/storage/PhortuneMerchant.php b/src/applications/phortune/storage/PhortuneMerchant.php index 2f09abd6d7..fef1728014 100644 --- a/src/applications/phortune/storage/PhortuneMerchant.php +++ b/src/applications/phortune/storage/PhortuneMerchant.php @@ -1,100 +1,102 @@ setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) ->attachMemberPHIDs(array()); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text255', 'description' => 'text', + 'contactInfo' => 'text', ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhortuneMerchantPHIDType::TYPECONST); } public function getMemberPHIDs() { return $this->assertAttached($this->memberPHIDs); } public function attachMemberPHIDs(array $member_phids) { $this->memberPHIDs = $member_phids; return $this; } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new PhortuneMerchantEditor(); } public function getApplicationTransactionObject() { return $this; } public function getApplicationTransactionTemplate() { return new PhortuneMerchantTransaction(); } public function willRenderTimeline( PhabricatorApplicationTransactionView $timeline, AphrontRequest $request) { return $timeline; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return PhabricatorPolicies::POLICY_NOONE; } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { $members = array_fuse($this->getMemberPHIDs()); if (isset($members[$viewer->getPHID()])) { return true; } return false; } public function describeAutomaticCapability($capability) { return pht("A merchant's members an always view and edit it."); } } diff --git a/src/applications/phortune/storage/PhortuneMerchantTransaction.php b/src/applications/phortune/storage/PhortuneMerchantTransaction.php index 32c32b0778..9c284ca7fd 100644 --- a/src/applications/phortune/storage/PhortuneMerchantTransaction.php +++ b/src/applications/phortune/storage/PhortuneMerchantTransaction.php @@ -1,75 +1,83 @@ getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_NAME: if ($old === null) { return pht( '%s created this merchant.', $this->renderHandleLink($author_phid)); } else { return pht( '%s renamed this merchant from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } break; case self::TYPE_DESCRIPTION: return pht( '%s updated the description for this merchant.', $this->renderHandleLink($author_phid)); + case self::TYPE_CONTACTINFO: + return pht( + '%s updated the contact information for this merchant.', + $this->renderHandleLink($author_phid)); } return parent::getTitle(); } public function shouldHide() { $old = $this->getOldValue(); switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: + case self::TYPE_CONTACTINFO: return ($old === null); } return parent::shouldHide(); } public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: return ($this->getOldValue() !== null); + case self::TYPE_CONTACTINFO: + return ($this->getOldValue() !== null); } return parent::hasChangeDetails(); } public function renderChangeDetails(PhabricatorUser $viewer) { return $this->renderTextCorpusChangeDetails( $viewer, $this->getOldValue(), $this->getNewValue()); } }