Differential D18398 Diff 44240 src/applications/differential/editor/DifferentialTransactionEditor.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/differential/editor/DifferentialTransactionEditor.php
| Show First 20 Lines • Show All 251 Lines • ▼ Show 20 Lines | protected function expandTransaction( | ||||
| $results = parent::expandTransaction($object, $xaction); | $results = parent::expandTransaction($object, $xaction); | ||||
| $actor = $this->getActor(); | $actor = $this->getActor(); | ||||
| $actor_phid = $this->getActingAsPHID(); | $actor_phid = $this->getActingAsPHID(); | ||||
| $type_edge = PhabricatorTransactions::TYPE_EDGE; | $type_edge = PhabricatorTransactions::TYPE_EDGE; | ||||
| $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; | $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED; | ||||
| $edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST; | |||||
| $edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST; | $edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST; | ||||
| $is_sticky_accept = PhabricatorEnv::getEnvConfig( | $is_sticky_accept = PhabricatorEnv::getEnvConfig( | ||||
| 'differential.sticky-accept'); | 'differential.sticky-accept'); | ||||
| $downgrade_rejects = false; | $downgrade_rejects = false; | ||||
| $downgrade_accepts = false; | $downgrade_accepts = false; | ||||
| if ($this->getIsCloseByCommit()) { | if ($this->getIsCloseByCommit()) { | ||||
| Show All 23 Lines | if ($this->getIsCloseByCommit()) { | ||||
| } | } | ||||
| } | } | ||||
| $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED; | $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED; | ||||
| $new_reject = DifferentialReviewerStatus::STATUS_REJECTED; | $new_reject = DifferentialReviewerStatus::STATUS_REJECTED; | ||||
| $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER; | $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER; | ||||
| $old_reject = DifferentialReviewerStatus::STATUS_REJECTED_OLDER; | $old_reject = DifferentialReviewerStatus::STATUS_REJECTED_OLDER; | ||||
| if ($downgrade_rejects || $downgrade_accepts) { | |||||
| // When a revision is updated, change all "reject" to "rejected older | |||||
| // revision". This means we won't immediately push the update back into | |||||
| // "needs review", but outstanding rejects will still block it from | |||||
| // moving to "accepted". | |||||
| // We also do this for "Request Review", even though the diff is not | |||||
| // updated directly. Essentially, this acts like an update which doesn't | |||||
| // actually change the diff text. | |||||
| $edits = array(); | |||||
| foreach ($object->getReviewers() as $reviewer) { | |||||
| if ($downgrade_rejects) { | |||||
| if ($reviewer->getReviewerStatus() == $new_reject) { | |||||
| $edits[$reviewer->getReviewerPHID()] = array( | |||||
| 'data' => array( | |||||
| 'status' => $old_reject, | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } | |||||
| if ($downgrade_accepts) { | |||||
| if ($reviewer->getReviewerStatus() == $new_accept) { | |||||
| $edits[$reviewer->getReviewerPHID()] = array( | |||||
| 'data' => array( | |||||
| 'status' => $old_accept, | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } | |||||
| } | |||||
| if ($edits) { | |||||
| $results[] = id(new DifferentialTransaction()) | |||||
| ->setTransactionType($type_edge) | |||||
| ->setMetadataValue('edge:type', $edge_reviewer) | |||||
| ->setIgnoreOnNoEffect(true) | |||||
| ->setNewValue(array('+' => $edits)); | |||||
| } | |||||
| } | |||||
| $downgrade = array(); | $downgrade = array(); | ||||
| if ($downgrade_accepts) { | if ($downgrade_accepts) { | ||||
| $downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; | $downgrade[] = DifferentialReviewerStatus::STATUS_ACCEPTED; | ||||
| } | } | ||||
| if ($downgrade_rejects) { | if ($downgrade_rejects) { | ||||
| $downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED; | $downgrade[] = DifferentialReviewerStatus::STATUS_REJECTED; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | switch ($xaction->getTransactionType()) { | ||||
| ->setMetadataValue('edge:type', $edge_ref_task) | ->setMetadataValue('edge:type', $edge_ref_task) | ||||
| ->setIgnoreOnNoEffect(true) | ->setIgnoreOnNoEffect(true) | ||||
| ->setNewValue(array('+' => array($task_phid => $task_phid))); | ->setNewValue(array('+' => array($task_phid => $task_phid))); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case PhabricatorTransactions::TYPE_COMMENT: | |||||
| // When a user leaves a comment, upgrade their reviewer status from | |||||
| // "added" to "commented" if they're also a reviewer. We may further | |||||
| // upgrade this based on other actions in the transaction group. | |||||
| if ($this->hasReviewTransaction) { | |||||
| // If we're also applying a review transaction, skip this. | |||||
| break; | |||||
| } | |||||
| $status_added = DifferentialReviewerStatus::STATUS_ADDED; | |||||
| $status_commented = DifferentialReviewerStatus::STATUS_COMMENTED; | |||||
| $data = array( | |||||
| 'status' => $status_commented, | |||||
| ); | |||||
| $edits = array(); | |||||
| foreach ($object->getReviewers() as $reviewer) { | |||||
| if ($reviewer->getReviewerPHID() == $actor_phid) { | |||||
| if ($reviewer->getReviewerStatus() == $status_added) { | |||||
| $edits[$actor_phid] = array( | |||||
| 'data' => $data, | |||||
| ); | |||||
| } | |||||
| } | |||||
| } | |||||
| if ($edits) { | |||||
| $results[] = id(new DifferentialTransaction()) | |||||
| ->setTransactionType($type_edge) | |||||
| ->setMetadataValue('edge:type', $edge_reviewer) | |||||
| ->setIgnoreOnNoEffect(true) | |||||
| ->setNewValue(array('+' => $edits)); | |||||
| } | |||||
| break; | |||||
| case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE: | case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE: | ||||
| $is_commandeer = true; | $is_commandeer = true; | ||||
| break; | break; | ||||
| case DifferentialTransaction::TYPE_ACTION: | case DifferentialTransaction::TYPE_ACTION: | ||||
| $action_type = $xaction->getNewValue(); | $action_type = $xaction->getNewValue(); | ||||
| switch ($action_type) { | switch ($action_type) { | ||||
| ▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | switch ($xaction->getTransactionType()) { | ||||
| $phid); | $phid); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| return parent::applyBuiltinExternalTransaction($object, $xaction); | return parent::applyBuiltinExternalTransaction($object, $xaction); | ||||
| } | } | ||||
| protected function mergeEdgeData($type, array $u, array $v) { | |||||
| $result = parent::mergeEdgeData($type, $u, $v); | |||||
| switch ($type) { | |||||
| case DifferentialRevisionHasReviewerEdgeType::EDGECONST: | |||||
| // When the same reviewer has their status updated by multiple | |||||
| // transactions, we want the strongest status to win. An example of | |||||
| // this is when a user adds a comment and also accepts a revision which | |||||
| // they are a reviewer on. The comment creates a "commented" status, | |||||
| // while the accept creates an "accepted" status. Since accept is | |||||
| // stronger, it should win and persist. | |||||
| $u_status = idx($u, 'status'); | |||||
| $v_status = idx($v, 'status'); | |||||
| $u_str = DifferentialReviewerStatus::getStatusStrength($u_status); | |||||
| $v_str = DifferentialReviewerStatus::getStatusStrength($v_status); | |||||
| if ($u_str > $v_str) { | |||||
| $result['status'] = $u_status; | |||||
| } else { | |||||
| $result['status'] = $v_status; | |||||
| } | |||||
| break; | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| protected function applyFinalEffects( | protected function applyFinalEffects( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| array $xactions) { | array $xactions) { | ||||
| // Load the most up-to-date version of the revision and its reviewers, | // Load the most up-to-date version of the revision and its reviewers, | ||||
| // so we don't need to try to deduce the state of reviewers by examining | // so we don't need to try to deduce the state of reviewers by examining | ||||
| // all the changes made by the transactions. Then, update the reviewers | // all the changes made by the transactions. Then, update the reviewers | ||||
| // on the object to make sure we're acting on the current reviewer set | // on the object to make sure we're acting on the current reviewer set | ||||
| ▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | protected function validateTransaction( | ||||
| $errors = parent::validateTransaction($object, $type, $xactions); | $errors = parent::validateTransaction($object, $type, $xactions); | ||||
| $config_self_accept_key = 'differential.allow-self-accept'; | $config_self_accept_key = 'differential.allow-self-accept'; | ||||
| $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key); | $allow_self_accept = PhabricatorEnv::getEnvConfig($config_self_accept_key); | ||||
| foreach ($xactions as $xaction) { | foreach ($xactions as $xaction) { | ||||
| switch ($type) { | switch ($type) { | ||||
| case PhabricatorTransactions::TYPE_EDGE: | |||||
| switch ($xaction->getMetadataValue('edge:type')) { | |||||
| case DifferentialRevisionHasReviewerEdgeType::EDGECONST: | |||||
| // Prevent the author from becoming a reviewer. | |||||
| // NOTE: This is pretty gross, but this restriction is unusual. | |||||
| // If we end up with too much more of this, we should try to clean | |||||
| // this up -- maybe by moving validation to after transactions | |||||
| // are adjusted (so we can just examine the final value) or adding | |||||
| // a second phase there? | |||||
| $author_phid = $object->getAuthorPHID(); | |||||
| $new = $xaction->getNewValue(); | |||||
| $add = idx($new, '+', array()); | |||||
| $eq = idx($new, '=', array()); | |||||
| $phids = array_keys($add + $eq); | |||||
| foreach ($phids as $phid) { | |||||
| if (($phid == $author_phid) && | |||||
| !$allow_self_accept && | |||||
| !$xaction->getIsCommandeerSideEffect()) { | |||||
| $errors[] = | |||||
| new PhabricatorApplicationTransactionValidationError( | |||||
| $type, | |||||
| pht('Invalid'), | |||||
| pht('The author of a revision can not be a reviewer.'), | |||||
| $xaction); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| break; | |||||
| case DifferentialTransaction::TYPE_UPDATE: | case DifferentialTransaction::TYPE_UPDATE: | ||||
| $diff = $this->loadDiff($xaction->getNewValue()); | $diff = $this->loadDiff($xaction->getNewValue()); | ||||
| if (!$diff) { | if (!$diff) { | ||||
| $errors[] = new PhabricatorApplicationTransactionValidationError( | $errors[] = new PhabricatorApplicationTransactionValidationError( | ||||
| $type, | $type, | ||||
| pht('Invalid'), | pht('Invalid'), | ||||
| pht('The specified diff does not exist.'), | pht('The specified diff does not exist.'), | ||||
| $xaction); | $xaction); | ||||
| ▲ Show 20 Lines • Show All 1,091 Lines • Show Last 20 Lines | |||||