@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Roughly generate transaction-oriented API methods from EditEngines

Summary:
Ref T5873. Ref T9132. This is really rough and feels pretty flimsy at the edges (missing validation, generality, modularity, clean error handling, etc) but gets us most of the way toward generating plausible "whatever.edit" Conduit API methods from EditEngines.

These methods are full-power methods which can do everything the edit form can, automatically support the same range of operations, and update when new fields are added.

Test Plan:
- Used new `paste.edit` to create a new Paste.
- Used new `paste.edit` to update an existing paste.
- Applied a variety of different transactions.
- Hit a reasonable set of errors.

{F941144}

{F941145}

{F941146}

{F941147}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T5873, T9132

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

+663 -2
+10
src/__phutil_library_map__.php
··· 1530 1530 'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.php', 1531 1531 'PasteDefaultEditCapability' => 'applications/paste/capability/PasteDefaultEditCapability.php', 1532 1532 'PasteDefaultViewCapability' => 'applications/paste/capability/PasteDefaultViewCapability.php', 1533 + 'PasteEditConduitAPIMethod' => 'applications/paste/conduit/PasteEditConduitAPIMethod.php', 1533 1534 'PasteEmbedView' => 'applications/paste/view/PasteEmbedView.php', 1534 1535 'PasteInfoConduitAPIMethod' => 'applications/paste/conduit/PasteInfoConduitAPIMethod.php', 1535 1536 'PasteMailReceiver' => 'applications/paste/mail/PasteMailReceiver.php', ··· 1571 1572 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 1572 1573 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', 1573 1574 'PhabricatorApplicationEditEngine' => 'applications/transactions/editengine/PhabricatorApplicationEditEngine.php', 1575 + 'PhabricatorApplicationEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php', 1574 1576 'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php', 1575 1577 'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php', 1576 1578 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', ··· 2094 2096 'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php', 2095 2097 'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/PhabricatorEdgeConstants.php', 2096 2098 'PhabricatorEdgeCycleException' => 'infrastructure/edges/exception/PhabricatorEdgeCycleException.php', 2099 + 'PhabricatorEdgeEditType' => 'applications/transactions/edittype/PhabricatorEdgeEditType.php', 2097 2100 'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/PhabricatorEdgeEditor.php', 2098 2101 'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php', 2099 2102 'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php', ··· 2101 2104 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php', 2102 2105 'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php', 2103 2106 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 2107 + 'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php', 2104 2108 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', 2105 2109 'PhabricatorElasticSearchEngine' => 'applications/search/engine/PhabricatorElasticSearchEngine.php', 2106 2110 'PhabricatorElasticSearchSetupCheck' => 'applications/config/check/PhabricatorElasticSearchSetupCheck.php', ··· 2936 2940 'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php', 2937 2941 'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php', 2938 2942 'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php', 2943 + 'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php', 2939 2944 'PhabricatorSite' => 'aphront/site/PhabricatorSite.php', 2940 2945 'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php', 2941 2946 'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/PhabricatorSlowvoteChoice.php', ··· 5457 5462 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 5458 5463 'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability', 5459 5464 'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability', 5465 + 'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod', 5460 5466 'PasteEmbedView' => 'AphrontView', 5461 5467 'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod', 5462 5468 'PasteMailReceiver' => 'PhabricatorObjectMailReceiver', ··· 5501 5507 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 5502 5508 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', 5503 5509 'PhabricatorApplicationEditEngine' => 'Phobject', 5510 + 'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod', 5504 5511 'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView', 5505 5512 'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController', 5506 5513 'PhabricatorApplicationLaunchView' => 'AphrontTagView', ··· 6121 6128 'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants', 6122 6129 'PhabricatorEdgeConstants' => 'Phobject', 6123 6130 'PhabricatorEdgeCycleException' => 'Exception', 6131 + 'PhabricatorEdgeEditType' => 'PhabricatorEditType', 6124 6132 'PhabricatorEdgeEditor' => 'Phobject', 6125 6133 'PhabricatorEdgeGraph' => 'AbstractDirectedGraph', 6126 6134 'PhabricatorEdgeQuery' => 'PhabricatorQuery', ··· 6128 6136 'PhabricatorEdgeType' => 'Phobject', 6129 6137 'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase', 6130 6138 'PhabricatorEditField' => 'Phobject', 6139 + 'PhabricatorEditType' => 'Phobject', 6131 6140 'PhabricatorEditor' => 'Phobject', 6132 6141 'PhabricatorElasticSearchEngine' => 'PhabricatorSearchEngine', 6133 6142 'PhabricatorElasticSearchSetupCheck' => 'PhabricatorSetupCheck', ··· 7120 7129 'PhabricatorSetupIssue' => 'Phobject', 7121 7130 'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample', 7122 7131 'PhabricatorSetupIssueView' => 'AphrontView', 7132 + 'PhabricatorSimpleEditType' => 'PhabricatorEditType', 7123 7133 'PhabricatorSite' => 'AphrontSite', 7124 7134 'PhabricatorSlowvoteApplication' => 'PhabricatorApplication', 7125 7135 'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO',
+19
src/applications/paste/conduit/PasteEditConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class PasteEditConduitAPIMethod 4 + extends PhabricatorApplicationEditEngineAPIMethod { 5 + 6 + public function getAPIMethodName() { 7 + return 'paste.edit'; 8 + } 9 + 10 + public function newEditEngine() { 11 + return new PhabricatorPasteEditEngine(); 12 + } 13 + 14 + public function getMethodSummary() { 15 + return pht( 16 + 'Apply transactions to create a new paste or edit an existing one.'); 17 + } 18 + 19 + }
+24
src/applications/paste/editor/PhabricatorPasteEditor.php
··· 72 72 $this->fileName = $name; 73 73 } 74 74 75 + protected function validateTransaction( 76 + PhabricatorLiskDAO $object, 77 + $type, 78 + array $xactions) { 79 + 80 + $errors = parent::validateTransaction($object, $type, $xactions); 81 + switch ($type) { 82 + case PhabricatorPasteTransaction::TYPE_CONTENT: 83 + if (!$object->getFilePHID() && !$xactions) { 84 + $error = new PhabricatorApplicationTransactionValidationError( 85 + $type, 86 + pht('Required'), 87 + pht('You must provide content to create a paste.'), 88 + null); 89 + 90 + $error->setIsMissingFieldError(true); 91 + $errors[] = $error; 92 + } 93 + break; 94 + } 95 + 96 + return $errors; 97 + } 98 + 75 99 protected function getCustomTransactionOldValue( 76 100 PhabricatorLiskDAO $object, 77 101 PhabricatorApplicationTransaction $xaction) {
+1
src/applications/paste/storage/PhabricatorPaste.php
··· 41 41 42 42 return id(new PhabricatorPaste()) 43 43 ->setTitle('') 44 + ->setLanguage('') 44 45 ->setStatus(self::STATUS_ACTIVE) 45 46 ->setAuthorPHID($actor->getPHID()) 46 47 ->setViewPolicy($view_policy)
+180 -2
src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php
··· 1 1 <?php 2 2 3 + 4 + /** 5 + * @task web Responding to Web Requests 6 + * @task conduit Responding to Conduit Requests 7 + */ 3 8 abstract class PhabricatorApplicationEditEngine extends Phobject { 4 9 5 10 private $viewer; ··· 47 52 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 48 53 'label' => pht('View Policy'), 49 54 'description' => pht('Controls who can view the object.'), 55 + 'edit' => 'view', 50 56 ), 51 57 PhabricatorTransactions::TYPE_EDIT_POLICY => array( 52 58 'key' => 'policy.edit', ··· 54 60 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 55 61 'label' => pht('Edit Policy'), 56 62 'description' => pht('Controls who can edit the object.'), 63 + 'edit' => 'edit', 57 64 ), 58 65 PhabricatorTransactions::TYPE_JOIN_POLICY => array( 59 66 'key' => 'policy.join', ··· 61 68 'capability' => PhabricatorPolicyCapability::CAN_JOIN, 62 69 'label' => pht('Join Policy'), 63 70 'description' => pht('Controls who can join the object.'), 71 + 'edit' => 'join', 64 72 ), 65 73 ); 66 74 ··· 74 82 $aliases = $spec['aliases']; 75 83 $label = $spec['label']; 76 84 $description = $spec['description']; 85 + $edit = $spec['edit']; 77 86 78 87 $policy_field = id(new PhabricatorPolicyEditField()) 79 88 ->setKey($key) ··· 83 92 ->setCapability($capability) 84 93 ->setPolicies($policies) 85 94 ->setTransactionType($type) 95 + ->setEditTypeKey($edit) 86 96 ->setValue($object->getPolicy($capability)); 87 97 $fields[] = $policy_field; 88 98 ··· 93 103 $space_field = id(new PhabricatorSpaceEditField()) 94 104 ->setKey('spacePHID') 95 105 ->setLabel(pht('Space')) 106 + ->setEditTypeKey('space') 96 107 ->setDescription( 97 108 pht('Shifts the object in the Spaces application.')) 98 109 ->setAliases(array('space', 'policy.space')) ··· 126 137 $edge_field = id(new PhabricatorDatasourceEditField()) 127 138 ->setKey('projectPHIDs') 128 139 ->setLabel(pht('Projects')) 140 + ->setEditTypeKey('projects') 129 141 ->setDescription( 130 142 pht( 131 143 'Add or remove associated projects.')) ··· 154 166 $subscribers_field = id(new PhabricatorDatasourceEditField()) 155 167 ->setKey('subscriberPHIDs') 156 168 ->setLabel(pht('Subscribers')) 169 + ->setEditTypeKey('subscribers') 157 170 ->setDescription(pht('Manage subscribers.')) 158 171 ->setDatasource(new PhabricatorMetaMTAMailableDatasource()) 159 172 ->setAliases(array('subscriber', 'subscribers')) ··· 240 253 if (!$object) { 241 254 return new Aphront404Response(); 242 255 } 243 - 244 256 $this->setIsCreate(false); 245 257 } else { 246 258 $object = $this->newEditableObject(); 247 - 248 259 $this->setIsCreate(true); 249 260 } 250 261 ··· 442 453 443 454 return $actions; 444 455 } 456 + 457 + 458 + /* -( Conduit )------------------------------------------------------------ */ 459 + 460 + 461 + /** 462 + * Respond to a Conduit edit request. 463 + * 464 + * This method accepts a list of transactions to apply to an object, and 465 + * either edits an existing object or creates a new one. 466 + * 467 + * @task conduit 468 + */ 469 + final public function buildConduitResponse(ConduitAPIRequest $request) { 470 + $viewer = $this->getViewer(); 471 + 472 + $phid = $request->getValue('objectPHID'); 473 + if ($phid) { 474 + $object = $this->newObjectQuery() 475 + ->setViewer($viewer) 476 + ->withPHIDs(array($phid)) 477 + ->requireCapabilities( 478 + array( 479 + PhabricatorPolicyCapability::CAN_VIEW, 480 + PhabricatorPolicyCapability::CAN_EDIT, 481 + )) 482 + ->executeOne(); 483 + if (!$object) { 484 + throw new Exception(pht('No such object with PHID "%s".', $phid)); 485 + } 486 + $this->setIsCreate(false); 487 + } else { 488 + $object = $this->newEditableObject(); 489 + $this->setIsCreate(true); 490 + } 491 + 492 + $fields = $this->buildEditFields($object); 493 + 494 + foreach ($fields as $field) { 495 + $field 496 + ->setViewer($viewer) 497 + ->setObject($object); 498 + } 499 + 500 + $types = $this->getAllEditTypesFromFields($fields); 501 + $template = $object->getApplicationTransactionTemplate(); 502 + 503 + $xactions = $this->getConduitTransactions($request, $types, $template); 504 + 505 + $editor = $object->getApplicationTransactionEditor() 506 + ->setActor($viewer) 507 + ->setContentSourceFromConduitRequest($request) 508 + ->setContinueOnNoEffect(true); 509 + 510 + $xactions = $editor->applyTransactions($object, $xactions); 511 + 512 + $xactions_struct = array(); 513 + foreach ($xactions as $xaction) { 514 + $xactions_struct[] = array( 515 + 'phid' => $xaction->getPHID(), 516 + ); 517 + } 518 + 519 + return array( 520 + 'object' => array( 521 + 'id' => $object->getID(), 522 + 'phid' => $object->getPHID(), 523 + ), 524 + 'transactions' => $xactions_struct, 525 + ); 526 + } 527 + 528 + 529 + /** 530 + * Generate transactions which can be applied from edit actions in a Conduit 531 + * request. 532 + * 533 + * @param ConduitAPIRequest The request. 534 + * @param list<PhabricatorEditType> Supported edit types. 535 + * @param PhabricatorApplicationTransaction Template transaction. 536 + * @return list<PhabricatorApplicationTransaction> Generated transactions. 537 + * @task conduit 538 + */ 539 + private function getConduitTransactions( 540 + ConduitAPIRequest $request, 541 + array $types, 542 + PhabricatorApplicationTransaction $template) { 543 + 544 + $transactions_key = 'transactions'; 545 + 546 + $xactions = $request->getValue($transactions_key); 547 + if (!is_array($xactions)) { 548 + throw new Exception( 549 + pht( 550 + 'Parameter "%s" is not a list of transactions.', 551 + $transactions_key)); 552 + } 553 + 554 + foreach ($xactions as $key => $xaction) { 555 + if (!is_array($xaction)) { 556 + throw new Exception( 557 + pht( 558 + 'Parameter "%s" must contain a list of transaction descriptions, '. 559 + 'but item with key "%s" is not a dictionary.', 560 + $transactions_key, 561 + $key)); 562 + } 563 + 564 + if (!array_key_exists('type', $xaction)) { 565 + throw new Exception( 566 + pht( 567 + 'Parameter "%s" must contain a list of transaction descriptions, '. 568 + 'but item with key "%s" is missing a "type" field. Each '. 569 + 'transaction must have a type field.', 570 + $transactions_key, 571 + $key)); 572 + } 573 + 574 + $type = $xaction['type']; 575 + if (empty($types[$type])) { 576 + throw new Exception( 577 + pht( 578 + 'Transaction with key "%s" has invalid type "%s". This type is '. 579 + 'not recognized. Valid types are: %s.', 580 + $key, 581 + $type, 582 + implode(', ', array_keys($types)))); 583 + } 584 + } 585 + 586 + $results = array(); 587 + foreach ($xactions as $xaction) { 588 + $type = $types[$xaction['type']]; 589 + 590 + $results[] = $type->generateTransaction( 591 + clone $template, 592 + $xaction); 593 + } 594 + 595 + return $results; 596 + } 597 + 598 + 599 + /** 600 + * @return map<string, PhabricatorEditType> 601 + * @task conduit 602 + */ 603 + private function getAllEditTypesFromFields(array $fields) { 604 + $types = array(); 605 + foreach ($fields as $field) { 606 + $field_types = $field->getEditTransactionTypes(); 607 + foreach ($field_types as $field_type) { 608 + $field_type->setField($field); 609 + $types[$field_type->getEditType()] = $field_type; 610 + } 611 + } 612 + return $types; 613 + } 614 + 615 + public function getAllEditTypes() { 616 + $object = $this->newEditableObject(); 617 + $fields = $this->buildEditFields($object); 618 + return $this->getAllEditTypesFromFields($fields); 619 + } 620 + 621 + 622 + 445 623 446 624 }
+188
src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorApplicationEditEngineAPIMethod 4 + extends ConduitAPIMethod { 5 + 6 + abstract public function newEditEngine(); 7 + 8 + public function getMethodStatus() { 9 + return self::METHOD_STATUS_UNSTABLE; 10 + } 11 + 12 + public function getMethodStatusDescription() { 13 + return pht('ApplicationEditor methods are highly unstable.'); 14 + } 15 + 16 + final protected function defineParamTypes() { 17 + return array( 18 + 'transactions' => 'list<map<string, wild>>', 19 + 'objectPHID' => 'optional phid', 20 + ); 21 + } 22 + 23 + final protected function defineReturnType() { 24 + return 'map<string, wild>'; 25 + } 26 + 27 + final protected function execute(ConduitAPIRequest $request) { 28 + $engine = $this->newEditEngine() 29 + ->setViewer($request->getUser()); 30 + 31 + return $engine->buildConduitResponse($request); 32 + } 33 + 34 + final public function getMethodDescription() { 35 + // TODO: We don't currently have a real viewer in this method. 36 + $viewer = new PhabricatorUser(); 37 + 38 + $engine = $this->newEditEngine() 39 + ->setViewer($viewer); 40 + 41 + $types = $engine->getAllEditTypes(); 42 + 43 + $out = array(); 44 + 45 + $out[] = pht(<<<EOTEXT 46 + This is a standard **ApplicationEditor** method which allows you to create and 47 + modify objects by applying transactions. 48 + 49 + Each transaction applies one change to the object. For example, to create an 50 + object with a specific title or change the title of an existing object you might 51 + start by building a transaction like this: 52 + 53 + ```lang=json, name=Example Single Transaction 54 + { 55 + "type": "title", 56 + "value": "New Object Title" 57 + } 58 + ``` 59 + 60 + By passing a list of transactions in the `transactions` parameter, you can 61 + apply a sequence of edits. For example, you'll often pass a value like this to 62 + create an object with several field values or apply changes to multiple fields: 63 + 64 + ```lang=json, name=Example Transaction List 65 + [ 66 + { 67 + "type": "title", 68 + "value": "New Object Title" 69 + }, 70 + { 71 + "type": "body", 72 + "value": "New body text for the object." 73 + }, 74 + { 75 + "type": "projects.add", 76 + "value": ["PHID-PROJ-1111", "PHID-PROJ-2222"] 77 + } 78 + ] 79 + ``` 80 + 81 + Exactly which types of edits are available depends on the object you're editing. 82 + 83 + 84 + Creating Objects 85 + ---------------- 86 + 87 + To create an object, pass a list of `transactions` but leave `objectPHID` 88 + empty. This will create a new object with the initial field values you 89 + specify. 90 + 91 + 92 + Editing Objects 93 + --------------- 94 + 95 + To edit an object, pass a list of `transactions` and specify an object to 96 + apply them to with `objectPHID`. This will apply the changes to the object. 97 + 98 + 99 + Return Type 100 + ----------- 101 + 102 + WARNING: The structure of the return value from these methods is likely to 103 + change as ApplicationEditor evolves. 104 + 105 + Return values look something like this for now: 106 + 107 + ```lang=json, name=Example Return Value 108 + { 109 + "object": { 110 + "phid": "PHID-XXXX-1111" 111 + }, 112 + "transactions": [ 113 + { 114 + "phid": "PHID-YYYY-1111", 115 + }, 116 + { 117 + "phid": "PHID-YYYY-2222", 118 + } 119 + ] 120 + } 121 + ``` 122 + 123 + The `object` key contains information about the object which was created or 124 + edited. 125 + 126 + The `transactions` key contains information about the transactions which were 127 + actually applied. For many reasons, the transactions which actually apply may 128 + be greater or fewer in number than the transactions you provided, or may differ 129 + in their nature in other ways. 130 + 131 + 132 + Edit Types 133 + ========== 134 + 135 + This API method supports these edit types: 136 + EOTEXT 137 + ); 138 + 139 + $key = pht('Key'); 140 + $summary = pht('Summary'); 141 + $description = pht('Description'); 142 + $head_type = pht('Type'); 143 + 144 + $table = array(); 145 + $table[] = "| {$key} | {$summary} |"; 146 + $table[] = '|--------|----------------|'; 147 + foreach ($types as $type) { 148 + $edit_type = $type->getEditType(); 149 + $edit_summary = $type->getSummary(); 150 + $table[] = "| `{$edit_type}` | {$edit_summary} |"; 151 + } 152 + 153 + $out[] = implode("\n", $table); 154 + 155 + foreach ($types as $type) { 156 + $section = array(); 157 + $section[] = pht('Edit Type: %s', $type->getEditType()); 158 + $section[] = '---------'; 159 + $section[] = null; 160 + $section[] = $type->getDescription(); 161 + $section[] = null; 162 + $section[] = pht( 163 + 'This edit generates transactions of type `%s` internally.', 164 + $type->getTransactionType()); 165 + $section[] = null; 166 + 167 + $type_description = pht( 168 + 'Use `%s` to select this edit type.', 169 + $type->getEditType()); 170 + 171 + $value_type = $type->getValueType(); 172 + $value_description = $type->getValueDescription(); 173 + 174 + $table = array(); 175 + $table[] = "| {$key} | {$head_type} | {$description} |"; 176 + $table[] = '|--------|--------------|----------------|'; 177 + $table[] = "| `type` | `const` | {$type_description} |"; 178 + $table[] = "| `value` | `{$value_type}` | {$value_description} |"; 179 + $section[] = implode("\n", $table); 180 + 181 + $out[] = implode("\n", $section); 182 + } 183 + 184 + $out = implode("\n\n", $out); 185 + return $out; 186 + } 187 + 188 + }
+73
src/applications/transactions/editfield/PhabricatorEditField.php
··· 12 12 private $transactionType; 13 13 private $metadata = array(); 14 14 private $description; 15 + private $editTypeKey; 16 + 15 17 16 18 public function setKey($key) { 17 19 $this->key = $key; ··· 221 223 222 224 public function getHTTPParameterType() { 223 225 return 'string'; 226 + } 227 + 228 + public function setEditTypeKey($edit_type_key) { 229 + $this->editTypeKey = $edit_type_key; 230 + return $this; 231 + } 232 + 233 + public function getEditTypeKey() { 234 + if ($this->editTypeKey === null) { 235 + return $this->getKey(); 236 + } 237 + return $this->editTypeKey; 238 + } 239 + 240 + public function getEditTransactionTypes() { 241 + $transaction_type = $this->getTransactionType(); 242 + $type_key = $this->getEditTypeKey(); 243 + 244 + // TODO: This is a pretty big pile of hard-coded hacks for now. 245 + 246 + $edge_types = array( 247 + PhabricatorTransactions::TYPE_EDGE => array( 248 + '+' => pht('Add projects.'), 249 + '-' => pht('Remove projects.'), 250 + '=' => pht('Set associated projects, overwriting current value.'), 251 + ), 252 + PhabricatorTransactions::TYPE_SUBSCRIBERS => array( 253 + '+' => pht('Add subscribers.'), 254 + '-' => pht('Remove subscribers.'), 255 + '=' => pht('Set subscribers, overwriting current value.'), 256 + ), 257 + ); 258 + 259 + if (isset($edge_types[$transaction_type])) { 260 + $base = id(new PhabricatorEdgeEditType()) 261 + ->setTransactionType($transaction_type) 262 + ->setMetadata($this->metadata); 263 + 264 + $strings = $edge_types[$transaction_type]; 265 + 266 + $add = id(clone $base) 267 + ->setEditType($type_key.'.add') 268 + ->setEdgeOperation('+') 269 + ->setDescription($strings['+']) 270 + ->setValueDescription(pht('List of PHIDs to add.')); 271 + $rem = id(clone $base) 272 + ->setEditType($type_key.'.remove') 273 + ->setEdgeOperation('-') 274 + ->setDescription($strings['-']) 275 + ->setValueDescription(pht('List of PHIDs to remove.')); 276 + $set = id(clone $base) 277 + ->setEditType($type_key.'.set') 278 + ->setEdgeOperation('=') 279 + ->setDescription($strings['=']) 280 + ->setValueDescription(pht('List of PHIDs to set.')); 281 + 282 + return array( 283 + $add, 284 + $rem, 285 + $set, 286 + ); 287 + } 288 + 289 + return array( 290 + id(new PhabricatorSimpleEditType()) 291 + ->setEditType($type_key) 292 + ->setTransactionType($transaction_type) 293 + ->setValueType($this->getHTTPParameterType()) 294 + ->setDescription($this->getDescription()) 295 + ->setMetadata($this->metadata), 296 + ); 224 297 } 225 298 226 299 }
+51
src/applications/transactions/edittype/PhabricatorEdgeEditType.php
··· 1 + <?php 2 + 3 + final class PhabricatorEdgeEditType extends PhabricatorEditType { 4 + 5 + private $edgeOperation; 6 + private $valueDescription; 7 + 8 + public function setEdgeOperation($edge_operation) { 9 + $this->edgeOperation = $edge_operation; 10 + return $this; 11 + } 12 + 13 + public function getEdgeOperation() { 14 + return $this->edgeOperation; 15 + } 16 + 17 + public function getValueType() { 18 + return 'list<phid>'; 19 + } 20 + 21 + public function generateTransaction( 22 + PhabricatorApplicationTransaction $template, 23 + array $spec) { 24 + 25 + $value = idx($spec, 'value'); 26 + $value = array_fuse($value); 27 + $value = array( 28 + $this->getEdgeOperation() => $value, 29 + ); 30 + 31 + $template 32 + ->setTransactionType($this->getTransactionType()) 33 + ->setNewValue($value); 34 + 35 + foreach ($this->getMetadata() as $key => $value) { 36 + $template->setMetadataValue($key, $value); 37 + } 38 + 39 + return $template; 40 + } 41 + 42 + public function setValueDescription($value_description) { 43 + $this->valueDescription = $value_description; 44 + return $this; 45 + } 46 + 47 + public function getValueDescription() { 48 + return $this->valueDescription; 49 + } 50 + 51 + }
+76
src/applications/transactions/edittype/PhabricatorEditType.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorEditType extends Phobject { 4 + 5 + private $editType; 6 + private $transactionType; 7 + private $field; 8 + private $description; 9 + private $summary; 10 + private $metadata = array(); 11 + 12 + public function setDescription($description) { 13 + $this->description = $description; 14 + return $this; 15 + } 16 + 17 + public function getDescription() { 18 + return $this->description; 19 + } 20 + 21 + public function setSummary($summary) { 22 + $this->summary = $summary; 23 + return $this; 24 + } 25 + 26 + public function getSummary() { 27 + if ($this->summary === null) { 28 + return $this->getDescription(); 29 + } 30 + return $this->summary; 31 + } 32 + 33 + public function setField(PhabricatorEditField $field) { 34 + $this->field = $field; 35 + return $this; 36 + } 37 + 38 + public function getField() { 39 + return $this->field; 40 + } 41 + 42 + public function setEditType($edit_type) { 43 + $this->editType = $edit_type; 44 + return $this; 45 + } 46 + 47 + public function getEditType() { 48 + return $this->editType; 49 + } 50 + 51 + public function setMetadata($metadata) { 52 + $this->metadata = $metadata; 53 + return $this; 54 + } 55 + 56 + public function getMetadata() { 57 + return $this->metadata; 58 + } 59 + 60 + public function setTransactionType($transaction_type) { 61 + $this->transactionType = $transaction_type; 62 + return $this; 63 + } 64 + 65 + public function getTransactionType() { 66 + return $this->transactionType; 67 + } 68 + 69 + abstract public function generateTransaction( 70 + PhabricatorApplicationTransaction $template, 71 + array $spec); 72 + 73 + abstract public function getValueType(); 74 + abstract public function getValueDescription(); 75 + 76 + }
+41
src/applications/transactions/edittype/PhabricatorSimpleEditType.php
··· 1 + <?php 2 + 3 + final class PhabricatorSimpleEditType extends PhabricatorEditType { 4 + 5 + private $valueType; 6 + private $valueDescription; 7 + 8 + public function setValueType($value_type) { 9 + $this->valueType = $value_type; 10 + return $this; 11 + } 12 + 13 + public function getValueType() { 14 + return $this->valueType; 15 + } 16 + 17 + public function generateTransaction( 18 + PhabricatorApplicationTransaction $template, 19 + array $spec) { 20 + 21 + $template 22 + ->setTransactionType($this->getTransactionType()) 23 + ->setNewValue(idx($spec, 'value')); 24 + 25 + foreach ($this->getMetadata() as $key => $value) { 26 + $template->setMetadataValue($key, $value); 27 + } 28 + 29 + return $template; 30 + } 31 + 32 + public function setValueDescription($value_description) { 33 + $this->valueDescription = $value_description; 34 + return $this; 35 + } 36 + 37 + public function getValueDescription() { 38 + return $this->valueDescription; 39 + } 40 + 41 + }