3 abstract class ManiphestConduitAPIMethod
extends ConduitAPIMethod
{
5 final public function getApplication() {
6 return PhabricatorApplication
::getByClass(
7 'PhabricatorManiphestApplication');
10 protected function defineErrorTypes() {
12 'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
16 protected function buildTaskInfoDictionary(ManiphestTask
$task) {
17 $results = $this->buildTaskInfoDictionaries(array($task));
18 return idx($results, $task->getPHID());
21 protected function getTaskFields($is_new) {
26 'id' => 'optional int',
27 'phid' => 'optional int',
32 'title' => $is_new ?
'required string' : 'optional string',
33 'description' => 'optional string',
34 'ownerPHID' => 'optional phid',
35 'viewPolicy' => 'optional phid or policy string',
36 'editPolicy' => 'optional phid or policy string',
37 'ccPHIDs' => 'optional list<phid>',
38 'priority' => 'optional int',
39 'projectPHIDs' => 'optional list<phid>',
40 'auxiliary' => 'optional dict',
45 'status' => 'optional string',
46 'comments' => 'optional string',
53 protected function applyRequest(
55 ConduitAPIRequest
$request,
61 $task->setTitle((string)$request->getValue('title'));
62 $task->setDescription((string)$request->getValue('description'));
63 $changes[ManiphestTaskStatusTransaction
::TRANSACTIONTYPE
] =
64 ManiphestTaskStatus
::getDefaultStatus();
65 $changes[PhabricatorTransactions
::TYPE_SUBSCRIBERS
] =
66 array('+' => array($request->getUser()->getPHID()));
69 $comments = $request->getValue('comments');
70 if (!$is_new && $comments !== null) {
71 $changes[PhabricatorTransactions
::TYPE_COMMENT
] = null;
74 $title = $request->getValue('title');
75 if ($title !== null) {
76 $changes[ManiphestTaskTitleTransaction
::TRANSACTIONTYPE
] = $title;
79 $desc = $request->getValue('description');
81 $changes[ManiphestTaskDescriptionTransaction
::TRANSACTIONTYPE
] = $desc;
84 $status = $request->getValue('status');
85 if ($status !== null) {
86 $valid_statuses = ManiphestTaskStatus
::getTaskStatusMap();
87 if (!isset($valid_statuses[$status])) {
88 throw id(new ConduitException('ERR-INVALID-PARAMETER'))
89 ->setErrorDescription(pht('Status set to invalid value.'));
91 $changes[ManiphestTaskStatusTransaction
::TRANSACTIONTYPE
] = $status;
95 $priority = $request->getValue('priority');
96 if ($priority !== null) {
97 $valid_priorities = ManiphestTaskPriority
::getTaskPriorityMap();
98 if (!isset($valid_priorities[$priority])) {
99 throw id(new ConduitException('ERR-INVALID-PARAMETER'))
100 ->setErrorDescription(pht('Priority set to invalid value.'));
102 $keyword_map = ManiphestTaskPriority
::getTaskPriorityKeywordsMap();
103 $keyword = head(idx($keyword_map, $priority));
104 $changes[ManiphestTaskPriorityTransaction
::TRANSACTIONTYPE
] = $keyword;
107 $owner_phid = $request->getValue('ownerPHID');
108 if ($owner_phid !== null) {
109 $this->validatePHIDList(
111 PhabricatorPeopleUserPHIDType
::TYPECONST
,
113 $changes[ManiphestTaskOwnerTransaction
::TRANSACTIONTYPE
] = $owner_phid;
116 $ccs = $request->getValue('ccPHIDs');
118 $changes[PhabricatorTransactions
::TYPE_SUBSCRIBERS
] =
119 array('=' => array_fuse($ccs));
122 $transactions = array();
124 $view_policy = $request->getValue('viewPolicy');
125 if ($view_policy !== null) {
126 $transactions[] = id(new ManiphestTransaction())
127 ->setTransactionType(PhabricatorTransactions
::TYPE_VIEW_POLICY
)
128 ->setNewValue($view_policy);
131 $edit_policy = $request->getValue('editPolicy');
132 if ($edit_policy !== null) {
133 $transactions[] = id(new ManiphestTransaction())
134 ->setTransactionType(PhabricatorTransactions
::TYPE_EDIT_POLICY
)
135 ->setNewValue($edit_policy);
138 $project_phids = $request->getValue('projectPHIDs');
139 if ($project_phids !== null) {
140 $this->validatePHIDList(
142 PhabricatorProjectProjectPHIDType
::TYPECONST
,
145 $project_type = PhabricatorProjectObjectHasProjectEdgeType
::EDGECONST
;
146 $transactions[] = id(new ManiphestTransaction())
147 ->setTransactionType(PhabricatorTransactions
::TYPE_EDGE
)
148 ->setMetadataValue('edge:type', $project_type)
151 '=' => array_fuse($project_phids),
155 $template = new ManiphestTransaction();
157 foreach ($changes as $type => $value) {
158 $transaction = clone $template;
159 $transaction->setTransactionType($type);
160 if ($type == PhabricatorTransactions
::TYPE_COMMENT
) {
161 $transaction->attachComment(
162 id(new ManiphestTransactionComment())
163 ->setContent($comments));
165 $transaction->setNewValue($value);
168 $transactions[] = $transaction;
171 $field_list = PhabricatorCustomField
::getObjectFields(
173 PhabricatorCustomField
::ROLE_EDIT
);
174 $field_list->readFieldsFromStorage($task);
176 $auxiliary = $request->getValue('auxiliary');
178 foreach ($field_list->getFields() as $key => $field) {
179 if (!array_key_exists($key, $auxiliary)) {
182 $transaction = clone $template;
183 $transaction->setTransactionType(
184 PhabricatorTransactions
::TYPE_CUSTOMFIELD
);
185 $transaction->setMetadataValue('customfield:key', $key);
186 $transaction->setOldValue(
187 $field->getOldValueForApplicationTransactions());
188 $transaction->setNewValue($auxiliary[$key]);
189 $transactions[] = $transaction;
193 if (!$transactions) {
197 $content_source = $request->newContentSource();
199 $editor = id(new ManiphestTransactionEditor())
200 ->setActor($request->getUser())
201 ->setContentSource($content_source)
202 ->setContinueOnNoEffect(true);
205 $editor->setContinueOnMissingFields(true);
208 $editor->applyTransactions($task, $transactions);
210 // reload the task now that we've done all the fun stuff
211 return id(new ManiphestTaskQuery())
212 ->setViewer($request->getUser())
213 ->withPHIDs(array($task->getPHID()))
214 ->needSubscriberPHIDs(true)
215 ->needProjectPHIDs(true)
219 protected function buildTaskInfoDictionaries(array $tasks) {
220 assert_instances_of($tasks, 'ManiphestTask');
225 $task_phids = mpull($tasks, 'getPHID');
227 $all_deps = id(new PhabricatorEdgeQuery())
228 ->withSourcePHIDs($task_phids)
229 ->withEdgeTypes(array(ManiphestTaskDependsOnTaskEdgeType
::EDGECONST
));
230 $all_deps->execute();
233 foreach ($tasks as $task) {
234 // TODO: Batch this get as CustomField gets cleaned up.
235 $field_list = PhabricatorCustomField
::getObjectFields(
237 PhabricatorCustomField
::ROLE_EDIT
);
238 $field_list->readFieldsFromStorage($task);
241 $field_list->getFields(),
242 'getValueForStorage',
245 $task_deps = $all_deps->getDestinationPHIDs(
246 array($task->getPHID()),
247 array(ManiphestTaskDependsOnTaskEdgeType
::EDGECONST
));
249 $result[$task->getPHID()] = array(
250 'id' => $task->getID(),
251 'phid' => $task->getPHID(),
252 'authorPHID' => $task->getAuthorPHID(),
253 'ownerPHID' => $task->getOwnerPHID(),
254 'ccPHIDs' => $task->getSubscriberPHIDs(),
255 'status' => $task->getStatus(),
256 'statusName' => ManiphestTaskStatus
::getTaskStatusName(
258 'isClosed' => $task->isClosed(),
259 'priority' => ManiphestTaskPriority
::getTaskPriorityName(
260 $task->getPriority()),
261 'priorityColor' => ManiphestTaskPriority
::getTaskPriorityColor(
262 $task->getPriority()),
263 'title' => $task->getTitle(),
264 'description' => $task->getDescription(),
265 'projectPHIDs' => $task->getProjectPHIDs(),
266 'uri' => PhabricatorEnv
::getProductionURI('/T'.$task->getID()),
267 'auxiliary' => $auxiliary,
269 'objectName' => 'T'.$task->getID(),
270 'dateCreated' => $task->getDateCreated(),
271 'dateModified' => $task->getDateModified(),
272 'dependsOnTaskPHIDs' => $task_deps,
280 * NOTE: This is a temporary stop gap since its easy to make malformed tasks.
281 * Long-term, the values set in @{method:defineParamTypes} will be used to
282 * validate data implicitly within the larger Conduit application.
284 * TODO: Remove this in favor of generalized Conduit hotness.
286 private function validatePHIDList(array $phid_list, $phid_type, $field) {
287 $phid_groups = phid_group_by_type($phid_list);
288 unset($phid_groups[$phid_type]);
289 if (!empty($phid_groups)) {
290 throw id(new ConduitException('ERR-INVALID-PARAMETER'))
291 ->setErrorDescription(
293 'One or more PHIDs were invalid for %s.',