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

Add modern "Accept", "Raise Concern" and "Resign" transactions to Audit

Summary:
Ref T10978. This prepares for swapping the comment UI to stacked actions.

These are only accessible via the API.

Test Plan: Used the API to accept, raise concern with, and reject commits.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10978

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

+480 -17
+10
src/__phutil_library_map__.php
··· 612 612 'DiffusionCloneURIView' => 'applications/diffusion/view/DiffusionCloneURIView.php', 613 613 'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php', 614 614 'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php', 615 + 'DiffusionCommitAcceptTransaction' => 'applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php', 616 + 'DiffusionCommitActionTransaction' => 'applications/diffusion/xaction/DiffusionCommitActionTransaction.php', 615 617 'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php', 618 + 'DiffusionCommitAuditTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditTransaction.php', 616 619 'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php', 617 620 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 618 621 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', 619 622 'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php', 620 623 'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php', 621 624 'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php', 625 + 'DiffusionCommitConcernTransaction' => 'applications/diffusion/xaction/DiffusionCommitConcernTransaction.php', 622 626 'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php', 623 627 'DiffusionCommitDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentAddedHeraldField.php', 624 628 'DiffusionCommitDiffContentHeraldField' => 'applications/diffusion/herald/DiffusionCommitDiffContentHeraldField.php', ··· 652 656 'DiffusionCommitRemarkupRuleTestCase' => 'applications/diffusion/remarkup/__tests__/DiffusionCommitRemarkupRuleTestCase.php', 653 657 'DiffusionCommitRepositoryHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryHeraldField.php', 654 658 'DiffusionCommitRepositoryProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitRepositoryProjectsHeraldField.php', 659 + 'DiffusionCommitResignTransaction' => 'applications/diffusion/xaction/DiffusionCommitResignTransaction.php', 655 660 'DiffusionCommitRevertedByCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertedByCommitEdgeType.php', 656 661 'DiffusionCommitRevertsCommitEdgeType' => 'applications/diffusion/edge/DiffusionCommitRevertsCommitEdgeType.php', 657 662 'DiffusionCommitReviewerHeraldField' => 'applications/diffusion/herald/DiffusionCommitReviewerHeraldField.php', ··· 5313 5318 'DiffusionCloneURIView' => 'AphrontView', 5314 5319 'DiffusionCommandEngine' => 'Phobject', 5315 5320 'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase', 5321 + 'DiffusionCommitAcceptTransaction' => 'DiffusionCommitAuditTransaction', 5322 + 'DiffusionCommitActionTransaction' => 'DiffusionCommitTransactionType', 5316 5323 'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField', 5324 + 'DiffusionCommitAuditTransaction' => 'DiffusionCommitActionTransaction', 5317 5325 'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType', 5318 5326 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 5319 5327 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', 5320 5328 'DiffusionCommitBranchesController' => 'DiffusionController', 5321 5329 'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField', 5322 5330 'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField', 5331 + 'DiffusionCommitConcernTransaction' => 'DiffusionCommitAuditTransaction', 5323 5332 'DiffusionCommitController' => 'DiffusionController', 5324 5333 'DiffusionCommitDiffContentAddedHeraldField' => 'DiffusionCommitHeraldField', 5325 5334 'DiffusionCommitDiffContentHeraldField' => 'DiffusionCommitHeraldField', ··· 5353 5362 'DiffusionCommitRemarkupRuleTestCase' => 'PhabricatorTestCase', 5354 5363 'DiffusionCommitRepositoryHeraldField' => 'DiffusionCommitHeraldField', 5355 5364 'DiffusionCommitRepositoryProjectsHeraldField' => 'DiffusionCommitHeraldField', 5365 + 'DiffusionCommitResignTransaction' => 'DiffusionCommitAuditTransaction', 5356 5366 'DiffusionCommitRevertedByCommitEdgeType' => 'PhabricatorEdgeType', 5357 5367 'DiffusionCommitRevertsCommitEdgeType' => 'PhabricatorEdgeType', 5358 5368 'DiffusionCommitReviewerHeraldField' => 'DiffusionCommitHeraldField',
+10
src/applications/diffusion/editor/DiffusionCommitEditEngine.php
··· 5 5 6 6 const ENGINECONST = 'diffusion.commit'; 7 7 8 + const ACTIONGROUP_AUDIT = 'audit'; 9 + const ACTIONGROUP_COMMIT = 'commit'; 10 + 8 11 public function isEngineConfigurable() { 9 12 return false; 10 13 } ··· 128 131 $fields[] = id(new PhabricatorStaticEditField()) 129 132 ->setLabel(pht('Autoclose?')) 130 133 ->setValue(array($desc, " \xC2\xB7 ", $doc_link)); 134 + } 135 + 136 + $actions = DiffusionCommitActionTransaction::loadAllActions(); 137 + $actions = msortv($actions, 'getCommitActionOrderVector'); 138 + 139 + foreach ($actions as $key => $action) { 140 + $fields[] = $action->newEditField($object, $viewer); 131 141 } 132 142 133 143 return $fields;
+74
src/applications/diffusion/xaction/DiffusionCommitAcceptTransaction.php
··· 1 + <?php 2 + 3 + final class DiffusionCommitAcceptTransaction 4 + extends DiffusionCommitAuditTransaction { 5 + 6 + const TRANSACTIONTYPE = 'diffusion.commit.accept'; 7 + const ACTIONKEY = 'accept'; 8 + 9 + protected function getCommitActionLabel() { 10 + return pht("Accept Commit \xE2\x9C\x94"); 11 + } 12 + 13 + protected function getCommitActionDescription() { 14 + return pht('This commit 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 + protected function getCommitActionOrder() { 26 + return 500; 27 + } 28 + 29 + public function generateOldValue($object) { 30 + $actor = $this->getActor(); 31 + return $this->isViewerAcceptingAuditor($object, $actor); 32 + } 33 + 34 + public function applyExternalEffects($object, $value) { 35 + $status = PhabricatorAuditStatusConstants::ACCEPTED; 36 + $actor = $this->getActor(); 37 + $this->applyAuditorEffect($object, $actor, $value, $status); 38 + } 39 + 40 + protected function validateAction($object, PhabricatorUser $viewer) { 41 + $config_key = 'audit.can-author-close-audit'; 42 + if (!PhabricatorEnv::getEnvConfig($config_key)) { 43 + if ($this->isViewerCommitAuthor($object, $viewer)) { 44 + throw new Exception( 45 + pht( 46 + 'You can not accept this commit because you are the commit '. 47 + 'author. You can only accept commits you did not author. You can '. 48 + 'change this behavior by adjusting the "%s" setting in Config.', 49 + $config_key)); 50 + } 51 + } 52 + 53 + if ($this->isViewerAcceptingAuditor($object, $viewer)) { 54 + throw new Exception( 55 + pht( 56 + 'You can not accept this commit because you have already '. 57 + 'accepted it.')); 58 + } 59 + } 60 + 61 + public function getTitle() { 62 + return pht( 63 + '%s accepted this commit.', 64 + $this->renderAuthor()); 65 + } 66 + 67 + public function getTitleForFeed() { 68 + return pht( 69 + '%s accepted %s.', 70 + $this->renderAuthor(), 71 + $this->renderObject()); 72 + } 73 + 74 + }
+118
src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php
··· 1 + <?php 2 + 3 + abstract class DiffusionCommitActionTransaction 4 + extends DiffusionCommitTransactionType { 5 + 6 + final public function getCommitActionKey() { 7 + return $this->getPhobjectClassConstant('ACTIONKEY', 32); 8 + } 9 + 10 + public function isActionAvailable($object, PhabricatorUser $viewer) { 11 + try { 12 + $this->validateAction($object, $viewer); 13 + return true; 14 + } catch (Exception $ex) { 15 + return false; 16 + } 17 + } 18 + 19 + abstract protected function validateAction($object, PhabricatorUser $viewer); 20 + abstract protected function getCommitActionLabel(); 21 + 22 + public function getCommandKeyword() { 23 + return null; 24 + } 25 + 26 + public function getCommandAliases() { 27 + return array(); 28 + } 29 + 30 + public function getCommandSummary() { 31 + return null; 32 + } 33 + 34 + protected function getCommitActionOrder() { 35 + return 1000; 36 + } 37 + 38 + public function getCommitActionOrderVector() { 39 + return id(new PhutilSortVector()) 40 + ->addInt($this->getCommitActionOrder()); 41 + } 42 + 43 + protected function getCommitActionGroupKey() { 44 + return DiffusionCommitEditEngine::ACTIONGROUP_COMMIT; 45 + } 46 + 47 + protected function getCommitActionDescription() { 48 + return null; 49 + } 50 + 51 + public static function loadAllActions() { 52 + return id(new PhutilClassMapQuery()) 53 + ->setAncestorClass(__CLASS__) 54 + ->setUniqueMethod('getCommitActionKey') 55 + ->execute(); 56 + } 57 + 58 + protected function isViewerCommitAuthor( 59 + PhabricatorRepositoryCommit $commit, 60 + PhabricatorUser $viewer) { 61 + 62 + if (!$viewer->getPHID()) { 63 + return false; 64 + } 65 + 66 + return ($viewer->getPHID() === $commit->getAuthorPHID()); 67 + } 68 + 69 + public function newEditField( 70 + PhabricatorRepositoryCommit $commit, 71 + PhabricatorUser $viewer) { 72 + 73 + $field = id(new PhabricatorApplyEditField()) 74 + ->setKey($this->getCommitActionKey()) 75 + ->setTransactionType($this->getTransactionTypeConstant()) 76 + ->setValue(true); 77 + 78 + if ($this->isActionAvailable($commit, $viewer)) { 79 + $label = $this->getCommitActionLabel(); 80 + if ($label !== null) { 81 + $field->setCommentActionLabel($label); 82 + 83 + $description = $this->getCommitActionDescription(); 84 + $field->setActionDescription($description); 85 + 86 + $group_key = $this->getCommitActionGroupKey(); 87 + $field->setCommentActionGroupKey($group_key); 88 + 89 + $field->setActionConflictKey('commit.action'); 90 + } 91 + } 92 + 93 + return $field; 94 + } 95 + 96 + public function validateTransactions($object, array $xactions) { 97 + $errors = array(); 98 + $actor = $this->getActor(); 99 + 100 + $action_exception = null; 101 + try { 102 + $this->validateAction($object, $actor); 103 + } catch (Exception $ex) { 104 + $action_exception = $ex; 105 + } 106 + 107 + foreach ($xactions as $xaction) { 108 + if ($action_exception) { 109 + $errors[] = $this->newInvalidError( 110 + $action_exception->getMessage(), 111 + $xaction); 112 + } 113 + } 114 + 115 + return $errors; 116 + } 117 + 118 + }
+101
src/applications/diffusion/xaction/DiffusionCommitAuditTransaction.php
··· 1 + <?php 2 + 3 + abstract class DiffusionCommitAuditTransaction 4 + extends DiffusionCommitActionTransaction { 5 + 6 + protected function getCommitActionGroupKey() { 7 + return DiffusionCommitEditEngine::ACTIONGROUP_AUDIT; 8 + } 9 + 10 + protected function isViewerAnyAuditor( 11 + PhabricatorRepositoryCommit $commit, 12 + PhabricatorUser $viewer) { 13 + return ($this->getViewerAuditStatus($commit, $viewer) !== null); 14 + } 15 + 16 + protected function isViewerAcceptingAuditor( 17 + PhabricatorRepositoryCommit $commit, 18 + PhabricatorUser $viewer) { 19 + return $this->isViewerAuditStatusAmong( 20 + $commit, 21 + $viewer, 22 + array( 23 + PhabricatorAuditStatusConstants::ACCEPTED, 24 + )); 25 + } 26 + 27 + protected function isViewerRejectingAuditor( 28 + PhabricatorRepositoryCommit $commit, 29 + PhabricatorUser $viewer) { 30 + return $this->isViewerAuditStatusAmong( 31 + $commit, 32 + $viewer, 33 + array( 34 + PhabricatorAuditStatusConstants::CONCERNED, 35 + )); 36 + } 37 + 38 + protected function getViewerAuditStatus( 39 + PhabricatorRepositoryCommit $commit, 40 + PhabricatorUser $viewer) { 41 + 42 + if (!$viewer->getPHID()) { 43 + return null; 44 + } 45 + 46 + foreach ($commit->getAudits() as $audit) { 47 + if ($audit->getAuditorPHID() != $viewer->getPHID()) { 48 + continue; 49 + } 50 + 51 + return $audit->getAuditStatus(); 52 + } 53 + 54 + return null; 55 + } 56 + 57 + protected function isViewerAuditStatusAmong( 58 + PhabricatorRepositoryCommit $commit, 59 + PhabricatorUser $viewer, 60 + array $status_list) { 61 + 62 + $status = $this->getViewerAuditStatus($commit, $viewer); 63 + if ($status === null) { 64 + return false; 65 + } 66 + 67 + $status_map = array_fuse($status_list); 68 + return isset($status_map[$status]); 69 + } 70 + 71 + protected function applyAuditorEffect( 72 + PhabricatorRepositoryCommit $commit, 73 + PhabricatorUser $viewer, 74 + $value, 75 + $status) { 76 + 77 + $audits = $commit->getAudits(); 78 + $audits = mpull($audits, null, 'getAuditorPHID'); 79 + 80 + $map = array(); 81 + 82 + $with_authority = ($status != PhabricatorAuditStatusConstants::RESIGNED); 83 + if ($with_authority) { 84 + $has_authority = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser( 85 + $viewer); 86 + $has_authority = array_fuse($has_authority); 87 + foreach ($audits as $audit) { 88 + $auditor_phid = $audit->getAuditorPHID(); 89 + if (isset($has_authority[$auditor_phid])) { 90 + $map[$auditor_phid] = $status; 91 + } 92 + } 93 + } 94 + 95 + // In all cases, you affect yourself. 96 + $map[$viewer->getPHID()] = $status; 97 + 98 + $this->updateAudits($commit, $map); 99 + } 100 + 101 + }
+1 -16
src/applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php
··· 75 75 } 76 76 } 77 77 78 - foreach ($new as $phid => $status) { 79 - $auditor = idx($auditors, $phid); 80 - if (!$auditor) { 81 - $auditor = id(new PhabricatorRepositoryAuditRequest()) 82 - ->setAuditorPHID($phid) 83 - ->setCommitPHID($object->getPHID()); 84 - } else { 85 - if ($auditor->getAuditStatus() === $status) { 86 - continue; 87 - } 88 - } 89 - 90 - $auditor 91 - ->setAuditStatus($status) 92 - ->save(); 93 - } 78 + $this->updateAudits($object, $new); 94 79 } 95 80 96 81 public function getTitle() {
+70
src/applications/diffusion/xaction/DiffusionCommitConcernTransaction.php
··· 1 + <?php 2 + 3 + final class DiffusionCommitConcernTransaction 4 + extends DiffusionCommitAuditTransaction { 5 + 6 + const TRANSACTIONTYPE = 'diffusion.commit.concern'; 7 + const ACTIONKEY = 'concern'; 8 + 9 + protected function getCommitActionLabel() { 10 + return pht("Raise Concern \xE2\x9C\x98"); 11 + } 12 + 13 + protected function getCommitActionDescription() { 14 + return pht('This commit will be returned to the author for consideration.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-times-circle-o'; 19 + } 20 + 21 + public function getColor() { 22 + return 'red'; 23 + } 24 + 25 + protected function getCommitActionOrder() { 26 + return 600; 27 + } 28 + 29 + public function generateOldValue($object) { 30 + $actor = $this->getActor(); 31 + return $this->isViewerRejectingAuditor($object, $actor); 32 + } 33 + 34 + public function applyExternalEffects($object, $value) { 35 + $status = PhabricatorAuditStatusConstants::CONCERNED; 36 + $actor = $this->getActor(); 37 + $this->applyAuditorEffect($object, $actor, $value, $status); 38 + } 39 + 40 + protected function validateAction($object, PhabricatorUser $viewer) { 41 + if ($this->isViewerCommitAuthor($object, $viewer)) { 42 + throw new Exception( 43 + pht( 44 + 'You can not raise a concern with this commit because you are '. 45 + 'the commit author. You can only raise concerns with commits '. 46 + 'you did not author.')); 47 + } 48 + 49 + if ($this->isViewerRejectingAuditor($object, $viewer)) { 50 + throw new Exception( 51 + pht( 52 + 'You can not raise a concern with this commit because you have '. 53 + 'already raised a concern with it.')); 54 + } 55 + } 56 + 57 + public function getTitle() { 58 + return pht( 59 + '%s raised a concern with this commit.', 60 + $this->renderAuthor()); 61 + } 62 + 63 + public function getTitleForFeed() { 64 + return pht( 65 + '%s raised a concern with %s.', 66 + $this->renderAuthor(), 67 + $this->renderObject()); 68 + } 69 + 70 + }
+62
src/applications/diffusion/xaction/DiffusionCommitResignTransaction.php
··· 1 + <?php 2 + 3 + final class DiffusionCommitResignTransaction 4 + extends DiffusionCommitAuditTransaction { 5 + 6 + const TRANSACTIONTYPE = 'diffusion.commit.resign'; 7 + const ACTIONKEY = 'resign'; 8 + 9 + protected function getCommitActionLabel() { 10 + return pht('Resign as Auditor'); 11 + } 12 + 13 + protected function getCommitActionDescription() { 14 + return pht('You will resign as an auditor for this commit.'); 15 + } 16 + 17 + public function getIcon() { 18 + return 'fa-flag'; 19 + } 20 + 21 + public function getColor() { 22 + return 'orange'; 23 + } 24 + 25 + protected function getCommitActionOrder() { 26 + return 700; 27 + } 28 + 29 + public function generateOldValue($object) { 30 + $actor = $this->getActor(); 31 + return !$this->isViewerAnyAuditor($object, $actor); 32 + } 33 + 34 + public function applyExternalEffects($object, $value) { 35 + $status = PhabricatorAuditStatusConstants::RESIGNED; 36 + $actor = $this->getActor(); 37 + $this->applyAuditorEffect($object, $actor, $value, $status); 38 + } 39 + 40 + protected function validateAction($object, PhabricatorUser $viewer) { 41 + if (!$this->isViewerAnyAuditor($object, $viewer)) { 42 + throw new Exception( 43 + pht( 44 + 'You can not resign from this commit because you are not an '. 45 + 'auditor.')); 46 + } 47 + } 48 + 49 + public function getTitle() { 50 + return pht( 51 + '%s resigned from this commit.', 52 + $this->renderAuthor()); 53 + } 54 + 55 + public function getTitleForFeed() { 56 + return pht( 57 + '%s resigned from %s.', 58 + $this->renderAuthor(), 59 + $this->renderObject()); 60 + } 61 + 62 + }
+34 -1
src/applications/diffusion/xaction/DiffusionCommitTransactionType.php
··· 1 1 <?php 2 2 3 3 abstract class DiffusionCommitTransactionType 4 - extends PhabricatorModularTransactionType {} 4 + extends PhabricatorModularTransactionType { 5 + 6 + protected function updateAudits( 7 + PhabricatorRepositoryCommit $commit, 8 + array $new) { 9 + 10 + $audits = $commit->getAudits(); 11 + $audits = mpull($audits, null, 'getAuditorPHID'); 12 + 13 + foreach ($new as $phid => $status) { 14 + $audit = idx($audits, $phid); 15 + if (!$audit) { 16 + $audit = id(new PhabricatorRepositoryAuditRequest()) 17 + ->setAuditorPHID($phid) 18 + ->setCommitPHID($commit->getPHID()); 19 + 20 + $audits[$phid] = $audit; 21 + } else { 22 + if ($audit->getAuditStatus() === $status) { 23 + continue; 24 + } 25 + } 26 + 27 + $audit 28 + ->setAuditStatus($status) 29 + ->save(); 30 + } 31 + 32 + $commit->attachAudits($audits); 33 + 34 + return $audits; 35 + } 36 + 37 + }