Path: blob/master/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php
12256 views
<?php12abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod {34final public function getApplication() {5return PhabricatorApplication::getByClass(6'PhabricatorManiphestApplication');7}89protected function defineErrorTypes() {10return array(11'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),12);13}1415protected function buildTaskInfoDictionary(ManiphestTask $task) {16$results = $this->buildTaskInfoDictionaries(array($task));17return idx($results, $task->getPHID());18}1920protected function getTaskFields($is_new) {21$fields = array();2223if (!$is_new) {24$fields += array(25'id' => 'optional int',26'phid' => 'optional int',27);28}2930$fields += array(31'title' => $is_new ? 'required string' : 'optional string',32'description' => 'optional string',33'ownerPHID' => 'optional phid',34'viewPolicy' => 'optional phid or policy string',35'editPolicy' => 'optional phid or policy string',36'ccPHIDs' => 'optional list<phid>',37'priority' => 'optional int',38'projectPHIDs' => 'optional list<phid>',39'auxiliary' => 'optional dict',40);4142if (!$is_new) {43$fields += array(44'status' => 'optional string',45'comments' => 'optional string',46);47}4849return $fields;50}5152protected function applyRequest(53ManiphestTask $task,54ConduitAPIRequest $request,55$is_new) {5657$changes = array();5859if ($is_new) {60$task->setTitle((string)$request->getValue('title'));61$task->setDescription((string)$request->getValue('description'));62$changes[ManiphestTaskStatusTransaction::TRANSACTIONTYPE] =63ManiphestTaskStatus::getDefaultStatus();64$changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] =65array('+' => array($request->getUser()->getPHID()));66} else {6768$comments = $request->getValue('comments');69if (!$is_new && $comments !== null) {70$changes[PhabricatorTransactions::TYPE_COMMENT] = null;71}7273$title = $request->getValue('title');74if ($title !== null) {75$changes[ManiphestTaskTitleTransaction::TRANSACTIONTYPE] = $title;76}7778$desc = $request->getValue('description');79if ($desc !== null) {80$changes[ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE] = $desc;81}8283$status = $request->getValue('status');84if ($status !== null) {85$valid_statuses = ManiphestTaskStatus::getTaskStatusMap();86if (!isset($valid_statuses[$status])) {87throw id(new ConduitException('ERR-INVALID-PARAMETER'))88->setErrorDescription(pht('Status set to invalid value.'));89}90$changes[ManiphestTaskStatusTransaction::TRANSACTIONTYPE] = $status;91}92}9394$priority = $request->getValue('priority');95if ($priority !== null) {96$valid_priorities = ManiphestTaskPriority::getTaskPriorityMap();97if (!isset($valid_priorities[$priority])) {98throw id(new ConduitException('ERR-INVALID-PARAMETER'))99->setErrorDescription(pht('Priority set to invalid value.'));100}101$keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();102$keyword = head(idx($keyword_map, $priority));103$changes[ManiphestTaskPriorityTransaction::TRANSACTIONTYPE] = $keyword;104}105106$owner_phid = $request->getValue('ownerPHID');107if ($owner_phid !== null) {108$this->validatePHIDList(109array($owner_phid),110PhabricatorPeopleUserPHIDType::TYPECONST,111'ownerPHID');112$changes[ManiphestTaskOwnerTransaction::TRANSACTIONTYPE] = $owner_phid;113}114115$ccs = $request->getValue('ccPHIDs');116if ($ccs !== null) {117$changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] =118array('=' => array_fuse($ccs));119}120121$transactions = array();122123$view_policy = $request->getValue('viewPolicy');124if ($view_policy !== null) {125$transactions[] = id(new ManiphestTransaction())126->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)127->setNewValue($view_policy);128}129130$edit_policy = $request->getValue('editPolicy');131if ($edit_policy !== null) {132$transactions[] = id(new ManiphestTransaction())133->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)134->setNewValue($edit_policy);135}136137$project_phids = $request->getValue('projectPHIDs');138if ($project_phids !== null) {139$this->validatePHIDList(140$project_phids,141PhabricatorProjectProjectPHIDType::TYPECONST,142'projectPHIDS');143144$project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;145$transactions[] = id(new ManiphestTransaction())146->setTransactionType(PhabricatorTransactions::TYPE_EDGE)147->setMetadataValue('edge:type', $project_type)148->setNewValue(149array(150'=' => array_fuse($project_phids),151));152}153154$template = new ManiphestTransaction();155156foreach ($changes as $type => $value) {157$transaction = clone $template;158$transaction->setTransactionType($type);159if ($type == PhabricatorTransactions::TYPE_COMMENT) {160$transaction->attachComment(161id(new ManiphestTransactionComment())162->setContent($comments));163} else {164$transaction->setNewValue($value);165}166167$transactions[] = $transaction;168}169170$field_list = PhabricatorCustomField::getObjectFields(171$task,172PhabricatorCustomField::ROLE_EDIT);173$field_list->readFieldsFromStorage($task);174175$auxiliary = $request->getValue('auxiliary');176if ($auxiliary) {177foreach ($field_list->getFields() as $key => $field) {178if (!array_key_exists($key, $auxiliary)) {179continue;180}181$transaction = clone $template;182$transaction->setTransactionType(183PhabricatorTransactions::TYPE_CUSTOMFIELD);184$transaction->setMetadataValue('customfield:key', $key);185$transaction->setOldValue(186$field->getOldValueForApplicationTransactions());187$transaction->setNewValue($auxiliary[$key]);188$transactions[] = $transaction;189}190}191192if (!$transactions) {193return;194}195196$content_source = $request->newContentSource();197198$editor = id(new ManiphestTransactionEditor())199->setActor($request->getUser())200->setContentSource($content_source)201->setContinueOnNoEffect(true);202203if (!$is_new) {204$editor->setContinueOnMissingFields(true);205}206207$editor->applyTransactions($task, $transactions);208209// reload the task now that we've done all the fun stuff210return id(new ManiphestTaskQuery())211->setViewer($request->getUser())212->withPHIDs(array($task->getPHID()))213->needSubscriberPHIDs(true)214->needProjectPHIDs(true)215->executeOne();216}217218protected function buildTaskInfoDictionaries(array $tasks) {219assert_instances_of($tasks, 'ManiphestTask');220if (!$tasks) {221return array();222}223224$task_phids = mpull($tasks, 'getPHID');225226$all_deps = id(new PhabricatorEdgeQuery())227->withSourcePHIDs($task_phids)228->withEdgeTypes(array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST));229$all_deps->execute();230231$result = array();232foreach ($tasks as $task) {233// TODO: Batch this get as CustomField gets cleaned up.234$field_list = PhabricatorCustomField::getObjectFields(235$task,236PhabricatorCustomField::ROLE_EDIT);237$field_list->readFieldsFromStorage($task);238239$auxiliary = mpull(240$field_list->getFields(),241'getValueForStorage',242'getFieldKey');243244$task_deps = $all_deps->getDestinationPHIDs(245array($task->getPHID()),246array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST));247248$result[$task->getPHID()] = array(249'id' => $task->getID(),250'phid' => $task->getPHID(),251'authorPHID' => $task->getAuthorPHID(),252'ownerPHID' => $task->getOwnerPHID(),253'ccPHIDs' => $task->getSubscriberPHIDs(),254'status' => $task->getStatus(),255'statusName' => ManiphestTaskStatus::getTaskStatusName(256$task->getStatus()),257'isClosed' => $task->isClosed(),258'priority' => ManiphestTaskPriority::getTaskPriorityName(259$task->getPriority()),260'priorityColor' => ManiphestTaskPriority::getTaskPriorityColor(261$task->getPriority()),262'title' => $task->getTitle(),263'description' => $task->getDescription(),264'projectPHIDs' => $task->getProjectPHIDs(),265'uri' => PhabricatorEnv::getProductionURI('/T'.$task->getID()),266'auxiliary' => $auxiliary,267268'objectName' => 'T'.$task->getID(),269'dateCreated' => $task->getDateCreated(),270'dateModified' => $task->getDateModified(),271'dependsOnTaskPHIDs' => $task_deps,272);273}274275return $result;276}277278/**279* NOTE: This is a temporary stop gap since its easy to make malformed tasks.280* Long-term, the values set in @{method:defineParamTypes} will be used to281* validate data implicitly within the larger Conduit application.282*283* TODO: Remove this in favor of generalized Conduit hotness.284*/285private function validatePHIDList(array $phid_list, $phid_type, $field) {286$phid_groups = phid_group_by_type($phid_list);287unset($phid_groups[$phid_type]);288if (!empty($phid_groups)) {289throw id(new ConduitException('ERR-INVALID-PARAMETER'))290->setErrorDescription(291pht(292'One or more PHIDs were invalid for %s.',293$field));294}295296return true;297}298299}300301302