public function save()
 {
     $revision = $this->revision;
     $action = $this->action;
     $actor_phid = $this->actorPHID;
     $actor = id(new PhabricatorUser())->loadOneWhere('PHID = %s', $actor_phid);
     $actor_is_author = $actor_phid == $revision->getAuthorPHID();
     $allow_self_accept = PhabricatorEnv::getEnvConfig('differential.allow-self-accept', false);
     $revision_status = $revision->getStatus();
     $revision->loadRelationships();
     $reviewer_phids = $revision->getReviewers();
     if ($reviewer_phids) {
         $reviewer_phids = array_combine($reviewer_phids, $reviewer_phids);
     }
     $metadata = array();
     $inline_comments = array();
     if ($this->attachInlineComments) {
         $inline_comments = id(new DifferentialInlineComment())->loadAllWhere('authorPHID = %s AND revisionID = %d AND commentID IS NULL', $this->actorPHID, $revision->getID());
     }
     switch ($action) {
         case DifferentialAction::ACTION_COMMENT:
             if (!$this->message && !$inline_comments) {
                 throw new DifferentialActionHasNoEffectException("You are submitting an empty comment with no action: " . "you must act on the revision or post a comment.");
             }
             break;
         case DifferentialAction::ACTION_RESIGN:
             if ($actor_is_author) {
                 throw new Exception('You can not resign from your own revision!');
             }
             if (empty($reviewer_phids[$actor_phid])) {
                 throw new DifferentialActionHasNoEffectException("You can not resign from this revision because you are not " . "a reviewer.");
             }
             DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array($actor_phid), $add = array(), $actor_phid);
             break;
         case DifferentialAction::ACTION_ABANDON:
             if (!$actor_is_author) {
                 throw new Exception('You can only abandon your own revisions.');
             }
             if ($revision_status == ArcanistDifferentialRevisionStatus::CLOSED) {
                 throw new DifferentialActionHasNoEffectException("You can not abandon this revision because it has already " . "been closed.");
             }
             if ($revision_status == ArcanistDifferentialRevisionStatus::ABANDONED) {
                 throw new DifferentialActionHasNoEffectException("You can not abandon this revision because it has already " . "been abandoned.");
             }
             $revision->setStatus(ArcanistDifferentialRevisionStatus::ABANDONED);
             break;
         case DifferentialAction::ACTION_ACCEPT:
             if ($actor_is_author && !$allow_self_accept) {
                 throw new Exception('You can not accept your own revision.');
             }
             if ($revision_status != ArcanistDifferentialRevisionStatus::NEEDS_REVIEW && $revision_status != ArcanistDifferentialRevisionStatus::NEEDS_REVISION) {
                 switch ($revision_status) {
                     case ArcanistDifferentialRevisionStatus::ACCEPTED:
                         throw new DifferentialActionHasNoEffectException("You can not accept this revision because someone else " . "already accepted it.");
                     case ArcanistDifferentialRevisionStatus::ABANDONED:
                         throw new DifferentialActionHasNoEffectException("You can not accept this revision because it has been " . "abandoned.");
                     case ArcanistDifferentialRevisionStatus::CLOSED:
                         throw new DifferentialActionHasNoEffectException("You can not accept this revision because it has already " . "been closed.");
                     default:
                         throw new Exception("Unexpected revision state '{$revision_status}'!");
                 }
             }
             $revision->setStatus(ArcanistDifferentialRevisionStatus::ACCEPTED);
             if (!isset($reviewer_phids[$actor_phid])) {
                 DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array(), $add = array($actor_phid), $actor_phid);
             }
             break;
         case DifferentialAction::ACTION_REQUEST:
             if (!$actor_is_author) {
                 throw new Exception('You must own a revision to request review.');
             }
             switch ($revision_status) {
                 case ArcanistDifferentialRevisionStatus::ACCEPTED:
                 case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
                     $revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
                     break;
                 case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
                     throw new DifferentialActionHasNoEffectException("You can not request review of this revision because it has " . "been abandoned.");
                 case ArcanistDifferentialRevisionStatus::ABANDONED:
                     throw new DifferentialActionHasNoEffectException("You can not request review of this revision because it has " . "been abandoned.");
                 case ArcanistDifferentialRevisionStatus::CLOSED:
                     throw new DifferentialActionHasNoEffectException("You can not request review of this revision because it has " . "already been closed.");
                 default:
                     throw new Exception("Unexpected revision state '{$revision_status}'!");
             }
             list($added_reviewers, $ignored) = $this->alterReviewers();
             if ($added_reviewers) {
                 $key = DifferentialComment::METADATA_ADDED_REVIEWERS;
                 $metadata[$key] = $added_reviewers;
             }
             break;
         case DifferentialAction::ACTION_REJECT:
             if ($actor_is_author) {
                 throw new Exception('You can not request changes to your own revision.');
             }
             switch ($revision_status) {
                 case ArcanistDifferentialRevisionStatus::ACCEPTED:
                 case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
                 case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
                     // NOTE: We allow you to reject an already-rejected revision
                     // because it doesn't create any ambiguity and avoids a rather
                     // needless dialog.
                     break;
                 case ArcanistDifferentialRevisionStatus::ABANDONED:
                     throw new DifferentialActionHasNoEffectException("You can not request changes to this revision because it has " . "been abandoned.");
                 case ArcanistDifferentialRevisionStatus::CLOSED:
                     throw new DifferentialActionHasNoEffectException("You can not request changes to this revision because it has " . "already been closed.");
                 default:
                     throw new Exception("Unexpected revision state '{$revision_status}'!");
             }
             if (!isset($reviewer_phids[$actor_phid])) {
                 DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array(), $add = array($actor_phid), $actor_phid);
             }
             $revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVISION);
             break;
         case DifferentialAction::ACTION_RETHINK:
             if (!$actor_is_author) {
                 throw new Exception("You can not plan changes to somebody else's revision");
             }
             switch ($revision_status) {
                 case ArcanistDifferentialRevisionStatus::ACCEPTED:
                 case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
                 case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
                     break;
                 case ArcanistDifferentialRevisionStatus::ABANDONED:
                     throw new DifferentialActionHasNoEffectException("You can not plan changes to this revision because it has " . "been abandoned.");
                 case ArcanistDifferentialRevisionStatus::CLOSED:
                     throw new DifferentialActionHasNoEffectException("You can not plan changes to this revision because it has " . "already been closed.");
                 default:
                     throw new Exception("Unexpected revision state '{$revision_status}'!");
             }
             $revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVISION);
             break;
         case DifferentialAction::ACTION_RECLAIM:
             if (!$actor_is_author) {
                 throw new Exception('You can not reclaim a revision you do not own.');
             }
             if ($revision_status != ArcanistDifferentialRevisionStatus::ABANDONED) {
                 throw new DifferentialActionHasNoEffectException("You can not reclaim this revision because it is not abandoned.");
             }
             $revision->setStatus(ArcanistDifferentialRevisionStatus::NEEDS_REVIEW);
             break;
         case DifferentialAction::ACTION_CLOSE:
             // NOTE: The daemons can mark things closed from any state. We treat
             // them as completely authoritative.
             if (!$this->isDaemonWorkflow) {
                 if (!$actor_is_author) {
                     throw new Exception("You can not mark a revision you don't own as closed.");
                 }
                 $status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
                 $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
                 if ($revision_status == $status_closed) {
                     throw new DifferentialActionHasNoEffectException("You can not mark this revision as closed because it has " . "already been marked as closed.");
                 }
                 if ($revision_status != $status_accepted) {
                     throw new DifferentialActionHasNoEffectException("You can not mark this revision as closed because it is " . "has not been accepted.");
                 }
             }
             if (!$revision->getDateCommitted()) {
                 $revision->setDateCommitted(time());
             }
             $revision->setStatus(ArcanistDifferentialRevisionStatus::CLOSED);
             break;
         case DifferentialAction::ACTION_ADDREVIEWERS:
             list($added_reviewers, $ignored) = $this->alterReviewers();
             if ($added_reviewers) {
                 $key = DifferentialComment::METADATA_ADDED_REVIEWERS;
                 $metadata[$key] = $added_reviewers;
             } else {
                 $user_tried_to_add = count($this->getAddedReviewers());
                 if ($user_tried_to_add == 0) {
                     throw new DifferentialActionHasNoEffectException("You can not add reviewers, because you did not specify any " . "reviewers.");
                 } else {
                     if ($user_tried_to_add == 1) {
                         throw new DifferentialActionHasNoEffectException("You can not add that reviewer, because they are already an " . "author or reviewer.");
                     } else {
                         throw new DifferentialActionHasNoEffectException("You can not add those reviewers, because they are all already " . "authors or reviewers.");
                     }
                 }
             }
             break;
         case DifferentialAction::ACTION_ADDCCS:
             $added_ccs = $this->getAddedCCs();
             $user_tried_to_add = count($added_ccs);
             $added_ccs = $this->filterAddedCCs($added_ccs);
             if ($added_ccs) {
                 foreach ($added_ccs as $cc) {
                     DifferentialRevisionEditor::addCC($revision, $cc, $this->actorPHID);
                 }
                 $key = DifferentialComment::METADATA_ADDED_CCS;
                 $metadata[$key] = $added_ccs;
             } else {
                 if ($user_tried_to_add == 0) {
                     throw new DifferentialActionHasNoEffectException("You can not add CCs, because you did not specify any " . "CCs.");
                 } else {
                     if ($user_tried_to_add == 1) {
                         throw new DifferentialActionHasNoEffectException("You can not add that CC, because they are already an " . "author, reviewer or CC.");
                     } else {
                         throw new DifferentialActionHasNoEffectException("You can not add those CCs, because they are all already " . "authors, reviewers or CCs.");
                     }
                 }
             }
             break;
         case DifferentialAction::ACTION_CLAIM:
             if ($actor_is_author) {
                 throw new Exception("You can not commandeer your own revision.");
             }
             switch ($revision_status) {
                 case ArcanistDifferentialRevisionStatus::CLOSED:
                     throw new DifferentialActionHasNoEffectException("You can not commandeer this revision because it has " . "already been closed.");
                     break;
             }
             $this->setAddedReviewers(array($revision->getAuthorPHID()));
             $this->setRemovedReviewers(array($actor_phid));
             // NOTE: Set the new author PHID before calling addReviewers(), since it
             // doesn't permit the author to become a reviewer.
             $revision->setAuthorPHID($actor_phid);
             list($added_reviewers, $removed_reviewers) = $this->alterReviewers();
             if ($added_reviewers) {
                 $key = DifferentialComment::METADATA_ADDED_REVIEWERS;
                 $metadata[$key] = $added_reviewers;
             }
             if ($removed_reviewers) {
                 $key = DifferentialComment::METADATA_REMOVED_REVIEWERS;
                 $metadata[$key] = $removed_reviewers;
             }
             break;
         default:
             throw new Exception('Unsupported action.');
     }
     // Update information about reviewer in charge.
     if ($action == DifferentialAction::ACTION_ACCEPT || $action == DifferentialAction::ACTION_REJECT) {
         $revision->setLastReviewerPHID($actor_phid);
     }
     // TODO: Call beginReadLocking() prior to loading the revision.
     $revision->openTransaction();
     // Always save the revision (even if we didn't actually change any of its
     // properties) so that it jumps to the top of the revision list when sorted
     // by "updated". Notably, this allows "ping" comments to push it to the
     // top of the action list.
     $revision->save();
     if ($action != DifferentialAction::ACTION_RESIGN) {
         DifferentialRevisionEditor::addCC($revision, $this->actorPHID, $this->actorPHID);
     }
     $comment = id(new DifferentialComment())->setAuthorPHID($this->actorPHID)->setRevisionID($revision->getID())->setAction($action)->setContent((string) $this->message)->setMetadata($metadata);
     if ($this->contentSource) {
         $comment->setContentSource($this->contentSource);
     }
     $comment->save();
     $changesets = array();
     if ($inline_comments) {
         $load_ids = mpull($inline_comments, 'getChangesetID');
         if ($load_ids) {
             $load_ids = array_unique($load_ids);
             $changesets = id(new DifferentialChangeset())->loadAllWhere('id in (%Ld)', $load_ids);
         }
         foreach ($inline_comments as $inline) {
             $inline->setCommentID($comment->getID());
             $inline->save();
         }
     }
     // Find any "@mentions" in the comment blocks.
     $content_blocks = array($comment->getContent());
     foreach ($inline_comments as $inline) {
         $content_blocks[] = $inline->getContent();
     }
     $mention_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions($content_blocks);
     if ($mention_ccs) {
         $mention_ccs = $this->filterAddedCCs($mention_ccs);
         if ($mention_ccs) {
             $metadata = $comment->getMetadata();
             $metacc = idx($metadata, DifferentialComment::METADATA_ADDED_CCS, array());
             foreach ($mention_ccs as $cc_phid) {
                 DifferentialRevisionEditor::addCC($revision, $cc_phid, $this->actorPHID);
                 $metacc[] = $cc_phid;
             }
             $metadata[DifferentialComment::METADATA_ADDED_CCS] = $metacc;
             $comment->setMetadata($metadata);
             $comment->save();
         }
     }
     $revision->saveTransaction();
     $phids = array($this->actorPHID);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $actor_handle = $handles[$this->actorPHID];
     $xherald_header = HeraldTranscript::loadXHeraldRulesHeader($revision->getPHID());
     id(new DifferentialCommentMail($revision, $actor_handle, $comment, $changesets, $inline_comments))->setToPHIDs(array_merge($revision->getReviewers(), array($revision->getAuthorPHID())))->setCCPHIDs($revision->getCCPHIDs())->setChangedByCommit($this->getChangedByCommit())->setXHeraldRulesHeader($xherald_header)->setParentMessageID($this->parentMessageID)->send();
     $event_data = array('revision_id' => $revision->getID(), 'revision_phid' => $revision->getPHID(), 'revision_name' => $revision->getTitle(), 'revision_author_phid' => $revision->getAuthorPHID(), 'action' => $comment->getAction(), 'feedback_content' => $comment->getContent(), 'actor_phid' => $this->actorPHID);
     id(new PhabricatorTimelineEvent('difx', $event_data))->recordEvent();
     // TODO: Move to a daemon?
     id(new PhabricatorFeedStoryPublisher())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)->setStoryData($event_data)->setStoryTime(time())->setStoryAuthorPHID($this->actorPHID)->setRelatedPHIDs(array($revision->getPHID(), $this->actorPHID, $revision->getAuthorPHID()))->setPrimaryObjectPHID($revision->getPHID())->setSubscribedPHIDs(array_merge(array($revision->getAuthorPHID()), $revision->getReviewers(), $revision->getCCPHIDs()))->publish();
     // TODO: Move to a daemon?
     PhabricatorSearchDifferentialIndexer::indexRevision($revision);
     return $comment;
 }
 public function save()
 {
     $revision = $this->revision;
     $action = $this->action;
     $actor_phid = $this->actorPHID;
     $actor_is_author = $actor_phid == $revision->getAuthorPHID();
     $revision_status = $revision->getStatus();
     $revision->loadRelationships();
     $reviewer_phids = $revision->getReviewers();
     if ($reviewer_phids) {
         $reviewer_phids = array_combine($reviewer_phids, $reviewer_phids);
     }
     $metadata = array();
     switch ($action) {
         case DifferentialAction::ACTION_COMMENT:
             break;
         case DifferentialAction::ACTION_RESIGN:
             if ($actor_is_author) {
                 throw new Exception('You can not resign from your own revision!');
             }
             if (isset($reviewer_phids[$actor_phid])) {
                 DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array($actor_phid), $add = array(), $actor_phid);
             }
             break;
         case DifferentialAction::ACTION_ABANDON:
             if (!$actor_is_author) {
                 throw new Exception('You can only abandon your revisions.');
             }
             if ($revision_status == DifferentialRevisionStatus::COMMITTED) {
                 throw new Exception('You can not abandon a committed revision.');
             }
             if ($revision_status == DifferentialRevisionStatus::ABANDONED) {
                 $action = DifferentialAction::ACTION_COMMENT;
                 break;
             }
             $revision->setStatus(DifferentialRevisionStatus::ABANDONED)->save();
             break;
         case DifferentialAction::ACTION_ACCEPT:
             if ($actor_is_author) {
                 throw new Exception('You can not accept your own revision.');
             }
             if ($revision_status != DifferentialRevisionStatus::NEEDS_REVIEW && $revision_status != DifferentialRevisionStatus::NEEDS_REVISION) {
                 $action = DifferentialAction::ACTION_COMMENT;
                 break;
             }
             $revision->setStatus(DifferentialRevisionStatus::ACCEPTED)->save();
             if (!isset($reviewer_phids[$actor_phid])) {
                 DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array(), $add = array($actor_phid), $actor_phid);
             }
             break;
         case DifferentialAction::ACTION_REQUEST:
             if (!$actor_is_author) {
                 throw new Exception('You must own a revision to request review.');
             }
             if ($revision_status != DifferentialRevisionStatus::NEEDS_REVISION && $revision_status != DifferentialRevisionStatus::ACCEPTED) {
                 $action = DifferentialAction::ACTION_COMMENT;
                 break;
             }
             $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW)->save();
             break;
         case DifferentialAction::ACTION_REJECT:
             if ($actor_is_author) {
                 throw new Exception('You can not request changes to your own revision.');
             }
             if ($revision_status != DifferentialRevisionStatus::NEEDS_REVIEW && $revision_status != DifferentialRevisionStatus::ACCEPTED) {
                 $action = DifferentialAction::ACTION_COMMENT;
                 break;
             }
             if (!isset($reviewer_phids[$actor_phid])) {
                 DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array(), $add = array($actor_phid), $actor_phid);
             }
             $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVISION)->save();
             break;
         case DifferentialAction::ACTION_RETHINK:
             if (!$actor_is_author) {
                 throw new Exception("You can not plan changes to somebody else's revision");
             }
             if ($revision_status != DifferentialRevisionStatus::NEEDS_REVIEW && $revision_status != DifferentialRevisionStatus::ACCEPTED) {
                 $action = DifferentialAction::ACTION_COMMENT;
                 break;
             }
             $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVISION)->save();
             break;
         case DifferentialAction::ACTION_RECLAIM:
             if (!$actor_is_author) {
                 throw new Exception('You can not reclaim a revision you do not own.');
             }
             if ($revision_status != DifferentialRevisionStatus::ABANDONED) {
                 $action = DifferentialAction::ACTION_COMMENT;
                 break;
             }
             $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW)->save();
             break;
         case DifferentialAction::ACTION_COMMIT:
             if (!$actor_is_author) {
                 throw new Exception('You can not commit a revision you do not own.');
             }
             $revision->setStatus(DifferentialRevisionStatus::COMMITTED)->save();
             break;
         case DifferentialAction::ACTION_ADDREVIEWERS:
             $added_reviewers = $this->getAddedReviewers();
             foreach ($added_reviewers as $k => $user_phid) {
                 if ($user_phid == $revision->getAuthorPHID()) {
                     unset($added_reviewers[$k]);
                 }
                 if (!empty($reviewer_phids[$user_phid])) {
                     unset($added_reviewers[$k]);
                 }
             }
             $added_reviewers = array_unique($added_reviewers);
             if ($added_reviewers) {
                 DifferentialRevisionEditor::alterReviewers($revision, $reviewer_phids, $rem = array(), $add = $added_reviewers, $actor_phid);
                 $key = DifferentialComment::METADATA_ADDED_REVIEWERS;
                 $metadata[$key] = $added_reviewers;
             } else {
                 $action = DifferentialAction::ACTION_COMMENT;
             }
             break;
         case DifferentialAction::ACTION_ADDCCS:
             $added_ccs = $this->getAddedCCs();
             $current_ccs = $revision->getCCPHIDs();
             if ($current_ccs) {
                 $current_ccs = array_fill_keys($current_ccs, true);
                 foreach ($added_ccs as $k => $cc) {
                     if (isset($current_ccs[$cc])) {
                         unset($added_ccs[$k]);
                     }
                 }
             }
             if ($added_ccs) {
                 foreach ($added_ccs as $cc) {
                     DifferentialRevisionEditor::addCC($revision, $cc, $this->actorPHID);
                 }
                 $key = DifferentialComment::METADATA_ADDED_CCS;
                 $metadata[$key] = $added_ccs;
             } else {
                 $action = DifferentialAction::ACTION_COMMENT;
             }
             break;
         default:
             throw new Exception('Unsupported action.');
     }
     if ($this->addCC) {
         DifferentialRevisionEditor::addCC($revision, $this->actorPHID, $this->actorPHID);
     }
     $inline_comments = array();
     if ($this->attachInlineComments) {
         $inline_comments = id(new DifferentialInlineComment())->loadAllWhere('authorPHID = %s AND revisionID = %d AND commentID IS NULL', $this->actorPHID, $revision->getID());
     }
     $comment = id(new DifferentialComment())->setAuthorPHID($this->actorPHID)->setRevisionID($revision->getID())->setAction($action)->setContent((string) $this->message)->setMetadata($metadata)->save();
     $changesets = array();
     if ($inline_comments) {
         $load_ids = mpull($inline_comments, 'getChangesetID');
         if ($load_ids) {
             $load_ids = array_unique($load_ids);
             $changesets = id(new DifferentialChangeset())->loadAllWhere('id in (%Ld)', $load_ids);
         }
         foreach ($inline_comments as $inline) {
             $inline->setCommentID($comment->getID());
             $inline->save();
         }
     }
     // Find any "@mentions" in the comment blocks.
     $content_blocks = array($comment->getContent());
     foreach ($inline_comments as $inline) {
         $content_blocks[] = $inline->getContent();
     }
     $mention_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions($content_blocks);
     if ($mention_ccs) {
         $current_ccs = $revision->getCCPHIDs();
         if ($current_ccs) {
             $current_ccs = array_fill_keys($current_ccs, true);
             foreach ($mention_ccs as $key => $mention_cc) {
                 if (isset($current_ccs[$mention_cc])) {
                     unset($mention_ccs);
                 }
             }
         }
         if ($mention_ccs) {
             $metadata = $comment->getMetadata();
             $metacc = idx($metadata, DifferentialComment::METADATA_ADDED_CCS, array());
             foreach ($mention_ccs as $cc_phid) {
                 DifferentialRevisionEditor::addCC($revision, $cc_phid, $this->actorPHID);
                 $metacc[] = $cc_phid;
             }
             $metadata[DifferentialComment::METADATA_ADDED_CCS] = $metacc;
             $comment->setMetadata($metadata);
             $comment->save();
         }
     }
     $phids = array($this->actorPHID);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $actor_handle = $handles[$this->actorPHID];
     $xherald_header = HeraldTranscript::loadXHeraldRulesHeader($revision->getPHID());
     id(new DifferentialCommentMail($revision, $actor_handle, $comment, $changesets, $inline_comments))->setToPHIDs(array_merge($revision->getReviewers(), array($revision->getAuthorPHID())))->setCCPHIDs($revision->getCCPHIDs())->setChangedByCommit($this->getChangedByCommit())->setXHeraldRulesHeader($xherald_header)->setParentMessageID($this->parentMessageID)->send();
     $event_data = array('revision_id' => $revision->getID(), 'revision_phid' => $revision->getPHID(), 'revision_name' => $revision->getTitle(), 'revision_author_phid' => $revision->getAuthorPHID(), 'action' => $comment->getAction(), 'feedback_content' => $comment->getContent(), 'actor_phid' => $this->actorPHID);
     id(new PhabricatorTimelineEvent('difx', $event_data))->recordEvent();
     // TODO: Move to a daemon?
     id(new PhabricatorFeedStoryPublisher())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_DIFFERENTIAL)->setStoryData($event_data)->setStoryTime(time())->setStoryAuthorPHID($this->actorPHID)->setRelatedPHIDs(array($revision->getPHID(), $this->actorPHID, $revision->getAuthorPHID()))->publish();
     // TODO: Move to a daemon?
     PhabricatorSearchDifferentialIndexer::indexRevision($revision);
     return $comment;
 }