PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/applications/maniphest/storage/ManiphestTask.php

http://github.com/facebook/phabricator
PHP | 607 lines | 442 code | 131 blank | 34 comment | 13 complexity | 83930cd580f87b5652ccbcaf6f797822 MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
  1. <?php
  2. final class ManiphestTask extends ManiphestDAO
  3. implements
  4. PhabricatorSubscribableInterface,
  5. PhabricatorMarkupInterface,
  6. PhabricatorPolicyInterface,
  7. PhabricatorTokenReceiverInterface,
  8. PhabricatorFlaggableInterface,
  9. PhabricatorMentionableInterface,
  10. PhrequentTrackableInterface,
  11. PhabricatorCustomFieldInterface,
  12. PhabricatorDestructibleInterface,
  13. PhabricatorApplicationTransactionInterface,
  14. PhabricatorProjectInterface,
  15. PhabricatorSpacesInterface,
  16. PhabricatorConduitResultInterface,
  17. PhabricatorFulltextInterface,
  18. PhabricatorFerretInterface,
  19. DoorkeeperBridgedObjectInterface,
  20. PhabricatorEditEngineSubtypeInterface,
  21. PhabricatorEditEngineLockableInterface,
  22. PhabricatorEditEngineMFAInterface,
  23. PhabricatorPolicyCodexInterface,
  24. PhabricatorUnlockableInterface {
  25. const MARKUP_FIELD_DESCRIPTION = 'markup:desc';
  26. protected $authorPHID;
  27. protected $ownerPHID;
  28. protected $status;
  29. protected $priority;
  30. protected $subpriority = 0;
  31. protected $title = '';
  32. protected $description = '';
  33. protected $originalEmailSource;
  34. protected $mailKey;
  35. protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
  36. protected $editPolicy = PhabricatorPolicies::POLICY_USER;
  37. protected $ownerOrdering;
  38. protected $spacePHID;
  39. protected $bridgedObjectPHID;
  40. protected $properties = array();
  41. protected $points;
  42. protected $subtype;
  43. protected $closedEpoch;
  44. protected $closerPHID;
  45. private $subscriberPHIDs = self::ATTACHABLE;
  46. private $groupByProjectPHID = self::ATTACHABLE;
  47. private $customFields = self::ATTACHABLE;
  48. private $edgeProjectPHIDs = self::ATTACHABLE;
  49. private $bridgedObject = self::ATTACHABLE;
  50. public static function initializeNewTask(PhabricatorUser $actor) {
  51. $app = id(new PhabricatorApplicationQuery())
  52. ->setViewer($actor)
  53. ->withClasses(array('PhabricatorManiphestApplication'))
  54. ->executeOne();
  55. $view_policy = $app->getPolicy(ManiphestDefaultViewCapability::CAPABILITY);
  56. $edit_policy = $app->getPolicy(ManiphestDefaultEditCapability::CAPABILITY);
  57. return id(new ManiphestTask())
  58. ->setStatus(ManiphestTaskStatus::getDefaultStatus())
  59. ->setPriority(ManiphestTaskPriority::getDefaultPriority())
  60. ->setAuthorPHID($actor->getPHID())
  61. ->setViewPolicy($view_policy)
  62. ->setEditPolicy($edit_policy)
  63. ->setSpacePHID($actor->getDefaultSpacePHID())
  64. ->setSubtype(PhabricatorEditEngineSubtype::SUBTYPE_DEFAULT)
  65. ->attachProjectPHIDs(array())
  66. ->attachSubscriberPHIDs(array());
  67. }
  68. protected function getConfiguration() {
  69. return array(
  70. self::CONFIG_AUX_PHID => true,
  71. self::CONFIG_SERIALIZATION => array(
  72. 'properties' => self::SERIALIZATION_JSON,
  73. ),
  74. self::CONFIG_COLUMN_SCHEMA => array(
  75. 'ownerPHID' => 'phid?',
  76. 'status' => 'text64',
  77. 'priority' => 'uint32',
  78. 'title' => 'sort',
  79. 'description' => 'text',
  80. 'mailKey' => 'bytes20',
  81. 'ownerOrdering' => 'text64?',
  82. 'originalEmailSource' => 'text255?',
  83. 'subpriority' => 'double',
  84. 'points' => 'double?',
  85. 'bridgedObjectPHID' => 'phid?',
  86. 'subtype' => 'text64',
  87. 'closedEpoch' => 'epoch?',
  88. 'closerPHID' => 'phid?',
  89. ),
  90. self::CONFIG_KEY_SCHEMA => array(
  91. 'key_phid' => null,
  92. 'phid' => array(
  93. 'columns' => array('phid'),
  94. 'unique' => true,
  95. ),
  96. 'priority' => array(
  97. 'columns' => array('priority', 'status'),
  98. ),
  99. 'status' => array(
  100. 'columns' => array('status'),
  101. ),
  102. 'ownerPHID' => array(
  103. 'columns' => array('ownerPHID', 'status'),
  104. ),
  105. 'authorPHID' => array(
  106. 'columns' => array('authorPHID', 'status'),
  107. ),
  108. 'ownerOrdering' => array(
  109. 'columns' => array('ownerOrdering'),
  110. ),
  111. 'priority_2' => array(
  112. 'columns' => array('priority', 'subpriority'),
  113. ),
  114. 'key_dateCreated' => array(
  115. 'columns' => array('dateCreated'),
  116. ),
  117. 'key_dateModified' => array(
  118. 'columns' => array('dateModified'),
  119. ),
  120. 'key_title' => array(
  121. 'columns' => array('title(64)'),
  122. ),
  123. 'key_bridgedobject' => array(
  124. 'columns' => array('bridgedObjectPHID'),
  125. 'unique' => true,
  126. ),
  127. 'key_subtype' => array(
  128. 'columns' => array('subtype'),
  129. ),
  130. 'key_closed' => array(
  131. 'columns' => array('closedEpoch'),
  132. ),
  133. 'key_closer' => array(
  134. 'columns' => array('closerPHID', 'closedEpoch'),
  135. ),
  136. ),
  137. ) + parent::getConfiguration();
  138. }
  139. public function loadDependsOnTaskPHIDs() {
  140. return PhabricatorEdgeQuery::loadDestinationPHIDs(
  141. $this->getPHID(),
  142. ManiphestTaskDependsOnTaskEdgeType::EDGECONST);
  143. }
  144. public function loadDependedOnByTaskPHIDs() {
  145. return PhabricatorEdgeQuery::loadDestinationPHIDs(
  146. $this->getPHID(),
  147. ManiphestTaskDependedOnByTaskEdgeType::EDGECONST);
  148. }
  149. public function generatePHID() {
  150. return PhabricatorPHID::generateNewPHID(ManiphestTaskPHIDType::TYPECONST);
  151. }
  152. public function getSubscriberPHIDs() {
  153. return $this->assertAttached($this->subscriberPHIDs);
  154. }
  155. public function getProjectPHIDs() {
  156. return $this->assertAttached($this->edgeProjectPHIDs);
  157. }
  158. public function attachProjectPHIDs(array $phids) {
  159. $this->edgeProjectPHIDs = $phids;
  160. return $this;
  161. }
  162. public function attachSubscriberPHIDs(array $phids) {
  163. $this->subscriberPHIDs = $phids;
  164. return $this;
  165. }
  166. public function setOwnerPHID($phid) {
  167. $this->ownerPHID = nonempty($phid, null);
  168. return $this;
  169. }
  170. public function getMonogram() {
  171. return 'T'.$this->getID();
  172. }
  173. public function getURI() {
  174. return '/'.$this->getMonogram();
  175. }
  176. public function attachGroupByProjectPHID($phid) {
  177. $this->groupByProjectPHID = $phid;
  178. return $this;
  179. }
  180. public function getGroupByProjectPHID() {
  181. return $this->assertAttached($this->groupByProjectPHID);
  182. }
  183. public function save() {
  184. if (!$this->mailKey) {
  185. $this->mailKey = Filesystem::readRandomCharacters(20);
  186. }
  187. $result = parent::save();
  188. return $result;
  189. }
  190. public function isClosed() {
  191. return ManiphestTaskStatus::isClosedStatus($this->getStatus());
  192. }
  193. public function areCommentsLocked() {
  194. if ($this->areEditsLocked()) {
  195. return true;
  196. }
  197. return ManiphestTaskStatus::areCommentsLockedInStatus($this->getStatus());
  198. }
  199. public function areEditsLocked() {
  200. return ManiphestTaskStatus::areEditsLockedInStatus($this->getStatus());
  201. }
  202. public function setProperty($key, $value) {
  203. $this->properties[$key] = $value;
  204. return $this;
  205. }
  206. public function getProperty($key, $default = null) {
  207. return idx($this->properties, $key, $default);
  208. }
  209. public function getCoverImageFilePHID() {
  210. return idx($this->properties, 'cover.filePHID');
  211. }
  212. public function getCoverImageThumbnailPHID() {
  213. return idx($this->properties, 'cover.thumbnailPHID');
  214. }
  215. public function getPriorityKeyword() {
  216. $priority = $this->getPriority();
  217. $keyword = ManiphestTaskPriority::getKeywordForTaskPriority($priority);
  218. if ($keyword !== null) {
  219. return $keyword;
  220. }
  221. return ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD;
  222. }
  223. /* -( PhabricatorSubscribableInterface )----------------------------------- */
  224. public function isAutomaticallySubscribed($phid) {
  225. return ($phid == $this->getOwnerPHID());
  226. }
  227. /* -( Markup Interface )--------------------------------------------------- */
  228. /**
  229. * @task markup
  230. */
  231. public function getMarkupFieldKey($field) {
  232. $content = $this->getMarkupText($field);
  233. return PhabricatorMarkupEngine::digestRemarkupContent($this, $content);
  234. }
  235. /**
  236. * @task markup
  237. */
  238. public function getMarkupText($field) {
  239. return $this->getDescription();
  240. }
  241. /**
  242. * @task markup
  243. */
  244. public function newMarkupEngine($field) {
  245. return PhabricatorMarkupEngine::newManiphestMarkupEngine();
  246. }
  247. /**
  248. * @task markup
  249. */
  250. public function didMarkupText(
  251. $field,
  252. $output,
  253. PhutilMarkupEngine $engine) {
  254. return $output;
  255. }
  256. /**
  257. * @task markup
  258. */
  259. public function shouldUseMarkupCache($field) {
  260. return (bool)$this->getID();
  261. }
  262. /* -( Policy Interface )--------------------------------------------------- */
  263. public function getCapabilities() {
  264. return array(
  265. PhabricatorPolicyCapability::CAN_VIEW,
  266. PhabricatorPolicyCapability::CAN_INTERACT,
  267. PhabricatorPolicyCapability::CAN_EDIT,
  268. );
  269. }
  270. public function getPolicy($capability) {
  271. switch ($capability) {
  272. case PhabricatorPolicyCapability::CAN_VIEW:
  273. return $this->getViewPolicy();
  274. case PhabricatorPolicyCapability::CAN_INTERACT:
  275. if ($this->areCommentsLocked()) {
  276. return PhabricatorPolicies::POLICY_NOONE;
  277. } else {
  278. return $this->getViewPolicy();
  279. }
  280. case PhabricatorPolicyCapability::CAN_EDIT:
  281. if ($this->areEditsLocked()) {
  282. return PhabricatorPolicies::POLICY_NOONE;
  283. } else {
  284. return $this->getEditPolicy();
  285. }
  286. }
  287. }
  288. public function hasAutomaticCapability($capability, PhabricatorUser $user) {
  289. // The owner of a task can always view and edit it.
  290. $owner_phid = $this->getOwnerPHID();
  291. if ($owner_phid) {
  292. $user_phid = $user->getPHID();
  293. if ($user_phid == $owner_phid) {
  294. return true;
  295. }
  296. }
  297. return false;
  298. }
  299. /* -( PhabricatorTokenReceiverInterface )---------------------------------- */
  300. public function getUsersToNotifyOfTokenGiven() {
  301. // Sort of ambiguous who this was intended for; just let them both know.
  302. return array_filter(
  303. array_unique(
  304. array(
  305. $this->getAuthorPHID(),
  306. $this->getOwnerPHID(),
  307. )));
  308. }
  309. /* -( PhabricatorCustomFieldInterface )------------------------------------ */
  310. public function getCustomFieldSpecificationForRole($role) {
  311. return PhabricatorEnv::getEnvConfig('maniphest.fields');
  312. }
  313. public function getCustomFieldBaseClass() {
  314. return 'ManiphestCustomField';
  315. }
  316. public function getCustomFields() {
  317. return $this->assertAttached($this->customFields);
  318. }
  319. public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
  320. $this->customFields = $fields;
  321. return $this;
  322. }
  323. /* -( PhabricatorDestructibleInterface )----------------------------------- */
  324. public function destroyObjectPermanently(
  325. PhabricatorDestructionEngine $engine) {
  326. $this->openTransaction();
  327. $this->delete();
  328. $this->saveTransaction();
  329. }
  330. /* -( PhabricatorApplicationTransactionInterface )------------------------- */
  331. public function getApplicationTransactionEditor() {
  332. return new ManiphestTransactionEditor();
  333. }
  334. public function getApplicationTransactionTemplate() {
  335. return new ManiphestTransaction();
  336. }
  337. /* -( PhabricatorSpacesInterface )----------------------------------------- */
  338. public function getSpacePHID() {
  339. return $this->spacePHID;
  340. }
  341. /* -( PhabricatorConduitResultInterface )---------------------------------- */
  342. public function getFieldSpecificationsForConduit() {
  343. return array(
  344. id(new PhabricatorConduitSearchFieldSpecification())
  345. ->setKey('title')
  346. ->setType('string')
  347. ->setDescription(pht('The title of the task.')),
  348. id(new PhabricatorConduitSearchFieldSpecification())
  349. ->setKey('description')
  350. ->setType('remarkup')
  351. ->setDescription(pht('The task description.')),
  352. id(new PhabricatorConduitSearchFieldSpecification())
  353. ->setKey('authorPHID')
  354. ->setType('phid')
  355. ->setDescription(pht('Original task author.')),
  356. id(new PhabricatorConduitSearchFieldSpecification())
  357. ->setKey('ownerPHID')
  358. ->setType('phid?')
  359. ->setDescription(pht('Current task owner, if task is assigned.')),
  360. id(new PhabricatorConduitSearchFieldSpecification())
  361. ->setKey('status')
  362. ->setType('map<string, wild>')
  363. ->setDescription(pht('Information about task status.')),
  364. id(new PhabricatorConduitSearchFieldSpecification())
  365. ->setKey('priority')
  366. ->setType('map<string, wild>')
  367. ->setDescription(pht('Information about task priority.')),
  368. id(new PhabricatorConduitSearchFieldSpecification())
  369. ->setKey('points')
  370. ->setType('points')
  371. ->setDescription(pht('Point value of the task.')),
  372. id(new PhabricatorConduitSearchFieldSpecification())
  373. ->setKey('subtype')
  374. ->setType('string')
  375. ->setDescription(pht('Subtype of the task.')),
  376. id(new PhabricatorConduitSearchFieldSpecification())
  377. ->setKey('closerPHID')
  378. ->setType('phid?')
  379. ->setDescription(
  380. pht('User who closed the task, if the task is closed.')),
  381. id(new PhabricatorConduitSearchFieldSpecification())
  382. ->setKey('dateClosed')
  383. ->setType('int?')
  384. ->setDescription(
  385. pht('Epoch timestamp when the task was closed.')),
  386. );
  387. }
  388. public function getFieldValuesForConduit() {
  389. $status_value = $this->getStatus();
  390. $status_info = array(
  391. 'value' => $status_value,
  392. 'name' => ManiphestTaskStatus::getTaskStatusName($status_value),
  393. 'color' => ManiphestTaskStatus::getStatusColor($status_value),
  394. );
  395. $priority_value = (int)$this->getPriority();
  396. $priority_info = array(
  397. 'value' => $priority_value,
  398. 'name' => ManiphestTaskPriority::getTaskPriorityName($priority_value),
  399. 'color' => ManiphestTaskPriority::getTaskPriorityColor($priority_value),
  400. );
  401. $closed_epoch = $this->getClosedEpoch();
  402. if ($closed_epoch !== null) {
  403. $closed_epoch = (int)$closed_epoch;
  404. }
  405. return array(
  406. 'name' => $this->getTitle(),
  407. 'description' => array(
  408. 'raw' => $this->getDescription(),
  409. ),
  410. 'authorPHID' => $this->getAuthorPHID(),
  411. 'ownerPHID' => $this->getOwnerPHID(),
  412. 'status' => $status_info,
  413. 'priority' => $priority_info,
  414. 'points' => $this->getPoints(),
  415. 'subtype' => $this->getSubtype(),
  416. 'closerPHID' => $this->getCloserPHID(),
  417. 'dateClosed' => $closed_epoch,
  418. );
  419. }
  420. public function getConduitSearchAttachments() {
  421. return array(
  422. id(new PhabricatorBoardColumnsSearchEngineAttachment())
  423. ->setAttachmentKey('columns'),
  424. );
  425. }
  426. public function newSubtypeObject() {
  427. $subtype_key = $this->getEditEngineSubtype();
  428. $subtype_map = $this->newEditEngineSubtypeMap();
  429. return $subtype_map->getSubtype($subtype_key);
  430. }
  431. /* -( PhabricatorFulltextInterface )--------------------------------------- */
  432. public function newFulltextEngine() {
  433. return new ManiphestTaskFulltextEngine();
  434. }
  435. /* -( DoorkeeperBridgedObjectInterface )----------------------------------- */
  436. public function getBridgedObject() {
  437. return $this->assertAttached($this->bridgedObject);
  438. }
  439. public function attachBridgedObject(
  440. DoorkeeperExternalObject $object = null) {
  441. $this->bridgedObject = $object;
  442. return $this;
  443. }
  444. /* -( PhabricatorEditEngineSubtypeInterface )------------------------------ */
  445. public function getEditEngineSubtype() {
  446. return $this->getSubtype();
  447. }
  448. public function setEditEngineSubtype($value) {
  449. return $this->setSubtype($value);
  450. }
  451. public function newEditEngineSubtypeMap() {
  452. $config = PhabricatorEnv::getEnvConfig('maniphest.subtypes');
  453. return PhabricatorEditEngineSubtype::newSubtypeMap($config)
  454. ->setDatasource(new ManiphestTaskSubtypeDatasource());
  455. }
  456. /* -( PhabricatorEditEngineLockableInterface )----------------------------- */
  457. public function newEditEngineLock() {
  458. return new ManiphestTaskEditEngineLock();
  459. }
  460. /* -( PhabricatorFerretInterface )----------------------------------------- */
  461. public function newFerretEngine() {
  462. return new ManiphestTaskFerretEngine();
  463. }
  464. /* -( PhabricatorEditEngineMFAInterface )---------------------------------- */
  465. public function newEditEngineMFAEngine() {
  466. return new ManiphestTaskMFAEngine();
  467. }
  468. /* -( PhabricatorPolicyCodexInterface )------------------------------------ */
  469. public function newPolicyCodex() {
  470. return new ManiphestTaskPolicyCodex();
  471. }
  472. /* -( PhabricatorUnlockableInterface )------------------------------------- */
  473. public function newUnlockEngine() {
  474. return new ManiphestTaskUnlockEngine();
  475. }
  476. }