mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 17:30:59 +01:00
Allow Maniphest tasks to be edited from the list view
Summary: Fixes T1945. Ref T2947. At various times, installs (Disqus, Dropbox, etc.) have asked for a way to edit tasks more quickly. Provide edit-from-lists. {F44700} {F44701} The one rough edge on this is that if you change the task priority we update it inline but don't move it. It's probably infeasible to actually move it, but maybe we could give it some sort of visual style to indicate that it's dirty. Test Plan: Edited tasks normally and via this action thing. Reviewers: chad, btrahan Reviewed By: chad CC: tido, deuresti, ahoffer, aran Maniphest Tasks: T1945, T2947 Differential Revision: https://secure.phabricator.com/D6086
This commit is contained in:
parent
fb765b8c93
commit
282b3f7988
7 changed files with 148 additions and 47 deletions
|
@ -1780,6 +1780,21 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/maniphest/behavior-task-preview.js',
|
'disk' => '/rsrc/js/application/maniphest/behavior-task-preview.js',
|
||||||
),
|
),
|
||||||
|
'javelin-behavior-maniphest-list-editor' =>
|
||||||
|
array(
|
||||||
|
'uri' => '/res/170f8457/rsrc/js/application/maniphest/behavior-list-edit.js',
|
||||||
|
'type' => 'js',
|
||||||
|
'requires' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-behavior',
|
||||||
|
1 => 'javelin-dom',
|
||||||
|
2 => 'javelin-stratcom',
|
||||||
|
3 => 'javelin-workflow',
|
||||||
|
4 => 'javelin-fx',
|
||||||
|
5 => 'javelin-util',
|
||||||
|
),
|
||||||
|
'disk' => '/rsrc/js/application/maniphest/behavior-list-edit.js',
|
||||||
|
),
|
||||||
'javelin-behavior-maniphest-subpriority-editor' =>
|
'javelin-behavior-maniphest-subpriority-editor' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/21b73c2a/rsrc/js/application/maniphest/behavior-subpriorityeditor.js',
|
'uri' => '/res/21b73c2a/rsrc/js/application/maniphest/behavior-subpriorityeditor.js',
|
||||||
|
@ -2737,7 +2752,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'javelin-workflow' =>
|
'javelin-workflow' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/8274d65f/rsrc/externals/javelin/lib/Workflow.js',
|
'uri' => '/res/7626494b/rsrc/externals/javelin/lib/Workflow.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -4156,7 +4171,7 @@ celerity_register_resource_map(array(
|
||||||
'uri' => '/res/pkg/96909266/diffusion.pkg.js',
|
'uri' => '/res/pkg/96909266/diffusion.pkg.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
),
|
),
|
||||||
'c1359b5d' =>
|
'a9f14d76' =>
|
||||||
array(
|
array(
|
||||||
'name' => 'javelin.pkg.js',
|
'name' => 'javelin.pkg.js',
|
||||||
'symbols' =>
|
'symbols' =>
|
||||||
|
@ -4182,7 +4197,7 @@ celerity_register_resource_map(array(
|
||||||
18 => 'javelin-tokenizer',
|
18 => 'javelin-tokenizer',
|
||||||
19 => 'javelin-history',
|
19 => 'javelin-history',
|
||||||
),
|
),
|
||||||
'uri' => '/res/pkg/c1359b5d/javelin.pkg.js',
|
'uri' => '/res/pkg/a9f14d76/javelin.pkg.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
),
|
),
|
||||||
'6b1fccc6' =>
|
'6b1fccc6' =>
|
||||||
|
@ -4242,7 +4257,7 @@ celerity_register_resource_map(array(
|
||||||
'global-drag-and-drop-css' => '1b14560c',
|
'global-drag-and-drop-css' => '1b14560c',
|
||||||
'inline-comment-summary-css' => 'dd27a69b',
|
'inline-comment-summary-css' => 'dd27a69b',
|
||||||
'javelin-aphlict' => '98f60e3f',
|
'javelin-aphlict' => '98f60e3f',
|
||||||
'javelin-behavior' => 'c1359b5d',
|
'javelin-behavior' => 'a9f14d76',
|
||||||
'javelin-behavior-aphlict-dropdown' => '98f60e3f',
|
'javelin-behavior-aphlict-dropdown' => '98f60e3f',
|
||||||
'javelin-behavior-aphlict-listen' => '98f60e3f',
|
'javelin-behavior-aphlict-listen' => '98f60e3f',
|
||||||
'javelin-behavior-aphront-basic-tokenizer' => '98f60e3f',
|
'javelin-behavior-aphront-basic-tokenizer' => '98f60e3f',
|
||||||
|
@ -4294,25 +4309,25 @@ celerity_register_resource_map(array(
|
||||||
'javelin-behavior-repository-crossreference' => '9488bb69',
|
'javelin-behavior-repository-crossreference' => '9488bb69',
|
||||||
'javelin-behavior-toggle-class' => '98f60e3f',
|
'javelin-behavior-toggle-class' => '98f60e3f',
|
||||||
'javelin-behavior-workflow' => '98f60e3f',
|
'javelin-behavior-workflow' => '98f60e3f',
|
||||||
'javelin-dom' => 'c1359b5d',
|
'javelin-dom' => 'a9f14d76',
|
||||||
'javelin-event' => 'c1359b5d',
|
'javelin-event' => 'a9f14d76',
|
||||||
'javelin-history' => 'c1359b5d',
|
'javelin-history' => 'a9f14d76',
|
||||||
'javelin-install' => 'c1359b5d',
|
'javelin-install' => 'a9f14d76',
|
||||||
'javelin-json' => 'c1359b5d',
|
'javelin-json' => 'a9f14d76',
|
||||||
'javelin-mask' => 'c1359b5d',
|
'javelin-mask' => 'a9f14d76',
|
||||||
'javelin-request' => 'c1359b5d',
|
'javelin-request' => 'a9f14d76',
|
||||||
'javelin-resource' => 'c1359b5d',
|
'javelin-resource' => 'a9f14d76',
|
||||||
'javelin-stratcom' => 'c1359b5d',
|
'javelin-stratcom' => 'a9f14d76',
|
||||||
'javelin-tokenizer' => 'c1359b5d',
|
'javelin-tokenizer' => 'a9f14d76',
|
||||||
'javelin-typeahead' => 'c1359b5d',
|
'javelin-typeahead' => 'a9f14d76',
|
||||||
'javelin-typeahead-normalizer' => 'c1359b5d',
|
'javelin-typeahead-normalizer' => 'a9f14d76',
|
||||||
'javelin-typeahead-ondemand-source' => 'c1359b5d',
|
'javelin-typeahead-ondemand-source' => 'a9f14d76',
|
||||||
'javelin-typeahead-preloaded-source' => 'c1359b5d',
|
'javelin-typeahead-preloaded-source' => 'a9f14d76',
|
||||||
'javelin-typeahead-source' => 'c1359b5d',
|
'javelin-typeahead-source' => 'a9f14d76',
|
||||||
'javelin-uri' => 'c1359b5d',
|
'javelin-uri' => 'a9f14d76',
|
||||||
'javelin-util' => 'c1359b5d',
|
'javelin-util' => 'a9f14d76',
|
||||||
'javelin-vector' => 'c1359b5d',
|
'javelin-vector' => 'a9f14d76',
|
||||||
'javelin-workflow' => 'c1359b5d',
|
'javelin-workflow' => 'a9f14d76',
|
||||||
'lightbox-attachment-css' => '1b14560c',
|
'lightbox-attachment-css' => '1b14560c',
|
||||||
'maniphest-task-summary-css' => '6b1fccc6',
|
'maniphest-task-summary-css' => '6b1fccc6',
|
||||||
'maniphest-transaction-detail-css' => '6b1fccc6',
|
'maniphest-transaction-detail-css' => '6b1fccc6',
|
||||||
|
|
|
@ -84,4 +84,27 @@ abstract class ManiphestController extends PhabricatorController {
|
||||||
return $this->defaultQuery;
|
return $this->defaultQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function renderSingleTask(ManiphestTask $task) {
|
||||||
|
$user = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
$phids = $task->getProjectPHIDs();
|
||||||
|
if ($task->getOwnerPHID()) {
|
||||||
|
$phids[] = $task->getOwnerPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
|
->setViewer($user)
|
||||||
|
->loadHandles();
|
||||||
|
|
||||||
|
$view = id(new ManiphestTaskListView())
|
||||||
|
->setUser($user)
|
||||||
|
->setShowSubpriorityControls(true)
|
||||||
|
->setShowBatchControls(true)
|
||||||
|
->setHandles($handles)
|
||||||
|
->setTasks(array($task));
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,25 +51,9 @@ final class ManiphestSubpriorityController extends ManiphestController {
|
||||||
$task->setSubpriority($new_sub);
|
$task->setSubpriority($new_sub);
|
||||||
$task->save();
|
$task->save();
|
||||||
|
|
||||||
$phids = $task->getProjectPHIDs();
|
|
||||||
if ($task->getOwnerPHID()) {
|
|
||||||
$phids[] = $task->getOwnerPHID();
|
|
||||||
}
|
|
||||||
|
|
||||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
|
||||||
->setViewer($user)
|
|
||||||
->loadHandles();
|
|
||||||
|
|
||||||
$view = id(new ManiphestTaskListView())
|
|
||||||
->setUser($user)
|
|
||||||
->setShowSubpriorityControls(true)
|
|
||||||
->setShowBatchControls(true)
|
|
||||||
->setHandles($handles)
|
|
||||||
->setTasks(array($task));
|
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent(
|
return id(new AphrontAjaxResponse())->setContent(
|
||||||
array(
|
array(
|
||||||
'tasks' => $view,
|
'tasks' => $this->renderSingleTask($task),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -242,6 +242,13 @@ final class ManiphestTaskEditController extends ManiphestController {
|
||||||
$workflow = $parent_task->getID();
|
$workflow = $parent_task->getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($request->isAjax()) {
|
||||||
|
return id(new AphrontAjaxResponse())->setContent(
|
||||||
|
array(
|
||||||
|
'tasks' => $this->renderSingleTask($task),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$redirect_uri = '/T'.$task->getID();
|
$redirect_uri = '/T'.$task->getID();
|
||||||
|
|
||||||
if ($workflow) {
|
if ($workflow) {
|
||||||
|
@ -354,12 +361,15 @@ final class ManiphestTaskEditController extends ManiphestController {
|
||||||
|
|
||||||
$project_tokenizer_id = celerity_generate_unique_node_id();
|
$project_tokenizer_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
$form = new AphrontFormView();
|
if ($request->isAjax()) {
|
||||||
$form->setFlexible(true);
|
$form = new AphrontFormLayoutView();
|
||||||
$form
|
} else {
|
||||||
->setUser($user)
|
$form = new AphrontFormView();
|
||||||
->setAction($request->getRequestURI()->getPath())
|
$form->setFlexible(true);
|
||||||
->addHiddenInput('template', $template_id);
|
$form
|
||||||
|
->setUser($user)
|
||||||
|
->addHiddenInput('template', $template_id);
|
||||||
|
}
|
||||||
|
|
||||||
if ($parent_task) {
|
if ($parent_task) {
|
||||||
$form
|
$form
|
||||||
|
@ -485,6 +495,22 @@ final class ManiphestTaskEditController extends ManiphestController {
|
||||||
$form
|
$form
|
||||||
->appendChild($description_control);
|
->appendChild($description_control);
|
||||||
|
|
||||||
|
|
||||||
|
if ($request->isAjax()) {
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($user)
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FULL)
|
||||||
|
->setTitle($header_name)
|
||||||
|
->appendChild(
|
||||||
|
array(
|
||||||
|
$error_view,
|
||||||
|
$form,
|
||||||
|
))
|
||||||
|
->addCancelButton($cancel_uri)
|
||||||
|
->addSubmitButton($button_name);
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
$form
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
|
|
|
@ -49,6 +49,10 @@ final class ManiphestTaskListView extends ManiphestView {
|
||||||
ManiphestTaskPriority::PRIORITY_WISH => 'sky',
|
ManiphestTaskPriority::PRIORITY_WISH => 'sky',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($this->showBatchControls) {
|
||||||
|
Javelin::initBehavior('maniphest-list-editor');
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->tasks as $task) {
|
foreach ($this->tasks as $task) {
|
||||||
$item = new PhabricatorObjectItemView();
|
$item = new PhabricatorObjectItemView();
|
||||||
$item->setObjectName('T'.$task->getID());
|
$item->setObjectName('T'.$task->getID());
|
||||||
|
@ -97,6 +101,14 @@ final class ManiphestTaskListView extends ManiphestView {
|
||||||
'taskID' => $task->getID(),
|
'taskID' => $task->getID(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if ($this->showBatchControls) {
|
||||||
|
$item->addAction(
|
||||||
|
id(new PhabricatorMenuItemView())
|
||||||
|
->setIcon('edit')
|
||||||
|
->addSigil('maniphest-edit-task')
|
||||||
|
->setHref('/maniphest/task/edit/'.$task->getID().'/'));
|
||||||
|
}
|
||||||
|
|
||||||
$list->addItem($item);
|
$list->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
webroot/rsrc/externals/javelin/lib/Workflow.js
vendored
14
webroot/rsrc/externals/javelin/lib/Workflow.js
vendored
|
@ -144,7 +144,13 @@ JX.install('Workflow', {
|
||||||
var d = JX.Vector.getDim(this._root);
|
var d = JX.Vector.getDim(this._root);
|
||||||
var v = JX.Vector.getViewport();
|
var v = JX.Vector.getViewport();
|
||||||
var s = JX.Vector.getScroll();
|
var s = JX.Vector.getScroll();
|
||||||
JX.$V((v.x - d.x) / 2, s.y + 100).setPos(this._root);
|
|
||||||
|
// Normally, we position dialogs 100px from the top of the screen.
|
||||||
|
// Use more space if the dialog is large (at least roughly the size
|
||||||
|
// of the viewport).
|
||||||
|
var offset = Math.min(Math.max(20, (v.y - d.y) / 2), 100);
|
||||||
|
JX.$V((v.x - d.x) / 2, s.y + offset).setPos(this._root);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JX.DOM.focus(JX.DOM.find(this._root, 'button', '__default__'));
|
JX.DOM.focus(JX.DOM.find(this._root, 'button', '__default__'));
|
||||||
var inputs = JX.DOM.scry(this._root, 'input')
|
var inputs = JX.DOM.scry(this._root, 'input')
|
||||||
|
@ -163,6 +169,12 @@ JX.install('Workflow', {
|
||||||
}
|
}
|
||||||
target && JX.DOM.focus(target);
|
target && JX.DOM.focus(target);
|
||||||
} catch (_ignored) {}
|
} catch (_ignored) {}
|
||||||
|
|
||||||
|
// The `focus()` call may have scrolled the window. Scroll it back to
|
||||||
|
// where it was before -- we want to focus the control, but not adjust
|
||||||
|
// the scroll position.
|
||||||
|
window.scrollTo(s.x, s.y);
|
||||||
|
|
||||||
} else if (this.getHandler()) {
|
} else if (this.getHandler()) {
|
||||||
this.getHandler()(r);
|
this.getHandler()(r);
|
||||||
this._pop();
|
this._pop();
|
||||||
|
|
29
webroot/rsrc/js/application/maniphest/behavior-list-edit.js
Normal file
29
webroot/rsrc/js/application/maniphest/behavior-list-edit.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-behavior-maniphest-list-editor
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-stratcom
|
||||||
|
* javelin-workflow
|
||||||
|
* javelin-fx
|
||||||
|
* javelin-util
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('maniphest-list-editor', function(config) {
|
||||||
|
|
||||||
|
var onedit = function(task, r) {
|
||||||
|
var nodes = JX.$H(r.tasks).getFragment().firstChild;
|
||||||
|
var new_task = JX.DOM.find(nodes, 'li', 'maniphest-task');
|
||||||
|
JX.DOM.replace(task, new_task);
|
||||||
|
|
||||||
|
new JX.FX(new_task).setDuration(500).start({opacity: [0, 1]});
|
||||||
|
};
|
||||||
|
|
||||||
|
JX.Stratcom.listen('click', 'maniphest-edit-task', function(e) {
|
||||||
|
e.kill();
|
||||||
|
var task = e.getNode('maniphest-task');
|
||||||
|
JX.Workflow.newFromLink(e.getNode('maniphest-edit-task'))
|
||||||
|
.setHandler(JX.bind(null, onedit, task))
|
||||||
|
.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in a new issue