diff --git a/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php index 6232594aae..c2baef034f 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAbandonTransaction.php @@ -1,89 +1,97 @@ isAbandoned(); } public function applyInternalEffects($object, $value) { $status_abandoned = DifferentialRevisionStatus::ABANDONED; $object->setModernRevisionStatus($status_abandoned); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( 'You can not abandon this revision because it has already been '. 'closed. Only open revisions can be abandoned.')); } $config_key = 'differential.always-allow-abandon'; if (!PhabricatorEnv::getEnvConfig($config_key)) { if (!$this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not abandon this revision because you are not the '. 'author. You can only abandon revisions you own. You can change '. 'this behavior by adjusting the "%s" setting in Config.', $config_key)); } } } public function getTitle() { return pht( '%s abandoned this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s abandoned %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'abandon'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php index 42cd798584..3d2df14899 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php @@ -1,237 +1,245 @@ getReviewers(); $options = array(); $value = array(); // Put the viewer's user reviewer first, if it exists, so that "Accept as // yourself" is always at the top. $head = array(); $tail = array(); foreach ($reviewers as $key => $reviewer) { if ($reviewer->isUser()) { $head[$key] = $reviewer; } else { $tail[$key] = $reviewer; } } $reviewers = $head + $tail; $diff_phid = $this->getActiveDiffPHID($revision); $reviewer_phids = array(); // If the viewer isn't a reviewer, add them to the list of options first. // This happens when you navigate to some revision you aren't involved in: // you can accept and become a reviewer. $viewer_phid = $viewer->getPHID(); if ($viewer_phid) { if (!isset($reviewers[$viewer_phid])) { $reviewer_phids[$viewer_phid] = $viewer_phid; } } $default_unchecked = array(); foreach ($reviewers as $reviewer) { $reviewer_phid = $reviewer->getReviewerPHID(); if (!$reviewer->hasAuthority($viewer)) { // If the viewer doesn't have authority to act on behalf of a reviewer, // we check if they can accept by force. if ($revision->canReviewerForceAccept($viewer, $reviewer)) { $default_unchecked[$reviewer_phid] = true; } else { continue; } } if (!$include_accepted) { if ($reviewer->isAccepted($diff_phid)) { // If a reviewer is already in a full "accepted" state, don't // include that reviewer as an option unless we're listing all // reviewers, including reviewers who have already accepted. continue; } } $reviewer_phids[$reviewer_phid] = $reviewer_phid; } $handles = $viewer->loadHandles($reviewer_phids); $head = array(); $tail = array(); foreach ($reviewer_phids as $reviewer_phid) { $is_force = isset($default_unchecked[$reviewer_phid]); if ($is_force) { $tail[] = $reviewer_phid; $options[$reviewer_phid] = pht( 'Force accept as %s', $viewer->renderHandle($reviewer_phid)); } else { $head[] = $reviewer_phid; $value[] = $reviewer_phid; $options[$reviewer_phid] = pht( 'Accept as %s', $viewer->renderHandle($reviewer_phid)); } } // Reorder reviewers so "force accept" reviewers come at the end. $options = array_select_keys($options, $head) + array_select_keys($options, $tail); return array($options, $value); } public function generateOldValue($object) { $actor = $this->getActor(); return $this->isViewerFullyAccepted($object, $actor); } public function applyExternalEffects($object, $value) { $status = DifferentialReviewerStatus::STATUS_ACCEPTED; $actor = $this->getActor(); $this->applyReviewerEffect($object, $actor, $value, $status); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( 'You can not accept this revision because it has already been '. 'closed. Only open revisions can be accepted.')); } if ($object->isDraft() || !$object->getShouldBroadcast()) { throw new Exception( pht('You can not accept a draft revision.')); } $config_key = 'differential.allow-self-accept'; if (!PhabricatorEnv::getEnvConfig($config_key)) { if ($this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not accept this revision because you are the revision '. 'author. You can only accept revisions you do not own. You can '. 'change this behavior by adjusting the "%s" setting in Config.', $config_key)); } } if ($this->isViewerFullyAccepted($object, $viewer)) { throw new Exception( pht( 'You can not accept this revision because you have already '. 'accepted it.')); } } protected function validateOptionValue($object, $actor, array $value) { if (!$value) { throw new Exception( pht( 'When accepting a revision, you must accept on behalf of at '. 'least one reviewer.')); } // NOTE: We're including reviewers who have already been accepted in this // check. Legitimate users may race one another to accept on behalf of // packages. If we get a form submission which includes a reviewer which // someone has already accepted, that's fine. See T12757. list($options) = $this->getActionOptions($actor, $object, true); foreach ($value as $phid) { if (!isset($options[$phid])) { throw new Exception( pht( 'Reviewer "%s" is not a valid reviewer which you have authority '. 'to accept on behalf of.', $phid)); } } } public function getTitle() { $new = $this->getNewValue(); if (is_array($new) && $new) { return pht( '%s accepted this revision as %s reviewer(s): %s.', $this->renderAuthor(), phutil_count($new), $this->renderHandleList($new)); } else { return pht( '%s accepted this revision.', $this->renderAuthor()); } } public function getTitleForFeed() { return pht( '%s accepted %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'accept'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php index 7ef0f2ba60..f20ba7a011 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCloseTransaction.php @@ -1,158 +1,157 @@ isClosed(); } public function applyInternalEffects($object, $value) { $was_accepted = $object->isAccepted(); $status_published = DifferentialRevisionStatus::PUBLISHED; $object->setModernRevisionStatus($status_published); $object->setProperty( DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED, $was_accepted); } protected function validateAction($object, PhabricatorUser $viewer) { if ($this->hasEditor()) { if ($this->getEditor()->getIsCloseByCommit()) { // If we're closing a revision because we discovered a commit, we don't // care what state it was in. return; } } if ($object->isClosed()) { throw new Exception( pht( 'You can not close this revision because it has already been '. 'closed. Only open revisions can be closed.')); } if (!$object->isAccepted()) { throw new Exception( pht( 'You can not close this revision because it has not been accepted. '. 'Revisions must be accepted before they can be closed.')); } $config_key = 'differential.always-allow-close'; if (!PhabricatorEnv::getEnvConfig($config_key)) { if (!$this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not close this revision because you are not the '. 'author. You can only close revisions you own. You can change '. 'this behavior by adjusting the "%s" setting in Config.', $config_key)); } } } public function getTitle() { if (!$this->getMetadataValue('isCommitClose')) { return pht( '%s closed this revision.', $this->renderAuthor()); } $commit_phid = $this->getMetadataValue('commitPHID'); $committer_phid = $this->getMetadataValue('committerPHID'); $author_phid = $this->getMetadataValue('authorPHID'); if ($committer_phid) { $committer_name = $this->renderHandle($committer_phid); } else { $committer_name = $this->getMetadataValue('committerName'); } if ($author_phid) { $author_name = $this->renderHandle($author_phid); } else { $author_name = $this->getMetadatavalue('authorName'); } $same_phid = strlen($committer_phid) && strlen($author_phid) && ($committer_phid == $author_phid); $same_name = !strlen($committer_phid) && !strlen($author_phid) && ($committer_name == $author_name); if ($same_name || $same_phid) { return pht( 'Closed by commit %s (authored by %s).', $this->renderHandle($commit_phid), $author_name); } else { return pht( 'Closed by commit %s (authored by %s, committed by %s).', $this->renderHandle($commit_phid), $author_name, $committer_name); } } public function getTitleForFeed() { return pht( '%s closed %s.', $this->renderAuthor(), $this->renderObject()); } public function getTransactionTypeForConduit($xaction) { return 'close'; } public function getFieldValuesForConduit($object, $data) { $commit_phid = $object->getMetadataValue('commitPHID'); if ($commit_phid) { $commit_phids = array($commit_phid); } else { $commit_phids = array(); } return array( 'commitPHIDs' => $commit_phids, ); } - } diff --git a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php index 561e57c7c4..5aafa536f3 100644 --- a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php @@ -1,90 +1,98 @@ getAuthorPHID(); } public function generateNewValue($object, $value) { $actor = $this->getActor(); return $actor->getPHID(); } public function applyInternalEffects($object, $value) { $object->setAuthorPHID($value); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( 'You can not commandeer this revision because it has already '. 'been closed. You can only commandeer open revisions.')); } if ($this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not commandeer this revision because you are already '. 'the author.')); } } public function getTitle() { return pht( '%s commandeered this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s commandeered %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'commandeer'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php index bdbe28d5cb..f4fb0a3eb1 100644 --- a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php @@ -1,122 +1,130 @@ isChangePlanned(); } public function applyInternalEffects($object, $value) { $status_planned = DifferentialRevisionStatus::CHANGES_PLANNED; $object->setModernRevisionStatus($status_planned); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isDraft()) { // See PHI346. Until the "Draft" state fully unprototypes, allow drafts // to be moved to "changes planned" via the API. This preserves the // behavior of "arc diff --plan-changes". We still prevent this // transition from the web UI. // TODO: Remove this once drafts leave prototype. $editor = $this->getEditor(); $type_web = PhabricatorWebContentSource::SOURCECONST; if ($editor->getContentSource()->getSource() == $type_web) { throw new Exception( pht('You can not plan changes to a draft revision.')); } } if ($object->isChangePlanned()) { throw new Exception( pht( 'You can not request review of this revision because this '. 'revision is already under review and the action would have '. 'no effect.')); } if ($object->isClosed()) { throw new Exception( pht( 'You can not plan changes to this this revision because it has '. 'already been closed.')); } if (!$this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not plan changes to this revision because you do not '. 'own it. Only the author of a revision can plan changes to it.')); } } public function getTitle() { if ($this->isDraftDemotion()) { return pht( '%s returned this revision to the author for changes because remote '. 'builds failed.', $this->renderAuthor()); } else { return pht( '%s planned changes to this revision.', $this->renderAuthor()); } } public function getTitleForFeed() { return pht( '%s planned changes to %s.', $this->renderAuthor(), $this->renderObject()); } private function isDraftDemotion() { return (bool)$this->getMetadataValue('draft.demote'); } + public function getTransactionTypeForConduit($xaction) { + return 'plan-changes'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php index 6d74589b64..e023dda0a1 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReclaimTransaction.php @@ -1,88 +1,96 @@ isAbandoned(); } public function applyInternalEffects($object, $value) { if ($object->getShouldBroadcast()) { $new_status = DifferentialRevisionStatus::NEEDS_REVIEW; } else { $new_status = DifferentialRevisionStatus::DRAFT; } $object->setModernRevisionStatus($new_status); } protected function validateAction($object, PhabricatorUser $viewer) { if (!$object->isAbandoned()) { throw new Exception( pht( 'You can not reclaim this revision because it has not been '. 'abandoned. Only abandoned revisions can be reclaimed.')); } if (!$this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not reclaim this revision because you are not the '. 'revision author. You can only reclaim revisions you own.')); } } public function getTitle() { return pht( '%s reclaimed this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s reclaimed %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'reclaim'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php index 0ed6db96bf..0001ce09ca 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php @@ -1,102 +1,110 @@ getActor(); return $this->isViewerFullyRejected($object, $actor); } public function applyExternalEffects($object, $value) { $status = DifferentialReviewerStatus::STATUS_REJECTED; $actor = $this->getActor(); $this->applyReviewerEffect($object, $actor, $value, $status); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( 'You can not request changes to this revision because it has '. 'already been closed. You can only request changes to open '. 'revisions.')); } if ($this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not request changes to this revision because you are the '. 'revision author. You can only request changes to revisions you do '. 'not own.')); } if ($object->isDraft() || !$object->getShouldBroadcast()) { throw new Exception( pht('You can not request changes to a draft revision.')); } if ($this->isViewerFullyRejected($object, $viewer)) { throw new Exception( pht( 'You can not request changes to this revision because you have '. 'already requested changes.')); } } public function getTitle() { return pht( '%s requested changes to this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s requested changes to %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'request-changes'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php index 4fc9f3a6f1..a2d25287bf 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReopenTransaction.php @@ -1,75 +1,83 @@ isClosed(); } public function applyInternalEffects($object, $value) { $status_review = DifferentialRevisionStatus::NEEDS_REVIEW; $object->setModernRevisionStatus($status_review); } protected function validateAction($object, PhabricatorUser $viewer) { if (!$object->isPublished()) { throw new Exception( pht( 'You can not reopen this revision because it is not closed. '. 'Only closed revisions can be reopened.')); } $config_key = 'differential.allow-reopen'; if (!PhabricatorEnv::getEnvConfig($config_key)) { throw new Exception( pht( 'You can not reopen this revision because configuration prevents '. 'any revision from being reopened. You can change this behavior '. 'by adjusting the "%s" setting in Config.', $config_key)); } } public function getTitle() { return pht( '%s reopened this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s reopened %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'reopen'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php index 08c590a9ae..a3d2699c74 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRequestReviewTransaction.php @@ -1,87 +1,95 @@ isDraft()) { return pht('This revision will be submitted to reviewers for feedback.'); } else { return pht('This revision will be returned to reviewers for feedback.'); } } public function getColor() { return 'sky'; } protected function getRevisionActionOrder() { return 200; } public function getActionName() { return pht('Requested Review'); } public function generateOldValue($object) { return $object->isNeedsReview(); } public function applyInternalEffects($object, $value) { $status_review = DifferentialRevisionStatus::NEEDS_REVIEW; $object ->setModernRevisionStatus($status_review) ->setShouldBroadcast(true); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isNeedsReview()) { throw new Exception( pht( 'You can not request review of this revision because this '. 'revision is already under review and the action would have '. 'no effect.')); } if ($object->isClosed()) { throw new Exception( pht( 'You can not request review of this revision because it has '. 'already been closed. You can only request review of open '. 'revisions.')); } // When revisions automatically promote out of "Draft" after builds finish, // the viewer may be acting as the Harbormaster application. if (!$viewer->isOmnipotent()) { if (!$this->isViewerRevisionAuthor($object, $viewer)) { throw new Exception( pht( 'You can not request review of this revision because you are not '. 'the author of the revision.')); } } } public function getTitle() { return pht( '%s requested review of this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s requested review of %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'request-review'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + } diff --git a/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php b/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php index 53b0b8da01..a2b4fd4337 100644 --- a/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionResignTransaction.php @@ -1,96 +1,104 @@ getActor(); $resigned = DifferentialReviewerStatus::STATUS_RESIGNED; return ($this->getViewerReviewerStatus($object, $actor) == $resigned); } public function applyExternalEffects($object, $value) { $status = DifferentialReviewerStatus::STATUS_RESIGNED; $actor = $this->getActor(); $this->applyReviewerEffect($object, $actor, $value, $status); } protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( 'You can not resign from this revision because it has already '. 'been closed. You can only resign from open revisions.')); } $resigned = DifferentialReviewerStatus::STATUS_RESIGNED; if ($this->getViewerReviewerStatus($object, $viewer) == $resigned) { throw new Exception( pht( 'You can not resign from this revision because you have already '. 'resigned.')); } if (!$this->isViewerAnyAuthority($object, $viewer)) { throw new Exception( pht( 'You can not resign from this revision because you are not a '. 'reviewer, and do not have authority over any reviewer.')); } } public function getTitle() { return pht( '%s resigned from this revision.', $this->renderAuthor()); } public function getTitleForFeed() { return pht( '%s resigned from %s.', $this->renderAuthor(), $this->renderObject()); } + public function getTransactionTypeForConduit($xaction) { + return 'resign'; + } + + public function getFieldValuesForConduit($object, $data) { + return array(); + } + }