2011-08-18 00:19:48 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @group conduit
|
|
|
|
*/
|
|
|
|
abstract class ConduitAPI_maniphest_Method extends ConduitAPIMethod {
|
|
|
|
|
2013-03-13 15:09:05 +01:00
|
|
|
public function getApplication() {
|
|
|
|
return PhabricatorApplication::getByClass(
|
|
|
|
'PhabricatorApplicationManiphest');
|
|
|
|
}
|
|
|
|
|
2012-05-20 01:18:13 +02:00
|
|
|
public function defineErrorTypes() {
|
|
|
|
return array(
|
|
|
|
'ERR-INVALID-PARAMETER' => 'Missing or malformed parameter.'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2011-08-18 00:19:48 +02:00
|
|
|
protected function buildTaskInfoDictionary(ManiphestTask $task) {
|
2011-08-27 01:52:57 +02:00
|
|
|
$results = $this->buildTaskInfoDictionaries(array($task));
|
|
|
|
return idx($results, $task->getPHID());
|
|
|
|
}
|
|
|
|
|
2012-01-06 04:02:50 +01:00
|
|
|
protected function getTaskFields($is_new) {
|
|
|
|
$fields = array();
|
|
|
|
|
|
|
|
if (!$is_new) {
|
|
|
|
$fields += array(
|
|
|
|
'id' => 'optional int',
|
|
|
|
'phid' => 'optional int',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$fields += array(
|
|
|
|
'title' => $is_new ? 'required string' : 'optional string',
|
|
|
|
'description' => 'optional string',
|
|
|
|
'ownerPHID' => 'optional phid',
|
|
|
|
'ccPHIDs' => 'optional list<phid>',
|
|
|
|
'priority' => 'optional int',
|
|
|
|
'projectPHIDs' => 'optional list<phid>',
|
|
|
|
'filePHIDs' => 'optional list<phid>',
|
|
|
|
'auxiliary' => 'optional dict',
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!$is_new) {
|
|
|
|
$fields += array(
|
|
|
|
'status' => 'optional int',
|
|
|
|
'comments' => 'optional string',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function applyRequest(
|
|
|
|
ManiphestTask $task,
|
|
|
|
ConduitAPIRequest $request,
|
|
|
|
$is_new) {
|
|
|
|
|
|
|
|
$changes = array();
|
|
|
|
|
|
|
|
if ($is_new) {
|
|
|
|
$task->setTitle((string)$request->getValue('title'));
|
|
|
|
$task->setDescription((string)$request->getValue('description'));
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_STATUS] =
|
2012-01-06 04:02:50 +01:00
|
|
|
ManiphestTaskStatus::STATUS_OPEN;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$comments = $request->getValue('comments');
|
|
|
|
if (!$is_new && $comments !== null) {
|
Migrate all Maniphest transaction data to new storage
Summary:
Ref T2217. This is the risky, hard part; everything after this should be smooth sailing. This is //mostly// clean, except:
- The old format would opportunistically combine a comment with some other transaction type if it could. We no longer do that, so:
- When migrating, "edit" + "comment" transactions need to be split in two.
- When editing now, we should no longer combine these transaction types.
- These changes are pretty straightforward and low-impact.
- This migration promotes "auxiliary field" data to the new CustomField/StandardField format, so that's not a straight migration either. The formats are very similar, though.
Broadly, this takes the same attack that the auth migration did: proxy all the code through to the new storage. `ManiphestTransaction` is now just an API on top of `ManiphestTransactionPro`, which is the new storage format. The two formats are very similar, so this was mostly a straight copy from one table to the other.
Test Plan:
- Without performing the migration, made a bunch of edits and comments on tasks and verified the new code works correctly.
- Droped the test data and performed the migration.
- Looked at the resulting data for obvious discrepancies.
- Looked at a bunch of tasks and their transaction history.
- Used Conduit to pull transaction data.
- Edited task description and clicked "View Details" on transaction.
- Used batch editor.
- Made a bunch more edits.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2217
Differential Revision: https://secure.phabricator.com/D7068
2013-09-23 23:25:28 +02:00
|
|
|
$changes[PhabricatorTransactions::TYPE_COMMENT] = null;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$title = $request->getValue('title');
|
|
|
|
if ($title !== null) {
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_TITLE] = $title;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$desc = $request->getValue('description');
|
|
|
|
if ($desc !== null) {
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_DESCRIPTION] = $desc;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$status = $request->getValue('status');
|
|
|
|
if ($status !== null) {
|
2012-05-20 01:18:13 +02:00
|
|
|
$valid_statuses = ManiphestTaskStatus::getTaskStatusMap();
|
|
|
|
if (!isset($valid_statuses[$status])) {
|
|
|
|
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
|
|
|
|
->setErrorDescription('Status set to invalid value.');
|
|
|
|
}
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_STATUS] = $status;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$priority = $request->getValue('priority');
|
|
|
|
if ($priority !== null) {
|
2012-05-20 01:18:13 +02:00
|
|
|
$valid_priorities = ManiphestTaskPriority::getTaskPriorityMap();
|
|
|
|
if (!isset($valid_priorities[$priority])) {
|
|
|
|
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
|
|
|
|
->setErrorDescription('Priority set to invalid value.');
|
|
|
|
}
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_PRIORITY] = $priority;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$owner_phid = $request->getValue('ownerPHID');
|
|
|
|
if ($owner_phid !== null) {
|
2012-05-20 01:18:13 +02:00
|
|
|
$this->validatePHIDList(array($owner_phid),
|
2013-07-26 23:05:19 +02:00
|
|
|
PhabricatorPeoplePHIDTypeUser::TYPECONST,
|
2012-05-20 01:18:13 +02:00
|
|
|
'ownerPHID');
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$ccs = $request->getValue('ccPHIDs');
|
|
|
|
if ($ccs !== null) {
|
2012-05-20 01:18:13 +02:00
|
|
|
$this->validatePHIDList($ccs,
|
2013-07-26 23:05:19 +02:00
|
|
|
PhabricatorPeoplePHIDTypeUser::TYPECONST,
|
2012-05-20 01:18:13 +02:00
|
|
|
'ccPHIDS');
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_CCS] = $ccs;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$project_phids = $request->getValue('projectPHIDs');
|
|
|
|
if ($project_phids !== null) {
|
2012-05-20 01:18:13 +02:00
|
|
|
$this->validatePHIDList($project_phids,
|
2013-07-22 18:26:26 +02:00
|
|
|
PhabricatorProjectPHIDTypeProject::TYPECONST,
|
2012-05-20 01:18:13 +02:00
|
|
|
'projectPHIDS');
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_PROJECTS] = $project_phids;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$file_phids = $request->getValue('filePHIDs');
|
|
|
|
if ($file_phids !== null) {
|
2012-05-20 01:18:13 +02:00
|
|
|
$this->validatePHIDList($file_phids,
|
2013-07-22 17:02:56 +02:00
|
|
|
PhabricatorFilePHIDTypeFile::TYPECONST,
|
2012-05-20 01:18:13 +02:00
|
|
|
'filePHIDS');
|
2012-01-06 04:02:50 +01:00
|
|
|
$file_map = array_fill_keys($file_phids, true);
|
|
|
|
$attached = $task->getAttached();
|
2013-07-22 17:02:56 +02:00
|
|
|
$attached[PhabricatorFilePHIDTypeFile::TYPECONST] = $file_map;
|
2012-01-06 04:02:50 +01:00
|
|
|
|
2013-09-25 20:16:43 +02:00
|
|
|
$changes[ManiphestTransaction::TYPE_ATTACH] = $attached;
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
2013-09-24 19:49:06 +02:00
|
|
|
$template = new ManiphestTransaction();
|
2012-01-06 04:02:50 +01:00
|
|
|
|
|
|
|
$transactions = array();
|
|
|
|
foreach ($changes as $type => $value) {
|
|
|
|
$transaction = clone $template;
|
|
|
|
$transaction->setTransactionType($type);
|
Migrate all Maniphest transaction data to new storage
Summary:
Ref T2217. This is the risky, hard part; everything after this should be smooth sailing. This is //mostly// clean, except:
- The old format would opportunistically combine a comment with some other transaction type if it could. We no longer do that, so:
- When migrating, "edit" + "comment" transactions need to be split in two.
- When editing now, we should no longer combine these transaction types.
- These changes are pretty straightforward and low-impact.
- This migration promotes "auxiliary field" data to the new CustomField/StandardField format, so that's not a straight migration either. The formats are very similar, though.
Broadly, this takes the same attack that the auth migration did: proxy all the code through to the new storage. `ManiphestTransaction` is now just an API on top of `ManiphestTransactionPro`, which is the new storage format. The two formats are very similar, so this was mostly a straight copy from one table to the other.
Test Plan:
- Without performing the migration, made a bunch of edits and comments on tasks and verified the new code works correctly.
- Droped the test data and performed the migration.
- Looked at the resulting data for obvious discrepancies.
- Looked at a bunch of tasks and their transaction history.
- Used Conduit to pull transaction data.
- Edited task description and clicked "View Details" on transaction.
- Used batch editor.
- Made a bunch more edits.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2217
Differential Revision: https://secure.phabricator.com/D7068
2013-09-23 23:25:28 +02:00
|
|
|
if ($type == PhabricatorTransactions::TYPE_COMMENT) {
|
2013-09-23 23:32:08 +02:00
|
|
|
$transaction->attachComment(
|
|
|
|
id(new ManiphestTransactionComment())
|
|
|
|
->setContent($comments));
|
|
|
|
} else {
|
|
|
|
$transaction->setNewValue($value);
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
2013-09-23 23:32:08 +02:00
|
|
|
|
2012-01-06 04:02:50 +01:00
|
|
|
$transactions[] = $transaction;
|
|
|
|
}
|
|
|
|
|
2013-09-17 01:03:01 +02:00
|
|
|
$field_list = PhabricatorCustomField::getObjectFields(
|
|
|
|
$task,
|
|
|
|
PhabricatorCustomField::ROLE_EDIT);
|
2013-09-23 23:32:08 +02:00
|
|
|
$field_list->readFieldsFromStorage($task);
|
2013-09-17 01:03:01 +02:00
|
|
|
|
2012-01-06 04:02:50 +01:00
|
|
|
$auxiliary = $request->getValue('auxiliary');
|
|
|
|
if ($auxiliary) {
|
2013-09-17 01:03:01 +02:00
|
|
|
foreach ($field_list->getFields() as $key => $field) {
|
|
|
|
if (!array_key_exists($key, $auxiliary)) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-01-06 04:02:50 +01:00
|
|
|
$transaction = clone $template;
|
|
|
|
$transaction->setTransactionType(
|
Migrate all Maniphest transaction data to new storage
Summary:
Ref T2217. This is the risky, hard part; everything after this should be smooth sailing. This is //mostly// clean, except:
- The old format would opportunistically combine a comment with some other transaction type if it could. We no longer do that, so:
- When migrating, "edit" + "comment" transactions need to be split in two.
- When editing now, we should no longer combine these transaction types.
- These changes are pretty straightforward and low-impact.
- This migration promotes "auxiliary field" data to the new CustomField/StandardField format, so that's not a straight migration either. The formats are very similar, though.
Broadly, this takes the same attack that the auth migration did: proxy all the code through to the new storage. `ManiphestTransaction` is now just an API on top of `ManiphestTransactionPro`, which is the new storage format. The two formats are very similar, so this was mostly a straight copy from one table to the other.
Test Plan:
- Without performing the migration, made a bunch of edits and comments on tasks and verified the new code works correctly.
- Droped the test data and performed the migration.
- Looked at the resulting data for obvious discrepancies.
- Looked at a bunch of tasks and their transaction history.
- Used Conduit to pull transaction data.
- Edited task description and clicked "View Details" on transaction.
- Used batch editor.
- Made a bunch more edits.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2217
Differential Revision: https://secure.phabricator.com/D7068
2013-09-23 23:25:28 +02:00
|
|
|
PhabricatorTransactions::TYPE_CUSTOMFIELD);
|
|
|
|
$transaction->setMetadataValue('customfield:key', $key);
|
2013-09-17 01:03:01 +02:00
|
|
|
$transaction->setOldValue(
|
|
|
|
$field->getOldValueForApplicationTransactions());
|
|
|
|
$transaction->setNewValue($auxiliary[$key]);
|
2012-01-06 04:02:50 +01:00
|
|
|
$transactions[] = $transaction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-17 01:03:01 +02:00
|
|
|
if (!$transactions) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-06 04:02:50 +01:00
|
|
|
$event = new PhabricatorEvent(
|
|
|
|
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
|
|
|
|
array(
|
|
|
|
'task' => $task,
|
|
|
|
'new' => $is_new,
|
|
|
|
'transactions' => $transactions,
|
|
|
|
));
|
|
|
|
$event->setUser($request->getUser());
|
|
|
|
$event->setConduitRequest($request);
|
|
|
|
PhutilEventEngine::dispatchEvent($event);
|
|
|
|
|
|
|
|
$task = $event->getValue('task');
|
|
|
|
$transactions = $event->getValue('transactions');
|
|
|
|
|
2013-09-23 23:32:08 +02:00
|
|
|
$content_source = PhabricatorContentSource::newForSource(
|
|
|
|
PhabricatorContentSource::SOURCE_CONDUIT,
|
|
|
|
array());
|
|
|
|
|
|
|
|
$editor = id(new ManiphestTransactionEditorPro())
|
|
|
|
->setActor($request->getUser())
|
|
|
|
->setContentSource($content_source)
|
|
|
|
->setContinueOnNoEffect(true);
|
|
|
|
|
2013-09-23 23:32:32 +02:00
|
|
|
if (!$is_new) {
|
|
|
|
$editor->setContinueOnMissingFields(true);
|
|
|
|
}
|
|
|
|
|
2012-01-06 04:02:50 +01:00
|
|
|
$editor->applyTransactions($task, $transactions);
|
2012-04-17 21:09:04 +02:00
|
|
|
|
|
|
|
$event = new PhabricatorEvent(
|
|
|
|
PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK,
|
|
|
|
array(
|
|
|
|
'task' => $task,
|
|
|
|
'new' => $is_new,
|
|
|
|
'transactions' => $transactions,
|
|
|
|
));
|
|
|
|
$event->setUser($request->getUser());
|
|
|
|
$event->setConduitRequest($request);
|
|
|
|
PhutilEventEngine::dispatchEvent($event);
|
|
|
|
|
2012-01-06 04:02:50 +01:00
|
|
|
}
|
|
|
|
|
2011-08-27 01:52:57 +02:00
|
|
|
protected function buildTaskInfoDictionaries(array $tasks) {
|
2012-04-03 21:10:45 +02:00
|
|
|
assert_instances_of($tasks, 'ManiphestTask');
|
2011-08-27 01:52:57 +02:00
|
|
|
if (!$tasks) {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2013-05-10 19:17:03 +02:00
|
|
|
$task_phids = mpull($tasks, 'getPHID');
|
|
|
|
|
|
|
|
$all_deps = id(new PhabricatorEdgeQuery())
|
|
|
|
->withSourcePHIDs($task_phids)
|
|
|
|
->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK));
|
|
|
|
$all_deps->execute();
|
|
|
|
|
2011-08-27 01:52:57 +02:00
|
|
|
$result = array();
|
|
|
|
foreach ($tasks as $task) {
|
2013-09-17 01:02:06 +02:00
|
|
|
// TODO: Batch this get as CustomField gets cleaned up.
|
2013-09-17 01:03:01 +02:00
|
|
|
$field_list = PhabricatorCustomField::getObjectFields(
|
|
|
|
$task,
|
|
|
|
PhabricatorCustomField::ROLE_EDIT);
|
|
|
|
$field_list->readFieldsFromStorage($task);
|
|
|
|
|
|
|
|
$auxiliary = mpull(
|
|
|
|
$field_list->getFields(),
|
|
|
|
'getValueForStorage',
|
|
|
|
'getFieldKey');
|
2011-08-27 01:52:57 +02:00
|
|
|
|
2013-05-10 19:17:03 +02:00
|
|
|
$task_deps = $all_deps->getDestinationPHIDs(
|
|
|
|
array($task->getPHID()),
|
|
|
|
array(PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK));
|
|
|
|
|
2011-08-27 01:52:57 +02:00
|
|
|
$result[$task->getPHID()] = array(
|
|
|
|
'id' => $task->getID(),
|
|
|
|
'phid' => $task->getPHID(),
|
|
|
|
'authorPHID' => $task->getAuthorPHID(),
|
|
|
|
'ownerPHID' => $task->getOwnerPHID(),
|
|
|
|
'ccPHIDs' => $task->getCCPHIDs(),
|
|
|
|
'status' => $task->getStatus(),
|
|
|
|
'priority' => ManiphestTaskPriority::getTaskPriorityName(
|
|
|
|
$task->getPriority()),
|
|
|
|
'title' => $task->getTitle(),
|
|
|
|
'description' => $task->getDescription(),
|
|
|
|
'projectPHIDs' => $task->getProjectPHIDs(),
|
|
|
|
'uri' => PhabricatorEnv::getProductionURI('/T'.$task->getID()),
|
|
|
|
'auxiliary' => $auxiliary,
|
|
|
|
|
|
|
|
'objectName' => 'T'.$task->getID(),
|
|
|
|
'dateCreated' => $task->getDateCreated(),
|
|
|
|
'dateModified' => $task->getDateModified(),
|
2013-05-10 19:17:03 +02:00
|
|
|
'dependsOnTaskPHIDs' => $task_deps,
|
2011-08-27 01:52:57 +02:00
|
|
|
);
|
|
|
|
}
|
2011-08-18 00:19:48 +02:00
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2012-05-20 01:18:13 +02:00
|
|
|
/**
|
|
|
|
* Note this is a temporary stop gap since its easy to make malformed Tasks.
|
|
|
|
* Long-term, the values set in @{method:defineParamTypes} will be used to
|
|
|
|
* validate data implicitly within the larger Conduit application.
|
|
|
|
*
|
|
|
|
* TODO -- remove this in favor of generalized Conduit hotness
|
|
|
|
*/
|
|
|
|
private function validatePHIDList(array $phid_list, $phid_type, $field) {
|
|
|
|
$phid_groups = phid_group_by_type($phid_list);
|
|
|
|
unset($phid_groups[$phid_type]);
|
|
|
|
if (!empty($phid_groups)) {
|
|
|
|
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
|
|
|
|
->setErrorDescription(
|
2013-02-19 22:33:10 +01:00
|
|
|
'One or more PHIDs were invalid for '.$field.'.');
|
2012-05-20 01:18:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-08-18 00:19:48 +02:00
|
|
|
}
|