diff --git a/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php index ca713ed339..4e209f066f 100644 --- a/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/taskdetail/ManiphestTaskDetailController.php @@ -220,6 +220,13 @@ class ManiphestTaskDetailController extends ManiphestController { $action->setClass('action-merge'); $actions[] = $action; + $action = new AphrontHeadsupActionView(); + $action->setName('Create Subtask'); + $action->setURI('/maniphest/task/create/?parent='.$task->getID()); + $action->setClass('action-branch'); + $actions[] = $action; + + $action = new AphrontHeadsupActionView(); $action->setName('Edit Dependencies'); $action->setURI('/search/attach/'.$task->getPHID().'/TASK/dependencies/'); diff --git a/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php b/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php index e3c9d5d426..2059bf89a7 100644 --- a/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php +++ b/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php @@ -33,6 +33,8 @@ class ManiphestTaskEditController extends ManiphestController { $user = $request->getUser(); $files = array(); + $parent_task = null; + $template_id = null; if ($this->id) { $task = id(new ManiphestTask())->load($this->id); @@ -70,9 +72,15 @@ class ManiphestTaskEditController extends ManiphestController { 'phid IN (%Ls)', $file_phids); } - } - $template_id = $request->getStr('template'); + $template_id = $request->getInt('template'); + + // You can only have a parent task if you're creating a new task. + $parent_id = $request->getInt('parent'); + if ($parent_id) { + $parent_task = id(new ManiphestTask())->load($parent_id); + } + } $errors = array(); $e_title = true; @@ -197,7 +205,26 @@ class ManiphestTaskEditController extends ManiphestController { $aux_field->getValueForStorage() ); } - + + if ($parent_task) { + $type_task = PhabricatorPHIDConstants::PHID_TYPE_TASK; + + // NOTE: It's safe to simply apply this transaction without doing + // cycle detection because we know the new task has no children. + $new_value = $parent_task->getAttached(); + $new_value[$type_task][$task->getPHID()] = true; + + $parent_xaction = clone $template; + $attach_type = ManiphestTransactionType::TYPE_ATTACH; + $parent_xaction->setTransactionType($attach_type); + $parent_xaction->setNewValue($new_value); + + $editor = new ManiphestTransactionEditor(); + $editor->applyTransactions($parent_task, array($parent_xaction)); + + $workflow = $parent_task->getID(); + } + $redirect_uri = '/T'.$task->getID(); if ($workflow) { @@ -228,6 +255,10 @@ class ManiphestTaskEditController extends ManiphestController { $task->getCCPHIDs(), $task->getProjectPHIDs()); + if ($parent_task) { + $phids[] = $parent_task->getPHID(); + } + $phids = array_filter($phids); $phids = array_unique($phids); @@ -275,6 +306,10 @@ class ManiphestTaskEditController extends ManiphestController { if ($task->getID()) { $button_name = 'Save Task'; $header_name = 'Edit Task'; + } else if ($parent_task) { + $cancel_uri = '/T'.$parent_task->getID(); + $button_name = 'Create Task'; + $header_name = 'Create New Subtask'; } else { $button_name = 'Create Task'; $header_name = 'Create New Task'; @@ -286,7 +321,18 @@ class ManiphestTaskEditController extends ManiphestController { $form ->setUser($user) ->setAction($request->getRequestURI()->getPath()) - ->addHiddenInput('template', $template_id) + ->addHiddenInput('template', $template_id); + + if ($parent_task) { + $form + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Parent Task') + ->setValue($handles[$parent_task->getPHID()]->getFullName())) + ->addHiddenInput('parent', $parent_task->getID()); + } + + $form ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Title') diff --git a/src/applications/maniphest/controller/taskedit/__init__.php b/src/applications/maniphest/controller/taskedit/__init__.php index 92a2a49dbd..ff0eea8157 100644 --- a/src/applications/maniphest/controller/taskedit/__init__.php +++ b/src/applications/maniphest/controller/taskedit/__init__.php @@ -26,6 +26,7 @@ phutil_require_module('phabricator', 'infrastructure/javelin/markup'); phutil_require_module('phabricator', 'view/form/base'); phutil_require_module('phabricator', 'view/form/control/markup'); phutil_require_module('phabricator', 'view/form/control/select'); +phutil_require_module('phabricator', 'view/form/control/static'); phutil_require_module('phabricator', 'view/form/control/submit'); phutil_require_module('phabricator', 'view/form/control/textarea'); phutil_require_module('phabricator', 'view/form/control/tokenizer'); diff --git a/webroot/rsrc/css/aphront/headsup-action-list-view.css b/webroot/rsrc/css/aphront/headsup-action-list-view.css index 80a0d0eedf..752aad4dab 100644 --- a/webroot/rsrc/css/aphront/headsup-action-list-view.css +++ b/webroot/rsrc/css/aphront/headsup-action-list-view.css @@ -66,6 +66,10 @@ background-image: url(/rsrc/image/icon/fatcow/arrow_merge.png); } +.aphront-headsup-action-list .action-branch { + background-image: url(/rsrc/image/icon/fatcow/arrow_branch.png); +} + .aphront-headsup-action-list .action-dependencies { background-image: url(/rsrc/image/icon/fatcow/link.png); } diff --git a/webroot/rsrc/image/icon/fatcow/arrow_branch.png b/webroot/rsrc/image/icon/fatcow/arrow_branch.png new file mode 100755 index 0000000000..d1fe71753d Binary files /dev/null and b/webroot/rsrc/image/icon/fatcow/arrow_branch.png differ