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

Separate all commit message field parsing out of Differential custom fields

Summary:
Ref T11114. See that task for some discussion.

Overall, Differential custom fields ended up with too many responsibilities. Later work in EditEngine provides a more promising model for achieving modularity with smaller, more consistent components.

In particular, we have some custom fields like `DifferentialGitSVNIDField` and `DifferentialConflictsField` which serve //only// to support the field parser.

This starts pulling commit message responsibilities out of the core list of custom fields and into simpler dedicated parsers.

Test Plan: Created and edited revisions from the CLI. Added a bit of test coverage.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11114

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

+632 -40
+32
src/__phutil_library_map__.php
··· 353 353 'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php', 354 354 'DifferentialApplyPatchField' => 'applications/differential/customfield/DifferentialApplyPatchField.php', 355 355 'DifferentialAsanaRepresentationField' => 'applications/differential/customfield/DifferentialAsanaRepresentationField.php', 356 + 'DifferentialAuditorsCommitMessageField' => 'applications/differential/field/DifferentialAuditorsCommitMessageField.php', 356 357 'DifferentialAuditorsField' => 'applications/differential/customfield/DifferentialAuditorsField.php', 357 358 'DifferentialAuthorField' => 'applications/differential/customfield/DifferentialAuthorField.php', 359 + 'DifferentialBlameRevisionCommitMessageField' => 'applications/differential/field/DifferentialBlameRevisionCommitMessageField.php', 358 360 'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php', 359 361 'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php', 360 362 'DifferentialBlockingReviewerDatasource' => 'applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php', ··· 383 385 'DifferentialCloseConduitAPIMethod' => 'applications/differential/conduit/DifferentialCloseConduitAPIMethod.php', 384 386 'DifferentialCommentPreviewController' => 'applications/differential/controller/DifferentialCommentPreviewController.php', 385 387 'DifferentialCommentSaveController' => 'applications/differential/controller/DifferentialCommentSaveController.php', 388 + 'DifferentialCommitMessageField' => 'applications/differential/field/DifferentialCommitMessageField.php', 386 389 'DifferentialCommitMessageParser' => 'applications/differential/parser/DifferentialCommitMessageParser.php', 387 390 'DifferentialCommitMessageParserTestCase' => 'applications/differential/parser/__tests__/DifferentialCommitMessageParserTestCase.php', 388 391 'DifferentialCommitsField' => 'applications/differential/customfield/DifferentialCommitsField.php', 389 392 'DifferentialConduitAPIMethod' => 'applications/differential/conduit/DifferentialConduitAPIMethod.php', 393 + 'DifferentialConflictsCommitMessageField' => 'applications/differential/field/DifferentialConflictsCommitMessageField.php', 390 394 'DifferentialConflictsField' => 'applications/differential/customfield/DifferentialConflictsField.php', 391 395 'DifferentialController' => 'applications/differential/controller/DifferentialController.php', 392 396 'DifferentialCoreCustomField' => 'applications/differential/customfield/DifferentialCoreCustomField.php', ··· 444 448 'DifferentialGetRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php', 445 449 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', 446 450 'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php', 451 + 'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php', 447 452 'DifferentialGitSVNIDField' => 'applications/differential/customfield/DifferentialGitSVNIDField.php', 448 453 'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php', 449 454 'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', ··· 461 466 'DifferentialInlineCommentMailView' => 'applications/differential/mail/DifferentialInlineCommentMailView.php', 462 467 'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/DifferentialInlineCommentPreviewController.php', 463 468 'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php', 469 + 'DifferentialJIRAIssuesCommitMessageField' => 'applications/differential/field/DifferentialJIRAIssuesCommitMessageField.php', 464 470 'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php', 465 471 'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php', 466 472 'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php', ··· 493 499 'DifferentialResponsibleDatasource' => 'applications/differential/typeahead/DifferentialResponsibleDatasource.php', 494 500 'DifferentialResponsibleUserDatasource' => 'applications/differential/typeahead/DifferentialResponsibleUserDatasource.php', 495 501 'DifferentialResponsibleViewerFunctionDatasource' => 'applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php', 502 + 'DifferentialRevertPlanCommitMessageField' => 'applications/differential/field/DifferentialRevertPlanCommitMessageField.php', 496 503 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 504 + 'DifferentialReviewedByCommitMessageField' => 'applications/differential/field/DifferentialReviewedByCommitMessageField.php', 497 505 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 498 506 'DifferentialReviewerDatasource' => 'applications/differential/typeahead/DifferentialReviewerDatasource.php', 499 507 'DifferentialReviewerForRevisionEdgeType' => 'applications/differential/edge/DifferentialReviewerForRevisionEdgeType.php', ··· 503 511 'DifferentialReviewersAddBlockingSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddBlockingSelfHeraldAction.php', 504 512 'DifferentialReviewersAddReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddReviewersHeraldAction.php', 505 513 'DifferentialReviewersAddSelfHeraldAction' => 'applications/differential/herald/DifferentialReviewersAddSelfHeraldAction.php', 514 + 'DifferentialReviewersCommitMessageField' => 'applications/differential/field/DifferentialReviewersCommitMessageField.php', 506 515 'DifferentialReviewersField' => 'applications/differential/customfield/DifferentialReviewersField.php', 507 516 'DifferentialReviewersHeraldAction' => 'applications/differential/herald/DifferentialReviewersHeraldAction.php', 508 517 'DifferentialReviewersView' => 'applications/differential/view/DifferentialReviewersView.php', ··· 530 539 'DifferentialRevisionHasTaskRelationship' => 'applications/differential/relationships/DifferentialRevisionHasTaskRelationship.php', 531 540 'DifferentialRevisionHeraldField' => 'applications/differential/herald/DifferentialRevisionHeraldField.php', 532 541 'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php', 542 + 'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php', 533 543 'DifferentialRevisionIDField' => 'applications/differential/customfield/DifferentialRevisionIDField.php', 534 544 'DifferentialRevisionLandController' => 'applications/differential/controller/DifferentialRevisionLandController.php', 535 545 'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php', ··· 563 573 'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php', 564 574 'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php', 565 575 'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php', 576 + 'DifferentialSubscribersCommitMessageField' => 'applications/differential/field/DifferentialSubscribersCommitMessageField.php', 566 577 'DifferentialSubscribersField' => 'applications/differential/customfield/DifferentialSubscribersField.php', 578 + 'DifferentialSummaryCommitMessageField' => 'applications/differential/field/DifferentialSummaryCommitMessageField.php', 567 579 'DifferentialSummaryField' => 'applications/differential/customfield/DifferentialSummaryField.php', 580 + 'DifferentialTagsCommitMessageField' => 'applications/differential/field/DifferentialTagsCommitMessageField.php', 581 + 'DifferentialTasksCommitMessageField' => 'applications/differential/field/DifferentialTasksCommitMessageField.php', 582 + 'DifferentialTestPlanCommitMessageField' => 'applications/differential/field/DifferentialTestPlanCommitMessageField.php', 568 583 'DifferentialTestPlanField' => 'applications/differential/customfield/DifferentialTestPlanField.php', 584 + 'DifferentialTitleCommitMessageField' => 'applications/differential/field/DifferentialTitleCommitMessageField.php', 569 585 'DifferentialTitleField' => 'applications/differential/customfield/DifferentialTitleField.php', 570 586 'DifferentialTransaction' => 'applications/differential/storage/DifferentialTransaction.php', 571 587 'DifferentialTransactionComment' => 'applications/differential/storage/DifferentialTransactionComment.php', ··· 4976 4992 'DifferentialAffectedPath' => 'DifferentialDAO', 4977 4993 'DifferentialApplyPatchField' => 'DifferentialCustomField', 4978 4994 'DifferentialAsanaRepresentationField' => 'DifferentialCustomField', 4995 + 'DifferentialAuditorsCommitMessageField' => 'DifferentialCommitMessageField', 4979 4996 'DifferentialAuditorsField' => 'DifferentialStoredCustomField', 4980 4997 'DifferentialAuthorField' => 'DifferentialCustomField', 4998 + 'DifferentialBlameRevisionCommitMessageField' => 'DifferentialCommitMessageField', 4981 4999 'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField', 4982 5000 'DifferentialBlockHeraldAction' => 'HeraldAction', 4983 5001 'DifferentialBlockingReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', ··· 5009 5027 'DifferentialCloseConduitAPIMethod' => 'DifferentialConduitAPIMethod', 5010 5028 'DifferentialCommentPreviewController' => 'DifferentialController', 5011 5029 'DifferentialCommentSaveController' => 'DifferentialController', 5030 + 'DifferentialCommitMessageField' => 'Phobject', 5012 5031 'DifferentialCommitMessageParser' => 'Phobject', 5013 5032 'DifferentialCommitMessageParserTestCase' => 'PhabricatorTestCase', 5014 5033 'DifferentialCommitsField' => 'DifferentialCustomField', 5015 5034 'DifferentialConduitAPIMethod' => 'ConduitAPIMethod', 5035 + 'DifferentialConflictsCommitMessageField' => 'DifferentialCommitMessageField', 5016 5036 'DifferentialConflictsField' => 'DifferentialCustomField', 5017 5037 'DifferentialController' => 'PhabricatorController', 5018 5038 'DifferentialCoreCustomField' => 'DifferentialCustomField', ··· 5077 5097 'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 5078 5098 'DifferentialGetWorkingCopy' => 'Phobject', 5079 5099 'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy', 5100 + 'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField', 5080 5101 'DifferentialGitSVNIDField' => 'DifferentialCustomField', 5081 5102 'DifferentialHarbormasterField' => 'DifferentialCustomField', 5082 5103 'DifferentialHiddenComment' => 'DifferentialDAO', ··· 5100 5121 'DifferentialInlineCommentMailView' => 'DifferentialMailView', 5101 5122 'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController', 5102 5123 'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery', 5124 + 'DifferentialJIRAIssuesCommitMessageField' => 'DifferentialCommitMessageField', 5103 5125 'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField', 5104 5126 'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener', 5105 5127 'DifferentialLandingStrategy' => 'Phobject', ··· 5132 5154 'DifferentialResponsibleDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5133 5155 'DifferentialResponsibleUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5134 5156 'DifferentialResponsibleViewerFunctionDatasource' => 'PhabricatorTypeaheadDatasource', 5157 + 'DifferentialRevertPlanCommitMessageField' => 'DifferentialCommitMessageField', 5135 5158 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 5159 + 'DifferentialReviewedByCommitMessageField' => 'DifferentialCommitMessageField', 5136 5160 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 5137 5161 'DifferentialReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 5138 5162 'DifferentialReviewerForRevisionEdgeType' => 'PhabricatorEdgeType', ··· 5142 5166 'DifferentialReviewersAddBlockingSelfHeraldAction' => 'DifferentialReviewersHeraldAction', 5143 5167 'DifferentialReviewersAddReviewersHeraldAction' => 'DifferentialReviewersHeraldAction', 5144 5168 'DifferentialReviewersAddSelfHeraldAction' => 'DifferentialReviewersHeraldAction', 5169 + 'DifferentialReviewersCommitMessageField' => 'DifferentialCommitMessageField', 5145 5170 'DifferentialReviewersField' => 'DifferentialCoreCustomField', 5146 5171 'DifferentialReviewersHeraldAction' => 'HeraldAction', 5147 5172 'DifferentialReviewersView' => 'AphrontView', ··· 5185 5210 'DifferentialRevisionHasTaskRelationship' => 'DifferentialRevisionRelationship', 5186 5211 'DifferentialRevisionHeraldField' => 'HeraldField', 5187 5212 'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup', 5213 + 'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField', 5188 5214 'DifferentialRevisionIDField' => 'DifferentialCustomField', 5189 5215 'DifferentialRevisionLandController' => 'DifferentialController', 5190 5216 'DifferentialRevisionListController' => 'DifferentialController', ··· 5218 5244 'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec', 5219 5245 'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod', 5220 5246 'DifferentialStoredCustomField' => 'DifferentialCustomField', 5247 + 'DifferentialSubscribersCommitMessageField' => 'DifferentialCommitMessageField', 5221 5248 'DifferentialSubscribersField' => 'DifferentialCoreCustomField', 5249 + 'DifferentialSummaryCommitMessageField' => 'DifferentialCommitMessageField', 5222 5250 'DifferentialSummaryField' => 'DifferentialCoreCustomField', 5251 + 'DifferentialTagsCommitMessageField' => 'DifferentialCommitMessageField', 5252 + 'DifferentialTasksCommitMessageField' => 'DifferentialCommitMessageField', 5253 + 'DifferentialTestPlanCommitMessageField' => 'DifferentialCommitMessageField', 5223 5254 'DifferentialTestPlanField' => 'DifferentialCoreCustomField', 5255 + 'DifferentialTitleCommitMessageField' => 'DifferentialCommitMessageField', 5224 5256 'DifferentialTitleField' => 'DifferentialCoreCustomField', 5225 5257 'DifferentialTransaction' => 'PhabricatorModularTransaction', 5226 5258 'DifferentialTransactionComment' => 'PhabricatorApplicationTransactionComment',
+21
src/applications/differential/field/DifferentialAuditorsCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialAuditorsCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'phabricator:auditors'; 7 + 8 + public function getFieldName() { 9 + return pht('Auditors'); 10 + } 11 + 12 + public function parseFieldValue($value) { 13 + return $this->parseObjectList( 14 + $value, 15 + array( 16 + PhabricatorPeopleUserPHIDType::TYPECONST, 17 + PhabricatorProjectProjectPHIDType::TYPECONST, 18 + )); 19 + } 20 + 21 + }
+22
src/applications/differential/field/DifferentialBlameRevisionCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialBlameRevisionCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'blameRevision'; 7 + 8 + public function getFieldName() { 9 + return pht('Blame Revision'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'Blame Rev', 15 + ); 16 + } 17 + 18 + public function isFieldEnabled() { 19 + return $this->isCustomFieldEnabled('phabricator:blame-revision'); 20 + } 21 + 22 + }
+92
src/applications/differential/field/DifferentialCommitMessageField.php
··· 1 + <?php 2 + 3 + abstract class DifferentialCommitMessageField 4 + extends Phobject { 5 + 6 + private $viewer; 7 + 8 + final public function setViewer(PhabricatorUser $viewer) { 9 + $this->viewer = $viewer; 10 + return $this; 11 + } 12 + 13 + final public function getViewer() { 14 + return $this->viewer; 15 + } 16 + 17 + abstract public function getFieldName(); 18 + 19 + public function isFieldEnabled() { 20 + return true; 21 + } 22 + 23 + public function getFieldAliases() { 24 + return array(); 25 + } 26 + 27 + public function validateFieldValue($value) { 28 + return; 29 + } 30 + 31 + public function parseFieldValue($value) { 32 + return $value; 33 + } 34 + 35 + final public function getCommitMessageFieldKey() { 36 + return $this->getPhobjectClassConstant('FIELDKEY', 64); 37 + } 38 + 39 + final public static function newEnabledFields(PhabricatorUser $viewer) { 40 + $fields = self::getAllFields(); 41 + 42 + $results = array(); 43 + foreach ($fields as $key => $field) { 44 + $field = clone $field; 45 + $field->setViewer($viewer); 46 + if ($field->isFieldEnabled()) { 47 + $results[$key] = $field; 48 + } 49 + } 50 + 51 + return $results; 52 + } 53 + 54 + final public static function getAllFields() { 55 + return id(new PhutilClassMapQuery()) 56 + ->setAncestorClass(__CLASS__) 57 + ->setUniqueMethod('getCommitMessageFieldKey') 58 + ->execute(); 59 + } 60 + 61 + protected function raiseParseException($message) { 62 + throw new DifferentialFieldParseException($message); 63 + } 64 + 65 + protected function raiseValidationException($message) { 66 + throw new DifferentialFieldValidationException($message); 67 + } 68 + 69 + protected function parseObjectList( 70 + $value, 71 + array $types, 72 + $allow_partial = false, 73 + array $suffixes = array()) { 74 + return id(new PhabricatorObjectListQuery()) 75 + ->setViewer($this->getViewer()) 76 + ->setAllowedTypes($types) 77 + ->setObjectList($value) 78 + ->setAllowPartialResults($allow_partial) 79 + ->setSuffixes($suffixes) 80 + ->execute(); 81 + } 82 + 83 + protected function isCustomFieldEnabled($key) { 84 + $field_list = PhabricatorCustomField::getObjectFields( 85 + new DifferentialRevision(), 86 + PhabricatorCustomField::ROLE_VIEW); 87 + 88 + $fields = $field_list->getFields(); 89 + return isset($fields[$key]); 90 + } 91 + 92 + }
+12
src/applications/differential/field/DifferentialConflictsCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialConflictsCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'conflicts'; 7 + 8 + public function getFieldName() { 9 + return pht('Conflicts'); 10 + } 11 + 12 + }
+12
src/applications/differential/field/DifferentialGitSVNIDCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialGitSVNIDCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'gitSVNID'; 7 + 8 + public function getFieldName() { 9 + return pht('git-svn-id'); 10 + } 11 + 12 + }
+27
src/applications/differential/field/DifferentialJIRAIssuesCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialJIRAIssuesCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'jira.issues'; 7 + 8 + public function getFieldName() { 9 + return pht('JIRA Issues'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'JIRA', 15 + 'JIRA Issue', 16 + ); 17 + } 18 + 19 + public function parseFieldValue($value) { 20 + return preg_split('/[\s,]+/', $value, $limit = -1, PREG_SPLIT_NO_EMPTY); 21 + } 22 + 23 + public function isFieldEnabled() { 24 + return (bool)PhabricatorJIRAAuthProvider::getJIRAProvider(); 25 + } 26 + 27 + }
+16
src/applications/differential/field/DifferentialRevertPlanCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialRevertPlanCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'revertPlan'; 7 + 8 + public function getFieldName() { 9 + return pht('Revert Plan'); 10 + } 11 + 12 + public function isFieldEnabled() { 13 + return $this->isCustomFieldEnabled('phabricator:revert-plan'); 14 + } 15 + 16 + }
+22
src/applications/differential/field/DifferentialReviewedByCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialReviewedByCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'reviewedByPHIDs'; 7 + 8 + public function getFieldName() { 9 + return pht('Reviewed By'); 10 + } 11 + 12 + public function parseFieldValue($value) { 13 + return $this->parseObjectList( 14 + $value, 15 + array( 16 + PhabricatorPeopleUserPHIDType::TYPECONST, 17 + PhabricatorProjectProjectPHIDType::TYPECONST, 18 + ), 19 + $allow_partial = true); 20 + } 21 + 22 + }
+64
src/applications/differential/field/DifferentialReviewersCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialReviewersCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'reviewerPHIDs'; 7 + 8 + public function getFieldName() { 9 + return pht('Reviewers'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'Reviewer', 15 + ); 16 + } 17 + 18 + public function parseFieldValue($value) { 19 + $results = $this->parseObjectList( 20 + $value, 21 + array( 22 + PhabricatorPeopleUserPHIDType::TYPECONST, 23 + PhabricatorProjectProjectPHIDType::TYPECONST, 24 + PhabricatorOwnersPackagePHIDType::TYPECONST, 25 + ), 26 + false, 27 + array('!')); 28 + 29 + return $this->flattenReviewers($results); 30 + } 31 + 32 + private function flattenReviewers(array $values) { 33 + // NOTE: For now, `arc` relies on this field returning only scalars, so we 34 + // need to reduce the results into scalars. See T10981. 35 + $result = array(); 36 + 37 + foreach ($values as $value) { 38 + $result[] = $value['phid'].implode('', array_keys($value['suffixes'])); 39 + } 40 + 41 + return $result; 42 + } 43 + 44 + private function inflateReviewers(array $values) { 45 + $result = array(); 46 + 47 + foreach ($values as $value) { 48 + if (substr($value, -1) == '!') { 49 + $value = substr($value, 0, -1); 50 + $suffixes = array('!' => '!'); 51 + } else { 52 + $suffixes = array(); 53 + } 54 + 55 + $result[] = array( 56 + 'phid' => $value, 57 + 'suffixes' => $suffixes, 58 + ); 59 + } 60 + 61 + return $result; 62 + } 63 + 64 + }
+52
src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionIDCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'revisionID'; 7 + 8 + public function getFieldName() { 9 + return pht('Differential Revision'); 10 + } 11 + 12 + public function parseFieldValue($value) { 13 + // If the value is just "D123" or similar, parse the ID from it directly. 14 + $value = trim($value); 15 + $matches = null; 16 + if (preg_match('/^[dD]([1-9]\d*)\z/', $value, $matches)) { 17 + return (int)$matches[1]; 18 + } 19 + 20 + // Otherwise, try to extract a URI value. 21 + return self::parseRevisionIDFromURI($value); 22 + } 23 + 24 + private static function parseRevisionIDFromURI($uri_string) { 25 + $uri = new PhutilURI($uri_string); 26 + $path = $uri->getPath(); 27 + 28 + $matches = null; 29 + if (preg_match('#^/D(\d+)$#', $path, $matches)) { 30 + $id = (int)$matches[1]; 31 + 32 + $prod_uri = new PhutilURI(PhabricatorEnv::getProductionURI('/D'.$id)); 33 + 34 + // Make sure the URI is the same as our URI. Basically, we want to ignore 35 + // commits from other Phabricator installs. 36 + if ($uri->getDomain() == $prod_uri->getDomain()) { 37 + return $id; 38 + } 39 + 40 + $allowed_uris = PhabricatorEnv::getAllowedURIs('/D'.$id); 41 + 42 + foreach ($allowed_uris as $allowed_uri) { 43 + if ($uri_string == $allowed_uri) { 44 + return $id; 45 + } 46 + } 47 + } 48 + 49 + return null; 50 + } 51 + 52 + }
+30
src/applications/differential/field/DifferentialSubscribersCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialSubscribersCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'ccPHIDs'; 7 + 8 + public function getFieldName() { 9 + return pht('Subscribers'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'CC', 15 + 'CCs', 16 + 'Subscriber', 17 + ); 18 + } 19 + 20 + public function parseFieldValue($value) { 21 + return $this->parseObjectList( 22 + $value, 23 + array( 24 + PhabricatorPeopleUserPHIDType::TYPECONST, 25 + PhabricatorProjectProjectPHIDType::TYPECONST, 26 + PhabricatorOwnersPackagePHIDType::TYPECONST, 27 + )); 28 + } 29 + 30 + }
+12
src/applications/differential/field/DifferentialSummaryCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialSummaryCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'summary'; 7 + 8 + public function getFieldName() { 9 + return pht('Summary'); 10 + } 11 + 12 + }
+28
src/applications/differential/field/DifferentialTagsCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialTagsCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'phabricator:projects'; 7 + 8 + public function getFieldName() { 9 + return pht('Tags'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'Tag', 15 + 'Project', 16 + 'Projects', 17 + ); 18 + } 19 + 20 + public function parseFieldValue($value) { 21 + return $this->parseObjectList( 22 + $value, 23 + array( 24 + PhabricatorProjectProjectPHIDType::TYPECONST, 25 + )); 26 + } 27 + 28 + }
+28
src/applications/differential/field/DifferentialTasksCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialTasksCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'maniphestTaskPHIDs'; 7 + 8 + public function getFieldName() { 9 + return pht('Maniphest Tasks'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'Task', 15 + 'Tasks', 16 + 'Maniphest Task', 17 + ); 18 + } 19 + 20 + public function parseFieldValue($value) { 21 + return $this->parseObjectList( 22 + $value, 23 + array( 24 + ManiphestTaskPHIDType::TYPECONST, 25 + )); 26 + } 27 + 28 + }
+36
src/applications/differential/field/DifferentialTestPlanCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialTestPlanCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'testPlan'; 7 + 8 + public function getFieldName() { 9 + return pht('Test Plan'); 10 + } 11 + 12 + public function getFieldAliases() { 13 + return array( 14 + 'Testplan', 15 + 'Tested', 16 + 'Tests', 17 + ); 18 + } 19 + 20 + public function isFieldEnabled() { 21 + return $this->isCustomFieldEnabled('differential:test-plan'); 22 + } 23 + 24 + public function validateFieldValue($value) { 25 + $is_required = PhabricatorEnv::getEnvConfig( 26 + 'differential.require-test-plan-field'); 27 + 28 + if ($is_required && !strlen($value)) { 29 + $this->raiseValidationException( 30 + pht( 31 + 'You must provide a test plan. Describe the actions you performed '. 32 + 'to verify the behavior of this change.')); 33 + } 34 + } 35 + 36 + }
+36
src/applications/differential/field/DifferentialTitleCommitMessageField.php
··· 1 + <?php 2 + 3 + final class DifferentialTitleCommitMessageField 4 + extends DifferentialCommitMessageField { 5 + 6 + const FIELDKEY = 'title'; 7 + 8 + public function getFieldName() { 9 + return pht('Title'); 10 + } 11 + 12 + public static function getDefaultTitle() { 13 + return pht('<<Replace this line with your revision title>'); 14 + } 15 + 16 + public function parseFieldValue($value) { 17 + if ($value === self::getDefaultTitle()) { 18 + $this->raiseParseException( 19 + pht( 20 + 'Replace the default title line with a human-readable revision '. 21 + 'title which describes the changes you are making.')); 22 + } 23 + 24 + return parent::parseFieldValue($value); 25 + } 26 + 27 + public function validateFieldValue($value) { 28 + if (!strlen($value)) { 29 + $this->raiseValidationException( 30 + pht( 31 + 'You must provide a revision title in the first line '. 32 + 'of your commit message.')); 33 + } 34 + } 35 + 36 + }
+60 -40
src/applications/differential/parser/DifferentialCommitMessageParser.php
··· 26 26 private $titleKey; 27 27 private $summaryKey; 28 28 private $errors; 29 + private $commitMessageFields; 29 30 private $raiseMissingFieldErrors = true; 30 31 31 32 public static function newStandardParser(PhabricatorUser $viewer) { 32 - 33 - $key_title = id(new DifferentialTitleField())->getFieldKeyForConduit(); 34 - $key_summary = id(new DifferentialSummaryField())->getFieldKeyForConduit(); 35 - 36 - $field_list = PhabricatorCustomField::getObjectFields( 37 - new DifferentialRevision(), 38 - DifferentialCustomField::ROLE_COMMITMESSAGE); 39 - $field_list->setViewer($viewer); 40 - 41 - $label_map = array(); 33 + $key_title = DifferentialTitleCommitMessageField::FIELDKEY; 34 + $key_summary = DifferentialSummaryCommitMessageField::FIELDKEY; 42 35 43 - foreach ($field_list->getFields() as $field) { 44 - $labels = $field->getCommitMessageLabels(); 45 - $key = $field->getFieldKeyForConduit(); 46 - 47 - foreach ($labels as $label) { 48 - $normal_label = self::normalizeFieldLabel( 49 - $label); 50 - if (!empty($label_map[$normal_label])) { 51 - throw new Exception( 52 - pht( 53 - 'Field label "%s" is parsed by two custom fields: "%s" and '. 54 - '"%s". Each label must be parsed by only one field.', 55 - $label, 56 - $key, 57 - $label_map[$normal_label])); 58 - } 59 - $label_map[$normal_label] = $key; 60 - } 61 - } 36 + $field_list = DifferentialCommitMessageField::newEnabledFields($viewer); 62 37 63 38 return id(new self()) 64 39 ->setViewer($viewer) 65 - ->setLabelMap($label_map) 40 + ->setCommitMessageFields($field_list) 66 41 ->setTitleKey($key_title) 67 42 ->setSummaryKey($key_summary); 68 43 } ··· 91 66 /** 92 67 * @task config 93 68 */ 69 + public function setCommitMessageFields($fields) { 70 + assert_instances_of($fields, 'DifferentialCommitMessageField'); 71 + $fields = mpull($fields, null, 'getCommitMessageFieldKey'); 72 + $this->commitMessageFields = $fields; 73 + return $this; 74 + } 75 + 76 + 77 + /** 78 + * @task config 79 + */ 80 + public function getCommitMessageFields() { 81 + return $this->commitMessageFields; 82 + } 83 + 84 + 85 + /** 86 + * @task config 87 + */ 94 88 public function setRaiseMissingFieldErrors($raise) { 95 89 $this->raiseMissingFieldErrors = $raise; 96 90 return $this; ··· 141 135 public function parseCorpus($corpus) { 142 136 $this->errors = array(); 143 137 144 - $label_map = $this->labelMap; 138 + $label_map = $this->getLabelMap(); 145 139 $key_title = $this->titleKey; 146 140 $key_summary = $this->summaryKey; 147 141 ··· 258 252 $viewer = $this->getViewer(); 259 253 $text_map = $this->parseCorpus($corpus); 260 254 261 - $field_list = PhabricatorCustomField::getObjectFields( 262 - new DifferentialRevision(), 263 - DifferentialCustomField::ROLE_COMMITMESSAGE); 264 - $field_list->setViewer($viewer); 265 - 266 - $field_map = $field_list->getFields(); 267 - $field_map = mpull($field_map, null, 'getFieldKeyForConduit'); 255 + $field_map = $this->getCommitMessageFields(); 268 256 269 257 $result_map = array(); 270 258 foreach ($text_map as $field_key => $text_value) { ··· 281 269 } 282 270 283 271 try { 284 - $result = $field->parseValueFromCommitMessage($text_value); 272 + $result = $field->parseFieldValue($text_value); 285 273 $result_map[$field_key] = $result; 286 274 } catch (DifferentialFieldParseException $ex) { 287 275 $this->errors[] = pht( ··· 294 282 if ($this->getRaiseMissingFieldErrors()) { 295 283 foreach ($field_map as $key => $field) { 296 284 try { 297 - $field->validateCommitMessageValue(idx($result_map, $key)); 285 + $field->validateFieldValue(idx($result_map, $key)); 298 286 } catch (DifferentialFieldValidationException $ex) { 299 287 $this->errors[] = pht( 300 288 'Invalid or missing field "%s": %s', ··· 328 316 329 317 330 318 /* -( Internals )---------------------------------------------------------- */ 319 + 320 + 321 + private function getLabelMap() { 322 + if ($this->labelMap === null) { 323 + $field_list = $this->getCommitMessageFields(); 324 + 325 + $label_map = array(); 326 + foreach ($field_list as $field_key => $field) { 327 + $labels = $field->getFieldAliases(); 328 + $labels[] = $field->getFieldName(); 329 + 330 + foreach ($labels as $label) { 331 + $normal_label = self::normalizeFieldLabel($label); 332 + if (!empty($label_map[$normal_label])) { 333 + throw new Exception( 334 + pht( 335 + 'Field label "%s" is parsed by two custom fields: "%s" and '. 336 + '"%s". Each label must be parsed by only one field.', 337 + $label, 338 + $field_key, 339 + $label_map[$normal_label])); 340 + } 341 + 342 + $label_map[$normal_label] = $field_key; 343 + } 344 + } 345 + 346 + $this->labelMap = $label_map; 347 + } 348 + 349 + return $this->labelMap; 350 + } 331 351 332 352 333 353 /**
+30
src/applications/differential/parser/__tests__/DifferentialCommitMessageParserTestCase.php
··· 41 41 } 42 42 } 43 43 44 + 45 + public function testDifferentialCommitMessageFieldParser() { 46 + $message = <<<EOMESSAGE 47 + This is the title. 48 + 49 + Summary: This is the summary. 50 + EOMESSAGE; 51 + 52 + $fields = array( 53 + new DifferentialTitleCommitMessageField(), 54 + new DifferentialSummaryCommitMessageField(), 55 + ); 56 + 57 + $expect = array( 58 + DifferentialTitleCommitMessageField::FIELDKEY => 59 + 'This is the title.', 60 + DifferentialSummaryCommitMessageField::FIELDKEY => 61 + 'This is the summary.', 62 + ); 63 + 64 + $parser = id(new DifferentialCommitMessageParser()) 65 + ->setCommitMessageFields($fields) 66 + ->setTitleKey(DifferentialTitleCommitMessageField::FIELDKEY) 67 + ->setSummaryKey(DifferentialSummaryCommitMessageField::FIELDKEY); 68 + 69 + $actual = $parser->parseFields($message); 70 + 71 + $this->assertEqual($expect, $actual); 72 + } 73 + 44 74 public function testDifferentialCommitMessageParserNormalization() { 45 75 $map = array( 46 76 'Test Plan' => 'test plan',