3 abstract class DifferentialRevisionActionTransaction
4 extends DifferentialRevisionTransactionType
{
6 final public function getRevisionActionKey() {
7 return $this->getPhobjectClassConstant('ACTIONKEY', 32);
10 public function isActionAvailable($object, PhabricatorUser
$viewer) {
12 $this->validateAction($object, $viewer);
14 } catch (Exception
$ex) {
19 abstract protected function validateAction($object, PhabricatorUser
$viewer);
20 abstract protected function getRevisionActionLabel(
21 DifferentialRevision
$revision,
22 PhabricatorUser
$viewer);
24 protected function validateOptionValue($object, $actor, array $value) {
28 public function getCommandKeyword() {
32 public function getCommandAliases() {
36 public function getCommandSummary() {
40 protected function getRevisionActionOrder() {
44 public function getActionStrength() {
48 public function getRevisionActionOrderVector() {
49 return id(new PhutilSortVector())
50 ->addInt($this->getRevisionActionOrder());
53 protected function getRevisionActionGroupKey() {
54 return DifferentialRevisionEditEngine
::ACTIONGROUP_REVISION
;
57 protected function getRevisionActionDescription(
58 DifferentialRevision
$revision,
59 PhabricatorUser
$viewer) {
63 protected function getRevisionActionSubmitButtonText(
64 DifferentialRevision
$revision,
65 PhabricatorUser
$viewer) {
69 protected function getRevisionActionMetadata(
70 DifferentialRevision
$revision,
71 PhabricatorUser
$viewer) {
75 public static function loadAllActions() {
76 return id(new PhutilClassMapQuery())
77 ->setAncestorClass(__CLASS__
)
78 ->setUniqueMethod('getRevisionActionKey')
82 protected function isViewerRevisionAuthor(
83 DifferentialRevision
$revision,
84 PhabricatorUser
$viewer) {
86 if (!$viewer->getPHID()) {
90 return ($viewer->getPHID() === $revision->getAuthorPHID());
93 protected function getActionOptions(
94 PhabricatorUser
$viewer,
95 DifferentialRevision
$revision) {
102 public function newEditField(
103 DifferentialRevision
$revision,
104 PhabricatorUser
$viewer) {
106 // Actions in the "review" group, like "Accept Revision", do not require
107 // that the actor be able to edit the revision.
108 $group_review = DifferentialRevisionEditEngine
::ACTIONGROUP_REVIEW
;
109 $is_review = ($this->getRevisionActionGroupKey() == $group_review);
111 $field = id(new PhabricatorApplyEditField())
112 ->setKey($this->getRevisionActionKey())
113 ->setTransactionType($this->getTransactionTypeConstant())
114 ->setCanApplyWithoutEditCapability($is_review)
117 if ($this->isActionAvailable($revision, $viewer)) {
118 $label = $this->getRevisionActionLabel($revision, $viewer);
119 if ($label !== null) {
120 $field->setCommentActionLabel($label);
122 $description = $this->getRevisionActionDescription($revision, $viewer);
123 $field->setActionDescription($description);
125 $group_key = $this->getRevisionActionGroupKey();
126 $field->setCommentActionGroupKey($group_key);
128 $button_text = $this->getRevisionActionSubmitButtonText(
131 $field->setActionSubmitButtonText($button_text);
133 // Currently, every revision action conflicts with every other
134 // revision action: for example, you can not simultaneously Accept and
135 // Reject a revision.
137 // Under some configurations, some combinations of actions are sort of
138 // technically permissible. For example, you could reasonably Reject
139 // and Abandon a revision if "anyone can abandon anything" is enabled.
141 // It's not clear that these combinations are actually useful, so just
142 // keep things simple for now.
143 $field->setActionConflictKey('revision.action');
145 list($options, $value) = $this->getActionOptions($viewer, $revision);
147 // Show the options if the user can select on behalf of two or more
148 // reviewers, or can force-accept on behalf of one or more reviewers,
149 // or can accept on behalf of a reviewer other than themselves (see
151 $can_multi = (count($options) > 1);
152 $can_force = (count($value) < count($options));
153 $not_self = (head_key($options) != $viewer->getPHID());
155 if ($can_multi ||
$can_force ||
$not_self) {
156 $field->setOptions($options);
157 $field->setValue($value);
160 $metadata = $this->getRevisionActionMetadata($revision, $viewer);
161 foreach ($metadata as $metadata_key => $metadata_value) {
162 $field->setMetadataValue($metadata_key, $metadata_value);
170 public function validateTransactions($object, array $xactions) {
172 $actor = $this->getActor();
174 $action_exception = null;
175 foreach ($xactions as $xaction) {
176 // If this is a draft demotion action, let it skip all the normal
177 // validation. This is a little hacky and should perhaps move down
178 // into the actual action implementations, but currently we can not
179 // apply this rule in validateAction() because it doesn't operate on
180 // the actual transaction.
181 if ($xaction->getMetadataValue('draft.demote')) {
186 $this->validateAction($object, $actor);
187 } catch (Exception
$ex) {
188 $action_exception = $ex;
194 foreach ($xactions as $xaction) {
195 if ($action_exception) {
196 $errors[] = $this->newInvalidError(
197 $action_exception->getMessage(),
202 $new = $xaction->getNewValue();
203 if (!is_array($new)) {
208 $this->validateOptionValue($object, $actor, $new);
209 } catch (Exception
$ex) {
210 $errors[] = $this->newInvalidError(