2013-03-15 12:28:43 +01:00
|
|
|
<?php
|
|
|
|
|
2013-04-26 18:07:38 +02:00
|
|
|
final class ReleephRequest extends ReleephDAO
|
2013-08-14 19:04:18 +02:00
|
|
|
implements
|
|
|
|
PhabricatorPolicyInterface,
|
|
|
|
PhabricatorCustomFieldInterface {
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
protected $branchID;
|
|
|
|
protected $requestUserPHID;
|
|
|
|
protected $details = array();
|
|
|
|
protected $userIntents = array();
|
|
|
|
protected $inBranch;
|
|
|
|
protected $pickStatus;
|
2013-05-08 11:38:07 +02:00
|
|
|
protected $mailKey;
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
// Information about the thing being requested
|
|
|
|
protected $requestCommitPHID;
|
|
|
|
|
|
|
|
// Information about the last commit to the releeph branch
|
|
|
|
protected $commitIdentifier;
|
|
|
|
protected $commitPHID;
|
|
|
|
|
|
|
|
// Pre-populated handles that we'll bulk load in ReleephBranch
|
2013-09-03 15:02:14 +02:00
|
|
|
private $handles = self::ATTACHABLE;
|
2013-08-14 19:04:18 +02:00
|
|
|
private $customFields = self::ATTACHABLE;
|
|
|
|
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* -( Constants and helper methods )--------------------------------------- */
|
|
|
|
|
|
|
|
const INTENT_WANT = 'want';
|
|
|
|
const INTENT_PASS = 'pass';
|
|
|
|
|
|
|
|
const PICK_PENDING = 1; // old
|
|
|
|
const PICK_FAILED = 2;
|
|
|
|
const PICK_OK = 3;
|
|
|
|
const PICK_MANUAL = 4; // old
|
|
|
|
const REVERT_OK = 5;
|
|
|
|
const REVERT_FAILED = 6;
|
|
|
|
|
|
|
|
public function shouldBeInBranch() {
|
|
|
|
return
|
|
|
|
$this->getPusherIntent() == self::INTENT_WANT &&
|
|
|
|
/**
|
|
|
|
* We use "!= pass" instead of "== want" in case the requestor intent is
|
|
|
|
* not present. In other words, only revert if the requestor explicitly
|
|
|
|
* passed.
|
|
|
|
*/
|
|
|
|
$this->getRequestorIntent() != self::INTENT_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will return INTENT_WANT if any pusher wants this request, and no pusher
|
|
|
|
* passes on this request.
|
|
|
|
*/
|
|
|
|
public function getPusherIntent() {
|
|
|
|
$project = $this->loadReleephProject();
|
2013-10-15 01:07:17 +02:00
|
|
|
if (!$project) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2013-03-15 12:28:43 +01:00
|
|
|
if (!$project->getPushers()) {
|
|
|
|
return self::INTENT_WANT;
|
|
|
|
}
|
|
|
|
|
|
|
|
$found_pusher_want = false;
|
|
|
|
foreach ($this->userIntents as $phid => $intent) {
|
2013-04-18 18:45:12 +02:00
|
|
|
if ($project->isAuthoritativePHID($phid)) {
|
2013-03-15 12:28:43 +01:00
|
|
|
if ($intent == self::INTENT_PASS) {
|
|
|
|
return self::INTENT_PASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
$found_pusher_want = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($found_pusher_want) {
|
|
|
|
return self::INTENT_WANT;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getRequestorIntent() {
|
|
|
|
return idx($this->userIntents, $this->requestUserPHID);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getStatus() {
|
|
|
|
return $this->calculateStatus();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function calculateStatus() {
|
|
|
|
if ($this->shouldBeInBranch()) {
|
|
|
|
if ($this->getInBranch()) {
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_PICKED;
|
2013-03-15 12:28:43 +01:00
|
|
|
} else {
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_NEEDS_PICK;
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ($this->getInBranch()) {
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_NEEDS_REVERT;
|
2013-03-15 12:28:43 +01:00
|
|
|
} else {
|
|
|
|
$has_been_in_branch = $this->getCommitIdentifier();
|
|
|
|
// Regardless of why we reverted something, always say reverted if it
|
|
|
|
// was once in the branch.
|
|
|
|
if ($has_been_in_branch) {
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_REVERTED;
|
2013-03-15 12:28:43 +01:00
|
|
|
} elseif ($this->getPusherIntent() === ReleephRequest::INTENT_PASS) {
|
|
|
|
// Otherwise, if it has never been in the branch, explicitly say why:
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_REJECTED;
|
2013-03-15 12:28:43 +01:00
|
|
|
} elseif ($this->getRequestorIntent() === ReleephRequest::INTENT_WANT) {
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_REQUESTED;
|
2013-03-15 12:28:43 +01:00
|
|
|
} else {
|
2013-05-10 18:07:33 +02:00
|
|
|
return ReleephRequestStatus::STATUS_ABANDONED;
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -( Lisk mechanics )----------------------------------------------------- */
|
|
|
|
|
|
|
|
public function getConfiguration() {
|
|
|
|
return array(
|
|
|
|
self::CONFIG_AUX_PHID => true,
|
|
|
|
self::CONFIG_SERIALIZATION => array(
|
|
|
|
'details' => self::SERIALIZATION_JSON,
|
|
|
|
'userIntents' => self::SERIALIZATION_JSON,
|
|
|
|
),
|
|
|
|
) + parent::getConfiguration();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function generatePHID() {
|
|
|
|
return PhabricatorPHID::generateNewPHID(
|
2013-07-30 21:38:32 +02:00
|
|
|
ReleephPHIDTypeRequest::TYPECONST);
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
2013-05-08 11:38:07 +02:00
|
|
|
public function save() {
|
|
|
|
if (!$this->getMailKey()) {
|
|
|
|
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
|
|
|
}
|
|
|
|
return parent::save();
|
|
|
|
}
|
|
|
|
|
2013-03-15 12:28:43 +01:00
|
|
|
|
|
|
|
/* -( Helpful accessors )--------------------------------------------------- */
|
|
|
|
|
|
|
|
public function setHandles($handles) {
|
|
|
|
$this->handles = $handles;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getHandles() {
|
2013-09-03 15:02:14 +02:00
|
|
|
return $this->assertAttached($this->handles);
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getDetail($key, $default = null) {
|
|
|
|
return idx($this->getDetails(), $key, $default);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setDetail($key, $value) {
|
|
|
|
$this->details[$key] = $value;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getReason() {
|
|
|
|
// Backward compatibility: reason used to be called comments
|
|
|
|
$reason = $this->getDetail('reason');
|
|
|
|
if (!$reason) {
|
|
|
|
return $this->getDetail('comments');
|
|
|
|
}
|
|
|
|
return $reason;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSummary() {
|
|
|
|
/**
|
|
|
|
* Instead, you can use:
|
|
|
|
* - getDetail('summary') // the actual user-chosen summary
|
|
|
|
* - getSummaryForDisplay() // falls back to the original commit title
|
|
|
|
*
|
|
|
|
* Or for the fastidious:
|
|
|
|
* - id(new ReleephSummaryFieldSpecification())
|
|
|
|
* ->setReleephRequest($rr)
|
|
|
|
* ->getValue() // programmatic equivalent to getDetail()
|
|
|
|
*/
|
|
|
|
throw new Exception(
|
|
|
|
"getSummary() has been deprecated!");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allow a null summary, and fall back to the title of the commit.
|
|
|
|
*/
|
|
|
|
public function getSummaryForDisplay() {
|
|
|
|
$summary = $this->getDetail('summary');
|
|
|
|
|
|
|
|
if (!$summary) {
|
|
|
|
$pr_commit_data = $this->loadPhabricatorRepositoryCommitData();
|
|
|
|
if ($pr_commit_data) {
|
|
|
|
$message_lines = explode("\n", $pr_commit_data->getCommitMessage());
|
|
|
|
$message_lines = array_filter($message_lines);
|
|
|
|
$summary = head($message_lines);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$summary) {
|
|
|
|
$summary = '(no summary given and commit message empty or unparsed)';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $summary;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function loadRequestCommitDiffPHID() {
|
2013-09-13 20:40:52 +02:00
|
|
|
$phids = array();
|
2013-05-01 11:22:28 +02:00
|
|
|
$commit = $this->loadPhabricatorRepositoryCommit();
|
|
|
|
if ($commit) {
|
2013-09-13 20:40:52 +02:00
|
|
|
$phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$commit->getPHID(),
|
|
|
|
PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV);
|
2013-05-01 11:22:28 +02:00
|
|
|
}
|
2013-09-13 20:40:52 +02:00
|
|
|
|
|
|
|
return head($phids);
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -( Loading external objects )------------------------------------------- */
|
|
|
|
|
|
|
|
public function loadReleephBranch() {
|
|
|
|
return $this->loadOneRelative(
|
|
|
|
new ReleephBranch(),
|
|
|
|
'id',
|
|
|
|
'getBranchID');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function loadReleephProject() {
|
2013-10-15 01:07:17 +02:00
|
|
|
$branch = $this->loadReleephBranch();
|
|
|
|
if ($branch) {
|
|
|
|
return $branch->loadReleephProject();
|
|
|
|
}
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function loadPhabricatorRepositoryCommit() {
|
|
|
|
return $this->loadOneRelative(
|
|
|
|
new PhabricatorRepositoryCommit(),
|
|
|
|
'phid',
|
|
|
|
'getRequestCommitPHID');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function loadPhabricatorRepositoryCommitData() {
|
2013-04-12 01:54:51 +02:00
|
|
|
$commit = $this->loadPhabricatorRepositoryCommit();
|
|
|
|
if ($commit) {
|
|
|
|
return $commit->loadOneRelative(
|
|
|
|
new PhabricatorRepositoryCommitData(),
|
|
|
|
'commitID');
|
|
|
|
}
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|
|
|
|
|
2013-09-26 21:37:19 +02:00
|
|
|
// TODO: (T603) Get rid of all this one-off ad-hoc loading.
|
2013-03-15 12:28:43 +01:00
|
|
|
public function loadDifferentialRevision() {
|
2013-05-07 12:13:22 +02:00
|
|
|
$diff_phid = $this->loadRequestCommitDiffPHID();
|
|
|
|
if (!$diff_phid) {
|
|
|
|
return null;
|
|
|
|
}
|
2013-03-15 12:28:43 +01:00
|
|
|
return $this->loadOneRelative(
|
|
|
|
new DifferentialRevision(),
|
|
|
|
'phid',
|
|
|
|
'loadRequestCommitDiffPHID');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -( State change helpers )----------------------------------------------- */
|
|
|
|
|
|
|
|
public function setUserIntent(PhabricatorUser $user, $intent) {
|
|
|
|
$this->userIntents[$user->getPHID()] = $intent;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -( Migrating to status-less ReleephRequests )--------------------------- */
|
|
|
|
|
|
|
|
protected function didReadData() {
|
|
|
|
if ($this->userIntents === null) {
|
|
|
|
$this->userIntents = array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setStatus($value) {
|
|
|
|
throw new Exception('`status` is now deprecated!');
|
|
|
|
}
|
|
|
|
|
2013-04-26 18:07:38 +02:00
|
|
|
/* -( Make magic Lisk methods private )------------------------------------ */
|
|
|
|
|
|
|
|
private function setUserIntents(array $ar) {
|
|
|
|
return parent::setUserIntents($ar);
|
|
|
|
}
|
|
|
|
|
2013-08-14 19:04:18 +02:00
|
|
|
|
2013-04-26 18:07:38 +02:00
|
|
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
|
|
|
|
2013-08-14 19:04:18 +02:00
|
|
|
|
2013-04-26 18:07:38 +02:00
|
|
|
public function getCapabilities() {
|
|
|
|
return array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
2013-07-30 21:38:32 +02:00
|
|
|
PhabricatorPolicyCapability::CAN_EDIT,
|
2013-04-26 18:07:38 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPolicy($capability) {
|
|
|
|
return PhabricatorPolicies::POLICY_USER;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-09-27 17:43:41 +02:00
|
|
|
public function describeAutomaticCapability($capability) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-14 19:04:18 +02:00
|
|
|
|
|
|
|
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
public function getCustomFieldSpecificationForRole($role) {
|
|
|
|
return PhabricatorEnv::getEnvConfig('releeph.fields');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCustomFieldBaseClass() {
|
|
|
|
return 'ReleephFieldSpecification';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCustomFields() {
|
|
|
|
return $this->assertAttached($this->customFields);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
|
|
|
|
$this->customFields = $fields;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-15 12:28:43 +01:00
|
|
|
}
|