Remove all "FileHasObject" edge reads and writes
[phabricator.git] / src / applications / maniphest / conduit / ManiphestConduitAPIMethod.php
blob640e30fee58b1bea6cbbb68b8dcaec966521829d
1 <?php
3 abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod {
5 final public function getApplication() {
6 return PhabricatorApplication::getByClass(
7 'PhabricatorManiphestApplication');
10 protected function defineErrorTypes() {
11 return array(
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) {
22 $fields = array();
24 if (!$is_new) {
25 $fields += array(
26 'id' => 'optional int',
27 'phid' => 'optional int',
31 $fields += array(
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',
43 if (!$is_new) {
44 $fields += array(
45 'status' => 'optional string',
46 'comments' => 'optional string',
50 return $fields;
53 protected function applyRequest(
54 ManiphestTask $task,
55 ConduitAPIRequest $request,
56 $is_new) {
58 $changes = array();
60 if ($is_new) {
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()));
67 } else {
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');
80 if ($desc !== null) {
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(
110 array($owner_phid),
111 PhabricatorPeopleUserPHIDType::TYPECONST,
112 'ownerPHID');
113 $changes[ManiphestTaskOwnerTransaction::TRANSACTIONTYPE] = $owner_phid;
116 $ccs = $request->getValue('ccPHIDs');
117 if ($ccs !== null) {
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(
141 $project_phids,
142 PhabricatorProjectProjectPHIDType::TYPECONST,
143 'projectPHIDS');
145 $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
146 $transactions[] = id(new ManiphestTransaction())
147 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
148 ->setMetadataValue('edge:type', $project_type)
149 ->setNewValue(
150 array(
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));
164 } else {
165 $transaction->setNewValue($value);
168 $transactions[] = $transaction;
171 $field_list = PhabricatorCustomField::getObjectFields(
172 $task,
173 PhabricatorCustomField::ROLE_EDIT);
174 $field_list->readFieldsFromStorage($task);
176 $auxiliary = $request->getValue('auxiliary');
177 if ($auxiliary) {
178 foreach ($field_list->getFields() as $key => $field) {
179 if (!array_key_exists($key, $auxiliary)) {
180 continue;
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) {
194 return;
197 $content_source = $request->newContentSource();
199 $editor = id(new ManiphestTransactionEditor())
200 ->setActor($request->getUser())
201 ->setContentSource($content_source)
202 ->setContinueOnNoEffect(true);
204 if (!$is_new) {
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)
216 ->executeOne();
219 protected function buildTaskInfoDictionaries(array $tasks) {
220 assert_instances_of($tasks, 'ManiphestTask');
221 if (!$tasks) {
222 return array();
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();
232 $result = array();
233 foreach ($tasks as $task) {
234 // TODO: Batch this get as CustomField gets cleaned up.
235 $field_list = PhabricatorCustomField::getObjectFields(
236 $task,
237 PhabricatorCustomField::ROLE_EDIT);
238 $field_list->readFieldsFromStorage($task);
240 $auxiliary = mpull(
241 $field_list->getFields(),
242 'getValueForStorage',
243 'getFieldKey');
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(
257 $task->getStatus()),
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,
276 return $result;
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(
292 pht(
293 'One or more PHIDs were invalid for %s.',
294 $field));
297 return true;