DifferentialRevisionEditor
This commit is contained in:
		@@ -64,6 +64,7 @@ phutil_register_library_map(array(
 | 
			
		||||
    'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
 | 
			
		||||
    'ConduitException' => 'applications/conduit/protocol/exception',
 | 
			
		||||
    'DifferentialAction' => 'applications/differential/constants/action',
 | 
			
		||||
    'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
 | 
			
		||||
    'DifferentialChangeType' => 'applications/differential/constants/changetype',
 | 
			
		||||
    'DifferentialChangeset' => 'applications/differential/storage/changeset',
 | 
			
		||||
    'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview',
 | 
			
		||||
@@ -73,14 +74,20 @@ phutil_register_library_map(array(
 | 
			
		||||
    'DifferentialController' => 'applications/differential/controller/base',
 | 
			
		||||
    'DifferentialDAO' => 'applications/differential/storage/base',
 | 
			
		||||
    'DifferentialDiff' => 'applications/differential/storage/diff',
 | 
			
		||||
    'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent',
 | 
			
		||||
    'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty',
 | 
			
		||||
    'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents',
 | 
			
		||||
    'DifferentialDiffViewController' => 'applications/differential/controller/diffview',
 | 
			
		||||
    'DifferentialFeedbackMail' => 'applications/differential/mail/feedback',
 | 
			
		||||
    'DifferentialHunk' => 'applications/differential/storage/hunk',
 | 
			
		||||
    'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
 | 
			
		||||
    'DifferentialMail' => 'applications/differential/mail/base',
 | 
			
		||||
    'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
 | 
			
		||||
    'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
 | 
			
		||||
    'DifferentialRevision' => 'applications/differential/storage/revision',
 | 
			
		||||
    'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem',
 | 
			
		||||
    'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit',
 | 
			
		||||
    'DifferentialRevisionEditor' => 'applications/differential/editor/revision',
 | 
			
		||||
    'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
 | 
			
		||||
    'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus',
 | 
			
		||||
    'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
 | 
			
		||||
@@ -206,6 +213,7 @@ phutil_register_library_map(array(
 | 
			
		||||
    'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
 | 
			
		||||
    'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
 | 
			
		||||
    'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
 | 
			
		||||
    'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
 | 
			
		||||
    'DifferentialChangeset' => 'DifferentialDAO',
 | 
			
		||||
    'DifferentialChangesetDetailView' => 'AphrontView',
 | 
			
		||||
    'DifferentialChangesetListView' => 'AphrontView',
 | 
			
		||||
@@ -213,10 +221,14 @@ phutil_register_library_map(array(
 | 
			
		||||
    'DifferentialController' => 'PhabricatorController',
 | 
			
		||||
    'DifferentialDAO' => 'PhabricatorLiskDAO',
 | 
			
		||||
    'DifferentialDiff' => 'DifferentialDAO',
 | 
			
		||||
    'DifferentialDiffContentMail' => 'DifferentialMail',
 | 
			
		||||
    'DifferentialDiffProperty' => 'DifferentialDAO',
 | 
			
		||||
    'DifferentialDiffTableOfContentsView' => 'AphrontView',
 | 
			
		||||
    'DifferentialDiffViewController' => 'DifferentialController',
 | 
			
		||||
    'DifferentialFeedbackMail' => 'DifferentialMail',
 | 
			
		||||
    'DifferentialHunk' => 'DifferentialDAO',
 | 
			
		||||
    'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
 | 
			
		||||
    'DifferentialReviewRequestMail' => 'DifferentialMail',
 | 
			
		||||
    'DifferentialRevision' => 'DifferentialDAO',
 | 
			
		||||
    'DifferentialRevisionEditController' => 'DifferentialController',
 | 
			
		||||
    'DifferentialRevisionListController' => 'DifferentialController',
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,10 @@
 | 
			
		||||
class PhabricatorConduitAPIController
 | 
			
		||||
  extends PhabricatorConduitController {
 | 
			
		||||
 | 
			
		||||
  public function shouldRequireLogin() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private $method;
 | 
			
		||||
 | 
			
		||||
  public function willProcessRequest(array $data) {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,8 @@ class DifferentialDiffViewController extends DifferentialController {
 | 
			
		||||
    $action_form = new AphrontFormView();
 | 
			
		||||
    $action_form
 | 
			
		||||
      ->setAction('/differential/revision/edit/')
 | 
			
		||||
      ->addHiddenInput('diffID', $diff->getID())
 | 
			
		||||
      ->addHiddenInput('viaDiffView', 1)
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormSelectControl())
 | 
			
		||||
          ->setLabel('Attach To')
 | 
			
		||||
 
 | 
			
		||||
@@ -34,25 +34,80 @@ class DifferentialRevisionEditController extends DifferentialController {
 | 
			
		||||
    } else {
 | 
			
		||||
      $revision = new DifferentialRevision();
 | 
			
		||||
    }
 | 
			
		||||
/*
 | 
			
		||||
    $e_name = true;
 | 
			
		||||
    $errors = array();
 | 
			
		||||
 | 
			
		||||
    $request = $this->getRequest();
 | 
			
		||||
    if ($request->isFormPost()) {
 | 
			
		||||
      $category->setName($request->getStr('name'));
 | 
			
		||||
      $category->setSequence($request->getStr('sequence'));
 | 
			
		||||
    $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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
      if (!strlen($category->getName())) {
 | 
			
		||||
        $errors[] = 'Category name is required.';
 | 
			
		||||
        $e_name = 'Required';
 | 
			
		||||
    $e_title = true;
 | 
			
		||||
    $e_testplan = true;
 | 
			
		||||
    $errors = array();
 | 
			
		||||
 | 
			
		||||
    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';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!strlen(trim($revision->getTestPlan()))) {
 | 
			
		||||
        $errors[] = 'You must provide a test plan.';
 | 
			
		||||
        $e_testplan = 'Required';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $user_phid = $request->getUser()->getPHID();
 | 
			
		||||
 | 
			
		||||
      if (in_array($user_phid, $request->getArr('reviewers'))) {
 | 
			
		||||
        $errors[] = 'You may not review your own revision.';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!$errors) {
 | 
			
		||||
        $category->save();
 | 
			
		||||
        return id(new AphrontRedirectResponse())
 | 
			
		||||
          ->setURI('/directory/category/');
 | 
			
		||||
        $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();
 | 
			
		||||
 | 
			
		||||
        $response = 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();
 | 
			
		||||
      $reviewer_phids = array();
 | 
			
		||||
      $cc_phids = array();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $form = new AphrontFormView();
 | 
			
		||||
    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;
 | 
			
		||||
@@ -61,29 +116,15 @@ class DifferentialRevisionEditController extends DifferentialController {
 | 
			
		||||
        ->setTitle('Form Errors')
 | 
			
		||||
        ->setErrors($errors);
 | 
			
		||||
    }
 | 
			
		||||
*/
 | 
			
		||||
    $e_name = true;
 | 
			
		||||
    $e_testplan = true;
 | 
			
		||||
 | 
			
		||||
    $form = new AphrontFormView();
 | 
			
		||||
    if ($revision->getID()) {
 | 
			
		||||
      $form->setAction('/differential/revision/edit/'.$revision->getID().'/');
 | 
			
		||||
    } else {
 | 
			
		||||
      $form->setAction('/differential/revision/edit/');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $reviewer_map = array(
 | 
			
		||||
      1 => 'A Zebra',
 | 
			
		||||
      2 => 'Pie Messenger',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    $form
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormTextAreaControl())
 | 
			
		||||
          ->setLabel('Name')
 | 
			
		||||
          ->setName('name')
 | 
			
		||||
          ->setValue($revision->getName())
 | 
			
		||||
          ->setError($e_name))
 | 
			
		||||
          ->setLabel('Title')
 | 
			
		||||
          ->setName('title')
 | 
			
		||||
          ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
 | 
			
		||||
          ->setValue($revision->getTitle())
 | 
			
		||||
          ->setError($e_title))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormTextAreaControl())
 | 
			
		||||
          ->setLabel('Summary')
 | 
			
		||||
@@ -99,13 +140,13 @@ class DifferentialRevisionEditController extends DifferentialController {
 | 
			
		||||
        id(new AphrontFormTokenizerControl())
 | 
			
		||||
          ->setLabel('Reviewers')
 | 
			
		||||
          ->setName('reviewers')
 | 
			
		||||
          ->setDatasource('/typeahead/common/user/')
 | 
			
		||||
          ->setDatasource('/typeahead/common/users/')
 | 
			
		||||
          ->setValue($reviewer_map))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormTokenizerControl())
 | 
			
		||||
          ->setLabel('CC')
 | 
			
		||||
          ->setName('cc')
 | 
			
		||||
          ->setDatasource('/typeahead/common/user/')
 | 
			
		||||
          ->setDatasource('/typeahead/common/mailable/')
 | 
			
		||||
          ->setValue($reviewer_map))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormTextControl())
 | 
			
		||||
@@ -116,13 +157,20 @@ class DifferentialRevisionEditController extends DifferentialController {
 | 
			
		||||
                       'change fixes.'))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormTextAreaControl())
 | 
			
		||||
          ->setLabel('Revert')
 | 
			
		||||
          ->setLabel('Revert Plan')
 | 
			
		||||
          ->setName('revert')
 | 
			
		||||
          ->setValue($revision->getRevertPlan())
 | 
			
		||||
          ->setCaption('Special steps required to safely revert this change.'))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormSubmitControl())
 | 
			
		||||
          ->setValue('Save'));
 | 
			
		||||
          ->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()) {
 | 
			
		||||
@@ -134,7 +182,6 @@ class DifferentialRevisionEditController extends DifferentialController {
 | 
			
		||||
    $panel->appendChild($form);
 | 
			
		||||
    $panel->setWidth(AphrontPanelView::WIDTH_FORM);
 | 
			
		||||
 | 
			
		||||
    $error_view = null;
 | 
			
		||||
    return $this->buildStandardPageResponse(
 | 
			
		||||
      array($error_view, $panel),
 | 
			
		||||
      array(
 | 
			
		||||
 
 | 
			
		||||
@@ -7,10 +7,15 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'aphront/response/404');
 | 
			
		||||
phutil_require_module('phabricator', 'aphront/response/redirect');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/controller/base');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/editor/revision');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/storage/diff');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/storage/revision');
 | 
			
		||||
phutil_require_module('phabricator', 'view/form/base');
 | 
			
		||||
phutil_require_module('phabricator', 'view/form/control/submit');
 | 
			
		||||
phutil_require_module('phabricator', 'view/form/control/textarea');
 | 
			
		||||
phutil_require_module('phabricator', 'view/form/error');
 | 
			
		||||
phutil_require_module('phabricator', 'view/layout/panel');
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phutil', 'utils');
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,550 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handle major edit operations to DifferentialRevision -- adding and removing
 | 
			
		||||
 * reviewers, diffs, and CCs. Unlike simple edits, these changes trigger
 | 
			
		||||
 * complicated email workflows.
 | 
			
		||||
 */
 | 
			
		||||
class DifferentialRevisionEditor {
 | 
			
		||||
 | 
			
		||||
  protected $revision;
 | 
			
		||||
  protected $actorPHID;
 | 
			
		||||
 | 
			
		||||
  protected $cc         = null;
 | 
			
		||||
  protected $reviewers  = null;
 | 
			
		||||
  protected $diff;
 | 
			
		||||
  protected $comments;
 | 
			
		||||
  protected $silentUpdate;
 | 
			
		||||
 | 
			
		||||
  public function __construct(DifferentialRevision $revision, $actor_phid) {
 | 
			
		||||
    $this->revision = $revision;
 | 
			
		||||
    $this->actorPHID = $actor_phid;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  public static function newRevisionFromRawMessageWithDiff(
 | 
			
		||||
    DifferentialRawMessage $message,
 | 
			
		||||
    Diff $diff,
 | 
			
		||||
    $user) {
 | 
			
		||||
 | 
			
		||||
    if ($message->getRevisionID()) {
 | 
			
		||||
      throw new Exception(
 | 
			
		||||
        "The provided commit message is already associated with a ".
 | 
			
		||||
        "Differential revision.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($message->getReviewedByNames()) {
 | 
			
		||||
      throw new Exception(
 | 
			
		||||
        "The provided commit message contains a 'Reviewed By:' field.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $revision = new DifferentialRevision();
 | 
			
		||||
    $revision->setPHID($revision->generatePHID());
 | 
			
		||||
 | 
			
		||||
    $revision->setOwnerID($user);
 | 
			
		||||
    $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
 | 
			
		||||
    $revision->attachReviewers(array());
 | 
			
		||||
    $revision->attachCCPHIDs(array());
 | 
			
		||||
 | 
			
		||||
    $editor = new DifferentialRevisionEditor($revision, $user);
 | 
			
		||||
 | 
			
		||||
    self::copyFields($editor, $revision, $message, $user);
 | 
			
		||||
 | 
			
		||||
    $editor->addDiff($diff, null);
 | 
			
		||||
    $editor->save();
 | 
			
		||||
 | 
			
		||||
    return $revision;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static function newRevisionFromConduitWithDiff(
 | 
			
		||||
    array $fields,
 | 
			
		||||
    Diff $diff,
 | 
			
		||||
    $user) {
 | 
			
		||||
 | 
			
		||||
    $revision = new DifferentialRevision();
 | 
			
		||||
    $revision->setPHID($revision->generatePHID());
 | 
			
		||||
 | 
			
		||||
    $revision->setOwnerID($user);
 | 
			
		||||
    $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
 | 
			
		||||
    $revision->attachReviewers(array());
 | 
			
		||||
    $revision->attachCCPHIDs(array());
 | 
			
		||||
 | 
			
		||||
    $editor = new DifferentialRevisionEditor($revision, $user);
 | 
			
		||||
 | 
			
		||||
    $editor->copyFieldFromConduit($fields);
 | 
			
		||||
 | 
			
		||||
    $editor->addDiff($diff, null);
 | 
			
		||||
    $editor->save();
 | 
			
		||||
 | 
			
		||||
    return $revision;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  public static function copyFields(
 | 
			
		||||
    DifferentialRevisionEditor $editor,
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    DifferentialRawMessage $message,
 | 
			
		||||
    $user) {
 | 
			
		||||
 | 
			
		||||
    $revision->setName($message->getTitle());
 | 
			
		||||
    $revision->setSummary($message->getSummary());
 | 
			
		||||
    $revision->setTestPlan($message->getTestPlan());
 | 
			
		||||
    $revision->setSVNBlameRevision($message->getBlameRevision());
 | 
			
		||||
    $revision->setRevert($message->getRevertPlan());
 | 
			
		||||
    $revision->setPlatformImpact($message->getPlatformImpact());
 | 
			
		||||
    $revision->setBugzillaID($message->getBugzillaID());
 | 
			
		||||
 | 
			
		||||
    $editor->setReviewers($message->getReviewerPHIDs());
 | 
			
		||||
    $editor->setCCPHIDs($message->getCCPHIDs());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function copyFieldFromConduit(array $fields) {
 | 
			
		||||
 | 
			
		||||
    $user = $this->actorPHID;
 | 
			
		||||
    $revision = $this->revision;
 | 
			
		||||
 | 
			
		||||
    $revision->setName($fields['title']);
 | 
			
		||||
    $revision->setSummary($fields['summary']);
 | 
			
		||||
    $revision->setTestPlan($fields['testPlan']);
 | 
			
		||||
    $revision->setSVNBlameRevision($fields['blameRevision']);
 | 
			
		||||
    $revision->setRevert($fields['revertPlan']);
 | 
			
		||||
    $revision->setPlatformImpact($fields['platformImpact']);
 | 
			
		||||
    $revision->setBugzillaID($fields['bugzillaID']);
 | 
			
		||||
 | 
			
		||||
    $this->setReviewers($fields['reviewerGUIDs']);
 | 
			
		||||
    $this->setCCPHIDs($fields['ccGUIDs']);
 | 
			
		||||
  }
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
  public function getRevision() {
 | 
			
		||||
    return $this->revision;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setReviewers(array $reviewers) {
 | 
			
		||||
    $this->reviewers = $reviewers;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setCCPHIDs(array $cc) {
 | 
			
		||||
    $this->cc = $cc;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function addDiff(DifferentialDiff $diff, $comments) {
 | 
			
		||||
    if ($diff->getRevisionID() &&
 | 
			
		||||
        $diff->getRevisionID() != $this->getRevision()->getID()) {
 | 
			
		||||
      $diff_id = (int)$diff->getID();
 | 
			
		||||
      $targ_id = (int)$this->getRevision()->getID();
 | 
			
		||||
      $real_id = (int)$diff->getRevisionID();
 | 
			
		||||
      throw new Exception(
 | 
			
		||||
        "Can not attach diff #{$diff_id} to Revision D{$targ_id}, it is ".
 | 
			
		||||
        "already attached to D{$real_id}.");
 | 
			
		||||
    }
 | 
			
		||||
    $this->diff = $diff;
 | 
			
		||||
    $this->comments = $comments;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getDiff() {
 | 
			
		||||
    return $this->diff;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getComments() {
 | 
			
		||||
    return $this->comments;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getActorPHID() {
 | 
			
		||||
    return $this->actorPHID;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function isNewRevision() {
 | 
			
		||||
    return !$this->getRevision()->getID();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A silent update does not trigger Herald rules or send emails. This is used
 | 
			
		||||
   * for auto-amends at commit time.
 | 
			
		||||
   */
 | 
			
		||||
  public function setSilentUpdate($silent) {
 | 
			
		||||
    $this->silentUpdate = $silent;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function save() {
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
//    $revision->openTransaction();
 | 
			
		||||
 | 
			
		||||
    $is_new = $this->isNewRevision();
 | 
			
		||||
    if ($is_new) {
 | 
			
		||||
      // These fields aren't nullable; set them to sensible defaults if they
 | 
			
		||||
      // haven't been configured. We're just doing this so we can generate an
 | 
			
		||||
      // ID for the revision if we don't have one already.
 | 
			
		||||
      $revision->setLineCount(0);
 | 
			
		||||
      if ($revision->getStatus() === null) {
 | 
			
		||||
        $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
 | 
			
		||||
      }
 | 
			
		||||
      if ($revision->getTitle() === null) {
 | 
			
		||||
        $revision->setTitle('Untitled Revision');
 | 
			
		||||
      }
 | 
			
		||||
      if ($revision->getOwnerPHID() === null) {
 | 
			
		||||
        $revision->setOwnerPHID($this->getActorPHID());
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $revision->save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $revision->loadRelationships();
 | 
			
		||||
 | 
			
		||||
    if ($this->reviewers === null) {
 | 
			
		||||
      $this->reviewers = $revision->getReviewers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($this->cc === null) {
 | 
			
		||||
      $this->cc = $revision->getCCPHIDs();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We're going to build up three dictionaries: $add, $rem, and $stable. The
 | 
			
		||||
    // $add dictionary has added reviewers/CCs. The $rem dictionary has
 | 
			
		||||
    // reviewers/CCs who have been removed, and the $stable array is
 | 
			
		||||
    // reviewers/CCs who haven't changed. We're going to send new reviewers/CCs
 | 
			
		||||
    // a different ("welcome") email than we send stable reviewers/CCs.
 | 
			
		||||
 | 
			
		||||
    $old = array(
 | 
			
		||||
      'rev' => array_fill_keys($revision->getReviewers(), true),
 | 
			
		||||
      'ccs' => array_fill_keys($revision->getCCPHIDs(), true),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    $diff = $this->getDiff();
 | 
			
		||||
 | 
			
		||||
    $xscript_header = null;
 | 
			
		||||
    $xscript_uri = null;
 | 
			
		||||
 | 
			
		||||
    $new = array(
 | 
			
		||||
      'rev' => array_fill_keys($this->reviewers, true),
 | 
			
		||||
      'ccs' => array_fill_keys($this->cc, true),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $rem_ccs = array();
 | 
			
		||||
    if ($diff) {
 | 
			
		||||
      $diff->setRevisionID($revision->getID());
 | 
			
		||||
      $revision->setLineCount($diff->getLineCount());
 | 
			
		||||
 | 
			
		||||
// TODO!
 | 
			
		||||
//      $revision->setRepositoryID($diff->getRepositoryID());
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
      $iface = new DifferentialRevisionHeraldable($revision);
 | 
			
		||||
      $iface->setExplicitCCs($new['ccs']);
 | 
			
		||||
      $iface->setExplicitReviewers($new['rev']);
 | 
			
		||||
      $iface->setForbiddenCCs($revision->getForbiddenCCPHIDs());
 | 
			
		||||
      $iface->setForbiddenReviewers($revision->getForbiddenReviewers());
 | 
			
		||||
      $iface->setDiff($diff);
 | 
			
		||||
 | 
			
		||||
      $xscript = HeraldEngine::loadAndApplyRules($iface);
 | 
			
		||||
      $xscript_uri = $xscript->getURI();
 | 
			
		||||
      $xscript_phid = $xscript->getPHID();
 | 
			
		||||
      $xscript_header = $xscript->getXHeraldRulesHeader();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      $sub = array(
 | 
			
		||||
        'rev' => array(),
 | 
			
		||||
        'ccs' => $iface->getCCsAddedByHerald(),
 | 
			
		||||
      );
 | 
			
		||||
      $rem_ccs = $iface->getCCsRemovedByHerald();
 | 
			
		||||
*/
 | 
			
		||||
  // TODO!
 | 
			
		||||
      $sub = array(
 | 
			
		||||
        'rev' => array(),
 | 
			
		||||
        'ccs' => array(),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
      $sub = array(
 | 
			
		||||
        'rev' => array(),
 | 
			
		||||
        'ccs' => array(),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remove any CCs which are prevented by Herald rules.
 | 
			
		||||
    $sub['ccs'] = array_diff_key($sub['ccs'], $rem_ccs);
 | 
			
		||||
    $new['ccs'] = array_diff_key($new['ccs'], $rem_ccs);
 | 
			
		||||
 | 
			
		||||
    $add = array();
 | 
			
		||||
    $rem = array();
 | 
			
		||||
    $stable = array();
 | 
			
		||||
    foreach (array('rev', 'ccs') as $key) {
 | 
			
		||||
      $add[$key] = array();
 | 
			
		||||
      if ($new[$key] !== null) {
 | 
			
		||||
        $add[$key] += array_diff_key($new[$key], $old[$key]);
 | 
			
		||||
      }
 | 
			
		||||
      $add[$key] += array_diff_key($sub[$key], $old[$key]);
 | 
			
		||||
 | 
			
		||||
      $combined = $sub[$key];
 | 
			
		||||
      if ($new[$key] !== null) {
 | 
			
		||||
        $combined += $new[$key];
 | 
			
		||||
      }
 | 
			
		||||
      $rem[$key] = array_diff_key($old[$key], $combined);
 | 
			
		||||
 | 
			
		||||
      $stable[$key] = array_diff_key($old[$key], $add[$key] + $rem[$key]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self::removeReviewers(
 | 
			
		||||
      $revision,
 | 
			
		||||
      array_keys($rem['rev']),
 | 
			
		||||
      $this->actorPHID);
 | 
			
		||||
    self::addReviewers(
 | 
			
		||||
      $revision,
 | 
			
		||||
      array_keys($add['rev']),
 | 
			
		||||
      $this->actorPHID);
 | 
			
		||||
 | 
			
		||||
    // Add the owner to the relevant set of users so they get a copy of the
 | 
			
		||||
    // email.
 | 
			
		||||
    if (!$this->silentUpdate) {
 | 
			
		||||
      if ($is_new) {
 | 
			
		||||
        $add['rev'][$this->getActorPHID()] = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        $stable['rev'][$this->getActorPHID()] = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $mail = array();
 | 
			
		||||
 | 
			
		||||
    $changesets = null;
 | 
			
		||||
    $feedback = null;
 | 
			
		||||
    if ($diff) {
 | 
			
		||||
      $changesets = $diff->loadChangesets();
 | 
			
		||||
      // TODO: move to DifferentialFeedbackEditor
 | 
			
		||||
      if (!$is_new) {
 | 
			
		||||
        // TODO
 | 
			
		||||
//        $feedback = $this->createFeedback();
 | 
			
		||||
      }
 | 
			
		||||
      if ($feedback) {
 | 
			
		||||
        $mail[] = id(new DifferentialNewDiffMail(
 | 
			
		||||
            $revision,
 | 
			
		||||
            $this->getActorPHID(),
 | 
			
		||||
            $changesets))
 | 
			
		||||
          ->setIsFirstMailAboutRevision($is_new)
 | 
			
		||||
          ->setIsFirstMailToRecipients($is_new)
 | 
			
		||||
          ->setComments($this->getComments())
 | 
			
		||||
          ->setToPHIDs(array_keys($stable['rev']))
 | 
			
		||||
          ->setCCPHIDs(array_keys($stable['ccs']));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Save the changes we made above.
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
//      $diff->setDescription(substr($this->getComments(), 0, 80));
 | 
			
		||||
      $diff->save();
 | 
			
		||||
 | 
			
		||||
      // An updated diff should require review, as long as it's not committed
 | 
			
		||||
      // or accepted. The "accepted" status is "sticky" to encourage courtesy
 | 
			
		||||
      // re-diffs after someone accepts with minor changes/suggestions.
 | 
			
		||||
 | 
			
		||||
      $status = $revision->getStatus();
 | 
			
		||||
      if ($status != DifferentialRevisionStatus::COMMITTED &&
 | 
			
		||||
          $status != DifferentialRevisionStatus::ACCEPTED) {
 | 
			
		||||
        $revision->setStatus(DifferentialRevisionStatus::NEEDS_REVIEW);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
      $diff = $revision->getActiveDiff();
 | 
			
		||||
      if ($diff) {
 | 
			
		||||
        $changesets = id(new DifferentialChangeset())->loadAllWithDiff($diff);
 | 
			
		||||
      } else {
 | 
			
		||||
        $changesets = array();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $revision->save();
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
//    $revision->saveTransaction();
 | 
			
		||||
 | 
			
		||||
    $event = array(
 | 
			
		||||
      'revision_id' => $revision->getID(),
 | 
			
		||||
      'PHID'        => $revision->getPHID(),
 | 
			
		||||
      'action'      => $is_new ? 'create' : 'update',
 | 
			
		||||
      'actor'       => $this->getActorPHID(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
//  TODO
 | 
			
		||||
//    id(new ToolsTimelineEvent('difx', fb_json_encode($event)))->record();
 | 
			
		||||
 | 
			
		||||
    if ($this->silentUpdate) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
//    $revision->attachReviewers(array_keys($new['rev']));
 | 
			
		||||
//    $revision->attachCCPHIDs(array_keys($new['ccs']));
 | 
			
		||||
 | 
			
		||||
    if ($add['ccs'] || $rem['ccs']) {
 | 
			
		||||
      foreach (array_keys($add['ccs']) as $id) {
 | 
			
		||||
        if (empty($new['ccs'][$id])) {
 | 
			
		||||
          $reason_phid = 'TODO';//$xscript_phid;
 | 
			
		||||
        } else {
 | 
			
		||||
          $reason_phid = $this->getActorPHID();
 | 
			
		||||
        }
 | 
			
		||||
        self::addCCPHID($revision, $id, $reason_phid);
 | 
			
		||||
      }
 | 
			
		||||
      foreach (array_keys($rem['ccs']) as $id) {
 | 
			
		||||
        if (empty($new['ccs'][$id])) {
 | 
			
		||||
          $reason_phid = $this->getActorPHID();
 | 
			
		||||
        } else {
 | 
			
		||||
          $reason_phid = 'TODO';//$xscript_phid;
 | 
			
		||||
        }
 | 
			
		||||
        self::removeCCPHID($revision, $id, $reason_phid);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($add['rev']) {
 | 
			
		||||
      $message = id(new DifferentialNewDiffMail(
 | 
			
		||||
          $revision,
 | 
			
		||||
          $this->getActorPHID(),
 | 
			
		||||
          $changesets))
 | 
			
		||||
        ->setIsFirstMailAboutRevision($is_new)
 | 
			
		||||
        ->setIsFirstMailToRecipients(true)
 | 
			
		||||
        ->setToPHIDs(array_keys($add['rev']));
 | 
			
		||||
 | 
			
		||||
      if ($is_new) {
 | 
			
		||||
        // The first time we send an email about a revision, put the CCs in
 | 
			
		||||
        // the "CC:" field of the same "Review Requested" email that reviewers
 | 
			
		||||
        // get, so you don't get two initial emails if you're on a list that
 | 
			
		||||
        // is CC'd.
 | 
			
		||||
        $message->setCCPHIDs(array_keys($add['ccs']));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $mail[] = $message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If you were added as a reviewer and a CC, just give you the reviewer
 | 
			
		||||
    // email. We could go to greater lengths to prevent this, but there's
 | 
			
		||||
    // bunch of stuff with list subscriptions anyway. You can still get two
 | 
			
		||||
    // emails, but only if a revision is updated and you are added as a reviewer
 | 
			
		||||
    // at the same time a list you are on is added as a CC, which is rare and
 | 
			
		||||
    // reasonable.
 | 
			
		||||
    $add['ccs'] = array_diff_key($add['ccs'], $add['rev']);
 | 
			
		||||
 | 
			
		||||
    if (!$is_new && $add['ccs']) {
 | 
			
		||||
      $mail[] = id(new DifferentialCCWelcomeMail(
 | 
			
		||||
          $revision,
 | 
			
		||||
          $this->getActorPHID(),
 | 
			
		||||
          $changesets))
 | 
			
		||||
        ->setIsFirstMailToRecipients(true)
 | 
			
		||||
        ->setToPHIDs(array_keys($add['ccs']));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    foreach ($mail as $message) {
 | 
			
		||||
// TODO
 | 
			
		||||
//      $message->setHeraldTranscriptURI($xscript_uri);
 | 
			
		||||
//      $message->setXHeraldRulesHeader($xscript_header);
 | 
			
		||||
      $message->send();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function addCCPHID(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    $phid,
 | 
			
		||||
    $reason_phid) {
 | 
			
		||||
    self::alterCCPHID($revision, $phid, true, $reason_phid);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function removeCCPHID(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    $phid,
 | 
			
		||||
    $reason_phid) {
 | 
			
		||||
    self::alterCCPHID($revision, $phid, false, $reason_phid);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected static function alterCCPHID(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    $phid,
 | 
			
		||||
    $add,
 | 
			
		||||
    $reason_phid) {
 | 
			
		||||
/*
 | 
			
		||||
    $relationship = new DifferentialRelationship();
 | 
			
		||||
    $relationship->setRevisionID($revision->getID());
 | 
			
		||||
    $relationship->setRelation(DifferentialRelationship::RELATION_SUBSCRIBED);
 | 
			
		||||
    $relationship->setRelatedPHID($phid);
 | 
			
		||||
    $relationship->setForbidden(!$add);
 | 
			
		||||
    $relationship->setReasonPHID($reason_phid);
 | 
			
		||||
    $relationship->replace();
 | 
			
		||||
*/
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  public static function addReviewers(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    array $reviewer_ids,
 | 
			
		||||
    $reason_phid) {
 | 
			
		||||
/*
 | 
			
		||||
    foreach ($reviewer_ids as $reviewer_id) {
 | 
			
		||||
      $relationship = new DifferentialRelationship();
 | 
			
		||||
      $relationship->setRevisionID($revision->getID());
 | 
			
		||||
      $relationship->setRelatedPHID($reviewer_id);
 | 
			
		||||
      $relationship->setForbidden(false);
 | 
			
		||||
      $relationship->setReasonPHID($reason_phid);
 | 
			
		||||
      $relationship->setRelation(DifferentialRelationship::RELATION_REVIEWER);
 | 
			
		||||
      $relationship->replace();
 | 
			
		||||
    }
 | 
			
		||||
*/
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static function removeReviewers(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    array $reviewer_ids,
 | 
			
		||||
    $reason_phid) {
 | 
			
		||||
/*
 | 
			
		||||
    if (!$reviewer_ids) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    foreach ($reviewer_ids as $reviewer_id) {
 | 
			
		||||
      $relationship = new DifferentialRelationship();
 | 
			
		||||
      $relationship->setRevisionID($revision->getID());
 | 
			
		||||
      $relationship->setRelatedPHID($reviewer_id);
 | 
			
		||||
      $relationship->setForbidden(true);
 | 
			
		||||
      $relationship->setReasonPHID($reason_phid);
 | 
			
		||||
      $relationship->setRelation(DifferentialRelationship::RELATION_REVIEWER);
 | 
			
		||||
      $relationship->replace();
 | 
			
		||||
    }
 | 
			
		||||
*/
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  protected function createFeedback() {
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
    $feedback = id(new DifferentialFeedback())
 | 
			
		||||
      ->setUserID($this->getActorPHID())
 | 
			
		||||
      ->setRevision($revision)
 | 
			
		||||
      ->setContent($this->getComments())
 | 
			
		||||
      ->setAction('update');
 | 
			
		||||
 | 
			
		||||
    $feedback->save();
 | 
			
		||||
 | 
			
		||||
    return $feedback;
 | 
			
		||||
  }
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								src/applications/differential/editor/revision/__init__.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/applications/differential/editor/revision/__init__.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/ccwelcome');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/newdiff');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/storage/changeset');
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phutil', 'utils');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialRevisionEditor.php');
 | 
			
		||||
							
								
								
									
										311
									
								
								src/applications/differential/mail/base/DifferentialMail.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										311
									
								
								src/applications/differential/mail/base/DifferentialMail.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,311 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
abstract class DifferentialMail {
 | 
			
		||||
 | 
			
		||||
  const SUBJECT_PREFIX  = '[Differential]';
 | 
			
		||||
 | 
			
		||||
  protected $to = array();
 | 
			
		||||
  protected $cc = array();
 | 
			
		||||
 | 
			
		||||
  protected $actorName;
 | 
			
		||||
  protected $actorID;
 | 
			
		||||
 | 
			
		||||
  protected $revision;
 | 
			
		||||
  protected $feedback;
 | 
			
		||||
  protected $changesets;
 | 
			
		||||
  protected $inlineComments;
 | 
			
		||||
  protected $isFirstMailAboutRevision;
 | 
			
		||||
  protected $isFirstMailToRecipients;
 | 
			
		||||
  protected $heraldTranscriptURI;
 | 
			
		||||
  protected $heraldRulesHeader;
 | 
			
		||||
 | 
			
		||||
  public function getActorName() {
 | 
			
		||||
    return $this->actorName;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setActorName($actor_name) {
 | 
			
		||||
    $this->actorName = $actor_name;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  abstract protected function renderSubject();
 | 
			
		||||
  abstract protected function renderBody();
 | 
			
		||||
 | 
			
		||||
  public function setXHeraldRulesHeader($header) {
 | 
			
		||||
    $this->heraldRulesHeader = $header;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function send() {
 | 
			
		||||
    $to_phids = $this->getToPHIDs();
 | 
			
		||||
    if (!$to_phids) {
 | 
			
		||||
      throw new Exception('No "To:" users provided!');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $message_id = $this->getMessageID();
 | 
			
		||||
 | 
			
		||||
    $cc_phids = $this->getCCPHIDs();
 | 
			
		||||
    $subject  = $this->buildSubject();
 | 
			
		||||
    $body     = $this->buildBody();
 | 
			
		||||
 | 
			
		||||
    $mail = new PhabricatorMetaMTAMail();
 | 
			
		||||
    if ($this->getActorID()) {
 | 
			
		||||
      $mail->setFrom($this->getActorID());
 | 
			
		||||
      $mail->setReplyTo($this->getReplyHandlerEmailAddress());
 | 
			
		||||
    } else {
 | 
			
		||||
      $mail->setFrom($this->getReplyHandlerEmailAddress());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $mail
 | 
			
		||||
      ->addTos($to_phids)
 | 
			
		||||
      ->addCCs($cc_phids)
 | 
			
		||||
      ->setSubject($subject)
 | 
			
		||||
      ->setBody($body)
 | 
			
		||||
      ->setIsHTML($this->shouldMarkMailAsHTML())
 | 
			
		||||
      ->addHeader('Thread-Topic', $this->getRevision()->getTitle())
 | 
			
		||||
      ->addHeader('Thread-Index', $this->generateThreadIndex());
 | 
			
		||||
 | 
			
		||||
    if ($this->isFirstMailAboutRevision()) {
 | 
			
		||||
      $mail->addHeader('Message-ID',  $message_id);
 | 
			
		||||
    } else {
 | 
			
		||||
      $mail->addHeader('In-Reply-To', $message_id);
 | 
			
		||||
      $mail->addHeader('References',  $message_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($this->heraldRulesHeader) {
 | 
			
		||||
      $mail->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $mail->setRelatedPHID($this->getRevision()->getPHID());
 | 
			
		||||
 | 
			
		||||
    // Save this to the MetaMTA queue for later delivery to the MTA.
 | 
			
		||||
    $mail->save();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildSubject() {
 | 
			
		||||
    return self::SUBJECT_PREFIX.' '.$this->renderSubject();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function shouldMarkMailAsHTML() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildBody() {
 | 
			
		||||
 | 
			
		||||
    $actions = array();
 | 
			
		||||
    $body = $this->renderBody();
 | 
			
		||||
    $body .= <<<EOTEXT
 | 
			
		||||
 | 
			
		||||
ACTIONS
 | 
			
		||||
  Reply to comment, or !accept, !reject, !abandon, !resign, or !showdiff.
 | 
			
		||||
 | 
			
		||||
EOTEXT;
 | 
			
		||||
 | 
			
		||||
    if ($this->getHeraldTranscriptURI() && $this->isFirstMailToRecipients()) {
 | 
			
		||||
      $xscript_uri = $this->getHeraldTranscriptURI();
 | 
			
		||||
      $body .= <<<EOTEXT
 | 
			
		||||
 | 
			
		||||
MANAGE HERALD RULES
 | 
			
		||||
  http://todo.com/herald/
 | 
			
		||||
 | 
			
		||||
WHY DID I GET THIS EMAIL?
 | 
			
		||||
  {$xscript_uri}
 | 
			
		||||
 | 
			
		||||
Tip: use the X-Herald-Rules header to filter Herald messages in your client.
 | 
			
		||||
 | 
			
		||||
EOTEXT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $body;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getReplyHandlerEmailAddress() {
 | 
			
		||||
    // TODO
 | 
			
		||||
    $phid = $this->getRevision()->getPHID();
 | 
			
		||||
    $server = 'todo.example.com';
 | 
			
		||||
    return "differential+{$phid}@{$server}";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function formatText($text) {
 | 
			
		||||
    $text = explode("\n", $text);
 | 
			
		||||
    foreach ($text as &$line) {
 | 
			
		||||
      $line = rtrim('  '.$line);
 | 
			
		||||
    }
 | 
			
		||||
    unset($line);
 | 
			
		||||
    return implode("\n", $text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setToPHIDs(array $to) {
 | 
			
		||||
    $this->to = $this->filterContactPHIDs($to);
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setCCPHIDs(array $cc) {
 | 
			
		||||
    $this->cc = $this->filterContactPHIDs($cc);
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function filterContactPHIDs(array $phids) {
 | 
			
		||||
    return $phids;
 | 
			
		||||
 | 
			
		||||
    // TODO: actually do this?
 | 
			
		||||
 | 
			
		||||
    // Differential revisions use Subscriptions for CCs, so any arbitrary
 | 
			
		||||
    // PHID can end up CC'd to them. Only try to actually send email PHIDs
 | 
			
		||||
    // which have ToolsHandle types that are marked emailable. If we don't
 | 
			
		||||
    // filter here, sending the email will fail.
 | 
			
		||||
/*
 | 
			
		||||
    $handles = array();
 | 
			
		||||
    prep(new ToolsHandleData($phids, $handles));
 | 
			
		||||
    foreach ($handles as $phid => $handle) {
 | 
			
		||||
      if (!$handle->isEmailable()) {
 | 
			
		||||
        unset($handles[$phid]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return array_keys($handles);
 | 
			
		||||
*/
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getToPHIDs() {
 | 
			
		||||
    return $this->to;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getCCPHIDs() {
 | 
			
		||||
    return $this->cc;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setActorID($actor_id) {
 | 
			
		||||
    $this->actorID = $actor_id;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getActorID() {
 | 
			
		||||
    return $this->actorID;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setRevision($revision) {
 | 
			
		||||
    $this->revision = $revision;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getRevision() {
 | 
			
		||||
    return $this->revision;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getMessageID() {
 | 
			
		||||
    $phid = $this->getRevision()->getPHID();
 | 
			
		||||
    // TODO
 | 
			
		||||
    return "<differential-rev-{$phid}-req@TODO.com>";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setFeedback($feedback) {
 | 
			
		||||
    $this->feedback = $feedback;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getFeedback() {
 | 
			
		||||
    return $this->feedback;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setChangesets($changesets) {
 | 
			
		||||
    $this->changesets = $changesets;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getChangesets() {
 | 
			
		||||
    return $this->changesets;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setInlineComments(array $inline_comments) {
 | 
			
		||||
    $this->inlineComments = $inline_comments;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getInlineComments() {
 | 
			
		||||
    return $this->inlineComments;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function renderRevisionDetailLink() {
 | 
			
		||||
    $uri = $this->getRevisionURI();
 | 
			
		||||
    return "REVISION DETAIL\n  {$uri}";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getRevisionURI() {
 | 
			
		||||
    // TODO
 | 
			
		||||
    return 'http://local.aphront.com/D'.$this->getRevision()->getID();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setIsFirstMailToRecipients($first) {
 | 
			
		||||
    $this->isFirstMailToRecipients = $first;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function isFirstMailToRecipients() {
 | 
			
		||||
    return $this->isFirstMailToRecipients;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setIsFirstMailAboutRevision($first) {
 | 
			
		||||
    $this->isFirstMailAboutRevision = $first;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function isFirstMailAboutRevision() {
 | 
			
		||||
    return $this->isFirstMailAboutRevision;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function generateThreadIndex() {
 | 
			
		||||
    // When threading, Outlook ignores the 'References' and 'In-Reply-To'
 | 
			
		||||
    // headers that most clients use. Instead, it uses a custom 'Thread-Index'
 | 
			
		||||
    // header. The format of this header is something like this (from
 | 
			
		||||
    // camel-exchange-folder.c in Evolution Exchange):
 | 
			
		||||
 | 
			
		||||
    /* A new post to a folder gets a 27-byte-long thread index. (The value
 | 
			
		||||
     * is apparently unique but meaningless.) Each reply to a post gets a
 | 
			
		||||
     * 32-byte-long thread index whose first 27 bytes are the same as the
 | 
			
		||||
     * parent's thread index. Each reply to any of those gets a
 | 
			
		||||
     * 37-byte-long thread index, etc. The Thread-Index header contains a
 | 
			
		||||
     * base64 representation of this value.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    // The specific implementation uses a 27-byte header for the first email
 | 
			
		||||
    // a recipient receives, and a random 5-byte suffix (32 bytes total)
 | 
			
		||||
    // thereafter. This means that all the replies are (incorrectly) siblings,
 | 
			
		||||
    // but it would be very difficult to keep track of the entire tree and this
 | 
			
		||||
    // gets us reasonable client behavior.
 | 
			
		||||
 | 
			
		||||
    $base = substr(md5($this->getRevision()->getPHID()), 0, 27);
 | 
			
		||||
    if (!$this->isFirstMailAboutRevision()) {
 | 
			
		||||
      // not totally sure, but it seems like outlook orders replies by
 | 
			
		||||
      // thread-index rather than timestamp, so to get these to show up in the
 | 
			
		||||
      // right order we use the time as the last 4 bytes.
 | 
			
		||||
      $base .= ' ' . pack("N", time());
 | 
			
		||||
    }
 | 
			
		||||
    return base64_encode($base);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setHeraldTranscriptURI($herald_transcript_uri) {
 | 
			
		||||
    $this->heraldTranscriptURI = $herald_transcript_uri;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getHeraldTranscriptURI() {
 | 
			
		||||
    return $this->heraldTranscriptURI;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/applications/differential/mail/base/__init__.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/applications/differential/mail/base/__init__.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/metamta/storage/mail');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialMail.php');
 | 
			
		||||
							
								
								
									
										39
									
								
								src/applications/differential/mail/ccwelcome/DifferentialCCWelcomeMail.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										39
									
								
								src/applications/differential/mail/ccwelcome/DifferentialCCWelcomeMail.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class DifferentialCCWelcomeMail extends DifferentialReviewRequestMail {
 | 
			
		||||
 | 
			
		||||
  protected function renderSubject() {
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
    return 'Added to CC: '.$revision->getName();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderBody() {
 | 
			
		||||
 | 
			
		||||
    $actor = $this->getActorName();
 | 
			
		||||
    $name  = $this->getRevision()->getName();
 | 
			
		||||
    $body = array();
 | 
			
		||||
 | 
			
		||||
    $body[] = "{$actor} added you to the CC list for the revision \"{$name}\".";
 | 
			
		||||
    $body[] = null;
 | 
			
		||||
 | 
			
		||||
    $body[] = $this->renderReviewRequestBody();
 | 
			
		||||
 | 
			
		||||
    return implode("\n", $body);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/applications/differential/mail/ccwelcome/__init__.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/applications/differential/mail/ccwelcome/__init__.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/reviewrequest');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialCCWelcomeMail.php');
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class DifferentialDiffContentMail extends DifferentialMail {
 | 
			
		||||
 | 
			
		||||
  protected $content;
 | 
			
		||||
 | 
			
		||||
  public function __construct(DifferentialRevision $revision, $content) {
 | 
			
		||||
    $this->setRevision($revision);
 | 
			
		||||
    $this->content = $content;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderSubject() {
 | 
			
		||||
    return "Content: ".$this->getRevision()->getName();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderBody() {
 | 
			
		||||
    return $this->content;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/applications/differential/mail/diffcontent/__init__.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/applications/differential/mail/diffcontent/__init__.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/base');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialDiffContentMail.php');
 | 
			
		||||
							
								
								
									
										114
									
								
								src/applications/differential/mail/feedback/DifferentialFeedbackMail.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										114
									
								
								src/applications/differential/mail/feedback/DifferentialFeedbackMail.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class DifferentialFeedbackMail extends DifferentialMail {
 | 
			
		||||
 | 
			
		||||
  protected $changedByCommit;
 | 
			
		||||
 | 
			
		||||
  public function setChangedByCommit($changed_by_commit) {
 | 
			
		||||
    $this->changedByCommit = $changed_by_commit;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getChangedByCommit() {
 | 
			
		||||
    return $this->changedByCommit;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function __construct(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    $actor_id,
 | 
			
		||||
    DifferentialFeedback $feedback,
 | 
			
		||||
    array $changesets,
 | 
			
		||||
    array $inline_comments) {
 | 
			
		||||
 | 
			
		||||
    $this->setRevision($revision);
 | 
			
		||||
    $this->setActorID($actor_id);
 | 
			
		||||
    $this->setFeedback($feedback);
 | 
			
		||||
    $this->setChangesets($changesets);
 | 
			
		||||
    $this->setInlineComments($inline_comments);
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderSubject() {
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
    $verb = $this->getVerb();
 | 
			
		||||
    return ucwords($verb).': '.$revision->getName();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getVerb() {
 | 
			
		||||
    $feedback = $this->getFeedback();
 | 
			
		||||
    $action = $feedback->getAction();
 | 
			
		||||
    $verb = DifferentialAction::getActionVerb($action);
 | 
			
		||||
    return $verb;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderBody() {
 | 
			
		||||
 | 
			
		||||
    $feedback = $this->getFeedback();
 | 
			
		||||
 | 
			
		||||
    $actor = $this->getActorName();
 | 
			
		||||
    $name  = $this->getRevision()->getName();
 | 
			
		||||
    $verb  = $this->getVerb();
 | 
			
		||||
 | 
			
		||||
    $body  = array();
 | 
			
		||||
 | 
			
		||||
    $body[] = "{$actor} has {$verb} the revision \"{$name}\".";
 | 
			
		||||
    $body[] = null;
 | 
			
		||||
 | 
			
		||||
    $content = $feedback->getContent();
 | 
			
		||||
    if (strlen($content)) {
 | 
			
		||||
      $body[] = $this->formatText($content);
 | 
			
		||||
      $body[] = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($this->getChangedByCommit()) {
 | 
			
		||||
      $body[] = 'CHANGED PRIOR TO COMMIT';
 | 
			
		||||
      $body[] = '  This revision was updated prior to commit.';
 | 
			
		||||
      $body[] = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $inlines = $this->getInlineComments();
 | 
			
		||||
    if ($inlines) {
 | 
			
		||||
      $body[] = 'INLINE COMMENTS';
 | 
			
		||||
      $changesets = $this->getChangesets();
 | 
			
		||||
      foreach ($inlines as $inline) {
 | 
			
		||||
        $changeset = $changesets[$inline->getChangesetID()];
 | 
			
		||||
        if (!$changeset) {
 | 
			
		||||
          throw new Exception('Changeset missing!');
 | 
			
		||||
        }
 | 
			
		||||
        $file = $changeset->getFilename();
 | 
			
		||||
        $line = $inline->renderLineRange();
 | 
			
		||||
        $content = $inline->getContent();
 | 
			
		||||
        $body[] = $this->formatText("{$file}:{$line} {$content}");
 | 
			
		||||
      }
 | 
			
		||||
      $body[] = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $body[] = $this->renderRevisionDetailLink();
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
    if ($revision->getStatus() == DifferentialRevisionStatus::COMMITTED) {
 | 
			
		||||
      $rev_ref = $revision->getRevisionRef();
 | 
			
		||||
      if ($rev_ref) {
 | 
			
		||||
        $body[] = "  Detail URL: ".$rev_ref->getDetailURL();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    $body[] = null;
 | 
			
		||||
 | 
			
		||||
    return implode("\n", $body);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/applications/differential/mail/feedback/__init__.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/applications/differential/mail/feedback/__init__.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/constants/action');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/base');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialFeedbackMail.php');
 | 
			
		||||
							
								
								
									
										65
									
								
								src/applications/differential/mail/newdiff/DifferentialNewDiffMail.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										65
									
								
								src/applications/differential/mail/newdiff/DifferentialNewDiffMail.php
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class DifferentialNewDiffMail extends DifferentialReviewRequestMail {
 | 
			
		||||
 | 
			
		||||
  protected function renderSubject() {
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
    $line_count = $revision->getLineCount();
 | 
			
		||||
    $lines = ($line_count == 1 ? "1 line" : "{$line_count} lines");
 | 
			
		||||
 | 
			
		||||
    if ($this->isFirstMailToRecipients()) {
 | 
			
		||||
      $verb = 'Request';
 | 
			
		||||
    } else {
 | 
			
		||||
      $verb = 'Updated';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return "{$verb} ({$lines}): ".$revision->getTitle();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildSubject() {
 | 
			
		||||
    if (!$this->isFirstMailToRecipients()) {
 | 
			
		||||
      return parent::buildSubject();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $prefix = self::SUBJECT_PREFIX;
 | 
			
		||||
 | 
			
		||||
    $subject = $this->renderSubject();
 | 
			
		||||
 | 
			
		||||
    return "{$prefix} {$subject}";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderBody() {
 | 
			
		||||
    $actor = $this->getActorName();
 | 
			
		||||
 | 
			
		||||
    $name  = $this->getRevision()->getTitle();
 | 
			
		||||
 | 
			
		||||
    $body = array();
 | 
			
		||||
 | 
			
		||||
    if ($this->isFirstMailToRecipients()) {
 | 
			
		||||
      $body[] = "{$actor} requested code review of \"{$name}\".";
 | 
			
		||||
    } else {
 | 
			
		||||
      $body[] = "{$actor} updated the revision \"{$name}\".";
 | 
			
		||||
    }
 | 
			
		||||
    $body[] = null;
 | 
			
		||||
 | 
			
		||||
    $body[] = $this->renderReviewRequestBody();
 | 
			
		||||
 | 
			
		||||
    return implode("\n", $body);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/applications/differential/mail/newdiff/__init__.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/applications/differential/mail/newdiff/__init__.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/reviewrequest');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialNewDiffMail.php');
 | 
			
		||||
@@ -0,0 +1,74 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Facebook, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *   http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
abstract class DifferentialReviewRequestMail extends DifferentialMail {
 | 
			
		||||
 | 
			
		||||
  protected $comments;
 | 
			
		||||
 | 
			
		||||
  public function setComments($comments) {
 | 
			
		||||
    $this->comments = $comments;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getComments() {
 | 
			
		||||
    return $this->comments;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function __construct(
 | 
			
		||||
    DifferentialRevision $revision,
 | 
			
		||||
    $actor_id,
 | 
			
		||||
    array $changesets) {
 | 
			
		||||
 | 
			
		||||
    $this->setRevision($revision);
 | 
			
		||||
    $this->setActorID($actor_id);
 | 
			
		||||
    $this->setChangesets($changesets);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderReviewRequestBody() {
 | 
			
		||||
    $revision = $this->getRevision();
 | 
			
		||||
 | 
			
		||||
    $body = array();
 | 
			
		||||
    if ($this->isFirstMailToRecipients()) {
 | 
			
		||||
      $body[] = $this->formatText($revision->getSummary());
 | 
			
		||||
      $body[] = null;
 | 
			
		||||
 | 
			
		||||
      $body[] = 'TEST PLAN';
 | 
			
		||||
      $body[] = $this->formatText($revision->getTestPlan());
 | 
			
		||||
      $body[] = null;
 | 
			
		||||
    } else {
 | 
			
		||||
      if (strlen($this->getComments())) {
 | 
			
		||||
        $body[] = $this->formatText($this->getComments());
 | 
			
		||||
        $body[] = null;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $body[] = $this->renderRevisionDetailLink();
 | 
			
		||||
    $body[] = null;
 | 
			
		||||
 | 
			
		||||
    $changesets = $this->getChangesets();
 | 
			
		||||
    if ($changesets) {
 | 
			
		||||
      $body[] = 'AFFECTED FILES';
 | 
			
		||||
      foreach ($changesets as $changeset) {
 | 
			
		||||
        $body[] = '  '.$changeset->getFilename();
 | 
			
		||||
      }
 | 
			
		||||
      $body[] = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return implode("\n", $body);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is automatically generated. Lint this module to rebuild it.
 | 
			
		||||
 * @generated
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_module('phabricator', 'applications/differential/mail/base');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
phutil_require_source('DifferentialReviewRequestMail.php');
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
 | 
			
		||||
class DifferentialRevision extends DifferentialDAO {
 | 
			
		||||
 | 
			
		||||
  protected $name;
 | 
			
		||||
  protected $title;
 | 
			
		||||
  protected $status;
 | 
			
		||||
 | 
			
		||||
  protected $summary;
 | 
			
		||||
@@ -33,4 +33,26 @@ class DifferentialRevision extends DifferentialDAO {
 | 
			
		||||
 | 
			
		||||
  protected $lineCount;
 | 
			
		||||
  
 | 
			
		||||
  public function getConfiguration() {
 | 
			
		||||
    return array(
 | 
			
		||||
      self::CONFIG_AUX_PHID => true,
 | 
			
		||||
    ) + parent::getConfiguration();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function generatePHID() {
 | 
			
		||||
    return PhabricatorPHID::generateNewPHID('DREV');
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  public function loadRelationships() {
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  public function getReviewers() {
 | 
			
		||||
    return array();
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  public function getCCPHIDs() {
 | 
			
		||||
    return array();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,11 @@ final class AphrontFormView extends AphrontView {
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function addHiddenInput($key, $value) {
 | 
			
		||||
    $this->data[$key] = $value;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function render() {
 | 
			
		||||
    require_celerity_resource('aphront-form-view-css');
 | 
			
		||||
    return phutil_render_tag(
 | 
			
		||||
@@ -59,6 +64,9 @@ final class AphrontFormView extends AphrontView {
 | 
			
		||||
    );
 | 
			
		||||
    $inputs = array();
 | 
			
		||||
    foreach ($data as $key => $value) {
 | 
			
		||||
      if ($value === null) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      $inputs[] = phutil_render_tag(
 | 
			
		||||
        'input',
 | 
			
		||||
        array(
 | 
			
		||||
 
 | 
			
		||||
@@ -18,16 +18,36 @@
 | 
			
		||||
 | 
			
		||||
class AphrontFormTextAreaControl extends AphrontFormControl {
 | 
			
		||||
 | 
			
		||||
  const HEIGHT_VERY_SHORT = 'very-short';
 | 
			
		||||
  const HEIGHT_SHORT      = 'short';
 | 
			
		||||
  
 | 
			
		||||
  private $height;
 | 
			
		||||
 | 
			
		||||
  public function setHeight($height) {
 | 
			
		||||
    $this->height = $height;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getCustomControlClass() {
 | 
			
		||||
    return 'aphront-form-control-textarea';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function renderInput() {
 | 
			
		||||
 | 
			
		||||
    $height_class = null;
 | 
			
		||||
    switch ($this->height) {
 | 
			
		||||
      case self::HEIGHT_VERY_SHORT:
 | 
			
		||||
      case self::HEIGHT_SHORT:
 | 
			
		||||
        $height_class = 'aphront-textarea-'.$this->height;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return phutil_render_tag(
 | 
			
		||||
      'textarea',
 | 
			
		||||
      array(
 | 
			
		||||
        'name'      => $this->getName(),
 | 
			
		||||
        'disabled'  => $this->getDisabled() ? 'disabled' : null,
 | 
			
		||||
        'class'     => $height_class,
 | 
			
		||||
      ),
 | 
			
		||||
      phutil_escape_html($this->getValue()));
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -114,11 +114,12 @@ class PhabricatorStandardPageView extends AphrontPageView {
 | 
			
		||||
 | 
			
		||||
    $login_stuff = null;
 | 
			
		||||
    $request = $this->getRequest();
 | 
			
		||||
    if ($request) {
 | 
			
		||||
      $user = $request->getUser();
 | 
			
		||||
 | 
			
		||||
      if ($user->getPHID()) {
 | 
			
		||||
        $login_stuff = 'Logged in as '.phutil_escape_html($user->getUsername());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return
 | 
			
		||||
      '<div class="phabricator-standard-page">'.
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,10 @@
 | 
			
		||||
  margin: 0.5em 0 0em 2%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.aphront-form-control-textarea textarea.aphront-textarea-very-short {
 | 
			
		||||
  height: 3em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.aphront-form-control-select .aphront-form-input {
 | 
			
		||||
  padding-top: 2px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user