mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
DifferentialRevisionEditor
This commit is contained in:
parent
d37692ac06
commit
de1fb8ac7d
24 changed files with 1448 additions and 44 deletions
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue