public static function indexRevision(DifferentialRevision $rev)
 {
     $doc = new PhabricatorSearchAbstractDocument();
     $doc->setPHID($rev->getPHID());
     $doc->setDocumentType(PhabricatorPHIDConstants::PHID_TYPE_DREV);
     $doc->setDocumentTitle($rev->getTitle());
     $doc->setDocumentCreated($rev->getDateCreated());
     $doc->setDocumentModified($rev->getDateModified());
     $doc->addField(PhabricatorSearchField::FIELD_BODY, $rev->getSummary());
     $doc->addField(PhabricatorSearchField::FIELD_TEST_PLAN, $rev->getTestPlan());
     $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR, $rev->getAuthorPHID(), PhabricatorPHIDConstants::PHID_TYPE_USER, $rev->getDateCreated());
     if ($rev->getStatus() != ArcanistDifferentialRevisionStatus::CLOSED && $rev->getStatus() != ArcanistDifferentialRevisionStatus::ABANDONED) {
         $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_OPEN, $rev->getPHID(), PhabricatorPHIDConstants::PHID_TYPE_DREV, time());
     }
     $comments = id(new DifferentialComment())->loadAllWhere('revisionID = %d', $rev->getID());
     $inlines = id(new DifferentialInlineComment())->loadAllWhere('revisionID = %d AND commentID IS NOT NULL', $rev->getID());
     $touches = array();
     foreach (array_merge($comments, $inlines) as $comment) {
         if (strlen($comment->getContent())) {
             $doc->addField(PhabricatorSearchField::FIELD_COMMENT, $comment->getContent());
         }
         $author = $comment->getAuthorPHID();
         $touches[$author] = $comment->getDateCreated();
     }
     foreach ($touches as $touch => $time) {
         $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_TOUCH, $touch, PhabricatorPHIDConstants::PHID_TYPE_USER, $time);
     }
     $rev->loadRelationships();
     // If a revision needs review, the owners are the reviewers. Otherwise, the
     // owner is the author (e.g., accepted, rejected, closed).
     if ($rev->getStatus() == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
         foreach ($rev->getReviewers() as $phid) {
             $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_OWNER, $phid, PhabricatorPHIDConstants::PHID_TYPE_USER, $rev->getDateModified());
             // Bogus timestamp.
         }
     } else {
         $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_OWNER, $rev->getAuthorPHID(), PhabricatorPHIDConstants::PHID_TYPE_USER, $rev->getDateCreated());
     }
     $ccphids = $rev->getCCPHIDs();
     $handles = id(new PhabricatorObjectHandleData($ccphids))->loadHandles();
     foreach ($handles as $phid => $handle) {
         $doc->addRelationship(PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER, $phid, $handle->getType(), $rev->getDateModified());
         // Bogus timestamp.
     }
     self::reindexAbstractDocument($doc);
 }
 private function unsubscribeUser(DifferentialRevision $revision, PhabricatorUser $user)
 {
     $revision->loadRelationships();
     DifferentialRevisionEditor::removeCCAndUpdateRevision($revision, $user->getPHID(), $user->getPHID());
 }
 public function __construct(DifferentialRevision $revision, DifferentialDiff $diff)
 {
     $revision->loadRelationships();
     $this->revision = $revision;
     $this->diff = $diff;
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     if (!$this->id) {
         $this->id = $request->getInt('revisionID');
     }
     if ($this->id) {
         $revision = id(new DifferentialRevision())->load($this->id);
         if (!$revision) {
             return new Aphront404Response();
         }
     } else {
         $revision = new DifferentialRevision();
     }
     $revision->loadRelationships();
     $aux_fields = $this->loadAuxiliaryFields($revision);
     $diff_id = $request->getInt('diffID');
     if ($diff_id) {
         $diff = id(new DifferentialDiff())->load($diff_id);
         if (!$diff) {
             return new Aphront404Response();
         }
         if ($diff->getRevisionID()) {
             // TODO: Redirect?
             throw new Exception("This diff is already attached to a revision!");
         }
     } else {
         $diff = null;
     }
     $errors = array();
     if ($request->isFormPost() && !$request->getStr('viaDiffView')) {
         $user_phid = $request->getUser()->getPHID();
         foreach ($aux_fields as $aux_field) {
             $aux_field->setValueFromRequest($request);
             try {
                 $aux_field->validateField();
             } catch (DifferentialFieldValidationException $ex) {
                 $errors[] = $ex->getMessage();
             }
         }
         if (!$errors) {
             $editor = new DifferentialRevisionEditor($revision, $user_phid);
             if ($diff) {
                 $editor->addDiff($diff, $request->getStr('comments'));
             }
             $editor->setAuxiliaryFields($aux_fields);
             $editor->save();
             return id(new AphrontRedirectResponse())->setURI('/D' . $revision->getID());
         }
     }
     $aux_phids = array();
     foreach ($aux_fields as $key => $aux_field) {
         $aux_phids[$key] = $aux_field->getRequiredHandlePHIDsForRevisionEdit();
     }
     $phids = array_mergev($aux_phids);
     $phids = array_unique($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     foreach ($aux_fields as $key => $aux_field) {
         $aux_field->setHandles(array_select_keys($handles, $aux_phids[$key]));
     }
     $form = new AphrontFormView();
     $form->setUser($request->getUser());
     if ($diff) {
         $form->addHiddenInput('diffID', $diff->getID());
     }
     if ($revision->getID()) {
         $form->setAction('/differential/revision/edit/' . $revision->getID() . '/');
     } else {
         $form->setAction('/differential/revision/edit/');
     }
     $error_view = null;
     if ($errors) {
         $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors);
     }
     if ($diff && $revision->getID()) {
         $form->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Comments')->setName('comments')->setCaption("Explain what's new in this diff.")->setValue($request->getStr('comments')))->appendChild(id(new AphrontFormSubmitControl())->setValue('Save'))->appendChild(id(new AphrontFormDividerControl()));
     }
     foreach ($aux_fields as $aux_field) {
         $control = $aux_field->renderEditControl();
         if ($control) {
             $form->appendChild($control);
         }
     }
     $submit = id(new AphrontFormSubmitControl())->setValue('Save');
     if ($diff) {
         $submit->addCancelButton('/differential/diff/' . $diff->getID() . '/');
     } else {
         $submit->addCancelButton('/D' . $revision->getID());
     }
     $form->appendChild($submit);
     $panel = new AphrontPanelView();
     if ($revision->getID()) {
         if ($diff) {
             $panel->setHeader('Update Differential Revision');
         } else {
             $panel->setHeader('Edit Differential Revision');
         }
     } else {
         $panel->setHeader('Create New Differential Revision');
     }
     $panel->appendChild($form);
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Edit Differential Revision'));
 }
 private static function alterRelationships(DifferentialRevision $revision, array $stable_phids, array $rem_phids, array $add_phids, $reason_phid, $relation_type)
 {
     $rem_map = array_fill_keys($rem_phids, true);
     $add_map = array_fill_keys($add_phids, true);
     $seq_map = array_values($stable_phids);
     $seq_map = array_flip($seq_map);
     foreach ($rem_map as $phid => $ignored) {
         if (!isset($seq_map[$phid])) {
             $seq_map[$phid] = count($seq_map);
         }
     }
     foreach ($add_map as $phid => $ignored) {
         if (!isset($seq_map[$phid])) {
             $seq_map[$phid] = count($seq_map);
         }
     }
     $raw = $revision->getRawRelations($relation_type);
     $raw = ipull($raw, null, 'objectPHID');
     $sequence = count($seq_map);
     foreach ($raw as $phid => $ignored) {
         if (isset($seq_map[$phid])) {
             $raw[$phid]['sequence'] = $seq_map[$phid];
         } else {
             $raw[$phid]['sequence'] = $sequence++;
         }
     }
     $raw = isort($raw, 'sequence');
     foreach ($raw as $phid => $ignored) {
         if (isset($rem_map[$phid])) {
             unset($raw[$phid]);
         }
     }
     foreach ($add_phids as $add) {
         $reason = is_array($reason_phid) ? idx($reason_phid, $add) : $reason_phid;
         $raw[$add] = array('objectPHID' => $add, 'sequence' => idx($seq_map, $add, $sequence++), 'reasonPHID' => $reason);
     }
     $conn_w = $revision->establishConnection('w');
     $sql = array();
     foreach ($raw as $relation) {
         $sql[] = qsprintf($conn_w, '(%d, %s, %s, %d, %s)', $revision->getID(), $relation_type, $relation['objectPHID'], $relation['sequence'], $relation['reasonPHID']);
     }
     $conn_w->openTransaction();
     queryfx($conn_w, 'DELETE FROM %T WHERE revisionID = %d AND relation = %s', DifferentialRevision::RELATIONSHIP_TABLE, $revision->getID(), $relation_type);
     if ($sql) {
         queryfx($conn_w, 'INSERT INTO %T
         (revisionID, relation, objectPHID, sequence, reasonPHID)
       VALUES %Q', DifferentialRevision::RELATIONSHIP_TABLE, implode(', ', $sql));
     }
     $conn_w->saveTransaction();
     $revision->loadRelationships();
 }
 protected function execute(ConduitAPIRequest $request)
 {
     $id = $request->getValue('revision_id');
     if ($id) {
         $revision = id(new DifferentialRevision())->load($id);
         if (!$revision) {
             throw new ConduitException('ERR_NOT_FOUND');
         }
     } else {
         $revision = new DifferentialRevision();
     }
     $revision->loadRelationships();
     $is_edit = $request->getValue('edit');
     $is_create = $is_edit == 'create';
     $aux_fields = DifferentialFieldSelector::newSelector()->getFieldSpecifications();
     foreach ($aux_fields as $key => $aux_field) {
         $aux_field->setRevision($revision);
         if (!$aux_field->shouldAppearOnCommitMessage()) {
             unset($aux_fields[$key]);
         }
     }
     $aux_fields = DifferentialAuxiliaryField::loadFromStorage($revision, $aux_fields);
     $aux_fields = mpull($aux_fields, null, 'getCommitMessageKey');
     if ($is_edit) {
         $fields = $request->getValue('fields');
         if (!is_array($fields)) {
             $fields = array();
         }
         foreach ($fields as $field => $value) {
             $aux_field = idx($aux_fields, $field);
             if (!$aux_field) {
                 throw new Exception("Commit message includes field '{$field}' which does not " . "correspond to any configured field.");
             }
             if ($is_create || $aux_field->shouldOverwriteWhenCommitMessageIsEdited()) {
                 $aux_field->setValueFromParsedCommitMessage($value);
             }
         }
     }
     $aux_phids = array();
     foreach ($aux_fields as $field_key => $field) {
         $aux_phids[$field_key] = $field->getRequiredHandlePHIDsForCommitMessage();
     }
     $phids = array_unique(array_mergev($aux_phids));
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     foreach ($aux_fields as $field_key => $field) {
         $field->setHandles(array_select_keys($handles, $aux_phids[$field_key]));
     }
     $commit_message = array();
     foreach ($aux_fields as $field_key => $field) {
         $value = $field->renderValueForCommitMessage($is_edit);
         $label = $field->renderLabelForCommitMessage();
         if (!strlen($value)) {
             if ($field_key === 'title') {
                 $commit_message[] = '<<Enter Revision Title>>';
             } else {
                 if ($field->shouldAppearOnCommitMessageTemplate() && $is_edit) {
                     $commit_message[] = $label . ': ';
                 }
             }
         } else {
             if ($field_key === 'title') {
                 $commit_message[] = $value;
             } else {
                 $value = str_replace(array("\r\n", "\r"), array("\n", "\n"), $value);
                 if (strpos($value, "\n") !== false || substr($value, 0, 2) === '  ') {
                     $commit_message[] = "{$label}:\n{$value}";
                 } else {
                     $commit_message[] = "{$label}: {$value}";
                 }
             }
         }
     }
     $commit_message = implode("\n\n", $commit_message);
     return $commit_message;
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     if (!$this->id) {
         $this->id = $request->getInt('revisionID');
     }
     if ($this->id) {
         $revision = id(new DifferentialRevision())->load($this->id);
         if (!$revision) {
             return new Aphront404Response();
         }
     } else {
         $revision = new DifferentialRevision();
     }
     $diff_id = $request->getInt('diffID');
     if ($diff_id) {
         $diff = id(new DifferentialDiff())->load($diff_id);
         if (!$diff) {
             return new Aphront404Response();
         }
         if ($diff->getRevisionID()) {
             // TODO: Redirect?
             throw new Exception("This diff is already attached to a revision!");
         }
     } else {
         $diff = null;
     }
     $e_title = true;
     $e_testplan = true;
     $e_reviewers = null;
     $errors = array();
     $revision->loadRelationships();
     if ($request->isFormPost() && !$request->getStr('viaDiffView')) {
         $revision->setTitle($request->getStr('title'));
         $revision->setSummary($request->getStr('summary'));
         $revision->setTestPlan($request->getStr('testplan'));
         $revision->setBlameRevision($request->getStr('blame'));
         $revision->setRevertPlan($request->getStr('revert'));
         if (!strlen(trim($revision->getTitle()))) {
             $errors[] = 'You must provide a title.';
             $e_title = 'Required';
         } else {
             $e_title = null;
         }
         if (!strlen(trim($revision->getTestPlan()))) {
             $errors[] = 'You must provide a test plan.';
             $e_testplan = 'Required';
         } else {
             $e_testplan = null;
         }
         $user_phid = $request->getUser()->getPHID();
         if (in_array($user_phid, $request->getArr('reviewers'))) {
             $errors[] = 'You may not review your own revision.';
             $e_reviewers = 'Invalid';
         }
         if (!$errors) {
             $editor = new DifferentialRevisionEditor($revision, $user_phid);
             if ($diff) {
                 $editor->addDiff($diff, $request->getStr('comments'));
             }
             $editor->setCCPHIDs($request->getArr('cc'));
             $editor->setReviewers($request->getArr('reviewers'));
             $editor->save();
             return id(new AphrontRedirectResponse())->setURI('/D' . $revision->getID());
         }
         $reviewer_phids = $request->getArr('reviewers');
         $cc_phids = $request->getArr('cc');
     } else {
         $reviewer_phids = $revision->getReviewers();
         $cc_phids = $revision->getCCPHIDs();
     }
     $phids = array_merge($reviewer_phids, $cc_phids);
     $phids = array_unique($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $handles = mpull($handles, 'getFullName', 'getPHID');
     $reviewer_map = array_select_keys($handles, $reviewer_phids);
     $cc_map = array_select_keys($handles, $cc_phids);
     $form = new AphrontFormView();
     $form->setUser($request->getUser());
     if ($diff) {
         $form->addHiddenInput('diffID', $diff->getID());
     }
     if ($revision->getID()) {
         $form->setAction('/differential/revision/edit/' . $revision->getID() . '/');
     } else {
         $form->setAction('/differential/revision/edit/');
     }
     $error_view = null;
     if ($errors) {
         $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors);
     }
     if ($diff && $revision->getID()) {
         $form->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Comments')->setName('comments')->setCaption("Explain what's new in this diff.")->setValue($request->getStr('comments')))->appendChild(id(new AphrontFormSubmitControl())->setValue('Save'))->appendChild(id(new AphrontFormDividerControl()));
     }
     $form->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Title')->setName('title')->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)->setValue($revision->getTitle())->setError($e_title))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Summary')->setName('summary')->setValue($revision->getSummary()))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Test Plan')->setName('testplan')->setValue($revision->getTestPlan())->setError($e_testplan))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('Reviewers')->setName('reviewers')->setDatasource('/typeahead/common/users/')->setError($e_reviewers)->setValue($reviewer_map))->appendChild(id(new AphrontFormTokenizerControl())->setLabel('CC')->setName('cc')->setDatasource('/typeahead/common/mailable/')->setValue($cc_map))->appendChild(id(new AphrontFormTextControl())->setLabel('Blame Revision')->setName('blame')->setValue($revision->getBlameRevision())->setCaption('Revision which broke the stuff which this ' . 'change fixes.'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Revert Plan')->setName('revert')->setValue($revision->getRevertPlan())->setCaption('Special steps required to safely revert this change.'));
     $submit = id(new AphrontFormSubmitControl())->setValue('Save');
     if ($diff) {
         $submit->addCancelButton('/differential/diff/' . $diff->getID() . '/');
     } else {
         $submit->addCancelButton('/D' . $revision->getID());
     }
     $form->appendChild($submit);
     $panel = new AphrontPanelView();
     if ($revision->getID()) {
         if ($diff) {
             $panel->setHeader('Update Differential Revision');
         } else {
             $panel->setHeader('Edit Differential Revision');
         }
     } else {
         $panel->setHeader('Create New Differential Revision');
     }
     $panel->appendChild($form);
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Edit Differential Revision'));
 }