@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+148 -47
+38 -23
src/__celerity_resource_map__.php
··· 1780 1780 ), 1781 1781 'disk' => '/rsrc/js/application/maniphest/behavior-task-preview.js', 1782 1782 ), 1783 + 'javelin-behavior-maniphest-list-editor' => 1784 + array( 1785 + 'uri' => '/res/170f8457/rsrc/js/application/maniphest/behavior-list-edit.js', 1786 + 'type' => 'js', 1787 + 'requires' => 1788 + array( 1789 + 0 => 'javelin-behavior', 1790 + 1 => 'javelin-dom', 1791 + 2 => 'javelin-stratcom', 1792 + 3 => 'javelin-workflow', 1793 + 4 => 'javelin-fx', 1794 + 5 => 'javelin-util', 1795 + ), 1796 + 'disk' => '/rsrc/js/application/maniphest/behavior-list-edit.js', 1797 + ), 1783 1798 'javelin-behavior-maniphest-subpriority-editor' => 1784 1799 array( 1785 1800 'uri' => '/res/21b73c2a/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', ··· 2737 2752 ), 2738 2753 'javelin-workflow' => 2739 2754 array( 2740 - 'uri' => '/res/8274d65f/rsrc/externals/javelin/lib/Workflow.js', 2755 + 'uri' => '/res/7626494b/rsrc/externals/javelin/lib/Workflow.js', 2741 2756 'type' => 'js', 2742 2757 'requires' => 2743 2758 array( ··· 4156 4171 'uri' => '/res/pkg/96909266/diffusion.pkg.js', 4157 4172 'type' => 'js', 4158 4173 ), 4159 - 'c1359b5d' => 4174 + 'a9f14d76' => 4160 4175 array( 4161 4176 'name' => 'javelin.pkg.js', 4162 4177 'symbols' => ··· 4182 4197 18 => 'javelin-tokenizer', 4183 4198 19 => 'javelin-history', 4184 4199 ), 4185 - 'uri' => '/res/pkg/c1359b5d/javelin.pkg.js', 4200 + 'uri' => '/res/pkg/a9f14d76/javelin.pkg.js', 4186 4201 'type' => 'js', 4187 4202 ), 4188 4203 '6b1fccc6' => ··· 4242 4257 'global-drag-and-drop-css' => '1b14560c', 4243 4258 'inline-comment-summary-css' => 'dd27a69b', 4244 4259 'javelin-aphlict' => '98f60e3f', 4245 - 'javelin-behavior' => 'c1359b5d', 4260 + 'javelin-behavior' => 'a9f14d76', 4246 4261 'javelin-behavior-aphlict-dropdown' => '98f60e3f', 4247 4262 'javelin-behavior-aphlict-listen' => '98f60e3f', 4248 4263 'javelin-behavior-aphront-basic-tokenizer' => '98f60e3f', ··· 4294 4309 'javelin-behavior-repository-crossreference' => '9488bb69', 4295 4310 'javelin-behavior-toggle-class' => '98f60e3f', 4296 4311 'javelin-behavior-workflow' => '98f60e3f', 4297 - 'javelin-dom' => 'c1359b5d', 4298 - 'javelin-event' => 'c1359b5d', 4299 - 'javelin-history' => 'c1359b5d', 4300 - 'javelin-install' => 'c1359b5d', 4301 - 'javelin-json' => 'c1359b5d', 4302 - 'javelin-mask' => 'c1359b5d', 4303 - 'javelin-request' => 'c1359b5d', 4304 - 'javelin-resource' => 'c1359b5d', 4305 - 'javelin-stratcom' => 'c1359b5d', 4306 - 'javelin-tokenizer' => 'c1359b5d', 4307 - 'javelin-typeahead' => 'c1359b5d', 4308 - 'javelin-typeahead-normalizer' => 'c1359b5d', 4309 - 'javelin-typeahead-ondemand-source' => 'c1359b5d', 4310 - 'javelin-typeahead-preloaded-source' => 'c1359b5d', 4311 - 'javelin-typeahead-source' => 'c1359b5d', 4312 - 'javelin-uri' => 'c1359b5d', 4313 - 'javelin-util' => 'c1359b5d', 4314 - 'javelin-vector' => 'c1359b5d', 4315 - 'javelin-workflow' => 'c1359b5d', 4312 + 'javelin-dom' => 'a9f14d76', 4313 + 'javelin-event' => 'a9f14d76', 4314 + 'javelin-history' => 'a9f14d76', 4315 + 'javelin-install' => 'a9f14d76', 4316 + 'javelin-json' => 'a9f14d76', 4317 + 'javelin-mask' => 'a9f14d76', 4318 + 'javelin-request' => 'a9f14d76', 4319 + 'javelin-resource' => 'a9f14d76', 4320 + 'javelin-stratcom' => 'a9f14d76', 4321 + 'javelin-tokenizer' => 'a9f14d76', 4322 + 'javelin-typeahead' => 'a9f14d76', 4323 + 'javelin-typeahead-normalizer' => 'a9f14d76', 4324 + 'javelin-typeahead-ondemand-source' => 'a9f14d76', 4325 + 'javelin-typeahead-preloaded-source' => 'a9f14d76', 4326 + 'javelin-typeahead-source' => 'a9f14d76', 4327 + 'javelin-uri' => 'a9f14d76', 4328 + 'javelin-util' => 'a9f14d76', 4329 + 'javelin-vector' => 'a9f14d76', 4330 + 'javelin-workflow' => 'a9f14d76', 4316 4331 'lightbox-attachment-css' => '1b14560c', 4317 4332 'maniphest-task-summary-css' => '6b1fccc6', 4318 4333 'maniphest-transaction-detail-css' => '6b1fccc6',
+23
src/applications/maniphest/controller/ManiphestController.php
··· 84 84 return $this->defaultQuery; 85 85 } 86 86 87 + protected function renderSingleTask(ManiphestTask $task) { 88 + $user = $this->getRequest()->getUser(); 89 + 90 + $phids = $task->getProjectPHIDs(); 91 + if ($task->getOwnerPHID()) { 92 + $phids[] = $task->getOwnerPHID(); 93 + } 94 + 95 + $handles = id(new PhabricatorObjectHandleData($phids)) 96 + ->setViewer($user) 97 + ->loadHandles(); 98 + 99 + $view = id(new ManiphestTaskListView()) 100 + ->setUser($user) 101 + ->setShowSubpriorityControls(true) 102 + ->setShowBatchControls(true) 103 + ->setHandles($handles) 104 + ->setTasks(array($task)); 105 + 106 + return $view; 107 + } 108 + 109 + 87 110 }
+1 -17
src/applications/maniphest/controller/ManiphestSubpriorityController.php
··· 51 51 $task->setSubpriority($new_sub); 52 52 $task->save(); 53 53 54 - $phids = $task->getProjectPHIDs(); 55 - if ($task->getOwnerPHID()) { 56 - $phids[] = $task->getOwnerPHID(); 57 - } 58 - 59 - $handles = id(new PhabricatorObjectHandleData($phids)) 60 - ->setViewer($user) 61 - ->loadHandles(); 62 - 63 - $view = id(new ManiphestTaskListView()) 64 - ->setUser($user) 65 - ->setShowSubpriorityControls(true) 66 - ->setShowBatchControls(true) 67 - ->setHandles($handles) 68 - ->setTasks(array($task)); 69 - 70 54 return id(new AphrontAjaxResponse())->setContent( 71 55 array( 72 - 'tasks' => $view, 56 + 'tasks' => $this->renderSingleTask($task), 73 57 )); 74 58 } 75 59
+32 -6
src/applications/maniphest/controller/ManiphestTaskEditController.php
··· 242 242 $workflow = $parent_task->getID(); 243 243 } 244 244 245 + if ($request->isAjax()) { 246 + return id(new AphrontAjaxResponse())->setContent( 247 + array( 248 + 'tasks' => $this->renderSingleTask($task), 249 + )); 250 + } 251 + 245 252 $redirect_uri = '/T'.$task->getID(); 246 253 247 254 if ($workflow) { ··· 354 361 355 362 $project_tokenizer_id = celerity_generate_unique_node_id(); 356 363 357 - $form = new AphrontFormView(); 358 - $form->setFlexible(true); 359 - $form 360 - ->setUser($user) 361 - ->setAction($request->getRequestURI()->getPath()) 362 - ->addHiddenInput('template', $template_id); 364 + if ($request->isAjax()) { 365 + $form = new AphrontFormLayoutView(); 366 + } else { 367 + $form = new AphrontFormView(); 368 + $form->setFlexible(true); 369 + $form 370 + ->setUser($user) 371 + ->addHiddenInput('template', $template_id); 372 + } 363 373 364 374 if ($parent_task) { 365 375 $form ··· 484 494 485 495 $form 486 496 ->appendChild($description_control); 497 + 498 + 499 + if ($request->isAjax()) { 500 + $dialog = id(new AphrontDialogView()) 501 + ->setUser($user) 502 + ->setWidth(AphrontDialogView::WIDTH_FULL) 503 + ->setTitle($header_name) 504 + ->appendChild( 505 + array( 506 + $error_view, 507 + $form, 508 + )) 509 + ->addCancelButton($cancel_uri) 510 + ->addSubmitButton($button_name); 511 + return id(new AphrontDialogResponse())->setDialog($dialog); 512 + } 487 513 488 514 $form 489 515 ->appendChild(
+12
src/applications/maniphest/view/ManiphestTaskListView.php
··· 49 49 ManiphestTaskPriority::PRIORITY_WISH => 'sky', 50 50 ); 51 51 52 + if ($this->showBatchControls) { 53 + Javelin::initBehavior('maniphest-list-editor'); 54 + } 55 + 52 56 foreach ($this->tasks as $task) { 53 57 $item = new PhabricatorObjectItemView(); 54 58 $item->setObjectName('T'.$task->getID()); ··· 96 100 array( 97 101 'taskID' => $task->getID(), 98 102 )); 103 + 104 + if ($this->showBatchControls) { 105 + $item->addAction( 106 + id(new PhabricatorMenuItemView()) 107 + ->setIcon('edit') 108 + ->addSigil('maniphest-edit-task') 109 + ->setHref('/maniphest/task/edit/'.$task->getID().'/')); 110 + } 99 111 100 112 $list->addItem($item); 101 113 }
+13 -1
webroot/rsrc/externals/javelin/lib/Workflow.js
··· 144 144 var d = JX.Vector.getDim(this._root); 145 145 var v = JX.Vector.getViewport(); 146 146 var s = JX.Vector.getScroll(); 147 - JX.$V((v.x - d.x) / 2, s.y + 100).setPos(this._root); 147 + 148 + // Normally, we position dialogs 100px from the top of the screen. 149 + // Use more space if the dialog is large (at least roughly the size 150 + // of the viewport). 151 + var offset = Math.min(Math.max(20, (v.y - d.y) / 2), 100); 152 + JX.$V((v.x - d.x) / 2, s.y + offset).setPos(this._root); 153 + 148 154 try { 149 155 JX.DOM.focus(JX.DOM.find(this._root, 'button', '__default__')); 150 156 var inputs = JX.DOM.scry(this._root, 'input') ··· 163 169 } 164 170 target && JX.DOM.focus(target); 165 171 } catch (_ignored) {} 172 + 173 + // The `focus()` call may have scrolled the window. Scroll it back to 174 + // where it was before -- we want to focus the control, but not adjust 175 + // the scroll position. 176 + window.scrollTo(s.x, s.y); 177 + 166 178 } else if (this.getHandler()) { 167 179 this.getHandler()(r); 168 180 this._pop();
+29
webroot/rsrc/js/application/maniphest/behavior-list-edit.js
··· 1 + /** 2 + * @provides javelin-behavior-maniphest-list-editor 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-stratcom 6 + * javelin-workflow 7 + * javelin-fx 8 + * javelin-util 9 + */ 10 + 11 + JX.behavior('maniphest-list-editor', function(config) { 12 + 13 + var onedit = function(task, r) { 14 + var nodes = JX.$H(r.tasks).getFragment().firstChild; 15 + var new_task = JX.DOM.find(nodes, 'li', 'maniphest-task'); 16 + JX.DOM.replace(task, new_task); 17 + 18 + new JX.FX(new_task).setDuration(500).start({opacity: [0, 1]}); 19 + }; 20 + 21 + JX.Stratcom.listen('click', 'maniphest-edit-task', function(e) { 22 + e.kill(); 23 + var task = e.getNode('maniphest-task'); 24 + JX.Workflow.newFromLink(e.getNode('maniphest-edit-task')) 25 + .setHandler(JX.bind(null, onedit, task)) 26 + .start(); 27 + }); 28 + 29 + });