mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Task -> Project assocation, file uploads
Summary: Test Plan: Reviewers: CC:
This commit is contained in:
parent
fdd510ba17
commit
7fb9a48690
16 changed files with 211 additions and 23 deletions
2
resources/sql/patches/001.maniphest_projects.sql
Normal file
2
resources/sql/patches/001.maniphest_projects.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
alter table maniphest_task add projectPHIDs longblob not null;
|
||||
update maniphest_task set projectPHIDs = '[]';
|
|
@ -22,6 +22,7 @@ final class ManiphestTransactionType {
|
|||
const TYPE_STATUS = 'status';
|
||||
const TYPE_OWNER = 'reassign';
|
||||
const TYPE_CCS = 'ccs';
|
||||
const TYPE_PROJECTS = 'projects';
|
||||
const TYPE_PRIORITY = 'priority';
|
||||
|
||||
const TYPE_ATTACH = 'attach';
|
||||
|
@ -36,6 +37,8 @@ final class ManiphestTransactionType {
|
|||
self::TYPE_OWNER => 'Reassign / Claim',
|
||||
self::TYPE_CCS => 'Add CCs',
|
||||
self::TYPE_PRIORITY => 'Change Priority',
|
||||
self::TYPE_ATTACH => 'Upload File',
|
||||
self::TYPE_PROJECTS => 'Associate Projects',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
foreach ($task->getCCPHIDs() as $phid) {
|
||||
$phids[$phid] = true;
|
||||
}
|
||||
foreach ($task->getProjectPHIDs() as $phid) {
|
||||
$phids[$phid] = true;
|
||||
}
|
||||
if ($task->getOwnerPHID()) {
|
||||
$phids[$task->getOwnerPHID()] = true;
|
||||
}
|
||||
|
@ -93,6 +96,17 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
|
||||
$dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink();
|
||||
|
||||
$projects = $task->getProjectPHIDs();
|
||||
if ($projects) {
|
||||
$project_links = array();
|
||||
foreach ($projects as $phid) {
|
||||
$project_links[] = $handles[$phid]->renderLink();
|
||||
}
|
||||
$dict['Projects'] = implode(', ', $project_links);
|
||||
} else {
|
||||
$dict['Projects'] = '<em>None</em>';
|
||||
}
|
||||
|
||||
if (idx($attached, 'DREV')) {
|
||||
$revs = idx($attached, 'DREV');
|
||||
$rev_links = array();
|
||||
|
@ -103,6 +117,17 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
$dict['Revisions'] = $rev_links;
|
||||
}
|
||||
|
||||
if (idx($attached, 'FILE')) {
|
||||
$revs = idx($attached, 'FILE');
|
||||
$rev_links = array();
|
||||
foreach ($revs as $rev => $info) {
|
||||
$rev_links[] = $handles[$rev]->renderLink();
|
||||
}
|
||||
$rev_links = implode(', ', $rev_links);
|
||||
$dict['Files'] = $rev_links;
|
||||
}
|
||||
|
||||
|
||||
$dict['Description'] =
|
||||
'<div class="maniphest-task-description">'.
|
||||
'<div class="phabricator-remarkup">'.
|
||||
|
@ -134,11 +159,6 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
$action->setClass('action-edit');
|
||||
$actions[] = $action;
|
||||
|
||||
$action = new AphrontHeadsupActionView();
|
||||
$action->setName('Upload File');
|
||||
$action->setClass('action-upload unavailable');
|
||||
$actions[] = $action;
|
||||
|
||||
$action = new AphrontHeadsupActionView();
|
||||
$action->setName('Edit Differential Revisions');
|
||||
$action->setClass('action-attach unavailable');
|
||||
|
@ -188,6 +208,7 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
$comment_form
|
||||
->setUser($user)
|
||||
->setAction('/maniphest/transaction/save/')
|
||||
->setEncType('multipart/form-data')
|
||||
->addHiddenInput('taskID', $task->getID())
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
|
@ -226,6 +247,20 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
->setControlID('priority')
|
||||
->setControlStyle('display: none')
|
||||
->setValue($task->getPriority()))
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel('Projects')
|
||||
->setName('projects')
|
||||
->setControlID('projects')
|
||||
->setControlStyle('display: none')
|
||||
->setID('projects-tokenizer')
|
||||
->setDisableBehavior(true))
|
||||
->appendChild(
|
||||
id(new AphrontFormFileControl())
|
||||
->setLabel('File')
|
||||
->setName('file')
|
||||
->setControlID('file')
|
||||
->setControlStyle('display: none'))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel('Comments')
|
||||
|
@ -242,8 +277,14 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
ManiphestTransactionType::TYPE_OWNER => 'assign_to',
|
||||
ManiphestTransactionType::TYPE_CCS => 'ccs',
|
||||
ManiphestTransactionType::TYPE_PRIORITY => 'priority',
|
||||
ManiphestTransactionType::TYPE_PROJECTS => 'projects',
|
||||
ManiphestTransactionType::TYPE_ATTACH => 'file',
|
||||
),
|
||||
'tokenizers' => array(
|
||||
ManiphestTransactionType::TYPE_PROJECTS => array(
|
||||
'id' => 'projects-tokenizer',
|
||||
'src' => '/typeahead/common/projects/',
|
||||
),
|
||||
ManiphestTransactionType::TYPE_OWNER => array(
|
||||
'id' => 'assign-tokenizer',
|
||||
'src' => '/typeahead/common/users/',
|
||||
|
|
|
@ -60,6 +60,8 @@ class ManiphestTaskEditController extends ManiphestController {
|
|||
} else {
|
||||
$task->setTitle($new_title);
|
||||
$task->setDescription($new_desc);
|
||||
$changes[ManiphestTransactionType::TYPE_STATUS] =
|
||||
ManiphestTaskStatus::STATUS_OPEN;
|
||||
}
|
||||
|
||||
$owner_tokenizer = $request->getArr('assigned_to');
|
||||
|
@ -72,22 +74,25 @@ class ManiphestTaskEditController extends ManiphestController {
|
|||
|
||||
if (!$errors) {
|
||||
|
||||
$changes[ManiphestTransactionType::TYPE_STATUS] =
|
||||
ManiphestTaskStatus::STATUS_OPEN;
|
||||
|
||||
if ($request->getInt('priority') != $task->getPriority()) {
|
||||
$changes[ManiphestTransactionType::TYPE_PRIORITY] =
|
||||
$request->getInt('priority');
|
||||
}
|
||||
|
||||
if ($owner_phid) {
|
||||
if ($owner_phid != $task->getOwnerPHID()) {
|
||||
$changes[ManiphestTransactionType::TYPE_OWNER] = $owner_phid;
|
||||
}
|
||||
|
||||
if ($request->getArr('cc')) {
|
||||
if ($request->getArr('cc') != $task->getCCPHIDs()) {
|
||||
$changes[ManiphestTransactionType::TYPE_CCS] = $request->getArr('cc');
|
||||
}
|
||||
|
||||
if ($request->getArr('projects') != $task->getProjectPHIDs()) {
|
||||
$changes[ManiphestTransactionType::TYPE_PROJECTS]
|
||||
= $request->getArr('projects');
|
||||
}
|
||||
|
||||
$template = new ManiphestTransaction();
|
||||
$template->setAuthorPHID($user->getPHID());
|
||||
$transactions = array();
|
||||
|
@ -115,7 +120,8 @@ class ManiphestTaskEditController extends ManiphestController {
|
|||
|
||||
$phids = array_merge(
|
||||
array($task->getOwnerPHID()),
|
||||
$task->getCCPHIDs());
|
||||
$task->getCCPHIDs(),
|
||||
$task->getProjectPHIDs());
|
||||
$phids = array_filter($phids);
|
||||
$phids = array_unique($phids);
|
||||
|
||||
|
@ -147,6 +153,12 @@ class ManiphestTaskEditController extends ManiphestController {
|
|||
$cc_value = array();
|
||||
}
|
||||
|
||||
if ($task->getProjectPHIDs()) {
|
||||
$projects_value = array_select_keys($tvalues, $task->getProjectPHIDs());
|
||||
} else {
|
||||
$projects_value = array();
|
||||
}
|
||||
|
||||
if ($task->getID()) {
|
||||
$cancel_uri = '/T'.$task->getID();
|
||||
$button_name = 'Save Task';
|
||||
|
@ -186,6 +198,12 @@ class ManiphestTaskEditController extends ManiphestController {
|
|||
->setName('priority')
|
||||
->setOptions($priority_map)
|
||||
->setValue($task->getPriority()))
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel('Projects')
|
||||
->setName('projects')
|
||||
->setValue($projects_value)
|
||||
->setDatasource('/typeahead/common/projects/'))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel('Description')
|
||||
|
|
|
@ -46,6 +46,13 @@ class ManiphestTransactionSaveController extends ManiphestController {
|
|||
$assign_to = reset($assign_to);
|
||||
$transaction->setNewValue($assign_to);
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_PROJECTS:
|
||||
$projects = $request->getArr('projects');
|
||||
$projects = array_merge($projects, $task->getProjectPHIDs());
|
||||
$projects = array_filter($projects);
|
||||
$projects = array_unique($projects);
|
||||
$transaction->setNewValue($projects);
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_CCS:
|
||||
$ccs = $request->getArr('ccs');
|
||||
$ccs = array_merge($ccs, $task->getCCPHIDs());
|
||||
|
@ -56,6 +63,30 @@ class ManiphestTransactionSaveController extends ManiphestController {
|
|||
case ManiphestTransactionType::TYPE_PRIORITY:
|
||||
$transaction->setNewValue($request->getInt('priority'));
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_ATTACH:
|
||||
// This means "attach a file" even though we store other types of data
|
||||
// as 'attached'.
|
||||
$phid = null;
|
||||
if (!empty($_FILES['file'])) {
|
||||
$err = idx($_FILES['file'], 'error');
|
||||
if ($err != UPLOAD_ERR_NO_FILE) {
|
||||
$file = PhabricatorFile::newFromPHPUpload($_FILES['file']);
|
||||
$phid = $file->getPHID();
|
||||
}
|
||||
}
|
||||
if ($phid) {
|
||||
$new = $task->getAttached();
|
||||
if (empty($new['FILE'])) {
|
||||
$new['FILE'] = array();
|
||||
}
|
||||
$new['FILE'][$phid] = array();
|
||||
}
|
||||
|
||||
var_dump($new);
|
||||
die();
|
||||
|
||||
$transaction->setNewValue($new);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('unknown action');
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
phutil_require_module('phabricator', 'aphront/response/404');
|
||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
||||
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
||||
|
|
|
@ -55,6 +55,9 @@ class ManiphestTransactionEditor {
|
|||
case ManiphestTransactionType::TYPE_DESCRIPTION:
|
||||
$old = $task->getDescription();
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_PROJECTS:
|
||||
$old = $task->getProjectPHIDs();
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown action type.');
|
||||
}
|
||||
|
@ -96,6 +99,9 @@ class ManiphestTransactionEditor {
|
|||
case ManiphestTransactionType::TYPE_DESCRIPTION:
|
||||
$task->setDescription($new);
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_PROJECTS:
|
||||
$task->setProjectPHIDs($new);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown action type.');
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class ManiphestTask extends ManiphestDAO {
|
|||
protected $phid;
|
||||
protected $authorPHID;
|
||||
protected $ownerPHID;
|
||||
protected $ccPHIDs;
|
||||
protected $ccPHIDs = array();
|
||||
|
||||
protected $status;
|
||||
protected $priority;
|
||||
|
@ -30,6 +30,7 @@ class ManiphestTask extends ManiphestDAO {
|
|||
protected $description;
|
||||
|
||||
protected $attached = array();
|
||||
protected $projectPHIDs = array();
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
|
@ -37,6 +38,7 @@ class ManiphestTask extends ManiphestDAO {
|
|||
self::CONFIG_SERIALIZATION => array(
|
||||
'ccPHIDs' => self::SERIALIZATION_JSON,
|
||||
'attached' => self::SERIALIZATION_JSON,
|
||||
'projectPHIDs' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ class ManiphestTransaction extends ManiphestDAO {
|
|||
|
||||
switch ($this->getTransactionType()) {
|
||||
case ManiphestTransactionType::TYPE_CCS:
|
||||
case ManiphestTransactionType::TYPE_PROJECTS:
|
||||
foreach ($this->getOldValue() as $phid) {
|
||||
$phids[] = $phid;
|
||||
}
|
||||
|
|
|
@ -204,6 +204,29 @@ class ManiphestTransactionDetailView extends AphrontView {
|
|||
'removed: '.$this->renderHandles($removed);
|
||||
}
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_PROJECTS:
|
||||
$added = array_diff($new, $old);
|
||||
$removed = array_diff($old, $new);
|
||||
if ($added && !$removed) {
|
||||
$verb = 'Added Project';
|
||||
if (count($added) == 1) {
|
||||
$desc = 'added project '.$this->renderHandles($added);
|
||||
} else {
|
||||
$desc = 'added projects: '.$this->renderHandles($added);
|
||||
}
|
||||
} else if ($removed && !$added) {
|
||||
$verb = 'Removed Project';
|
||||
if (count($removed) == 1) {
|
||||
$desc = 'removed project '.$this->renderHandles($removed);
|
||||
} else {
|
||||
$desc = 'removed projectss: '.$this->renderHandles($removed);
|
||||
}
|
||||
} else {
|
||||
$verb = 'Changed Projects';
|
||||
$desc = 'changed projects, added: '.$this->renderHandles($added).'; '.
|
||||
'removed: '.$this->renderHandles($removed);
|
||||
}
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_STATUS:
|
||||
if ($new == ManiphestTaskStatus::STATUS_OPEN) {
|
||||
if ($old) {
|
||||
|
@ -240,34 +263,51 @@ class ManiphestTransactionDetailView extends AphrontView {
|
|||
}
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_ATTACH:
|
||||
$old = nonempty($old, array());
|
||||
$new = nonempty($new, array());
|
||||
$old_raw = nonempty($old, array());
|
||||
$new_raw = nonempty($new, array());
|
||||
|
||||
foreach (array('DREV', 'FILE') as $type) {
|
||||
$old = array_keys(idx($old_raw, $type, array()));
|
||||
$new = array_keys(idx($new_raw, $type, array()));
|
||||
if ($old != $new) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$old = array_keys(idx($old, 'DREV', array()));
|
||||
$new = array_keys(idx($new, 'DREV', array()));
|
||||
$added = array_diff($new, $old);
|
||||
$removed = array_diff($old, $new);
|
||||
|
||||
$add_desc = $this->renderHandles($added);
|
||||
$rem_desc = $this->renderHandles($removed);
|
||||
|
||||
switch ($type) {
|
||||
case 'DREV':
|
||||
$singular = 'Differential Revision';
|
||||
$plural = 'Differential Revisions';
|
||||
break;
|
||||
case 'FILE':
|
||||
$singular = 'file';
|
||||
$plural = 'files';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($added && !$removed) {
|
||||
$verb = 'Attached';
|
||||
if (count($added) == 1) {
|
||||
$desc = 'attached Differential Revision: '.$add_desc;
|
||||
$desc = 'attached '.$singular.': '.$add_desc;
|
||||
} else {
|
||||
$desc = 'attached Differential Revisions: '.$add_desc;
|
||||
$desc = 'attached '.$plural.': '.$add_desc;
|
||||
}
|
||||
} else if ($removed && !$added) {
|
||||
$verb = 'Detached';
|
||||
if (count($removed) == 1) {
|
||||
$desc = 'detached Differential Revision: '.$rem_desc;
|
||||
$desc = 'detached '.$singular.': '.$rem_desc;
|
||||
} else {
|
||||
$desc = 'detached Differential Revisions: '.$rem_desc;
|
||||
$desc = 'detached '.$plural.': '.$rem_desc;
|
||||
}
|
||||
} else {
|
||||
$desc = 'changed attached Differential Revisions, added: '.$add_desc.
|
||||
'removed: '.$rem_desc;
|
||||
$desc = 'changed attached '.$plural.', added: '.$add_desc.
|
||||
'removed: '.$rem_desc;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -59,7 +59,10 @@ class PhabricatorObjectHandle {
|
|||
}
|
||||
|
||||
public function getFullName() {
|
||||
return $this->fullName;
|
||||
if ($this->fullName !== null) {
|
||||
return $this->fullName;
|
||||
}
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
public function setType($type) {
|
||||
|
|
|
@ -167,12 +167,36 @@ class PhabricatorObjectHandleData {
|
|||
$handles[$phid] = $handle;
|
||||
}
|
||||
break;
|
||||
case 'PROJ':
|
||||
$class = 'PhabricatorProject';
|
||||
PhutilSymbolLoader::loadClass($class);
|
||||
$object = newv($class, array());
|
||||
|
||||
$projects = $object->loadAllWhere('phid IN (%Ls)', $phids);
|
||||
$projects = mpull($projects, null, 'getPHID');
|
||||
|
||||
foreach ($phids as $phid) {
|
||||
$handle = new PhabricatorObjectHandle();
|
||||
$handle->setPHID($phid);
|
||||
if (empty($projects[$phid])) {
|
||||
$handle->setType(self::TYPE_UNKNOWN);
|
||||
$handle->setName('Unknown Project');
|
||||
} else {
|
||||
$project = $projects[$phid];
|
||||
$handle->setType($type);
|
||||
$handle->setName($project->getName());
|
||||
$handle->setURI('/project/view/'.$project->getID().'/');
|
||||
}
|
||||
$handles[$phid] = $handle;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
foreach ($phids as $phid) {
|
||||
$handle = new PhabricatorObjectHandle();
|
||||
$handle->setType($type);
|
||||
$handle->setPHID($phid);
|
||||
$handle->setName('Unknown Object');
|
||||
$handle->setFullName('An Unknown Object');
|
||||
$handles[$phid] = $handle;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -34,7 +34,7 @@ class PhabricatorProjectListController
|
|||
'class' => 'small grey button',
|
||||
'href' => '/project/view/'.$project->getID().'/',
|
||||
),
|
||||
'View Profile'),
|
||||
'View Project Project Profile'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ class PhabricatorTypeaheadCommonDatasourceController
|
|||
|
||||
$need_users = false;
|
||||
$need_lists = false;
|
||||
$need_projs = false;
|
||||
switch ($this->type) {
|
||||
case 'users':
|
||||
$need_users = true;
|
||||
|
@ -35,6 +36,9 @@ class PhabricatorTypeaheadCommonDatasourceController
|
|||
$need_users = true;
|
||||
$need_lists = true;
|
||||
break;
|
||||
case 'projects':
|
||||
$need_projs = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
@ -62,6 +66,17 @@ class PhabricatorTypeaheadCommonDatasourceController
|
|||
}
|
||||
}
|
||||
|
||||
if ($need_projs) {
|
||||
$projs = id(new PhabricatorProject())->loadAll();
|
||||
foreach ($projs as $proj) {
|
||||
$data[] = array(
|
||||
$proj->getName(),
|
||||
'/project/view/'.$proj->getID().'/',
|
||||
$proj->getPHID(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())
|
||||
->setContent($data);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||
phutil_require_module('phabricator', 'applications/metamta/storage/mailinglist');
|
||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||
phutil_require_module('phabricator', 'applications/project/storage/project');
|
||||
phutil_require_module('phabricator', 'applications/typeahead/controller/base');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
|
Loading…
Reference in a new issue