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

Don't let users write summaries or test plans which will become ambiguous in commit messages

Summary:
Ref T11085. To recreate the issue:

- From the web UI, click "Edit Revision".
- Write something like this as your "Summary" (i.e., put another field marker, like "Test Plan:", into the summary):

> This is a test of the
> Test Plan: field to see
> if it works.

- Save changes.

Later, when the summary is amended into a commit message, the parser will see two "Test Plan:" fields and fail to parse the message.

Instead, prevent users from making this edit.

Test Plan: {F1917640}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11085

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

+89 -44
+7 -44
src/applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php
··· 29 29 $corpus = $request->getValue('corpus'); 30 30 $is_partial = $request->getValue('partial'); 31 31 32 - $revision = new DifferentialRevision(); 33 - 34 32 $field_list = PhabricatorCustomField::getObjectFields( 35 - $revision, 33 + new DifferentialRevision(), 36 34 DifferentialCustomField::ROLE_COMMITMESSAGE); 37 35 $field_list->setViewer($viewer); 38 - $field_map = mpull($field_list->getFields(), null, 'getFieldKeyForConduit'); 36 + $field_map = mpull($field_list, null, 'getFieldKeyForConduit'); 39 37 40 - $this->errors = array(); 41 - 42 - $label_map = $this->buildLabelMap($field_list); 43 - $corpus_map = $this->parseCommitMessage($corpus, $label_map); 38 + $corpus_map = $this->parseCommitMessage($corpus); 44 39 45 40 $values = array(); 46 41 foreach ($corpus_map as $field_key => $text_value) { ··· 94 89 ); 95 90 } 96 91 97 - private function buildLabelMap(PhabricatorCustomFieldList $field_list) { 98 - $label_map = array(); 99 - 100 - foreach ($field_list->getFields() as $key => $field) { 101 - $labels = $field->getCommitMessageLabels(); 102 - $key = $field->getFieldKeyForConduit(); 103 - 104 - foreach ($labels as $label) { 105 - $normal_label = DifferentialCommitMessageParser::normalizeFieldLabel( 106 - $label); 107 - if (!empty($label_map[$normal_label])) { 108 - throw new Exception( 109 - pht( 110 - 'Field label "%s" is parsed by two custom fields: "%s" and '. 111 - '"%s". Each label must be parsed by only one field.', 112 - $label, 113 - $key, 114 - $label_map[$normal_label])); 115 - } 116 - $label_map[$normal_label] = $key; 117 - } 118 - } 119 - 120 - return $label_map; 121 - } 122 - 123 - 124 - private function parseCommitMessage($corpus, array $label_map) { 125 - $key_title = id(new DifferentialTitleField())->getFieldKeyForConduit(); 126 - $key_summary = id(new DifferentialSummaryField())->getFieldKeyForConduit(); 127 - 128 - $parser = id(new DifferentialCommitMessageParser()) 129 - ->setLabelMap($label_map) 130 - ->setTitleKey($key_title) 131 - ->setSummaryKey($key_summary); 132 - 92 + private function parseCommitMessage($corpus) { 93 + $viewer = $this->getViewer(); 94 + $parser = DifferentialCommitMessageParser::newStandardParser($viewer); 133 95 $result = $parser->parseCorpus($corpus); 134 96 97 + $this->errors = array(); 135 98 foreach ($parser->getErrors() as $error) { 136 99 $this->errors[] = $error; 137 100 }
+43
src/applications/differential/customfield/DifferentialCoreCustomField.php
··· 10 10 11 11 private $value; 12 12 private $fieldError; 13 + private $fieldParser; 13 14 14 15 abstract protected function readValueFromRevision( 15 16 DifferentialRevision $revision); ··· 60 61 $error->setIsMissingFieldError(true); 61 62 $errors[] = $error; 62 63 $this->setFieldError(pht('Required')); 64 + continue; 65 + } 66 + } 67 + 68 + if (is_string($value)) { 69 + $parser = $this->getFieldParser(); 70 + $result = $parser->parseCorpus($value); 71 + 72 + unset($result['__title__']); 73 + unset($result['__summary__']); 74 + 75 + if ($result) { 76 + $error = new PhabricatorApplicationTransactionValidationError( 77 + $type, 78 + pht('Invalid'), 79 + pht( 80 + 'The value you have entered in "%s" can not be parsed '. 81 + 'unambiguously when rendered in a commit message. Edit the '. 82 + 'message so that keywords like "Summary:" and "Test Plan:" do '. 83 + 'not appear at the beginning of lines. Parsed keys: %s.', 84 + $this->getFieldName(), 85 + implode(', ', array_keys($result))), 86 + $xaction); 87 + $errors[] = $error; 88 + $this->setFieldError(pht('Invalid')); 89 + continue; 63 90 } 64 91 } 65 92 } 66 93 67 94 return $errors; 95 + } 96 + 97 + private function getFieldParser() { 98 + if (!$this->fieldParser) { 99 + $viewer = $this->getViewer(); 100 + $parser = DifferentialCommitMessageParser::newStandardParser($viewer); 101 + 102 + // Set custom title and summary keys so we can detect the presence of 103 + // "Summary:" in, e.g., a test plan. 104 + $parser->setTitleKey('__title__'); 105 + $parser->setSummaryKey('__summary__'); 106 + 107 + $this->fieldParser = $parser; 108 + } 109 + 110 + return $this->fieldParser; 68 111 } 69 112 70 113 public function canDisableField() {
+39
src/applications/differential/parser/DifferentialCommitMessageParser.php
··· 27 27 private $errors; 28 28 29 29 30 + public static function newStandardParser(PhabricatorUser $viewer) { 31 + 32 + $key_title = id(new DifferentialTitleField())->getFieldKeyForConduit(); 33 + $key_summary = id(new DifferentialSummaryField())->getFieldKeyForConduit(); 34 + 35 + $field_list = PhabricatorCustomField::getObjectFields( 36 + new DifferentialRevision(), 37 + DifferentialCustomField::ROLE_COMMITMESSAGE); 38 + $field_list->setViewer($viewer); 39 + 40 + $label_map = array(); 41 + 42 + foreach ($field_list->getFields() as $field) { 43 + $labels = $field->getCommitMessageLabels(); 44 + $key = $field->getFieldKeyForConduit(); 45 + 46 + foreach ($labels as $label) { 47 + $normal_label = self::normalizeFieldLabel( 48 + $label); 49 + if (!empty($label_map[$normal_label])) { 50 + throw new Exception( 51 + pht( 52 + 'Field label "%s" is parsed by two custom fields: "%s" and '. 53 + '"%s". Each label must be parsed by only one field.', 54 + $label, 55 + $key, 56 + $label_map[$normal_label])); 57 + } 58 + $label_map[$normal_label] = $key; 59 + } 60 + } 61 + 62 + return id(new self()) 63 + ->setLabelMap($label_map) 64 + ->setTitleKey($key_title) 65 + ->setSummaryKey($key_summary); 66 + } 67 + 68 + 30 69 /* -( Configuring the Parser )--------------------------------------------- */ 31 70 32 71