protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $setting_key = $xaction->getMetadataValue(PhabricatorUserPreferencesTransaction::PROPERTY_SETTING);
     $settings = $this->getSettings();
     $setting = idx($settings, $setting_key);
     if ($setting) {
         return $setting->expandSettingTransaction($object, $xaction);
     }
     return parent::expandTransaction($object, $xaction);
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $results = parent::expandTransaction($object, $xaction);
     switch ($xaction->getTransactionType()) {
         case ManiphestTransaction::TYPE_OWNER:
             // When a task is reassigned, move the old owner to the subscriber
             // list so they're still in the loop.
             $owner_phid = $object->getOwnerPHID();
             if ($owner_phid) {
                 $results[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)->setIgnoreOnNoEffect(true)->setNewValue(array('+' => array($owner_phid => $owner_phid)));
             }
             break;
     }
     return $results;
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $results = parent::expandTransaction($object, $xaction);
     $actor = $this->getActor();
     $actor_phid = $this->getActingAsPHID();
     $type_edge = PhabricatorTransactions::TYPE_EDGE;
     $status_plan = ArcanistDifferentialRevisionStatus::CHANGES_PLANNED;
     $edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
     $edge_ref_task = DifferentialRevisionHasTaskEdgeType::EDGECONST;
     $is_sticky_accept = PhabricatorEnv::getEnvConfig('differential.sticky-accept');
     $downgrade_rejects = false;
     $downgrade_accepts = false;
     if ($this->getIsCloseByCommit()) {
         // Never downgrade reviewers when we're closing a revision after a
         // commit.
     } else {
         switch ($xaction->getTransactionType()) {
             case DifferentialTransaction::TYPE_UPDATE:
                 $downgrade_rejects = true;
                 if (!$is_sticky_accept) {
                     // If "sticky accept" is disabled, also downgrade the accepts.
                     $downgrade_accepts = true;
                 }
                 break;
             case DifferentialTransaction::TYPE_ACTION:
                 switch ($xaction->getNewValue()) {
                     case DifferentialAction::ACTION_REQUEST:
                         $downgrade_rejects = true;
                         if (!$is_sticky_accept || $object->getStatus() != $status_plan) {
                             // If the old state isn't "changes planned", downgrade the
                             // accepts. This exception allows an accepted revision to
                             // go through Plan Changes -> Request Review to return to
                             // "accepted" if the author didn't update the revision.
                             $downgrade_accepts = true;
                         }
                         break;
                 }
                 break;
         }
     }
     $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED;
     $new_reject = DifferentialReviewerStatus::STATUS_REJECTED;
     $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_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->getReviewerStatus() as $reviewer) {
             if ($downgrade_rejects) {
                 if ($reviewer->getStatus() == $new_reject) {
                     $edits[$reviewer->getReviewerPHID()] = array('data' => array('status' => $old_reject));
                 }
             }
             if ($downgrade_accepts) {
                 if ($reviewer->getStatus() == $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));
         }
     }
     switch ($xaction->getTransactionType()) {
         case DifferentialTransaction::TYPE_UPDATE:
             if ($this->getIsCloseByCommit()) {
                 // Don't bother with any of this if this update is a side effect of
                 // commit detection.
                 break;
             }
             // When a revision is updated and the diff comes from a branch named
             // "T123" or similar, automatically associate the commit with the
             // task that the branch names.
             $maniphest = 'PhabricatorManiphestApplication';
             if (PhabricatorApplication::isClassInstalled($maniphest)) {
                 $diff = $this->requireDiff($xaction->getNewValue());
                 $branch = $diff->getBranch();
                 // No "$", to allow for branches like T123_demo.
                 $match = null;
                 if (preg_match('/^T(\\d+)/i', $branch, $match)) {
                     $task_id = $match[1];
                     $tasks = id(new ManiphestTaskQuery())->setViewer($this->getActor())->withIDs(array($task_id))->execute();
                     if ($tasks) {
                         $task = head($tasks);
                         $task_phid = $task->getPHID();
                         $results[] = id(new DifferentialTransaction())->setTransactionType($type_edge)->setMetadataValue('edge:type', $edge_ref_task)->setIgnoreOnNoEffect(true)->setNewValue(array('+' => array($task_phid => $task_phid)));
                     }
                 }
             }
             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.
             $status_added = DifferentialReviewerStatus::STATUS_ADDED;
             $status_commented = DifferentialReviewerStatus::STATUS_COMMENTED;
             $data = array('status' => $status_commented);
             $edits = array();
             foreach ($object->getReviewerStatus() as $reviewer) {
                 if ($reviewer->getReviewerPHID() == $actor_phid) {
                     if ($reviewer->getStatus() == $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 DifferentialTransaction::TYPE_ACTION:
             $action_type = $xaction->getNewValue();
             switch ($action_type) {
                 case DifferentialAction::ACTION_ACCEPT:
                 case DifferentialAction::ACTION_REJECT:
                     if ($action_type == DifferentialAction::ACTION_ACCEPT) {
                         $data = array('status' => DifferentialReviewerStatus::STATUS_ACCEPTED);
                     } else {
                         $data = array('status' => DifferentialReviewerStatus::STATUS_REJECTED);
                     }
                     $edits = array();
                     foreach ($object->getReviewerStatus() as $reviewer) {
                         if ($reviewer->hasAuthority($actor)) {
                             $edits[$reviewer->getReviewerPHID()] = array('data' => $data);
                         }
                     }
                     // Also either update or add the actor themselves as a reviewer.
                     $edits[$actor_phid] = array('data' => $data);
                     $results[] = id(new DifferentialTransaction())->setTransactionType($type_edge)->setMetadataValue('edge:type', $edge_reviewer)->setIgnoreOnNoEffect(true)->setNewValue(array('+' => $edits));
                     break;
                 case DifferentialAction::ACTION_CLAIM:
                     // If the user is commandeering, add the previous owner as a
                     // reviewer and remove the actor.
                     $edits = array('-' => array($actor_phid => $actor_phid));
                     $owner_phid = $object->getAuthorPHID();
                     if ($owner_phid) {
                         $reviewer = new DifferentialReviewer($owner_phid, array('status' => DifferentialReviewerStatus::STATUS_ADDED));
                         $edits['+'] = array($owner_phid => array('data' => $reviewer->getEdgeData()));
                     }
                     // NOTE: We're setting setIsCommandeerSideEffect() on this because
                     // normally you can't add a revision's author as a reviewer, but
                     // this action swaps them after validation executes.
                     $results[] = id(new DifferentialTransaction())->setTransactionType($type_edge)->setMetadataValue('edge:type', $edge_reviewer)->setIgnoreOnNoEffect(true)->setIsCommandeerSideEffect(true)->setNewValue($edits);
                     break;
                 case DifferentialAction::ACTION_RESIGN:
                     // If the user is resigning, add a separate reviewer edit
                     // transaction which removes them as a reviewer.
                     $results[] = id(new DifferentialTransaction())->setTransactionType($type_edge)->setMetadataValue('edge:type', $edge_reviewer)->setIgnoreOnNoEffect(true)->setNewValue(array('-' => array($actor_phid => $actor_phid)));
                     break;
             }
             break;
     }
     if (!$this->didExpandInlineState) {
         switch ($xaction->getTransactionType()) {
             case PhabricatorTransactions::TYPE_COMMENT:
             case DifferentialTransaction::TYPE_ACTION:
             case DifferentialTransaction::TYPE_UPDATE:
             case DifferentialTransaction::TYPE_INLINE:
                 $this->didExpandInlineState = true;
                 $actor_phid = $this->getActingAsPHID();
                 $actor_is_author = $object->getAuthorPHID() == $actor_phid;
                 if (!$actor_is_author) {
                     break;
                 }
                 $state_map = PhabricatorTransactions::getInlineStateMap();
                 $inlines = id(new DifferentialDiffInlineCommentQuery())->setViewer($this->getActor())->withRevisionPHIDs(array($object->getPHID()))->withFixedStates(array_keys($state_map))->execute();
                 if (!$inlines) {
                     break;
                 }
                 $old_value = mpull($inlines, 'getFixedState', 'getPHID');
                 $new_value = array();
                 foreach ($old_value as $key => $state) {
                     $new_value[$key] = $state_map[$state];
                 }
                 $results[] = id(new DifferentialTransaction())->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)->setIgnoreOnNoEffect(true)->setOldValue($old_value)->setNewValue($new_value);
                 break;
         }
     }
     return $results;
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $results = parent::expandTransaction($object, $xaction);
     switch ($xaction->getTransactionType()) {
         case ManiphestTransaction::TYPE_COLUMN:
             $column_phid = $xaction->getNewValue();
             if (!$column_phid) {
                 break;
             }
             // When a task is created into a column, we also generate a transaction
             // to actually put it in that column.
             $column = $this->loadProjectColumn($column_phid);
             $results[] = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN)->setOldValue(array('projectPHID' => $column->getProjectPHID(), 'columnPHIDs' => array()))->setNewValue(array('projectPHID' => $column->getProjectPHID(), 'columnPHIDs' => array($column->getPHID())));
             break;
         case ManiphestTransaction::TYPE_OWNER:
             // When a task is reassigned, move the old owner to the subscriber
             // list so they're still in the loop.
             $owner_phid = $object->getOwnerPHID();
             if ($owner_phid) {
                 $results[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)->setIgnoreOnNoEffect(true)->setNewValue(array('+' => array($owner_phid => $owner_phid)));
             }
             break;
     }
     return $results;
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $results = parent::expandTransaction($object, $xaction);
     $type = $xaction->getTransactionType();
     switch ($type) {
         case PhabricatorTransactions::TYPE_COLUMNS:
             try {
                 $more_xactions = $this->buildMoveTransaction($object, $xaction);
                 foreach ($more_xactions as $more_xaction) {
                     $results[] = $more_xaction;
                 }
             } catch (Exception $ex) {
                 $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction);
                 $this->moreValidationErrors[] = $error;
             }
             break;
         case ManiphestTransaction::TYPE_OWNER:
             // If this is a no-op update, don't expand it.
             $old_value = $object->getOwnerPHID();
             $new_value = $xaction->getNewValue();
             if ($old_value === $new_value) {
                 continue;
             }
             // When a task is reassigned, move the old owner to the subscriber
             // list so they're still in the loop.
             if ($old_value) {
                 $results[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)->setIgnoreOnNoEffect(true)->setNewValue(array('+' => array($old_value => $old_value)));
             }
             break;
     }
     return $results;
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $xactions = parent::expandTransaction($object, $xaction);
     switch ($xaction->getTransactionType()) {
         case ManiphestTransaction::TYPE_SUBPRIORITY:
             $data = $xaction->getNewValue();
             $new_pri = $data['newPriority'];
             if ($new_pri != $object->getPriority()) {
                 $xactions[] = id(new ManiphestTransaction())->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)->setNewValue($new_pri);
             }
             break;
         default:
             break;
     }
     return $xactions;
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $xactions = parent::expandTransaction($object, $xaction);
     switch ($xaction->getTransactionType()) {
         case PhabricatorAuditTransaction::TYPE_COMMIT:
             $request = $this->createAuditRequestTransactionFromCommitMessage($object);
             if ($request) {
                 $xactions[] = $request;
                 $this->setUnmentionablePHIDMap($request->getNewValue());
             }
             break;
         default:
             break;
     }
     if (!$this->didExpandInlineState) {
         switch ($xaction->getTransactionType()) {
             case PhabricatorTransactions::TYPE_COMMENT:
             case PhabricatorAuditActionConstants::ACTION:
                 $this->didExpandInlineState = true;
                 $actor_phid = $this->getActingAsPHID();
                 $actor_is_author = $object->getAuthorPHID() == $actor_phid;
                 if (!$actor_is_author) {
                     break;
                 }
                 $state_map = PhabricatorTransactions::getInlineStateMap();
                 $inlines = id(new DiffusionDiffInlineCommentQuery())->setViewer($this->getActor())->withCommitPHIDs(array($object->getPHID()))->withFixedStates(array_keys($state_map))->execute();
                 if (!$inlines) {
                     break;
                 }
                 $old_value = mpull($inlines, 'getFixedState', 'getPHID');
                 $new_value = array();
                 foreach ($old_value as $key => $state) {
                     $new_value[$key] = $state_map[$state];
                 }
                 $xactions[] = id(new PhabricatorAuditTransaction())->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)->setIgnoreOnNoEffect(true)->setOldValue($old_value)->setNewValue($new_value);
                 break;
         }
     }
     return $xactions;
 }
 protected function expandTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $xactions = parent::expandTransaction($object, $xaction);
     switch ($xaction->getTransactionType()) {
         case PhrictionTransaction::TYPE_CONTENT:
             if ($this->getIsNewObject()) {
                 break;
             }
             $content = $xaction->getNewValue();
             if ($content === '') {
                 $xactions[] = id(new PhrictionTransaction())->setTransactionType(PhrictionTransaction::TYPE_DELETE)->setNewValue(true)->setMetadataValue('contentDelete', true);
             }
             break;
         case PhrictionTransaction::TYPE_MOVE_TO:
             $document = $xaction->getNewValue();
             $xactions[] = id(new PhrictionTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($document->getViewPolicy());
             $xactions[] = id(new PhrictionTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)->setNewValue($document->getEditPolicy());
             break;
         default:
             break;
     }
     return $xactions;
 }