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

Maniphest - allow for searching for tasks based on dependency relationships

Summary:
Fixes T5352. This is very useful for finding things that should be easy to do ("not blocked") as well as things that are important to do ("blocking"). I have wanted to check out the latter case in our installation, though no promises on what I would end up actually doing from that search result list. =D

I also think supporting something like T6638 is reasonable but the UI seems trickier to me; its some sort of task tokenizer, which I don't think we've done before?

Test Plan: toggled various search options and got reasonable results. When i clicked conflicting things like "blocking" and "not blocking" verified it was like I had not clicked anything at all.

Reviewers: chad, epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T5352

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

+114 -1
+83
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 53 53 private $needSubscriberPHIDs; 54 54 private $needProjectPHIDs; 55 55 56 + private $blockingTasks; 57 + private $blockedTasks; 58 + 56 59 const DEFAULT_PAGE_SIZE = 1000; 57 60 58 61 public function withAuthors(array $authors) { ··· 161 164 return $this; 162 165 } 163 166 167 + /** 168 + * True returns tasks that are blocking other tasks only. 169 + * False returns tasks that are not blocking other tasks only. 170 + * Null returns tasks regardless of blocking status. 171 + */ 172 + public function withBlockingTasks($mode) { 173 + $this->blockingTasks = $mode; 174 + return $this; 175 + } 176 + 177 + public function shouldJoinBlockingTasks() { 178 + return $this->blockingTasks !== null; 179 + } 180 + 181 + /** 182 + * True returns tasks that are blocked by other tasks only. 183 + * False returns tasks that are not blocked by other tasks only. 184 + * Null returns tasks regardless of blocked by status. 185 + */ 186 + public function withBlockedTasks($mode) { 187 + $this->blockedTasks = $mode; 188 + return $this; 189 + } 190 + 191 + public function shouldJoinBlockedTasks() { 192 + return $this->blockedTasks !== null; 193 + } 194 + 164 195 public function withDateCreatedBefore($date_created_before) { 165 196 $this->dateCreatedBefore = $date_created_before; 166 197 return $this; ··· 207 238 $where[] = $this->buildStatusWhereClause($conn); 208 239 $where[] = $this->buildStatusesWhereClause($conn); 209 240 $where[] = $this->buildPrioritiesWhereClause($conn); 241 + $where[] = $this->buildDependenciesWhereClause($conn); 210 242 $where[] = $this->buildAuthorWhereClause($conn); 211 243 $where[] = $this->buildOwnerWhereClause($conn); 212 244 $where[] = $this->buildProjectWhereClause($conn); ··· 520 552 $fulltext_results); 521 553 } 522 554 555 + private function buildDependenciesWhereClause( 556 + AphrontDatabaseConnection $conn) { 557 + 558 + if (!$this->shouldJoinBlockedTasks() && 559 + !$this->shouldJoinBlockingTasks()) { 560 + return null; 561 + } 562 + 563 + $parts = array(); 564 + if ($this->blockingTasks === true) { 565 + $parts[] = qsprintf( 566 + $conn, 567 + 'blocking.dst IS NOT NULL'); 568 + } else if ($this->blockingTasks === false) { 569 + $parts[] = qsprintf( 570 + $conn, 571 + 'blocking.dst IS NULL'); 572 + } 573 + 574 + if ($this->blockedTasks === true) { 575 + $parts[] = qsprintf( 576 + $conn, 577 + 'blocked.dst IS NOT NULL'); 578 + } else if ($this->blockedTasks === false) { 579 + $parts[] = qsprintf( 580 + $conn, 581 + 'blocked.dst IS NULL'); 582 + } 583 + 584 + return '('.implode(') OR (', $parts).')'; 585 + } 586 + 523 587 private function buildProjectWhereClause(AphrontDatabaseConnection $conn) { 524 588 if (!$this->projectPHIDs && !$this->includeNoProject) { 525 589 return null; ··· 699 763 PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 700 764 } 701 765 766 + if ($this->shouldJoinBlockingTasks()) { 767 + $joins[] = qsprintf( 768 + $conn_r, 769 + 'LEFT JOIN %T blocking ON blocking.src = task.phid '. 770 + 'AND blocking.type = %d', 771 + $edge_table, 772 + ManiphestTaskDependedOnByTaskEdgeType::EDGECONST); 773 + } 774 + if ($this->shouldJoinBlockedTasks()) { 775 + $joins[] = qsprintf( 776 + $conn_r, 777 + 'LEFT JOIN %T blocked ON blocked.src = task.phid '. 778 + 'AND blocked.type = %d', 779 + $edge_table, 780 + ManiphestTaskDependsOnTaskEdgeType::EDGECONST); 781 + } 782 + 702 783 if ($this->anyProjectPHIDs || $this->anyUserProjectPHIDs) { 703 784 $joins[] = qsprintf( 704 785 $conn_r, ··· 766 847 private function buildGroupClause(AphrontDatabaseConnection $conn_r) { 767 848 $joined_multiple_rows = (count($this->projectPHIDs) > 1) || 768 849 (count($this->anyProjectPHIDs) > 1) || 850 + $this->shouldJoinBlockingTasks() || 851 + $this->shouldJoinBlockedTasks() || 769 852 ($this->getApplicationSearchMayJoinMultipleRows()); 770 853 771 854 $joined_project_name = ($this->groupBy == self::GROUP_PROJECT);
+31 -1
src/applications/maniphest/query/ManiphestTaskSearchEngine.php
··· 67 67 'priorities', 68 68 $this->readListFromRequest($request, 'priorities')); 69 69 70 + $saved->setParameter( 71 + 'blocking', 72 + $this->readBoolFromRequest($request, 'blocking')); 73 + $saved->setParameter( 74 + 'blocked', 75 + $this->readBoolFromRequest($request, 'blocked')); 76 + 70 77 $saved->setParameter('group', $request->getStr('group')); 71 78 $saved->setParameter('order', $request->getStr('order')); 72 79 ··· 151 158 if ($priorities) { 152 159 $query->withPriorities($priorities); 153 160 } 161 + 162 + 163 + $query->withBlockingTasks($saved->getParameter('blocking')); 164 + $query->withBlockedTasks($saved->getParameter('blocked')); 154 165 155 166 $this->applyOrderByToQuery( 156 167 $query, ··· 302 313 isset($priorities[$pri])); 303 314 } 304 315 316 + $blocking_control = id(new AphrontFormSelectControl()) 317 + ->setLabel(pht('Blocking')) 318 + ->setName('blocking') 319 + ->setValue($this->getBoolFromQuery($saved, 'blocking')) 320 + ->setOptions(array( 321 + '' => pht('Show All Tasks'), 322 + 'true' => pht('Show Tasks Blocking Other Tasks'), 323 + 'false' => pht('Show Tasks Not Blocking Other Tasks'),)); 324 + $blocked_control = id(new AphrontFormSelectControl()) 325 + ->setLabel(pht('Blocked')) 326 + ->setName('blocked') 327 + ->setValue($this->getBoolFromQuery($saved, 'blocked')) 328 + ->setOptions(array( 329 + '' => pht('Show All Tasks'), 330 + 'true' => pht('Show Tasks Blocked By Other Tasks'), 331 + 'false' => pht('Show Tasks Not Blocked By Other Tasks'),)); 332 + 305 333 $ids = $saved->getParameter('ids', array()); 306 334 307 335 $builtin_orders = $this->getOrderOptions(); ··· 377 405 ->setLabel(pht('Contains Words')) 378 406 ->setValue($saved->getParameter('fulltext'))) 379 407 ->appendChild($status_control) 380 - ->appendChild($priority_control); 408 + ->appendChild($priority_control) 409 + ->appendChild($blocking_control) 410 + ->appendChild($blocked_control); 381 411 382 412 if (!$this->getIsBoardView()) { 383 413 $form