@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Nuance - federate out the design of NuanceSource via NuanceSourceDefinition

Summary: ...and get the basic edit flow "working" for a new NuanceSourceDefinition - the Phabricator Form. ...and fix a dumb bug in the query class so when you redirect to the view page / try to edit an existing NuanceSource you don't fatal.

Test Plan: played around with the edit form and it worked!

Reviewers: epriestley

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Differential Revision: https://secure.phabricator.com/D7585

+557 -82
+11
resources/sql/patches/20131120.nuancesourcetype.sql
··· 1 + ALTER TABLE {$NAMESPACE}_nuance.nuance_source 2 + DROP KEY key_type; 3 + 4 + ALTER TABLE {$NAMESPACE}_nuance.nuance_source 5 + DROP COLUMN type; 6 + 7 + ALTER TABLE {$NAMESPACE}_nuance.nuance_source 8 + ADD type VARCHAR(32) NOT NULL COLLATE utf8_bin AFTER name; 9 + 10 + ALTER TABLE {$NAMESPACE}_nuance.nuance_source 11 + ADD KEY `key_type` (type, dateModified);
+4 -2
src/__phutil_library_map__.php
··· 867 867 'NuancePHIDTypeQueue' => 'applications/nuance/phid/NuancePHIDTypeQueue.php', 868 868 'NuancePHIDTypeRequestor' => 'applications/nuance/phid/NuancePHIDTypeRequestor.php', 869 869 'NuancePHIDTypeSource' => 'applications/nuance/phid/NuancePHIDTypeSource.php', 870 + 'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php', 870 871 'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php', 871 872 'NuanceQueue' => 'applications/nuance/storage/NuanceQueue.php', 872 873 'NuanceQueueEditController' => 'applications/nuance/controller/NuanceQueueEditController.php', ··· 887 888 'NuanceRequestorTransactionQuery' => 'applications/nuance/query/NuanceRequestorTransactionQuery.php', 888 889 'NuanceRequestorViewController' => 'applications/nuance/controller/NuanceRequestorViewController.php', 889 890 'NuanceSource' => 'applications/nuance/storage/NuanceSource.php', 891 + 'NuanceSourceDefinition' => 'applications/nuance/source/NuanceSourceDefinition.php', 890 892 'NuanceSourceEditController' => 'applications/nuance/controller/NuanceSourceEditController.php', 891 893 'NuanceSourceEditor' => 'applications/nuance/editor/NuanceSourceEditor.php', 892 894 'NuanceSourceQuery' => 'applications/nuance/query/NuanceSourceQuery.php', 893 895 'NuanceSourceTransaction' => 'applications/nuance/storage/NuanceSourceTransaction.php', 894 896 'NuanceSourceTransactionComment' => 'applications/nuance/storage/NuanceSourceTransactionComment.php', 895 897 'NuanceSourceTransactionQuery' => 'applications/nuance/query/NuanceSourceTransactionQuery.php', 896 - 'NuanceSourceType' => 'applications/nuance/constants/NuanceSourceType.php', 897 898 'NuanceSourceViewController' => 'applications/nuance/controller/NuanceSourceViewController.php', 898 899 'NuanceTransaction' => 'applications/nuance/storage/NuanceTransaction.php', 899 900 'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php', ··· 3238 3239 'NuancePHIDTypeQueue' => 'PhabricatorPHIDType', 3239 3240 'NuancePHIDTypeRequestor' => 'PhabricatorPHIDType', 3240 3241 'NuancePHIDTypeSource' => 'PhabricatorPHIDType', 3242 + 'NuancePhabricatorFormSourceDefinition' => 'NuanceSourceDefinition', 3241 3243 'NuanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3242 3244 'NuanceQueue' => 3243 3245 array( ··· 3266 3268 0 => 'NuanceDAO', 3267 3269 1 => 'PhabricatorPolicyInterface', 3268 3270 ), 3271 + 'NuanceSourceDefinition' => 'Phobject', 3269 3272 'NuanceSourceEditController' => 'NuanceController', 3270 3273 'NuanceSourceEditor' => 'PhabricatorApplicationTransactionEditor', 3271 3274 'NuanceSourceQuery' => 'NuanceQuery', 3272 3275 'NuanceSourceTransaction' => 'NuanceTransaction', 3273 3276 'NuanceSourceTransactionComment' => 'PhabricatorApplicationTransactionComment', 3274 3277 'NuanceSourceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 3275 - 'NuanceSourceType' => 'NuanceConstants', 3276 3278 'NuanceSourceViewController' => 'NuanceController', 3277 3279 'NuanceTransaction' => 'PhabricatorApplicationTransaction', 3278 3280 'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler',
+4
src/applications/nuance/application/PhabricatorApplicationNuance.php
··· 23 23 return true; 24 24 } 25 25 26 + public function getBaseURI() { 27 + return '/nuance/'; 28 + } 29 + 26 30 public function getRoutes() { 27 31 return array( 28 32 '/nuance/' => array(
-22
src/applications/nuance/constants/NuanceSourceType.php
··· 1 - <?php 2 - 3 - final class NuanceSourceType extends NuanceConstants { 4 - 5 - /* internal source types */ 6 - const PHABRICATOR_FORM = 1; 7 - 8 - /* social media source types */ 9 - const TWITTER = 101; 10 - 11 - /* engineering media source types */ 12 - const GITHUB = 201; 13 - 14 - 15 - public static function getSelectOptions() { 16 - 17 - return array( 18 - self::PHABRICATOR_FORM => pht('Phabricator Form'), 19 - ); 20 - } 21 - 22 - }
+8 -51
src/applications/nuance/controller/NuanceSourceEditController.php
··· 28 28 29 29 if ($is_new) { 30 30 $source = NuanceSource::initializeNewSource($user); 31 - $title = pht('Create Source'); 32 31 } else { 33 32 $source = id(new NuanceSourceQuery()) 34 33 ->setViewer($user) ··· 39 38 PhabricatorPolicyCapability::CAN_EDIT, 40 39 )) 41 40 ->executeOne(); 42 - $title = pht('Edit Source'); 43 41 } 44 42 45 43 if (!$source) { 46 44 return new Aphront404Response(); 47 45 } 48 46 49 - $error_view = null; 50 - $e_name = null; 51 - if ($request->isFormPost()) { 52 - $error_view = id(new AphrontErrorView()) 53 - ->setTitle(pht('This does not work at all yet.')); 54 - } 47 + $definition = NuanceSourceDefinition::getDefinitionForSource($source); 48 + $definition->setActor($user); 55 49 56 - $policies = id(new PhabricatorPolicyQuery()) 57 - ->setViewer($user) 58 - ->setObject($source) 59 - ->execute(); 50 + $response = $definition->buildEditLayout($request); 51 + if ($response instanceof AphrontResponse) { 52 + return $response; 53 + } 54 + $layout = $response; 60 55 61 56 $crumbs = $this->buildApplicationCrumbs(); 62 - 63 - $form = id(new AphrontFormView()) 64 - ->setUser($user) 65 - ->appendChild( 66 - id(new AphrontFormTextControl()) 67 - ->setLabel(pht('Name')) 68 - ->setName('name') 69 - ->setError($e_name) 70 - ->setValue($source->getName())) 71 - ->appendChild( 72 - id(new AphrontFormSelectControl()) 73 - ->setLabel(pht('Type')) 74 - ->setName('type') 75 - ->setOptions(NuanceSourceType::getSelectOptions()) 76 - ->setValue($source->getType())) 77 - ->appendChild( 78 - id(new AphrontFormPolicyControl()) 79 - ->setUser($user) 80 - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 81 - ->setPolicyObject($source) 82 - ->setPolicies($policies) 83 - ->setName('viewPolicy')) 84 - ->appendChild( 85 - id(new AphrontFormPolicyControl()) 86 - ->setUser($user) 87 - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 88 - ->setPolicyObject($source) 89 - ->setPolicies($policies) 90 - ->setName('editPolicy')) 91 - ->appendChild( 92 - id(new AphrontFormSubmitControl()) 93 - ->setValue(pht('Save'))); 94 - 95 - $layout = id(new PHUIObjectBoxView()) 96 - ->setHeaderText($title) 97 - ->setFormError($error_view) 98 - ->setForm($form); 99 - 100 57 return $this->buildApplicationPage( 101 58 array( 102 59 $crumbs, 103 60 $layout, 104 61 ), 105 62 array( 106 - 'title' => $title, 63 + 'title' => $definition->getEditTitle(), 107 64 'device' => true)); 108 65 } 109 66 }
+99 -5
src/applications/nuance/controller/NuanceSourceViewController.php
··· 18 18 19 19 public function processRequest() { 20 20 $request = $this->getRequest(); 21 - $user = $request->getUser(); 21 + $viewer = $request->getUser(); 22 22 23 23 $source_id = $this->getSourceID(); 24 24 $source = id(new NuanceSourceQuery()) 25 - ->setViewer($user) 25 + ->setViewer($viewer) 26 26 ->withIDs(array($source_id)) 27 27 ->executeOne(); 28 28 ··· 30 30 return new Aphront404Response(); 31 31 } 32 32 33 + $source_phid = $source->getPHID(); 34 + $xactions = id(new NuanceSourceTransactionQuery()) 35 + ->setViewer($viewer) 36 + ->withObjectPHIDs(array($source_phid)) 37 + ->execute(); 38 + 39 + $engine = id(new PhabricatorMarkupEngine()) 40 + ->setViewer($viewer); 41 + 42 + $timeline = id(new PhabricatorApplicationTransactionView()) 43 + ->setUser($viewer) 44 + ->setObjectPHID($source_phid) 45 + ->setMarkupEngine($engine) 46 + ->setTransactions($xactions); 47 + 48 + $title = pht('%s', $source->getName()); 33 49 $crumbs = $this->buildApplicationCrumbs(); 34 - $title = 'TODO'; 50 + $crumbs->addCrumb( 51 + id(new PhabricatorCrumbView()) 52 + ->setName($title)); 53 + 54 + $header = $this->buildHeaderView($source); 55 + $actions = $this->buildActionView($source); 56 + $properties = $this->buildPropertyView($source, $actions); 57 + 58 + $box = id(new PHUIObjectBoxView()) 59 + ->setHeader($header) 60 + ->addPropertyList($properties); 35 61 36 62 return $this->buildApplicationPage( 37 - $crumbs, 63 + array( 64 + $crumbs, 65 + $box, 66 + $timeline, 67 + ), 38 68 array( 39 69 'title' => $title, 40 - 'device' => true)); 70 + 'device' => true, 71 + )); 72 + 73 + } 74 + 75 + 76 + private function buildHeaderView(NuanceSource $source) { 77 + $viewer = $this->getRequest()->getUser(); 78 + 79 + $header = id(new PHUIHeaderView()) 80 + ->setUser($viewer) 81 + ->setHeader($source->getName()) 82 + ->setPolicyObject($source); 83 + 84 + return $header; 85 + } 86 + 87 + private function buildActionView(NuanceSource $source) { 88 + $viewer = $this->getRequest()->getUser(); 89 + $id = $source->getID(); 90 + 91 + $actions = id(new PhabricatorActionListView()) 92 + ->setObjectURI($source->getURI()) 93 + ->setUser($viewer); 94 + 95 + $can_edit = PhabricatorPolicyFilter::hasCapability( 96 + $viewer, 97 + $source, 98 + PhabricatorPolicyCapability::CAN_EDIT); 99 + 100 + $actions->addAction( 101 + id(new PhabricatorActionView()) 102 + ->setName(pht('Edit Source')) 103 + ->setIcon('edit') 104 + ->setHref($this->getApplicationURI("source/edit/{$id}/")) 105 + ->setDisabled(!$can_edit) 106 + ->setWorkflow(!$can_edit)); 107 + 108 + return $actions; 109 + } 110 + 111 + private function buildPropertyView( 112 + NuanceSource $source, 113 + PhabricatorActionListView $actions) { 114 + $viewer = $this->getRequest()->getUser(); 115 + 116 + $properties = id(new PHUIPropertyListView()) 117 + ->setUser($viewer) 118 + ->setObject($source) 119 + ->setActionList($actions); 120 + 121 + $definition = NuanceSourceDefinition::getDefinitionForSource($source); 122 + $properties->addProperty( 123 + pht('Source Type'), 124 + $definition->getName()); 125 + 126 + $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( 127 + $viewer, 128 + $source); 129 + 130 + $properties->addProperty( 131 + pht('Editable By'), 132 + $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); 133 + 134 + return $properties; 41 135 } 42 136 }
+78
src/applications/nuance/editor/NuanceSourceEditor.php
··· 6 6 public function getTransactionTypes() { 7 7 $types = parent::getTransactionTypes(); 8 8 9 + $types[] = NuanceSourceTransaction::TYPE_NAME; 10 + 9 11 $types[] = PhabricatorTransactions::TYPE_EDGE; 10 12 $types[] = PhabricatorTransactions::TYPE_COMMENT; 11 13 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 12 14 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 13 15 14 16 return $types; 17 + } 18 + 19 + protected function getCustomTransactionOldValue( 20 + PhabricatorLiskDAO $object, 21 + PhabricatorApplicationTransaction $xaction) { 22 + 23 + switch ($xaction->getTransactionType()) { 24 + case NuanceSourceTransaction::TYPE_NAME: 25 + return $object->getName(); 26 + } 27 + 28 + return parent::getCustomTransactionOldValue($object, $xaction); 29 + } 30 + 31 + protected function getCustomTransactionNewValue( 32 + PhabricatorLiskDAO $object, 33 + PhabricatorApplicationTransaction $xaction) { 34 + 35 + switch ($xaction->getTransactionType()) { 36 + case NuanceSourceTransaction::TYPE_NAME: 37 + return $xaction->getNewValue(); 38 + } 39 + 40 + return parent::getCustomTransactionNewValue($object, $xaction); 41 + } 42 + 43 + protected function applyCustomInternalTransaction( 44 + PhabricatorLiskDAO $object, 45 + PhabricatorApplicationTransaction $xaction) { 46 + 47 + switch ($xaction->getTransactionType()) { 48 + case NuanceSourceTransaction::TYPE_NAME: 49 + $object->setName($xaction->getNewValue()); 50 + break; 51 + } 52 + } 53 + 54 + protected function applyCustomExternalTransaction( 55 + PhabricatorLiskDAO $object, 56 + PhabricatorApplicationTransaction $xaction) { 57 + 58 + switch ($xaction->getTransactionType()) { 59 + case NuanceSourceTransaction::TYPE_NAME: 60 + return; 61 + } 62 + 63 + return parent::applyCustomExternalTransaction($object, $xaction); 64 + } 65 + 66 + protected function validateTransaction( 67 + PhabricatorLiskDAO $object, 68 + $type, 69 + array $xactions) { 70 + 71 + $errors = parent::validateTransaction($object, $type, $xactions); 72 + 73 + switch ($type) { 74 + case NuanceSourceTransaction::TYPE_NAME: 75 + $missing = $this->validateIsEmptyTextField( 76 + $object->getName(), 77 + $xactions); 78 + 79 + if ($missing) { 80 + $error = new PhabricatorApplicationTransactionValidationError( 81 + $type, 82 + pht('Required'), 83 + pht('Source name is required.'), 84 + nonempty(last($xactions), null)); 85 + 86 + $error->setIsMissingFieldError(true); 87 + $errors[] = $error; 88 + } 89 + break; 90 + } 91 + 92 + return $errors; 15 93 } 16 94 17 95 }
+1 -1
src/applications/nuance/query/NuanceSourceQuery.php
··· 35 35 36 36 $data = queryfx_all( 37 37 $conn_r, 38 - 'SELECT FROM %T %Q %Q %Q', 38 + 'SELECT * FROM %T %Q %Q %Q', 39 39 $table->getTableName(), 40 40 $this->buildWhereClause($conn_r), 41 41 $this->buildOrderClause($conn_r),
+43
src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php
··· 1 + <?php 2 + 3 + final class NuancePhabricatorFormSourceDefinition 4 + extends NuanceSourceDefinition { 5 + 6 + public function getName() { 7 + return pht('Phabricator Form'); 8 + } 9 + 10 + public function getSourceTypeConstant() { 11 + return 'phabricator-form'; 12 + } 13 + 14 + public function updateItems() { 15 + return null; 16 + } 17 + 18 + protected function augmentEditForm( 19 + AphrontFormView $form, 20 + PhabricatorApplicationTransactionValidationException $ex = null) { 21 + 22 + /* TODO - add a box to allow for custom fields to be defined here, so that 23 + * these NuanceSource objects made from this defintion can be used to 24 + * capture arbitrary data */ 25 + 26 + return $form; 27 + } 28 + 29 + protected function buildTransactions(AphrontRequest $request) { 30 + $transactions = parent::buildTransactions($request); 31 + 32 + // TODO -- as above 33 + 34 + return $transactions; 35 + } 36 + 37 + public function renderView() { 38 + } 39 + 40 + public function renderListView() { 41 + } 42 + 43 + }
+274
src/applications/nuance/source/NuanceSourceDefinition.php
··· 1 + <?php 2 + 3 + abstract class NuanceSourceDefinition extends Phobject { 4 + 5 + private $actor; 6 + private $sourceObject; 7 + 8 + public function setActor(PhabricatorUser $actor) { 9 + $this->actor = $actor; 10 + return $this; 11 + } 12 + public function getActor() { 13 + return $this->actor; 14 + } 15 + public function requireActor() { 16 + $actor = $this->getActor(); 17 + if (!$actor) { 18 + throw new Exception('You must "setActor()" first!'); 19 + } 20 + return $actor; 21 + } 22 + 23 + public function setSourceObject(NuanceSource $source) { 24 + $source->setType($this->getSourceTypeConstant()); 25 + $this->sourceObject = $source; 26 + return $this; 27 + } 28 + public function getSourceObject() { 29 + return $this->sourceObject; 30 + } 31 + public function requireSourceObject() { 32 + $source = $this->getSourceObject(); 33 + if (!$source) { 34 + throw new Exception('You must "setSourceObject()" first!'); 35 + } 36 + return $source; 37 + } 38 + 39 + public static function getSelectOptions() { 40 + $definitions = self::getAllDefinitions(); 41 + 42 + $options = array(); 43 + foreach ($definitions as $definition) { 44 + $key = $definition->getSourceTypeConstant(); 45 + $name = $definition->getName(); 46 + $options[$key] = $name; 47 + } 48 + 49 + return $options; 50 + } 51 + 52 + /** 53 + * Gives a @{class:NuanceSourceDefinition} object for a given 54 + * @{class:NuanceSource}. Note you still need to @{method:setActor} 55 + * before the @{class:NuanceSourceDefinition} object will be useful. 56 + */ 57 + public static function getDefinitionForSource(NuanceSource $source) { 58 + $definitions = self::getAllDefinitions(); 59 + $map = mpull($definitions, null, 'getSourceTypeConstant'); 60 + $definition = $map[$source->getType()]; 61 + $definition->setSourceObject($source); 62 + 63 + return $definition; 64 + } 65 + 66 + public static function getAllDefinitions() { 67 + static $definitions; 68 + 69 + if ($definitions === null) { 70 + $objects = id(new PhutilSymbolLoader()) 71 + ->setAncestorClass(__CLASS__) 72 + ->loadObjects(); 73 + foreach ($objects as $definition) { 74 + $key = $definition->getSourceTypeConstant(); 75 + $name = $definition->getName(); 76 + if (isset($definitions[$key])) { 77 + $conflict = $definitions[$key]; 78 + throw new Exception(sprintf( 79 + 'Defintion %s conflicts with definition %s. This is a programming '. 80 + 'error.', 81 + $conflict, 82 + $name)); 83 + } 84 + } 85 + $definitions = $objects; 86 + } 87 + return $definitions; 88 + } 89 + 90 + /** 91 + * A human readable string like "Twitter" or "Phabricator Form". 92 + */ 93 + abstract public function getName(); 94 + 95 + /** 96 + * This should be a any VARCHAR(32). 97 + * 98 + * @{method:getAllDefinitions} will throw if you choose a string that 99 + * collides with another @{class:NuanceSourceDefinition} class. 100 + */ 101 + abstract public function getSourceTypeConstant(); 102 + 103 + /** 104 + * Code to create and update @{class:NuanceItem}s and 105 + * @{class:NuanceRequestor}s via daemons goes here. 106 + * 107 + * If that does not make sense for the @{class:NuanceSource} you are 108 + * defining, simply return null. For example, 109 + * @{class:NuancePhabricatorFormSourceDefinition} since these are one-way 110 + * contact forms. 111 + */ 112 + abstract public function updateItems(); 113 + 114 + private function loadSourceObjectPolicies( 115 + PhabricatorUser $user, 116 + NuanceSource $source) { 117 + 118 + $user = $this->requireActor(); 119 + $source = $this->requireSourceObject(); 120 + return id(new PhabricatorPolicyQuery()) 121 + ->setViewer($user) 122 + ->setObject($source) 123 + ->execute(); 124 + } 125 + 126 + final public function getEditTitle() { 127 + $source = $this->requireSourceObject(); 128 + if ($source->getPHID()) { 129 + $title = pht('Edit "%s" source.', $source->getName()); 130 + } else { 131 + $title = pht('Create a new "%s" source.', $this->getName()); 132 + } 133 + 134 + return $title; 135 + } 136 + 137 + final public function buildEditLayout(AphrontRequest $request) { 138 + $actor = $this->requireActor(); 139 + $source = $this->requireSourceObject(); 140 + 141 + $form_errors = array(); 142 + $error_messages = array(); 143 + $transactions = array(); 144 + $validation_exception = null; 145 + if ($request->isFormPost()) { 146 + $transactions = $this->buildTransactions($request); 147 + try { 148 + $editor = id(new NuanceSourceEditor()) 149 + ->setActor($actor) 150 + ->setContentSourceFromRequest($request) 151 + ->setContinueOnNoEffect(true) 152 + ->applyTransactions($source, $transactions); 153 + 154 + return id(new AphrontRedirectResponse()) 155 + ->setURI($source->getURI()); 156 + 157 + } catch (PhabricatorApplicationTransactionValidationException $ex) { 158 + $validation_exception = $ex; 159 + } 160 + 161 + } 162 + 163 + $form = $this->renderEditForm($validation_exception); 164 + $layout = id(new PHUIObjectBoxView()) 165 + ->setHeaderText($this->getEditTitle()) 166 + ->setValidationException($validation_exception) 167 + ->setForm($form); 168 + if ($error_messages) { 169 + $layout->setFormError($this->renderEditErrorView($error_messages)); 170 + } 171 + 172 + return $layout; 173 + } 174 + 175 + /** 176 + * Code to create a form to edit the @{class:NuanceItem} you are defining. 177 + * 178 + * return @{class:AphrontFormView} 179 + */ 180 + private function renderEditForm( 181 + PhabricatorApplicationTransactionValidationException $ex = null) { 182 + $user = $this->requireActor(); 183 + $source = $this->requireSourceObject(); 184 + $policies = $this->loadSourceObjectPolicies($user, $source); 185 + $e_name = null; 186 + if ($ex) { 187 + $e_name = $ex->getShortMessage(NuanceSourceTransaction::TYPE_NAME); 188 + } 189 + 190 + $form = id(new AphrontFormView()) 191 + ->setUser($user) 192 + ->appendChild( 193 + id(new AphrontFormTextControl()) 194 + ->setLabel(pht('Name')) 195 + ->setName('name') 196 + ->setError($e_name) 197 + ->setValue($source->getName())) 198 + ->appendChild( 199 + id(new AphrontFormSelectControl()) 200 + ->setLabel(pht('Type')) 201 + ->setName('type') 202 + ->setOptions(self::getSelectOptions()) 203 + ->setValue($source->getType())); 204 + 205 + $form = $this->augmentEditForm($form, $ex); 206 + 207 + $form 208 + ->appendChild( 209 + id(new AphrontFormPolicyControl()) 210 + ->setUser($user) 211 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 212 + ->setPolicyObject($source) 213 + ->setPolicies($policies) 214 + ->setName('viewPolicy')) 215 + ->appendChild( 216 + id(new AphrontFormPolicyControl()) 217 + ->setUser($user) 218 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 219 + ->setPolicyObject($source) 220 + ->setPolicies($policies) 221 + ->setName('editPolicy')) 222 + ->appendChild( 223 + id(new AphrontFormSubmitControl()) 224 + ->addCancelButton($source->getURI()) 225 + ->setValue(pht('Save'))); 226 + 227 + return $form; 228 + } 229 + 230 + /** 231 + * return @{class:AphrontFormView} 232 + */ 233 + protected function augmentEditForm( 234 + AphrontFormView $form, 235 + PhabricatorApplicationTransactionValidationException $ex = null) { 236 + 237 + return $form; 238 + } 239 + 240 + /** 241 + * return @{class:AphrontErrorView} 242 + */ 243 + public function renderEditErrorView(array $errors) { 244 + return id(new AphrontErrorView()) 245 + ->setTitle(pht('Error with submission.')) 246 + ->setErrors($errors); 247 + } 248 + 249 + /** 250 + * Hook to build up @{class:PhabricatorTransactions}. 251 + * 252 + * return array $transactions 253 + */ 254 + protected function buildTransactions(AphrontRequest $request) { 255 + $transactions = array(); 256 + 257 + $transactions[] = id(new NuanceSourceTransaction()) 258 + ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) 259 + ->setNewValue($request->getStr('editPolicy')); 260 + $transactions[] = id(new NuanceSourceTransaction()) 261 + ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 262 + ->setNewValue($request->getStr('viewPolicy')); 263 + $transactions[] = id(new NuanceSourceTransaction()) 264 + ->setTransactionType(NuanceSourceTransaction::TYPE_NAME) 265 + ->setNewvalue($request->getStr('name')); 266 + 267 + return $transactions; 268 + } 269 + 270 + abstract public function renderView(); 271 + 272 + abstract public function renderListView(); 273 + } 274 +
+6 -1
src/applications/nuance/storage/NuanceSource.php
··· 47 47 $edit_policy = $app->getPolicy( 48 48 NuanceCapabilitySourceDefaultEdit::CAPABILITY); 49 49 50 + $definitions = NuanceSourceDefinition::getAllDefinitions(); 51 + $lucky_definition = head($definitions); 52 + 50 53 return id(new NuanceSource()) 51 54 ->setViewPolicy($view_policy) 52 - ->setEditPolicy($edit_policy); 55 + ->setEditPolicy($edit_policy) 56 + ->setType($lucky_definition->getSourceTypeConstant()); 57 + 53 58 } 54 59 55 60 public function getCapabilities() {
+25
src/applications/nuance/storage/NuanceSourceTransaction.php
··· 3 3 final class NuanceSourceTransaction 4 4 extends NuanceTransaction { 5 5 6 + const TYPE_NAME = 'name-source'; 7 + 6 8 public function getApplicationTransactionType() { 7 9 return NuancePHIDTypeSource::TYPECONST; 8 10 } 9 11 10 12 public function getApplicationTransactionCommentObject() { 11 13 return new NuanceSourceTransactionComment(); 14 + } 15 + 16 + public function getTitle() { 17 + $old = $this->getOldValue(); 18 + $new = $this->getNewValue(); 19 + $author_phid = $this->getAuthorPHID(); 20 + 21 + switch ($this->getTransactionType()) { 22 + case self::TYPE_NAME: 23 + if ($old === null) { 24 + return pht( 25 + '%s created this source.', 26 + $this->renderHandleLink($author_phid)); 27 + } else { 28 + return pht( 29 + '%s renamed this source from "%s" to "%s".', 30 + $this->renderHandleLink($author_phid), 31 + $old, 32 + $new); 33 + } 34 + break; 35 + } 36 + 12 37 } 13 38 14 39 }
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1768 1768 'type' => 'sql', 1769 1769 'name' => $this->getPatchPath('20131119.passphrase.sql'), 1770 1770 ), 1771 + '20131120.nuancesourcetype.sql' => array( 1772 + 'type' => 'sql', 1773 + 'name' => $this->getPatchPath('20131120.nuancesourcetype.sql'), 1774 + ), 1771 1775 ); 1772 1776 } 1773 1777 }