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

Drive CLI-based revision edits through "differential.revision.edit" API + EditEngine

Summary:
Ref T11114. This creates `differential.revision.edit` (a modern, v3 API method) and redefines the existing methods in terms of it.

Both `differential.createrevision` and `differential.updaterevision` are now internally implemented by building a `differential.revision.edit` API call and then executing it.

I //think// this covers everything except custom fields, which need some tweaking to work with EditEngine. I'll clean that up in the next change.

Test Plan:
- Created, updated, and edited revisions via `arc`.
- Called APIs manually via test console.
- Stored custom fields ("Blame Rev", "Revert Plan") aren't exposed yet.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11114

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

+231 -98
+2
src/__phutil_library_map__.php
··· 527 527 'DifferentialRevisionControlSystem' => 'applications/differential/constants/DifferentialRevisionControlSystem.php', 528 528 'DifferentialRevisionDependedOnByRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependedOnByRevisionEdgeType.php', 529 529 'DifferentialRevisionDependsOnRevisionEdgeType' => 'applications/differential/edge/DifferentialRevisionDependsOnRevisionEdgeType.php', 530 + 'DifferentialRevisionEditConduitAPIMethod' => 'applications/differential/conduit/DifferentialRevisionEditConduitAPIMethod.php', 530 531 'DifferentialRevisionEditEngine' => 'applications/differential/editor/DifferentialRevisionEditEngine.php', 531 532 'DifferentialRevisionEditProController' => 'applications/differential/controller/DifferentialRevisionEditProController.php', 532 533 'DifferentialRevisionFulltextEngine' => 'applications/differential/search/DifferentialRevisionFulltextEngine.php', ··· 5203 5204 'DifferentialRevisionControlSystem' => 'Phobject', 5204 5205 'DifferentialRevisionDependedOnByRevisionEdgeType' => 'PhabricatorEdgeType', 5205 5206 'DifferentialRevisionDependsOnRevisionEdgeType' => 'PhabricatorEdgeType', 5207 + 'DifferentialRevisionEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 5206 5208 'DifferentialRevisionEditEngine' => 'PhabricatorEditEngine', 5207 5209 'DifferentialRevisionEditProController' => 'DifferentialController', 5208 5210 'DifferentialRevisionFulltextEngine' => 'PhabricatorFulltextEngine',
+27 -61
src/applications/differential/conduit/DifferentialConduitAPIMethod.php
··· 53 53 54 54 $viewer = $request->getUser(); 55 55 56 - $field_list = PhabricatorCustomField::getObjectFields( 57 - $revision, 58 - DifferentialCustomField::ROLE_COMMITMESSAGEEDIT); 59 - 60 - $field_list 61 - ->setViewer($viewer) 62 - ->readFieldsFromStorage($revision); 63 - $field_map = mpull($field_list->getFields(), null, 'getFieldKeyForConduit'); 56 + // We're going to build the body of a "differential.revision.edit" API 57 + // request, then just call that code directly. 64 58 65 59 $xactions = array(); 66 - 67 - $xactions[] = id(new DifferentialTransaction()) 68 - ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) 69 - ->setNewValue($diff->getPHID()); 60 + $xactions[] = array( 61 + 'type' => DifferentialRevisionEditEngine::KEY_UPDATE, 62 + 'value' => $diff->getPHID(), 63 + ); 70 64 65 + $field_map = DifferentialCommitMessageField::newEnabledFields($viewer); 71 66 $values = $request->getValue('fields', array()); 72 67 foreach ($values as $key => $value) { 73 68 $field = idx($field_map, $key); ··· 79 74 continue; 80 75 } 81 76 82 - $role = PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS; 83 - if (!$field->shouldEnableForRole($role)) { 84 - continue; 85 - } 86 - 87 - // TODO: This is fairly similar to PhabricatorCustomField's 88 - // buildFieldTransactionsFromRequest() method, but that's currently not 89 - // easy to reuse. 90 - 91 - $transaction_type = $field->getApplicationTransactionType(); 92 - $xaction = id(new DifferentialTransaction()) 93 - ->setTransactionType($transaction_type); 94 - 95 - if ($transaction_type == PhabricatorTransactions::TYPE_CUSTOMFIELD) { 96 - // For TYPE_CUSTOMFIELD transactions only, we provide the old value 97 - // as an input. 98 - $old_value = $field->getOldValueForApplicationTransactions(); 99 - $xaction->setOldValue($old_value); 100 - } 101 - 102 77 // The transaction itself will be validated so this is somewhat 103 78 // redundant, but this validator will sometimes give us a better error 104 79 // message or a better reaction to a bad value type. 105 - $field->validateCommitMessageValue($value); 106 - $field->readValueFromCommitMessage($value); 107 - 108 - $xaction 109 - ->setNewValue($field->getNewValueForApplicationTransactions()); 80 + $value = $field->readFieldValueFromConduit($value); 110 81 111 - if ($transaction_type == PhabricatorTransactions::TYPE_CUSTOMFIELD) { 112 - // For TYPE_CUSTOMFIELD transactions, add the field key in metadata. 113 - $xaction->setMetadataValue('customfield:key', $field->getFieldKey()); 114 - } 115 - 116 - $metadata = $field->getApplicationTransactionMetadata(); 117 - foreach ($metadata as $meta_key => $meta_value) { 118 - $xaction->setMetadataValue($meta_key, $meta_value); 82 + foreach ($field->getFieldTransactions($value) as $xaction) { 83 + $xactions[] = $xaction; 119 84 } 120 - 121 - $xactions[] = $xaction; 122 85 } 123 86 124 87 $message = $request->getValue('message'); 125 88 if (strlen($message)) { 126 - // This is a little awkward, and should maybe move inside the transaction 127 - // editor. It largely exists for legacy reasons. See some discussion in 128 - // T7899. 89 + // This is a little awkward, and should move elsewhere or be removed. It 90 + // largely exists for legacy reasons. See some discussion in T7899. 129 91 $first_line = head(phutil_split_lines($message, false)); 130 92 131 93 $first_line = id(new PhutilUTF8StringTruncator()) ··· 136 98 $diff->setDescription($first_line); 137 99 $diff->save(); 138 100 139 - $xactions[] = id(new DifferentialTransaction()) 140 - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 141 - ->attachComment( 142 - id(new DifferentialTransactionComment()) 143 - ->setContent($message)); 101 + $xactions[] = array( 102 + 'type' => PhabricatorCommentEditEngineExtension::EDITKEY, 103 + 'value' => $message, 104 + ); 144 105 } 145 106 146 - $editor = id(new DifferentialTransactionEditor()) 147 - ->setActor($viewer) 148 - ->setContentSource($request->newContentSource()) 149 - ->setContinueOnNoEffect(true) 150 - ->setContinueOnMissingFields(true); 107 + $method = 'differential.revision.edit'; 108 + $params = array( 109 + 'transactions' => $xactions, 110 + ); 151 111 152 - $editor->applyTransactions($revision, $xactions); 112 + if ($revision->getID()) { 113 + $params['objectIdentifier'] = $revision->getID(); 114 + } 115 + 116 + return id(new ConduitCall($method, $params, $strict = true)) 117 + ->setUser($viewer) 118 + ->execute(); 153 119 } 154 120 155 121 protected function loadCustomFieldsForRevisions(
+5 -3
src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php
··· 45 45 $revision = DifferentialRevision::initializeNewRevision($viewer); 46 46 $revision->attachReviewerStatus(array()); 47 47 48 - $this->applyFieldEdit( 48 + $result = $this->applyFieldEdit( 49 49 $request, 50 50 $revision, 51 51 $diff, 52 52 $request->getValue('fields', array()), 53 53 $message = null); 54 54 55 + $revision_id = $result['object']['id']; 56 + 55 57 return array( 56 - 'revisionid' => $revision->getID(), 57 - 'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()), 58 + 'revisionid' => $revision_id, 59 + 'uri' => PhabricatorEnv::getURI('/D'.$revision_id), 58 60 ); 59 61 } 60 62
+9 -3
src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php
··· 49 49 $revision = DifferentialRevision::initializeNewRevision($viewer); 50 50 } 51 51 52 + // There are three modes here: "edit", "create", and "read" (which has 53 + // no value for the "edit" parameter). 54 + 55 + // In edit or create mode, we hide read-only fields. In create mode, we 56 + // show "Field:" templates for some fields even if they are empty. 52 57 $edit_mode = $request->getValue('edit'); 58 + 59 + $is_any_edit = (bool)strlen($edit_mode); 53 60 $is_create = ($edit_mode == 'create'); 54 - $is_edit = ($edit_mode && !$is_create); 55 61 56 62 $field_list = DifferentialCommitMessageField::newEnabledFields($viewer); 57 63 ··· 62 68 63 69 // If we're editing the message, remove fields like "Conflicts" and 64 70 // "git-svn-id" which should not be presented to the user for editing. 65 - if ($is_edit) { 71 + if ($is_any_edit) { 66 72 foreach ($field_list as $field_key => $field) { 67 73 if (!$field->isFieldEditable()) { 68 74 unset($field_list[$field_key]); ··· 96 102 $wire_value = $value_map[$field_key]; 97 103 $value = $field->renderFieldValue($wire_value); 98 104 99 - $is_template = ($is_edit && $field->isTemplateField()); 105 + $is_template = ($is_create && $field->isTemplateField()); 100 106 101 107 if (!is_string($value) && !is_null($value)) { 102 108 throw new Exception(
+18
src/applications/differential/conduit/DifferentialRevisionEditConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionEditConduitAPIMethod 4 + extends PhabricatorEditEngineAPIMethod { 5 + 6 + public function getAPIMethodName() { 7 + return 'differential.revision.edit'; 8 + } 9 + 10 + public function newEditEngine() { 11 + return new DifferentialRevisionEditEngine(); 12 + } 13 + 14 + public function getMethodSummary() { 15 + return pht('Apply transactions to create or update a revision.'); 16 + } 17 + 18 + }
+20 -19
src/applications/differential/editor/DifferentialRevisionEditEngine.php
··· 7 7 8 8 const ENGINECONST = 'differential.revision'; 9 9 10 + const KEY_UPDATE = 'update'; 11 + 10 12 public function getEngineName() { 11 13 return pht('Revisions'); 12 14 } ··· 100 102 101 103 $fields = array(); 102 104 103 - if ($diff || $is_create) { 104 - $fields[] = id(new PhabricatorHandlesEditField()) 105 - ->setKey('update') 106 - ->setLabel(pht('Update Diff')) 107 - ->setDescription(pht('New diff to create or update the revision with.')) 108 - ->setConduitDescription(pht('Create or update a revision with a diff.')) 109 - ->setConduitTypeDescription(pht('PHID of the diff.')) 110 - ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) 111 - ->setHandleParameterType(new AphrontPHIDListHTTPParameterType()) 112 - ->setSingleValue($diff_phid) 113 - ->setIsReorderable(false) 114 - ->setIsDefaultable(false) 115 - ->setIsInvisible(true) 116 - ->setIsLockable(false); 117 - } 105 + $fields[] = id(new PhabricatorHandlesEditField()) 106 + ->setKey(self::KEY_UPDATE) 107 + ->setLabel(pht('Update Diff')) 108 + ->setDescription(pht('New diff to create or update the revision with.')) 109 + ->setConduitDescription(pht('Create or update a revision with a diff.')) 110 + ->setConduitTypeDescription(pht('PHID of the diff.')) 111 + ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) 112 + ->setHandleParameterType(new AphrontPHIDListHTTPParameterType()) 113 + ->setSingleValue($diff_phid) 114 + ->setIsConduitOnly(!$diff) 115 + ->setIsReorderable(false) 116 + ->setIsDefaultable(false) 117 + ->setIsInvisible(true) 118 + ->setIsLockable(false); 118 119 119 120 if ($is_update) { 120 121 $fields[] = id(new PhabricatorInstructionsEditField()) ··· 134 135 } 135 136 136 137 $fields[] = id(new PhabricatorTextEditField()) 137 - ->setKey('title') 138 + ->setKey(DifferentialRevisionTitleTransaction::EDITKEY) 138 139 ->setLabel(pht('Title')) 139 140 ->setIsRequired(true) 140 141 ->setTransactionType( ··· 145 146 ->setValue($object->getTitle()); 146 147 147 148 $fields[] = id(new PhabricatorRemarkupEditField()) 148 - ->setKey('summary') 149 + ->setKey(DifferentialRevisionSummaryTransaction::EDITKEY) 149 150 ->setLabel(pht('Summary')) 150 151 ->setTransactionType( 151 152 DifferentialRevisionSummaryTransaction::TRANSACTIONTYPE) ··· 156 157 157 158 if ($plan_enabled) { 158 159 $fields[] = id(new PhabricatorRemarkupEditField()) 159 - ->setKey('testPlan') 160 + ->setKey(DifferentialRevisionTestPlanTransaction::EDITKEY) 160 161 ->setLabel(pht('Test Plan')) 161 162 ->setIsRequired($plan_required) 162 163 ->setTransactionType( ··· 169 170 } 170 171 171 172 $fields[] = id(new PhabricatorDatasourceEditField()) 172 - ->setKey('reviewerPHIDs') 173 + ->setKey(DifferentialRevisionReviewersTransaction::EDITKEY) 173 174 ->setLabel(pht('Reviewers')) 174 175 ->setDatasource(new DifferentialReviewerDatasource()) 175 176 ->setUseEdgeTransactions(true)
+5
src/applications/differential/field/DifferentialCommitMessageCustomField.php
··· 60 60 return $idx; 61 61 } 62 62 63 + public function getFieldTransactions($value) { 64 + // TODO: Implement this! 65 + return array(); 66 + } 67 + 63 68 }
+7
src/applications/differential/field/DifferentialCommitMessageField.php
··· 67 67 return $value; 68 68 } 69 69 70 + public function getFieldTransactions($value) { 71 + if (!$this->isFieldEditable()) { 72 + return array(); 73 + } 74 + throw new PhutilMethodNotImplementedException(); 75 + } 76 + 70 77 final public function getCommitMessageFieldKey() { 71 78 return $this->getPhobjectClassConstant('FIELDKEY', 64); 72 79 }
+4
src/applications/differential/field/DifferentialConflictsCommitMessageField.php
··· 17 17 return false; 18 18 } 19 19 20 + public function isTemplateField() { 21 + return false; 22 + } 23 + 20 24 }
+4
src/applications/differential/field/DifferentialGitSVNIDCommitMessageField.php
··· 17 17 return false; 18 18 } 19 19 20 + public function isTemplateField() { 21 + return false; 22 + } 23 + 20 24 }
+4
src/applications/differential/field/DifferentialReviewedByCommitMessageField.php
··· 27 27 return false; 28 28 } 29 29 30 + public function isTemplateField() { 31 + return false; 32 + } 33 + 30 34 public function readFieldValueFromObject(DifferentialRevision $revision) { 31 35 if (!$revision->getPHID()) { 32 36 return array();
+24
src/applications/differential/field/DifferentialReviewersCommitMessageField.php
··· 77 77 return $this->renderHandleList($phid_list, $suffix_map); 78 78 } 79 79 80 + public function getFieldTransactions($value) { 81 + $value = $this->inflateReviewers($value); 82 + 83 + $reviewer_list = array(); 84 + foreach ($value as $reviewer) { 85 + $phid = $reviewer['phid']; 86 + if (isset($reviewer['suffixes']['!'])) { 87 + $reviewer_list[] = 'blocking('.$phid.')'; 88 + } else { 89 + $reviewer_list[] = $phid; 90 + } 91 + } 92 + 93 + $xaction_key = DifferentialRevisionReviewersTransaction::EDITKEY; 94 + $xaction_type = "{$xaction_key}.set"; 95 + 96 + return array( 97 + array( 98 + 'type' => $xaction_type, 99 + 'value' => $reviewer_list, 100 + ), 101 + ); 102 + } 103 + 80 104 private function flattenReviewers(array $values) { 81 105 // NOTE: For now, `arc` relies on this field returning only scalars, so we 82 106 // need to reduce the results into scalars. See T10981.
+4
src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php
··· 76 76 return PhabricatorEnv::getProductionURI('/D'.$value); 77 77 } 78 78 79 + public function getFieldTransactions($value) { 80 + return array(); 81 + } 82 + 79 83 }
+9
src/applications/differential/field/DifferentialSubscribersCommitMessageField.php
··· 48 48 return $this->renderHandleList($value); 49 49 } 50 50 51 + public function getFieldTransactions($value) { 52 + return array( 53 + array( 54 + 'type' => PhabricatorSubscriptionsEditEngineExtension::EDITKEY_SET, 55 + 'value' => $value, 56 + ), 57 + ); 58 + } 59 + 51 60 }
+9
src/applications/differential/field/DifferentialSummaryCommitMessageField.php
··· 17 17 return $revision->getSummary(); 18 18 } 19 19 20 + public function getFieldTransactions($value) { 21 + return array( 22 + array( 23 + 'type' => DifferentialRevisionSummaryTransaction::EDITKEY, 24 + 'value' => $value, 25 + ), 26 + ); 27 + } 28 + 20 29 }
+8
src/applications/differential/field/DifferentialTagsCommitMessageField.php
··· 54 54 return $this->renderHandleList($value); 55 55 } 56 56 57 + public function getFieldTransactions($value) { 58 + return array( 59 + array( 60 + 'type' => PhabricatorProjectsEditEngineExtension::EDITKEY_SET, 61 + 'value' => $value, 62 + ), 63 + ); 64 + } 57 65 58 66 }
+4
src/applications/differential/field/DifferentialTasksCommitMessageField.php
··· 54 54 return $this->renderHandleList($value); 55 55 } 56 56 57 + public function getFieldTransactions($value) { 58 + // TODO: Implement this! 59 + return array(); 60 + } 57 61 }
+9
src/applications/differential/field/DifferentialTestPlanCommitMessageField.php
··· 41 41 return $revision->getTestPlan(); 42 42 } 43 43 44 + public function getFieldTransactions($value) { 45 + return array( 46 + array( 47 + 'type' => DifferentialRevisionTestPlanTransaction::EDITKEY, 48 + 'value' => $value, 49 + ), 50 + ); 51 + } 52 + 44 53 }
+9
src/applications/differential/field/DifferentialTitleCommitMessageField.php
··· 47 47 return $value; 48 48 } 49 49 50 + public function getFieldTransactions($value) { 51 + return array( 52 + array( 53 + 'type' => DifferentialRevisionTitleTransaction::EDITKEY, 54 + 'value' => $value, 55 + ), 56 + ); 57 + } 58 + 50 59 }
+12 -1
src/applications/differential/xaction/DifferentialRevisionReviewersTransaction.php
··· 4 4 extends DifferentialRevisionTransactionType { 5 5 6 6 const TRANSACTIONTYPE = 'differential.revision.reviewers'; 7 + const EDITKEY = 'reviewers'; 7 8 8 9 public function generateOldValue($object) { 9 10 $reviewers = $object->getReviewerStatus(); ··· 18 19 ->setViewer($actor); 19 20 20 21 $reviewers = $this->generateOldValue($object); 22 + $old_reviewers = $reviewers; 21 23 22 24 // First, remove any reviewers we're getting rid of. 23 25 $rem = idx($value, '-', array()); ··· 63 65 64 66 $status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING; 65 67 foreach ($add_map as $phid => $new_status) { 66 - $old_status = idx($reviewers, $phid); 68 + $old_status = idx($old_reviewers, $phid); 67 69 68 70 // If we have an old status and this didn't make the reviewer blocking 69 71 // or nonblocking, just retain the old status. This makes sure we don't ··· 76 78 $is_unblock = (!$now_blocking && $was_blocking); 77 79 78 80 if (!$is_block && !$is_unblock) { 81 + $reviewers[$phid] = $old_status; 79 82 continue; 80 83 } 81 84 } ··· 84 87 } 85 88 86 89 return $reviewers; 90 + } 91 + 92 + public function getTransactionHasEffect($object, $old, $new) { 93 + // At least for now, we ignore transactions which ONLY reorder reviewers 94 + // without making any actual changes. 95 + ksort($old); 96 + ksort($new); 97 + return ($old !== $new); 87 98 } 88 99 89 100 public function applyExternalEffects($object, $value) {
+1
src/applications/differential/xaction/DifferentialRevisionSummaryTransaction.php
··· 4 4 extends DifferentialRevisionTransactionType { 5 5 6 6 const TRANSACTIONTYPE = 'differential.revision.summary'; 7 + const EDITKEY = 'summary'; 7 8 8 9 public function generateOldValue($object) { 9 10 return $object->getSummary();
+1
src/applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php
··· 4 4 extends DifferentialRevisionTransactionType { 5 5 6 6 const TRANSACTIONTYPE = 'differential.revision.testplan'; 7 + const EDITKEY = 'testPlan'; 7 8 8 9 public function generateOldValue($object) { 9 10 return $object->getTestPlan();
+1
src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php
··· 4 4 extends DifferentialRevisionTransactionType { 5 5 6 6 const TRANSACTIONTYPE = 'differential.revision.title'; 7 + const EDITKEY = 'title'; 7 8 8 9 public function generateOldValue($object) { 9 10 return $object->getTitle();
+7 -3
src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php
··· 5 5 6 6 const EXTENSIONKEY = 'projects.projects'; 7 7 8 + const EDITKEY_ADD = 'projects.add'; 9 + const EDITKEY_SET = 'projects.set'; 10 + const EDITKEY_REMOVE = 'projects.remove'; 11 + 8 12 public function getExtensionPriority() { 9 13 return 500; 10 14 } ··· 58 62 59 63 $projects_field->setViewer($engine->getViewer()); 60 64 61 - $edit_add = $projects_field->getConduitEditType('projects.add') 65 + $edit_add = $projects_field->getConduitEditType(self::EDITKEY_ADD) 62 66 ->setConduitDescription(pht('Add project tags.')); 63 67 64 - $edit_set = $projects_field->getConduitEditType('projects.set') 68 + $edit_set = $projects_field->getConduitEditType(self::EDITKEY_SET) 65 69 ->setConduitDescription( 66 70 pht('Set project tags, overwriting current value.')); 67 71 68 - $edit_rem = $projects_field->getConduitEditType('projects.remove') 72 + $edit_rem = $projects_field->getConduitEditType(self::EDITKEY_REMOVE) 69 73 ->setConduitDescription(pht('Remove project tags.')); 70 74 71 75 return array(
+7 -3
src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php
··· 5 5 6 6 const EXTENSIONKEY = 'subscriptions.subscribers'; 7 7 8 + const EDITKEY_ADD = 'subscribers.add'; 9 + const EDITKEY_SET = 'subscribers.set'; 10 + const EDITKEY_REMOVE = 'subscribers.remove'; 11 + 8 12 public function getExtensionPriority() { 9 13 return 750; 10 14 } ··· 52 56 53 57 $subscribers_field->setViewer($engine->getViewer()); 54 58 55 - $edit_add = $subscribers_field->getConduitEditType('subscribers.add') 59 + $edit_add = $subscribers_field->getConduitEditType(self::EDITKEY_ADD) 56 60 ->setConduitDescription(pht('Add subscribers.')); 57 61 58 - $edit_set = $subscribers_field->getConduitEditType('subscribers.set') 62 + $edit_set = $subscribers_field->getConduitEditType(self::EDITKEY_SET) 59 63 ->setConduitDescription( 60 64 pht('Set subscribers, overwriting current value.')); 61 65 62 - $edit_rem = $subscribers_field->getConduitEditType('subscribers.remove') 66 + $edit_rem = $subscribers_field->getConduitEditType(self::EDITKEY_REMOVE) 63 67 ->setConduitDescription(pht('Remove subscribers.')); 64 68 65 69 return array(
+2 -1
src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php
··· 4 4 extends PhabricatorEditEngineExtension { 5 5 6 6 const EXTENSIONKEY = 'transactions.comment'; 7 + const EDITKEY = 'comment'; 7 8 8 9 public function getExtensionPriority() { 9 10 return 9000; ··· 39 40 $comment_type = PhabricatorTransactions::TYPE_COMMENT; 40 41 41 42 $comment_field = id(new PhabricatorCommentEditField()) 42 - ->setKey('comment') 43 + ->setKey(self::EDITKEY) 43 44 ->setLabel(pht('Comments')) 44 45 ->setAliases(array('comments')) 45 46 ->setIsHidden(true)
+19 -4
src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php
··· 396 396 if (!self::isFunctionToken($token)) { 397 397 $results[] = $token; 398 398 } else { 399 - $evaluate[] = $token; 399 + // Put a placeholder in the result list so that we retain token order 400 + // when possible. We'll overwrite this below. 401 + $results[] = null; 402 + $evaluate[last_key($results)] = $token; 400 403 } 401 404 } 402 405 403 406 $results = $this->evaluateValues($results); 404 407 405 - foreach ($evaluate as $function) { 408 + foreach ($evaluate as $result_key => $function) { 406 409 $function = self::parseFunction($function); 407 410 if (!$function) { 408 411 throw new PhabricatorTypeaheadInvalidTokenException(); ··· 411 414 $name = $function['name']; 412 415 $argv = $function['argv']; 413 416 414 - foreach ($this->evaluateFunction($name, array($argv)) as $phid) { 415 - $results[] = $phid; 417 + $evaluated_tokens = $this->evaluateFunction($name, array($argv)); 418 + if (!$evaluated_tokens) { 419 + unset($results[$result_key]); 420 + } else { 421 + $is_first = true; 422 + foreach ($evaluated_tokens as $phid) { 423 + if ($is_first) { 424 + $results[$result_key] = $phid; 425 + $is_first = false; 426 + } else { 427 + $results[] = $phid; 428 + } 429 + } 416 430 } 417 431 } 418 432 433 + $results = array_values($results); 419 434 $results = $this->didEvaluateTokens($results); 420 435 421 436 return $results;