2011-02-08 19:53:59 +01:00
|
|
|
<?php
|
|
|
|
|
2012-07-11 20:40:10 +02:00
|
|
|
final class ManiphestTask extends ManiphestDAO
|
2013-02-15 16:47:14 +01:00
|
|
|
implements
|
|
|
|
PhabricatorMarkupInterface,
|
|
|
|
PhabricatorPolicyInterface,
|
2013-03-30 17:32:29 +01:00
|
|
|
PhabricatorTokenReceiverInterface,
|
2013-10-25 21:52:00 +02:00
|
|
|
PhabricatorFlaggableInterface,
|
2013-09-17 00:58:35 +02:00
|
|
|
PhrequentTrackableInterface,
|
|
|
|
PhabricatorCustomFieldInterface {
|
2012-07-11 20:40:10 +02:00
|
|
|
|
|
|
|
const MARKUP_FIELD_DESCRIPTION = 'markup:desc';
|
2011-02-08 19:53:59 +01:00
|
|
|
|
|
|
|
protected $authorPHID;
|
|
|
|
protected $ownerPHID;
|
2011-02-21 05:08:16 +01:00
|
|
|
protected $ccPHIDs = array();
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2013-09-24 21:03:14 +02:00
|
|
|
protected $status = ManiphestTaskStatus::STATUS_OPEN;
|
2011-02-08 19:53:59 +01:00
|
|
|
protected $priority;
|
2013-09-24 21:03:14 +02:00
|
|
|
protected $subpriority = 0;
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2013-09-25 20:16:55 +02:00
|
|
|
protected $title = '';
|
2013-10-14 20:46:14 +02:00
|
|
|
protected $originalTitle = '';
|
2013-09-25 20:16:55 +02:00
|
|
|
protected $description = '';
|
2011-10-14 22:11:58 +02:00
|
|
|
protected $originalEmailSource;
|
2011-05-05 08:09:42 +02:00
|
|
|
protected $mailKey;
|
2013-09-25 20:18:47 +02:00
|
|
|
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
|
|
|
|
protected $editPolicy = PhabricatorPolicies::POLICY_USER;
|
2011-05-05 08:09:42 +02:00
|
|
|
|
2011-02-17 23:32:01 +01:00
|
|
|
protected $attached = array();
|
2011-02-21 05:08:16 +01:00
|
|
|
protected $projectPHIDs = array();
|
2011-06-30 01:16:33 +02:00
|
|
|
private $projectsNeedUpdate;
|
2011-07-07 19:24:49 +02:00
|
|
|
private $subscribersNeedUpdate;
|
2011-02-08 19:53:59 +01:00
|
|
|
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-27 03:50:17 +02:00
|
|
|
protected $ownerOrdering;
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
private $groupByProjectPHID = self::ATTACHABLE;
|
2013-09-17 00:58:35 +02:00
|
|
|
private $customFields = self::ATTACHABLE;
|
2011-12-24 04:03:28 +01:00
|
|
|
|
2013-10-09 22:53:17 +02:00
|
|
|
public static function initializeNewTask(PhabricatorUser $actor) {
|
|
|
|
$app = id(new PhabricatorApplicationQuery())
|
|
|
|
->setViewer($actor)
|
|
|
|
->withClasses(array('PhabricatorApplicationManiphest'))
|
|
|
|
->executeOne();
|
|
|
|
|
|
|
|
$view_policy = $app->getPolicy(ManiphestCapabilityDefaultView::CAPABILITY);
|
|
|
|
$edit_policy = $app->getPolicy(ManiphestCapabilityDefaultEdit::CAPABILITY);
|
|
|
|
|
|
|
|
return id(new ManiphestTask())
|
|
|
|
->setPriority(ManiphestTaskPriority::getDefaultPriority())
|
|
|
|
->setAuthorPHID($actor->getPHID())
|
|
|
|
->setViewPolicy($view_policy)
|
|
|
|
->setEditPolicy($edit_policy);
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
public function getConfiguration() {
|
|
|
|
return array(
|
|
|
|
self::CONFIG_AUX_PHID => true,
|
|
|
|
self::CONFIG_SERIALIZATION => array(
|
|
|
|
'ccPHIDs' => self::SERIALIZATION_JSON,
|
2011-02-17 23:32:01 +01:00
|
|
|
'attached' => self::SERIALIZATION_JSON,
|
2011-02-21 05:08:16 +01:00
|
|
|
'projectPHIDs' => self::SERIALIZATION_JSON,
|
2011-02-08 19:53:59 +01:00
|
|
|
),
|
|
|
|
) + parent::getConfiguration();
|
|
|
|
}
|
|
|
|
|
2012-07-19 05:41:42 +02:00
|
|
|
public function loadDependsOnTaskPHIDs() {
|
|
|
|
return PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$this->getPHID(),
|
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function loadDependedOnByTaskPHIDs() {
|
|
|
|
return PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$this->getPHID(),
|
|
|
|
PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK);
|
|
|
|
}
|
|
|
|
|
2011-02-17 23:32:01 +01:00
|
|
|
public function getAttachedPHIDs($type) {
|
|
|
|
return array_keys(idx($this->attached, $type, array()));
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
public function generatePHID() {
|
2013-07-21 21:05:28 +02:00
|
|
|
return PhabricatorPHID::generateNewPHID(ManiphestPHIDTypeTask::TYPECONST);
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|
|
|
|
|
2011-02-09 21:47:24 +01:00
|
|
|
public function getCCPHIDs() {
|
2011-11-16 18:21:11 +01:00
|
|
|
return array_values(nonempty($this->ccPHIDs, array()));
|
2011-02-09 21:47:24 +01:00
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
public function setProjectPHIDs(array $phids) {
|
2011-11-16 18:21:11 +01:00
|
|
|
$this->projectPHIDs = array_values($phids);
|
2011-06-30 01:16:33 +02:00
|
|
|
$this->projectsNeedUpdate = true;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-11-16 18:21:11 +01:00
|
|
|
public function getProjectPHIDs() {
|
|
|
|
return array_values(nonempty($this->projectPHIDs, array()));
|
|
|
|
}
|
|
|
|
|
2011-07-07 19:24:49 +02:00
|
|
|
public function setCCPHIDs(array $phids) {
|
2011-11-16 18:21:11 +01:00
|
|
|
$this->ccPHIDs = array_values($phids);
|
2011-07-07 19:24:49 +02:00
|
|
|
$this->subscribersNeedUpdate = true;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setOwnerPHID($phid) {
|
2013-05-23 18:22:18 +02:00
|
|
|
$this->ownerPHID = nonempty($phid, null);
|
2011-07-07 19:24:49 +02:00
|
|
|
$this->subscribersNeedUpdate = true;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-06-14 05:52:10 +02:00
|
|
|
public function setTitle($title) {
|
|
|
|
$this->title = $title;
|
|
|
|
if (!$this->getID()) {
|
|
|
|
$this->originalTitle = $title;
|
|
|
|
}
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
public function attachGroupByProjectPHID($phid) {
|
|
|
|
$this->groupByProjectPHID = $phid;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getGroupByProjectPHID() {
|
|
|
|
return $this->assertAttached($this->groupByProjectPHID);
|
|
|
|
}
|
|
|
|
|
2011-05-05 08:09:42 +02:00
|
|
|
public function save() {
|
|
|
|
if (!$this->mailKey) {
|
Replace callsites to sha1() that use it to asciify entropy with
Filesystem::readRandomCharacters()
Summary: See T547. To improve auditability of use of crypto-sensitive hash
functions, use Filesystem::readRandomCharacters() in place of
sha1(Filesystem::readRandomBytes()) when we're just generating random ASCII
strings.
Test Plan:
- Generated a new PHID.
- Logged out and logged back in (to test sessions).
- Regenerated Conduit certificate.
- Created a new task, verified mail key generated sensibly.
- Created a new revision, verified mail key generated sensibly.
- Ran "arc list", got blocked, installed new certificate, ran "arc list"
again.
Reviewers: jungejason, nh, tuomaspelkonen, aran, benmathews
Reviewed By: jungejason
CC: aran, epriestley, jungejason
Differential Revision: 1000
2011-10-11 04:22:30 +02:00
|
|
|
$this->mailKey = Filesystem::readRandomCharacters(20);
|
2011-05-05 08:09:42 +02:00
|
|
|
}
|
2011-06-30 01:16:33 +02:00
|
|
|
|
|
|
|
$result = parent::save();
|
|
|
|
|
|
|
|
if ($this->projectsNeedUpdate) {
|
|
|
|
// If we've changed the project PHIDs for this task, update the link
|
|
|
|
// table.
|
|
|
|
ManiphestTaskProject::updateTaskProjects($this);
|
|
|
|
$this->projectsNeedUpdate = false;
|
|
|
|
}
|
|
|
|
|
2011-07-07 19:24:49 +02:00
|
|
|
if ($this->subscribersNeedUpdate) {
|
|
|
|
// If we've changed the subscriber PHIDs for this task, update the link
|
|
|
|
// table.
|
|
|
|
ManiphestTaskSubscriber::updateTaskSubscribers($this);
|
|
|
|
$this->subscribersNeedUpdate = false;
|
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
return $result;
|
2011-05-05 08:09:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-24 04:03:28 +01:00
|
|
|
|
2012-07-11 20:40:10 +02:00
|
|
|
|
|
|
|
/* -( Markup Interface )--------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task markup
|
|
|
|
*/
|
|
|
|
public function getMarkupFieldKey($field) {
|
|
|
|
$hash = PhabricatorHash::digest($this->getMarkupText($field));
|
|
|
|
$id = $this->getID();
|
|
|
|
return "maniphest:T{$id}:{$field}:{$hash}";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task markup
|
|
|
|
*/
|
|
|
|
public function getMarkupText($field) {
|
|
|
|
return $this->getDescription();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task markup
|
|
|
|
*/
|
|
|
|
public function newMarkupEngine($field) {
|
|
|
|
return PhabricatorMarkupEngine::newManiphestMarkupEngine();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task markup
|
|
|
|
*/
|
|
|
|
public function didMarkupText(
|
|
|
|
$field,
|
|
|
|
$output,
|
|
|
|
PhutilMarkupEngine $engine) {
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task markup
|
|
|
|
*/
|
|
|
|
public function shouldUseMarkupCache($field) {
|
|
|
|
return (bool)$this->getID();
|
|
|
|
}
|
|
|
|
|
2013-02-15 16:47:14 +01:00
|
|
|
|
|
|
|
/* -( Policy Interface )--------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
public function getCapabilities() {
|
|
|
|
return array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getPolicy($capability) {
|
2013-09-25 22:44:45 +02:00
|
|
|
switch ($capability) {
|
|
|
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
|
|
|
return $this->getViewPolicy();
|
|
|
|
case PhabricatorPolicyCapability::CAN_EDIT:
|
|
|
|
return $this->getEditPolicy();
|
|
|
|
}
|
2013-02-15 16:47:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function hasAutomaticCapability($capability, PhabricatorUser $user) {
|
2013-09-25 22:44:45 +02:00
|
|
|
|
|
|
|
// The owner of a task can always view and edit it.
|
|
|
|
$owner_phid = $this->getOwnerPHID();
|
|
|
|
if ($owner_phid) {
|
|
|
|
$user_phid = $user->getPHID();
|
|
|
|
if ($user_phid == $owner_phid) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-15 16:47:14 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-09-27 17:43:41 +02:00
|
|
|
public function describeAutomaticCapability($capability) {
|
|
|
|
return pht(
|
|
|
|
'The owner of a task can always view and edit it.');
|
|
|
|
}
|
|
|
|
|
2013-02-19 02:44:45 +01:00
|
|
|
|
|
|
|
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
|
|
|
|
|
|
|
|
public function getUsersToNotifyOfTokenGiven() {
|
|
|
|
// Sort of ambiguous who this was intended for; just let them both know.
|
|
|
|
return array_filter(
|
|
|
|
array_unique(
|
|
|
|
array(
|
|
|
|
$this->getAuthorPHID(),
|
|
|
|
$this->getOwnerPHID(),
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
2013-09-17 00:58:35 +02:00
|
|
|
|
|
|
|
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
public function getCustomFieldSpecificationForRole($role) {
|
|
|
|
return PhabricatorEnv::getEnvConfig('maniphest.fields');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCustomFieldBaseClass() {
|
|
|
|
return 'ManiphestCustomField';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCustomFields() {
|
|
|
|
return $this->assertAttached($this->customFields);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
|
|
|
|
$this->customFields = $fields;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|