@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.

Support "template objects" generically in EditEngine

Summary:
Ref T9132. Ref T9908. Fixes T5622. This allows you to copy some fields (projects, subscribers, custom fields, some per-application) from another object when creating a new object by passing the `?template=xyz` parameter.

Extend "copy" support to work with all custom fields.

Test Plan:
- Created new pastes, packages, tasks using `?template=...`
- Viewed new template docs page.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T5622, T9132, T9908

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

+154 -19
+3
src/applications/maniphest/editor/ManiphestEditEngine.php
··· 81 81 ->setLabel(pht('Status')) 82 82 ->setDescription(pht('Status of the task.')) 83 83 ->setTransactionType(ManiphestTransaction::TYPE_STATUS) 84 + ->setIsCopyable(true) 84 85 ->setValue($object->getStatus()) 85 86 ->setOptions($status_map) 86 87 ->setCommentActionLabel(pht('Change Status')) ··· 91 92 ->setLabel(pht('Assigned To')) 92 93 ->setDescription(pht('User who is responsible for the task.')) 93 94 ->setTransactionType(ManiphestTransaction::TYPE_OWNER) 95 + ->setIsCopyable(true) 94 96 ->setSingleValue($object->getOwnerPHID()) 95 97 ->setCommentActionLabel(pht('Assign / Claim')) 96 98 ->setCommentActionDefaultValue($owner_value), ··· 99 101 ->setLabel(pht('Priority')) 100 102 ->setDescription(pht('Priority of the task.')) 101 103 ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) 104 + ->setIsCopyable(true) 102 105 ->setValue($object->getPriority()) 103 106 ->setOptions($priority_map) 104 107 ->setCommentActionLabel($priority_label),
+2
src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php
··· 58 58 ->setDescription(pht('Users and projects which own the package.')) 59 59 ->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_OWNERS) 60 60 ->setDatasource(new PhabricatorProjectOrUserDatasource()) 61 + ->setIsCopyable(true) 61 62 ->setValue($object->getOwnerPHIDs()), 62 63 id(new PhabricatorSelectEditField()) 63 64 ->setKey('status') ··· 74 75 'Automatically trigger audits for commits affecting files in '. 75 76 'this package.')) 76 77 ->setTransactionType(PhabricatorOwnersPackageTransaction::TYPE_AUDITING) 78 + ->setIsCopyable(true) 77 79 ->setValue($object->getAuditingEnabled()) 78 80 ->setOptions( 79 81 array(
+1
src/applications/paste/editor/PhabricatorPasteEditEngine.php
··· 73 73 'title.')) 74 74 ->setAliases(array('lang')) 75 75 ->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE) 76 + ->setIsCopyable(true) 76 77 ->setValue($object->getLanguage()) 77 78 ->setOptions($langs), 78 79 id(new PhabricatorSelectEditField())
+2
src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php
··· 83 83 ->setLabel($label) 84 84 ->setDescription($description) 85 85 ->setAliases($aliases) 86 + ->setIsCopyable(true) 86 87 ->setCapability($capability) 87 88 ->setPolicies($policies) 88 89 ->setTransactionType($type) ··· 100 101 ->setEditTypeKey('space') 101 102 ->setDescription( 102 103 pht('Shifts the object in the Spaces application.')) 104 + ->setIsCopyable(true) 103 105 ->setIsReorderable(false) 104 106 ->setAliases(array('space', 'policy.space')) 105 107 ->setTransactionType($type_space)
+1
src/applications/project/editor/PhabricatorProjectsEditEngineExtension.php
··· 48 48 ->setEditTypeKey('projects') 49 49 ->setDescription(pht('Add or remove associated projects.')) 50 50 ->setAliases(array('project', 'projects')) 51 + ->setIsCopyable(true) 51 52 ->setUseEdgeTransactions(true) 52 53 ->setEdgeTransactionDescriptions( 53 54 pht('Add projects.'),
+1
src/applications/subscriptions/editor/PhabricatorSubscriptionsEditEngineExtension.php
··· 45 45 ->setEditTypeKey('subscribers') 46 46 ->setDescription(pht('Manage subscribers.')) 47 47 ->setAliases(array('subscriber', 'subscribers')) 48 + ->setIsCopyable(true) 48 49 ->setUseEdgeTransactions(true) 49 50 ->setEdgeTransactionDescriptions( 50 51 pht('Add subscribers.'),
+48 -7
src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 95 95 } 96 96 97 97 $config = $this->getEditEngineConfiguration(); 98 - $fields = $config->applyConfigurationToFields($this, $fields); 98 + $fields = $config->applyConfigurationToFields($this, $object, $fields); 99 99 100 100 foreach ($fields as $field) { 101 101 $field ··· 443 443 * to make Conduit a little easier to use. 444 444 * 445 445 * @param wild ID, PHID, or monogram. 446 + * @param list<const> List of required capability constants, or omit for 447 + * defaults. 446 448 * @return object Corresponding editable object. 447 449 * @task load 448 450 */ 449 - private function newObjectFromIdentifier($identifier) { 451 + private function newObjectFromIdentifier( 452 + $identifier, 453 + array $capabilities = array()) { 450 454 if (is_int($identifier) || ctype_digit($identifier)) { 451 - $object = $this->newObjectFromID($identifier); 455 + $object = $this->newObjectFromID($identifier, $capabilities); 452 456 453 457 if (!$object) { 454 458 throw new Exception( ··· 462 466 463 467 $type_unknown = PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN; 464 468 if (phid_get_type($identifier) != $type_unknown) { 465 - $object = $this->newObjectFromPHID($identifier); 469 + $object = $this->newObjectFromPHID($identifier, $capabilities); 466 470 467 471 if (!$object) { 468 472 throw new Exception( ··· 503 507 // sure it's really valid, goes through standard policy check logic, and 504 508 // picks up any `need...()` clauses we want it to load with. 505 509 506 - $object = $this->newObjectFromPHID($target->getPHID()); 510 + $object = $this->newObjectFromPHID($target->getPHID(), $capabilities); 507 511 if (!$object) { 508 512 throw new Exception( 509 513 pht( ··· 536 540 * Load an object by PHID. 537 541 * 538 542 * @param phid Object PHID. 543 + * @param list<const> List of required capability constants, or omit for 544 + * defaults. 539 545 * @return object|null Object, or null if no such object exists. 540 546 * @task load 541 547 */ 542 - private function newObjectFromPHID($phid) { 548 + private function newObjectFromPHID($phid, array $capabilities = array()) { 543 549 $query = $this->newObjectQuery() 544 550 ->withPHIDs(array($phid)); 545 551 546 - return $this->newObjectFromQuery($query); 552 + return $this->newObjectFromQuery($query, $capabilities); 547 553 } 548 554 549 555 ··· 627 633 $capabilities = array( 628 634 PhabricatorPolicyCapability::CAN_VIEW, 629 635 ); 636 + $use_default = true; 637 + break; 638 + case 'parameters': 630 639 $use_default = true; 631 640 break; 632 641 default: ··· 769 778 } 770 779 } else { 771 780 if ($this->getIsCreate()) { 781 + $template = $request->getStr('template'); 782 + 783 + if (strlen($template)) { 784 + $template_object = $this->newObjectFromIdentifier( 785 + $template, 786 + array( 787 + PhabricatorPolicyCapability::CAN_VIEW, 788 + )); 789 + if (!$template_object) { 790 + return new Aphront404Response(); 791 + } 792 + } else { 793 + $template_object = null; 794 + } 795 + 796 + if ($template_object) { 797 + $copy_fields = $this->buildEditFields($template_object); 798 + $copy_fields = mpull($copy_fields, null, 'getKey'); 799 + foreach ($copy_fields as $copy_key => $copy_field) { 800 + if (!$copy_field->getIsCopyable()) { 801 + unset($copy_fields[$copy_key]); 802 + } 803 + } 804 + } else { 805 + $copy_fields = array(); 806 + } 807 + 772 808 foreach ($fields as $field) { 773 809 if ($field->getIsLocked() || $field->getIsHidden()) { 774 810 continue; 811 + } 812 + 813 + $field_key = $field->getKey(); 814 + if (isset($copy_fields[$field_key])) { 815 + $field->readValueFromField($copy_fields[$field_key]); 775 816 } 776 817 777 818 $field->readValueFromRequest($request);
+19
src/applications/transactions/editfield/PhabricatorEditField.php
··· 28 28 private $isReorderable = true; 29 29 private $isDefaultable = true; 30 30 private $isLockable = true; 31 + private $isCopyable = false; 31 32 32 33 public function setKey($key) { 33 34 $this->key = $key; ··· 144 145 145 146 public function getIsHidden() { 146 147 return $this->isHidden; 148 + } 149 + 150 + public function setIsCopyable($is_copyable) { 151 + $this->isCopyable = $is_copyable; 152 + return $this; 153 + } 154 + 155 + public function getIsCopyable() { 156 + return $this->isCopyable; 147 157 } 148 158 149 159 public function setIsSubmittedForm($is_submitted) { ··· 364 374 365 375 protected function getValueFromRequest(AphrontRequest $request, $key) { 366 376 return $this->getHTTPParameterValue($request, $key); 377 + } 378 + 379 + public function readValueFromField(PhabricatorEditField $other) { 380 + $this->value = $this->getValueFromField($other); 381 + return $this; 382 + } 383 + 384 + protected function getValueFromField(PhabricatorEditField $other) { 385 + return $other->getValue(); 367 386 } 368 387 369 388
+4 -1
src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php
··· 89 89 90 90 public function applyConfigurationToFields( 91 91 PhabricatorEditEngine $engine, 92 + $object, 92 93 array $fields) { 93 94 $fields = mpull($fields, null, 'getKey'); 95 + 96 + $is_new = !$object->getID(); 94 97 95 98 $values = $this->getProperty('defaults', array()); 96 99 foreach ($fields as $key => $field) { 97 - if ($engine->getIsCreate()) { 100 + if ($is_new) { 98 101 if (array_key_exists($key, $values)) { 99 102 $field->readDefaultValueFromConfiguration($values[$key]); 100 103 }
+55
src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php
··· 176 176 'wide', 177 177 )); 178 178 179 + $template_text = pht(<<<EOTEXT 180 + Template Objects 181 + ---------------- 182 + 183 + Instead of specifying each field value individually, you can specify another 184 + object to use as a template. Some of the initial fields will be copied from the 185 + template object. 186 + 187 + Specify a template object with the `template` parameter. You can use an ID, 188 + PHID, or monogram (for objects which have monograms). For example, you might 189 + use URIs like these: 190 + 191 + ``` 192 + %s?template=123 193 + %s?template=PHID-WXYZ-abcdef... 194 + %s?template=T123 195 + ``` 196 + 197 + You can combine the `template` parameter with HTTP parameters: the template 198 + object will be copied first, then any HTTP parameters will be read. 199 + 200 + When using `template`, these fields will be copied: 201 + EOTEXT 202 + , 203 + $uri, 204 + $uri, 205 + $uri); 206 + 207 + $yes = id(new PHUIIconView())->setIconFont('fa-check-circle green'); 208 + $no = id(new PHUIIconView())->setIconFont('fa-times grey'); 209 + 210 + $rows = array(); 211 + foreach ($fields as $field) { 212 + $rows[] = array( 213 + $field->getLabel(), 214 + $field->getIsCopyable() ? $yes : $no, 215 + ); 216 + } 217 + 218 + $template_table = id(new AphrontTableView($rows)) 219 + ->setNoDataString( 220 + pht('None of the fields on this object support templating.')) 221 + ->setHeaders( 222 + array( 223 + pht('Field'), 224 + pht('Will Copy'), 225 + )) 226 + ->setColumnClasses( 227 + array( 228 + 'pri', 229 + 'wide', 230 + )); 231 + 179 232 $select_text = pht(<<<EOTEXT 180 233 Select Fields 181 234 ------------- ··· 243 296 $main_table, 244 297 $this->renderInstructions($aliases_text), 245 298 $alias_table, 299 + $this->renderInstructions($template_text), 300 + $template_table, 246 301 $this->renderInstructions($select_text), 247 302 $select_table, 248 303 $this->renderInstructions($types_text),
+2 -9
src/docs/user/configuration/custom_fields.diviner
··· 119 119 above the control when rendered on the edit view. 120 120 - **placeholder**: A placeholder text that appears on text boxes. Only 121 121 supported in text, int and remarkup fields (optional). 122 + - **copy**: If true, this field's value will be copied when an object is 123 + created using another object as a template. 122 124 123 125 The `strings` value supports different strings per control type. They are: 124 126 ··· 127 129 - **view.yes** Text for the view interface, defaults to "Yes". 128 130 - **search.default** Text for the search interface, defaults to "(Any)". 129 131 - **search.require** Text for the search interface, defaults to "Require". 130 - 131 - Some applications have specific options which only work in that application. 132 - 133 - In **Maniphest**: 134 - 135 - - **copy**: When a user creates a task, the UI gives them an option to 136 - "Create Another Similar Task". Some fields from the original task are copied 137 - into the new task, while others are not; by default, fields are not copied. 138 - If you want this field to be copied, specify `true` for the `copy` property. 139 132 140 133 Internally, Phabricator implements some additional custom field types and 141 134 options. These are not intended for general use and are subject to abrupt
+1 -1
src/infrastructure/customfield/editor/PhabricatorCustomFieldEditEngineExtension.php
··· 35 35 36 36 $field_list->setViewer($viewer); 37 37 38 - if (!$engine->getIsCreate()) { 38 + if ($object->getID()) { 39 39 $field_list->readFieldsFromStorage($object); 40 40 } 41 41
+15 -1
src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
··· 15 15 private $fieldError; 16 16 private $required; 17 17 private $default; 18 + private $isCopyable; 18 19 19 20 abstract public function getFieldType(); 20 21 ··· 115 116 case 'default': 116 117 $this->setFieldValue($value); 117 118 break; 119 + case 'copy': 120 + $this->setIsCopyable($value); 121 + break; 118 122 case 'type': 119 123 // We set this earlier on. 120 124 break; ··· 183 187 184 188 public function getString($key, $default = null) { 185 189 return idx($this->strings, $key, $default); 190 + } 191 + 192 + public function setIsCopyable($is_copyable) { 193 + $this->isCopyable = $is_copyable; 194 + return $this; 195 + } 196 + 197 + public function getIsCopyable() { 198 + return $this->isCopyable; 186 199 } 187 200 188 201 public function shouldUseStorage() { ··· 430 443 $short = 'custom.'.$this->getRawStandardFieldKey(); 431 444 432 445 return parent::newStandardEditField() 433 - ->setEditTypeKey($short); 446 + ->setEditTypeKey($short) 447 + ->setIsCopyable($this->getIsCopyable()); 434 448 } 435 449 436 450 public function shouldAppearInConduitTransactions() {