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

Restore "Accept", "Reject" and "Resign" actions to Differential on EditEngine

Summary:
Ref T11114. Some rough edges, but this largely makes Accept, Reject and Resign work in the new EditEngine comment area.

Ref T11050. This lays a little bit of groundwork for having "resign" mean "I don't want to review this, even if projects or packages I'm a member of need to", not just "remove me personally as a user reviewer".

Test Plan: Accepted, rejected and resigned from revisions without any major state issues.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11114, T11050

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

+337 -1
+6
src/__phutil_library_map__.php
··· 506 506 'DifferentialReviewersView' => 'applications/differential/view/DifferentialReviewersView.php', 507 507 'DifferentialRevision' => 'applications/differential/storage/DifferentialRevision.php', 508 508 'DifferentialRevisionAbandonTransaction' => 'applications/differential/xaction/DifferentialRevisionAbandonTransaction.php', 509 + 'DifferentialRevisionAcceptTransaction' => 'applications/differential/xaction/DifferentialRevisionAcceptTransaction.php', 509 510 'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php', 510 511 'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php', 511 512 'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php', ··· 545 546 'DifferentialRevisionPlanChangesTransaction' => 'applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php', 546 547 'DifferentialRevisionQuery' => 'applications/differential/query/DifferentialRevisionQuery.php', 547 548 'DifferentialRevisionReclaimTransaction' => 'applications/differential/xaction/DifferentialRevisionReclaimTransaction.php', 549 + 'DifferentialRevisionRejectTransaction' => 'applications/differential/xaction/DifferentialRevisionRejectTransaction.php', 548 550 'DifferentialRevisionRelationship' => 'applications/differential/relationships/DifferentialRevisionRelationship.php', 549 551 'DifferentialRevisionRelationshipSource' => 'applications/search/relationship/DifferentialRevisionRelationshipSource.php', 550 552 'DifferentialRevisionReopenTransaction' => 'applications/differential/xaction/DifferentialRevisionReopenTransaction.php', ··· 553 555 'DifferentialRevisionRepositoryTransaction' => 'applications/differential/xaction/DifferentialRevisionRepositoryTransaction.php', 554 556 'DifferentialRevisionRequestReviewTransaction' => 'applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php', 555 557 'DifferentialRevisionRequiredActionResultBucket' => 'applications/differential/query/DifferentialRevisionRequiredActionResultBucket.php', 558 + 'DifferentialRevisionResignTransaction' => 'applications/differential/xaction/DifferentialRevisionResignTransaction.php', 556 559 'DifferentialRevisionResultBucket' => 'applications/differential/query/DifferentialRevisionResultBucket.php', 557 560 'DifferentialRevisionReviewersHeraldField' => 'applications/differential/herald/DifferentialRevisionReviewersHeraldField.php', 558 561 'DifferentialRevisionReviewersTransaction' => 'applications/differential/xaction/DifferentialRevisionReviewersTransaction.php', ··· 5180 5183 'PhabricatorConduitResultInterface', 5181 5184 ), 5182 5185 'DifferentialRevisionAbandonTransaction' => 'DifferentialRevisionActionTransaction', 5186 + 'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionActionTransaction', 5183 5187 'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType', 5184 5188 'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField', 5185 5189 'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField', ··· 5219 5223 'DifferentialRevisionPlanChangesTransaction' => 'DifferentialRevisionActionTransaction', 5220 5224 'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 5221 5225 'DifferentialRevisionReclaimTransaction' => 'DifferentialRevisionActionTransaction', 5226 + 'DifferentialRevisionRejectTransaction' => 'DifferentialRevisionActionTransaction', 5222 5227 'DifferentialRevisionRelationship' => 'PhabricatorObjectRelationship', 5223 5228 'DifferentialRevisionRelationshipSource' => 'PhabricatorObjectRelationshipSource', 5224 5229 'DifferentialRevisionReopenTransaction' => 'DifferentialRevisionActionTransaction', ··· 5227 5232 'DifferentialRevisionRepositoryTransaction' => 'DifferentialRevisionTransactionType', 5228 5233 'DifferentialRevisionRequestReviewTransaction' => 'DifferentialRevisionActionTransaction', 5229 5234 'DifferentialRevisionRequiredActionResultBucket' => 'DifferentialRevisionResultBucket', 5235 + 'DifferentialRevisionResignTransaction' => 'DifferentialRevisionActionTransaction', 5230 5236 'DifferentialRevisionResultBucket' => 'PhabricatorSearchResultBucket', 5231 5237 'DifferentialRevisionReviewersHeraldField' => 'DifferentialRevisionHeraldField', 5232 5238 'DifferentialRevisionReviewersTransaction' => 'DifferentialRevisionTransactionType',
+2
src/applications/differential/constants/DifferentialReviewerStatus.php
··· 9 9 const STATUS_COMMENTED = 'commented'; 10 10 const STATUS_ACCEPTED_OLDER = 'accepted-older'; 11 11 const STATUS_REJECTED_OLDER = 'rejected-older'; 12 + const STATUS_RESIGNED = 'resigned'; 12 13 13 14 /** 14 15 * Returns the relative strength of a status, used to pick a winner when a ··· 34 35 35 36 self::STATUS_ACCEPTED => 5, 36 37 self::STATUS_REJECTED => 5, 38 + self::STATUS_RESIGNED => 5, 37 39 ); 38 40 39 41 return idx($map, $constant, 0);
+2 -1
src/applications/differential/editor/DifferentialRevisionEditEngine.php
··· 38 38 protected function newObjectQuery() { 39 39 return id(new DifferentialRevisionQuery()) 40 40 ->needActiveDiffs(true) 41 - ->needReviewerStatus(true); 41 + ->needReviewerStatus(true) 42 + ->needReviewerAuthority(true); 42 43 } 43 44 44 45 protected function getObjectCreateTitleText($object) {
+77
src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionAcceptTransaction 4 + extends DifferentialRevisionActionTransaction { 5 + 6 + const TRANSACTIONTYPE = 'differential.revision.accept'; 7 + const ACTIONKEY = 'accept'; 8 + 9 + protected function getRevisionActionLabel() { 10 + return pht("Accept Revision \xE2\x9C\x94"); 11 + } 12 + 13 + protected function getRevisionActionDescription() { 14 + return pht('These changes will be approved.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-check-circle-o'; 19 + } 20 + 21 + public function getColor() { 22 + return 'green'; 23 + } 24 + 25 + public function generateOldValue($object) { 26 + $actor = $this->getActor(); 27 + return $this->isViewerAcceptingReviewer($object, $actor); 28 + } 29 + 30 + public function applyExternalEffects($object, $value) { 31 + $status = DifferentialReviewerStatus::STATUS_ACCEPTED; 32 + $actor = $this->getActor(); 33 + $this->applyReviewerEffect($object, $actor, $value, $status); 34 + } 35 + 36 + protected function validateAction($object, PhabricatorUser $viewer) { 37 + if ($object->isClosed()) { 38 + throw new Exception( 39 + pht( 40 + 'You can not accept this revision because it has already been '. 41 + 'closed. Only open revisions can be accepted.')); 42 + } 43 + 44 + $config_key = 'differential.allow-self-accept'; 45 + if (!PhabricatorEnv::getEnvConfig($config_key)) { 46 + if ($this->isViewerRevisionAuthor($object, $viewer)) { 47 + throw new Exception( 48 + pht( 49 + 'You can not accept this revision because you are the revision '. 50 + 'author. You can only accept revisions you do not own. You can '. 51 + 'change this behavior by adjusting the "%s" setting in Config.', 52 + $config_key)); 53 + } 54 + } 55 + 56 + if ($this->isViewerAcceptingReviewer($object, $viewer)) { 57 + throw new Exception( 58 + pht( 59 + 'You can not accept this revision because you have already '. 60 + 'accepted it.')); 61 + } 62 + } 63 + 64 + public function getTitle() { 65 + return pht( 66 + '%s accepted this revision.', 67 + $this->renderAuthor()); 68 + } 69 + 70 + public function getTitleForFeed() { 71 + return pht( 72 + '%s accepted %s.', 73 + $this->renderAuthor(), 74 + $this->renderObject()); 75 + } 76 + 77 + }
+110
src/applications/differential/xaction/DifferentialRevisionActionTransaction.php
··· 41 41 return ($viewer->getPHID() === $revision->getAuthorPHID()); 42 42 } 43 43 44 + protected function isViewerAnyReviewer( 45 + DifferentialRevision $revision, 46 + PhabricatorUser $viewer) { 47 + return ($this->getViewerReviewerStatus($revision, $viewer) !== null); 48 + } 49 + 50 + protected function isViewerAcceptingReviewer( 51 + DifferentialRevision $revision, 52 + PhabricatorUser $viewer) { 53 + return $this->isViewerReviewerStatusAmong( 54 + $revision, 55 + $viewer, 56 + array( 57 + DifferentialReviewerStatus::STATUS_ACCEPTED, 58 + )); 59 + } 60 + 61 + protected function isViewerRejectingReviewer( 62 + DifferentialRevision $revision, 63 + PhabricatorUser $viewer) { 64 + return $this->isViewerReviewerStatusAmong( 65 + $revision, 66 + $viewer, 67 + array( 68 + DifferentialReviewerStatus::STATUS_REJECTED, 69 + )); 70 + } 71 + 72 + protected function getViewerReviewerStatus( 73 + DifferentialRevision $revision, 74 + PhabricatorUser $viewer) { 75 + 76 + if (!$viewer->getPHID()) { 77 + return null; 78 + } 79 + 80 + foreach ($revision->getReviewerStatus() as $reviewer) { 81 + if ($reviewer->getReviewerPHID() != $viewer->getPHID()) { 82 + continue; 83 + } 84 + 85 + return $reviewer->getStatus(); 86 + } 87 + 88 + return null; 89 + } 90 + 91 + protected function isViewerReviewerStatusAmong( 92 + DifferentialRevision $revision, 93 + PhabricatorUser $viewer, 94 + array $status_list) { 95 + 96 + $status = $this->getViewerReviewerStatus($revision, $viewer); 97 + if ($status === null) { 98 + return false; 99 + } 100 + 101 + $status_map = array_fuse($status_list); 102 + return isset($status_map[$status]); 103 + } 104 + 105 + protected function applyReviewerEffect( 106 + DifferentialRevision $revision, 107 + PhabricatorUser $viewer, 108 + $value, 109 + $status) { 110 + 111 + $map = array(); 112 + 113 + // When you accept or reject, you may accept or reject on behalf of all 114 + // reviewers you have authority for. When you resign, you only affect 115 + // yourself. 116 + $with_authority = ($status != DifferentialReviewerStatus::STATUS_RESIGNED); 117 + if ($with_authority) { 118 + foreach ($revision->getReviewerStatus() as $reviewer) { 119 + if ($reviewer->hasAuthority($viewer)) { 120 + $map[$reviewer->getReviewerPHID()] = $status; 121 + } 122 + } 123 + } 124 + 125 + // In all cases, you affect yourself. 126 + $map[$viewer->getPHID()] = $status; 127 + 128 + // Convert reviewer statuses into edge data. 129 + foreach ($map as $reviewer_phid => $reviewer_status) { 130 + $map[$reviewer_phid] = array( 131 + 'data' => array( 132 + 'status' => $reviewer_status, 133 + ), 134 + ); 135 + } 136 + 137 + $src_phid = $revision->getPHID(); 138 + $edge_type = DifferentialRevisionHasReviewerEdgeType::EDGECONST; 139 + 140 + $editor = new PhabricatorEdgeEditor(); 141 + foreach ($map as $dst_phid => $edge_data) { 142 + if ($status == DifferentialReviewerStatus::STATUS_RESIGNED) { 143 + // TODO: For now, we just remove these reviewers. In the future, we will 144 + // store resignations explicitly. 145 + $editor->removeEdge($src_phid, $edge_type, $dst_phid); 146 + } else { 147 + $editor->addEdge($src_phid, $edge_type, $dst_phid, $edge_data); 148 + } 149 + } 150 + 151 + $editor->save(); 152 + } 153 + 44 154 public function newEditField( 45 155 DifferentialRevision $revision, 46 156 PhabricatorUser $viewer) {
+74
src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionRejectTransaction 4 + extends DifferentialRevisionActionTransaction { 5 + 6 + const TRANSACTIONTYPE = 'differential.revision.reject'; 7 + const ACTIONKEY = 'reject'; 8 + 9 + protected function getRevisionActionLabel() { 10 + return pht("Request Changes \xE2\x9C\x98"); 11 + } 12 + 13 + protected function getRevisionActionDescription() { 14 + return pht('This revision will be returned to the author for updates.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-times-circle-o'; 19 + } 20 + 21 + public function getColor() { 22 + return 'red'; 23 + } 24 + 25 + public function generateOldValue($object) { 26 + $actor = $this->getActor(); 27 + return $this->isViewerRejectingReviewer($object, $actor); 28 + } 29 + 30 + public function applyExternalEffects($object, $value) { 31 + $status = DifferentialReviewerStatus::STATUS_REJECTED; 32 + $actor = $this->getActor(); 33 + $this->applyReviewerEffect($object, $actor, $value, $status); 34 + } 35 + 36 + protected function validateAction($object, PhabricatorUser $viewer) { 37 + if ($object->isClosed()) { 38 + throw new Exception( 39 + pht( 40 + 'You can not request changes to this revision because it has '. 41 + 'already been closed. You can only request changes to open '. 42 + 'revisions.')); 43 + } 44 + 45 + if ($this->isViewerRevisionAuthor($object, $viewer)) { 46 + throw new Exception( 47 + pht( 48 + 'You can not request changes to this revision because you are the '. 49 + 'revision author. You can only request changes to revisions you do '. 50 + 'not own.')); 51 + } 52 + 53 + if ($this->isViewerRejectingReviewer($object, $viewer)) { 54 + throw new Exception( 55 + pht( 56 + 'You can not request changes to this revision because you have '. 57 + 'already requested changes.')); 58 + } 59 + } 60 + 61 + public function getTitle() { 62 + return pht( 63 + '%s requested changes to this revision.', 64 + $this->renderAuthor()); 65 + } 66 + 67 + public function getTitleForFeed() { 68 + return pht( 69 + '%s requested changes to %s.', 70 + $this->renderAuthor(), 71 + $this->renderObject()); 72 + } 73 + 74 + }
+66
src/applications/differential/xaction/DifferentialRevisionResignTransaction.php
··· 1 + <?php 2 + 3 + final class DifferentialRevisionResignTransaction 4 + extends DifferentialRevisionActionTransaction { 5 + 6 + const TRANSACTIONTYPE = 'differential.revision.resign'; 7 + const ACTIONKEY = 'resign'; 8 + 9 + protected function getRevisionActionLabel() { 10 + return pht('Resign as Reviewer'); 11 + } 12 + 13 + protected function getRevisionActionDescription() { 14 + return pht('You will resign as a reviewer for this change.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-flag'; 19 + } 20 + 21 + public function getColor() { 22 + return 'orange'; 23 + } 24 + 25 + public function generateOldValue($object) { 26 + $actor = $this->getActor(); 27 + return !$this->isViewerAnyReviewer($object, $actor); 28 + } 29 + 30 + public function applyExternalEffects($object, $value) { 31 + $status = DifferentialReviewerStatus::STATUS_RESIGNED; 32 + $actor = $this->getActor(); 33 + $this->applyReviewerEffect($object, $actor, $value, $status); 34 + } 35 + 36 + protected function validateAction($object, PhabricatorUser $viewer) { 37 + if ($object->isClosed()) { 38 + throw new Exception( 39 + pht( 40 + 'You can not resign from this revision because it has already '. 41 + 'been closed. You can only resign from open revisions.')); 42 + } 43 + 44 + if (!$this->isViewerAnyReviewer($object, $viewer)) { 45 + throw new Exception( 46 + pht( 47 + 'You can not resign from this revision because you are not a '. 48 + 'reviewer. You can only resign from revisions where you are a '. 49 + 'reviewer.')); 50 + } 51 + } 52 + 53 + public function getTitle() { 54 + return pht( 55 + '%s resigned from this revision.', 56 + $this->renderAuthor()); 57 + } 58 + 59 + public function getTitleForFeed() { 60 + return pht( 61 + '%s resigned from %s.', 62 + $this->renderAuthor(), 63 + $this->renderObject()); 64 + } 65 + 66 + }