@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.

Add a standalone view for the Maniphest task graph

Summary:
See PHI1073. Improve the UX here:

- When there are a small number of connected tasks, no changes.
- When there are too many total connected tasks, but not too many directly connected tasks, show hint text with a "View Standalone Graph" button to view more of the graph.
- When there are too many directly connected tasks, show better hint text with a "View Standalone Graph" button.
- Always show a "View Standalone Graph" option in the dropdown menu.
- Add a standalone view which works the same way but has a limit of 2,000.
- This view doesn't have "View Standalone Graph" links, since they'd just link back to the same page, but is basically the same otherwise.
- Increase the main page task limit from 100 to 200.

Test Plan:
Mobile View:

{F6210326}

Way too much stuff:

{F6210327}

New persistent link to the standalone page:

{F6210328}

Kind of too much stuff:

{F6210329}

Standalone view:

{F6210330}

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: 20after4

Differential Revision: https://secure.phabricator.com/D20164

+300 -42
+3 -3
resources/celerity/map.php
··· 9 9 'names' => array( 10 10 'conpherence.pkg.css' => '3c8a0668', 11 11 'conpherence.pkg.js' => '020aebcf', 12 - 'core.pkg.css' => '4ed8ce1f', 12 + 'core.pkg.css' => '85a1da99', 13 13 'core.pkg.js' => '5c737607', 14 14 'differential.pkg.css' => 'b8df73d4', 15 15 'differential.pkg.js' => '67c9ea4c', ··· 30 30 'rsrc/css/aphront/notification.css' => '30240bd2', 31 31 'rsrc/css/aphront/panel-view.css' => '46923d46', 32 32 'rsrc/css/aphront/phabricator-nav-view.css' => 'f8a0c1bf', 33 - 'rsrc/css/aphront/table-view.css' => 'daa1f9df', 33 + 'rsrc/css/aphront/table-view.css' => '205053cd', 34 34 'rsrc/css/aphront/tokenizer.css' => 'b52d0668', 35 35 'rsrc/css/aphront/tooltip.css' => 'e3f2412f', 36 36 'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2', ··· 520 520 'aphront-list-filter-view-css' => 'feb64255', 521 521 'aphront-multi-column-view-css' => 'fbc00ba3', 522 522 'aphront-panel-view-css' => '46923d46', 523 - 'aphront-table-view-css' => 'daa1f9df', 523 + 'aphront-table-view-css' => '205053cd', 524 524 'aphront-tokenizer-control-css' => 'b52d0668', 525 525 'aphront-tooltip-css' => 'e3f2412f', 526 526 'aphront-typeahead-control-css' => '8779483d',
+2
src/__phutil_library_map__.php
··· 1714 1714 'ManiphestTaskFerretEngine' => 'applications/maniphest/search/ManiphestTaskFerretEngine.php', 1715 1715 'ManiphestTaskFulltextEngine' => 'applications/maniphest/search/ManiphestTaskFulltextEngine.php', 1716 1716 'ManiphestTaskGraph' => 'infrastructure/graph/ManiphestTaskGraph.php', 1717 + 'ManiphestTaskGraphController' => 'applications/maniphest/controller/ManiphestTaskGraphController.php', 1717 1718 'ManiphestTaskHasCommitEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasCommitEdgeType.php', 1718 1719 'ManiphestTaskHasCommitRelationship' => 'applications/maniphest/relationship/ManiphestTaskHasCommitRelationship.php', 1719 1720 'ManiphestTaskHasDuplicateTaskEdgeType' => 'applications/maniphest/edge/ManiphestTaskHasDuplicateTaskEdgeType.php', ··· 7401 7402 'ManiphestTaskFerretEngine' => 'PhabricatorFerretEngine', 7402 7403 'ManiphestTaskFulltextEngine' => 'PhabricatorFulltextEngine', 7403 7404 'ManiphestTaskGraph' => 'PhabricatorObjectGraph', 7405 + 'ManiphestTaskGraphController' => 'ManiphestController', 7404 7406 'ManiphestTaskHasCommitEdgeType' => 'PhabricatorEdgeType', 7405 7407 'ManiphestTaskHasCommitRelationship' => 'ManiphestTaskRelationship', 7406 7408 'ManiphestTaskHasDuplicateTaskEdgeType' => 'PhabricatorEdgeType',
+1
src/applications/maniphest/application/PhabricatorManiphestApplication.php
··· 55 55 'subtask/(?P<id>[1-9]\d*)/' => 'ManiphestTaskSubtaskController', 56 56 ), 57 57 'subpriority/' => 'ManiphestSubpriorityController', 58 + 'graph/(?P<id>[1-9]\d*)/' => 'ManiphestTaskGraphController', 58 59 ), 59 60 ); 60 61 }
+98
src/applications/maniphest/controller/ManiphestController.php
··· 61 61 return $view; 62 62 } 63 63 64 + final protected function newTaskGraphDropdownMenu( 65 + ManiphestTask $task, 66 + $has_parents, 67 + $has_subtasks, 68 + $include_standalone) { 69 + $viewer = $this->getViewer(); 70 + 71 + $parents_uri = urisprintf( 72 + '/?subtaskIDs=%d#R', 73 + $task->getID()); 74 + $parents_uri = $this->getApplicationURI($parents_uri); 75 + 76 + $subtasks_uri = urisprintf( 77 + '/?parentIDs=%d#R', 78 + $task->getID()); 79 + $subtasks_uri = $this->getApplicationURI($subtasks_uri); 80 + 81 + $dropdown_menu = id(new PhabricatorActionListView()) 82 + ->setViewer($viewer) 83 + ->addAction( 84 + id(new PhabricatorActionView()) 85 + ->setHref($parents_uri) 86 + ->setName(pht('Search Parent Tasks')) 87 + ->setDisabled(!$has_parents) 88 + ->setIcon('fa-chevron-circle-up')) 89 + ->addAction( 90 + id(new PhabricatorActionView()) 91 + ->setHref($subtasks_uri) 92 + ->setName(pht('Search Subtasks')) 93 + ->setDisabled(!$has_subtasks) 94 + ->setIcon('fa-chevron-circle-down')); 95 + 96 + if ($include_standalone) { 97 + $standalone_uri = urisprintf('/graph/%d/', $task->getID()); 98 + $standalone_uri = $this->getApplicationURI($standalone_uri); 99 + 100 + $dropdown_menu->addAction( 101 + id(new PhabricatorActionView()) 102 + ->setHref($standalone_uri) 103 + ->setName(pht('View Standalone Graph')) 104 + ->setIcon('fa-code-fork')); 105 + } 106 + 107 + $graph_menu = id(new PHUIButtonView()) 108 + ->setTag('a') 109 + ->setIcon('fa-search') 110 + ->setText(pht('Search...')) 111 + ->setDropdownMenu($dropdown_menu); 112 + 113 + return $graph_menu; 114 + } 115 + 116 + final protected function newTaskGraphOverflowView( 117 + ManiphestTask $task, 118 + $overflow_message, 119 + $include_standalone) { 120 + 121 + $id = $task->getID(); 122 + 123 + if ($include_standalone) { 124 + $standalone_uri = $this->getApplicationURI("graph/{$id}/"); 125 + 126 + $standalone_link = id(new PHUIButtonView()) 127 + ->setTag('a') 128 + ->setHref($standalone_uri) 129 + ->setColor(PHUIButtonView::GREY) 130 + ->setIcon('fa-code-fork') 131 + ->setText(pht('View Standalone Graph')); 132 + } else { 133 + $standalone_link = null; 134 + } 135 + 136 + $standalone_icon = id(new PHUIIconView()) 137 + ->setIcon('fa-exclamation-triangle', 'yellow') 138 + ->addClass('object-graph-header-icon'); 139 + 140 + $standalone_view = phutil_tag( 141 + 'div', 142 + array( 143 + 'class' => 'object-graph-header', 144 + ), 145 + array( 146 + $standalone_link, 147 + $standalone_icon, 148 + phutil_tag( 149 + 'div', 150 + array( 151 + 'class' => 'object-graph-header-message', 152 + ), 153 + array( 154 + $overflow_message, 155 + )), 156 + )); 157 + 158 + return $standalone_view; 159 + } 160 + 161 + 64 162 }
+34 -39
src/applications/maniphest/controller/ManiphestTaskDetailController.php
··· 80 80 $related_tabs = array(); 81 81 $graph_menu = null; 82 82 83 - $graph_limit = 100; 83 + $graph_limit = 200; 84 + $overflow_message = null; 84 85 $task_graph = id(new ManiphestTaskGraph()) 85 86 ->setViewer($viewer) 86 87 ->setSeedPHID($task->getPHID()) ··· 96 97 $has_parents = (bool)$parent_list; 97 98 $has_subtasks = (bool)$subtask_list; 98 99 99 - $search_text = pht('Search...'); 100 - 101 100 // First, get a count of direct parent tasks and subtasks. If there 102 101 // are too many of these, we just don't draw anything. You can use 103 102 // the search button to browse tasks with the search UI instead. 104 103 $direct_count = count($parent_list) + count($subtask_list); 105 104 106 105 if ($direct_count > $graph_limit) { 107 - $message = pht( 108 - 'Task graph too large to display (this task is directly connected '. 109 - 'to more than %s other tasks). Use %s to explore connected tasks.', 110 - $graph_limit, 111 - phutil_tag('strong', array(), $search_text)); 112 - $message = phutil_tag('em', array(), $message); 113 - $graph_table = id(new PHUIPropertyListView()) 114 - ->addTextContent($message); 106 + $overflow_message = pht( 107 + 'This task is directly connected to more than %s other tasks. '. 108 + 'Use %s to browse parents or subtasks, or %s to show more of the '. 109 + 'graph.', 110 + new PhutilNumber($graph_limit), 111 + phutil_tag('strong', array(), pht('Search...')), 112 + phutil_tag('strong', array(), pht('View Standalone Graph'))); 113 + 114 + $graph_table = null; 115 115 } else { 116 116 // If there aren't too many direct tasks, but there are too many total 117 117 // tasks, we'll only render directly connected tasks. 118 118 if ($task_graph->isOverLimit()) { 119 119 $task_graph->setRenderOnlyAdjacentNodes(true); 120 + 121 + $overflow_message = pht( 122 + 'This task is connected to more than %s other tasks. '. 123 + 'Only direct parents and subtasks are shown here. Use '. 124 + '%s to show more of the graph.', 125 + new PhutilNumber($graph_limit), 126 + phutil_tag('strong', array(), pht('View Standalone Graph'))); 120 127 } 128 + 121 129 $graph_table = $task_graph->newGraphTable(); 122 130 } 123 131 124 - $parents_uri = urisprintf( 125 - '/?subtaskIDs=%d#R', 126 - $task->getID()); 127 - $parents_uri = $this->getApplicationURI($parents_uri); 132 + if ($overflow_message) { 133 + $overflow_view = $this->newTaskGraphOverflowView( 134 + $task, 135 + $overflow_message, 136 + true); 128 137 129 - $subtasks_uri = urisprintf( 130 - '/?parentIDs=%d#R', 131 - $task->getID()); 132 - $subtasks_uri = $this->getApplicationURI($subtasks_uri); 133 - 134 - $dropdown_menu = id(new PhabricatorActionListView()) 135 - ->setViewer($viewer) 136 - ->addAction( 137 - id(new PhabricatorActionView()) 138 - ->setHref($parents_uri) 139 - ->setName(pht('Search Parent Tasks')) 140 - ->setDisabled(!$has_parents) 141 - ->setIcon('fa-chevron-circle-up')) 142 - ->addAction( 143 - id(new PhabricatorActionView()) 144 - ->setHref($subtasks_uri) 145 - ->setName(pht('Search Subtasks')) 146 - ->setDisabled(!$has_subtasks) 147 - ->setIcon('fa-chevron-circle-down')); 138 + $graph_table = array( 139 + $overflow_view, 140 + $graph_table, 141 + ); 142 + } 148 143 149 - $graph_menu = id(new PHUIButtonView()) 150 - ->setTag('a') 151 - ->setIcon('fa-search') 152 - ->setText($search_text) 153 - ->setDropdownMenu($dropdown_menu); 144 + $graph_menu = $this->newTaskGraphDropdownMenu( 145 + $task, 146 + $has_parents, 147 + $has_subtasks, 148 + true); 154 149 155 150 $related_tabs[] = id(new PHUITabView()) 156 151 ->setName(pht('Task Graph'))
+125
src/applications/maniphest/controller/ManiphestTaskGraphController.php
··· 1 + <?php 2 + 3 + final class ManiphestTaskGraphController 4 + extends ManiphestController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $this->getViewer(); 12 + $id = $request->getURIData('id'); 13 + 14 + $task = id(new ManiphestTaskQuery()) 15 + ->setViewer($viewer) 16 + ->withIDs(array($id)) 17 + ->executeOne(); 18 + if (!$task) { 19 + return new Aphront404Response(); 20 + } 21 + 22 + $crumbs = $this->buildApplicationCrumbs() 23 + ->addTextCrumb($task->getMonogram(), $task->getURI()) 24 + ->addTextCrumb(pht('Graph')) 25 + ->setBorder(true); 26 + 27 + $graph_limit = 2000; 28 + $overflow_message = null; 29 + $task_graph = id(new ManiphestTaskGraph()) 30 + ->setViewer($viewer) 31 + ->setSeedPHID($task->getPHID()) 32 + ->setLimit($graph_limit) 33 + ->loadGraph(); 34 + if (!$task_graph->isEmpty()) { 35 + $parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST; 36 + $subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST; 37 + $parent_map = $task_graph->getEdges($parent_type); 38 + $subtask_map = $task_graph->getEdges($subtask_type); 39 + $parent_list = idx($parent_map, $task->getPHID(), array()); 40 + $subtask_list = idx($subtask_map, $task->getPHID(), array()); 41 + $has_parents = (bool)$parent_list; 42 + $has_subtasks = (bool)$subtask_list; 43 + 44 + // First, get a count of direct parent tasks and subtasks. If there 45 + // are too many of these, we just don't draw anything. You can use 46 + // the search button to browse tasks with the search UI instead. 47 + $direct_count = count($parent_list) + count($subtask_list); 48 + 49 + if ($direct_count > $graph_limit) { 50 + $overflow_message = pht( 51 + 'This task is directly connected to more than %s other tasks, '. 52 + 'which is too many tasks to display. Use %s to browse parents '. 53 + 'or subtasks.', 54 + new PhutilNumber($graph_limit), 55 + phutil_tag('strong', array(), pht('Search...'))); 56 + 57 + $graph_table = null; 58 + } else { 59 + // If there aren't too many direct tasks, but there are too many total 60 + // tasks, we'll only render directly connected tasks. 61 + if ($task_graph->isOverLimit()) { 62 + $task_graph->setRenderOnlyAdjacentNodes(true); 63 + 64 + $overflow_message = pht( 65 + 'This task is connected to more than %s other tasks. '. 66 + 'Only direct parents and subtasks are shown here.', 67 + new PhutilNumber($graph_limit)); 68 + } 69 + 70 + $graph_table = $task_graph->newGraphTable(); 71 + } 72 + 73 + $graph_menu = $this->newTaskGraphDropdownMenu( 74 + $task, 75 + $has_parents, 76 + $has_subtasks, 77 + false); 78 + } else { 79 + $graph_menu = null; 80 + $graph_table = null; 81 + 82 + $overflow_message = pht( 83 + 'This task has no parent tasks and no subtasks, so there is no '. 84 + 'graph to draw.'); 85 + } 86 + 87 + if ($overflow_message) { 88 + $overflow_view = $this->newTaskGraphOverflowView( 89 + $task, 90 + $overflow_message, 91 + false); 92 + 93 + $graph_table = array( 94 + $overflow_view, 95 + $graph_table, 96 + ); 97 + } 98 + 99 + $header = id(new PHUIHeaderView()) 100 + ->setHeader(pht('Task Graph')); 101 + 102 + if ($graph_menu) { 103 + $header->addActionLink($graph_menu); 104 + } 105 + 106 + $tab_view = id(new PHUIObjectBoxView()) 107 + ->setHeader($header) 108 + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 109 + ->appendChild($graph_table); 110 + 111 + $view = id(new PHUITwoColumnView()) 112 + ->setFooter($tab_view); 113 + 114 + return $this->newPage() 115 + ->setTitle( 116 + array( 117 + $task->getMonogram(), 118 + pht('Graph'), 119 + )) 120 + ->setCrumbs($crumbs) 121 + ->appendChild($view); 122 + } 123 + 124 + 125 + }
+37
webroot/rsrc/css/aphront/table-view.css
··· 327 327 .phui-object-box .aphront-table-view { 328 328 border: none; 329 329 } 330 + 331 + .object-graph-header { 332 + padding: 8px 12px; 333 + overflow: hidden; 334 + background: {$lightyellow}; 335 + border-bottom: 1px solid {$lightblueborder}; 336 + vertical-align: middle; 337 + } 338 + 339 + .object-graph-header .object-graph-header-icon { 340 + float: left; 341 + margin-top: 10px; 342 + } 343 + 344 + .object-graph-header a.button { 345 + float: right; 346 + } 347 + 348 + .object-graph-header-message { 349 + margin: 8px 200px 8px 20px; 350 + } 351 + 352 + .device .object-graph-header .object-graph-header-icon { 353 + display: none; 354 + } 355 + 356 + .device .object-graph-header-message { 357 + clear: both; 358 + margin: 0; 359 + } 360 + 361 + .device .object-graph-header a.button { 362 + margin: 0 auto 12px; 363 + display: block; 364 + width: 180px; 365 + float: none; 366 + }