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

Fix "Blocked" task queries with multiple subtasks, and update language

Summary:
Ref T8126. See that task for discussion. This change:

- Updates language to be more consistent ("Parents", "Subtasks") since I moved us away from the often-confusing "Block" language in T4788.
- Fixes bugs with finding the wrong set of tasks if tasks have a mixture of open and closed subtasks or parents.

Test Plan:
- Created four tasks: no subtasks, one closed subtask, one open subtask, mixture of open and closed subtasks.
- Created four more tasks: no parents, one closed parent, one open parent, mixture of open and closed parents.
- Searched for all this stuff, got the proper results:

{F1740683}

{F1740684}

{F1740685}

{F1740686}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T8126

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

+99 -97
+83 -83
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 20 20 private $subpriorityMin; 21 21 private $subpriorityMax; 22 22 private $bridgedObjectPHIDs; 23 + private $hasOpenParents; 24 + private $hasOpenSubtasks; 23 25 24 26 private $fullTextSearch = ''; 25 27 ··· 51 53 52 54 private $needSubscriberPHIDs; 53 55 private $needProjectPHIDs; 54 - private $blockingTasks; 55 - private $blockedTasks; 56 56 57 57 public function withAuthors(array $authors) { 58 58 $this->authorPHIDs = $authors; ··· 151 151 return $this; 152 152 } 153 153 154 - /** 155 - * True returns tasks that are blocking other tasks only. 156 - * False returns tasks that are not blocking other tasks only. 157 - * Null returns tasks regardless of blocking status. 158 - */ 159 - public function withBlockingTasks($mode) { 160 - $this->blockingTasks = $mode; 154 + public function withOpenSubtasks($value) { 155 + $this->hasOpenSubtasks = $value; 161 156 return $this; 162 157 } 163 158 164 - public function shouldJoinBlockingTasks() { 165 - return $this->blockingTasks !== null; 166 - } 167 - 168 - /** 169 - * True returns tasks that are blocked by other tasks only. 170 - * False returns tasks that are not blocked by other tasks only. 171 - * Null returns tasks regardless of blocked by status. 172 - */ 173 - public function withBlockedTasks($mode) { 174 - $this->blockedTasks = $mode; 159 + public function withOpenParents($value) { 160 + $this->hasOpenParents = $value; 175 161 return $this; 176 - } 177 - 178 - public function shouldJoinBlockedTasks() { 179 - return $this->blockedTasks !== null; 180 162 } 181 163 182 164 public function withDateCreatedBefore($date_created_before) { ··· 335 317 $where = parent::buildWhereClauseParts($conn); 336 318 337 319 $where[] = $this->buildStatusWhereClause($conn); 338 - $where[] = $this->buildDependenciesWhereClause($conn); 339 320 $where[] = $this->buildOwnerWhereClause($conn); 340 321 $where[] = $this->buildFullTextWhereClause($conn); 341 322 ··· 526 507 $fulltext_results); 527 508 } 528 509 529 - private function buildDependenciesWhereClause( 530 - AphrontDatabaseConnection $conn) { 510 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 511 + $open_statuses = ManiphestTaskStatus::getOpenStatusConstants(); 512 + $edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE; 513 + $task_table = $this->newResultObject()->getTableName(); 531 514 532 - if (!$this->shouldJoinBlockedTasks() && 533 - !$this->shouldJoinBlockingTasks()) { 534 - return null; 535 - } 515 + $joins = array(); 516 + if ($this->hasOpenParents !== null) { 517 + $parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST; 536 518 537 - $parts = array(); 538 - if ($this->blockingTasks === true) { 539 - $parts[] = qsprintf( 540 - $conn, 541 - 'blocking.dst IS NOT NULL AND blockingtask.status IN (%Ls)', 542 - ManiphestTaskStatus::getOpenStatusConstants()); 543 - } else if ($this->blockingTasks === false) { 544 - $parts[] = qsprintf( 545 - $conn, 546 - 'blocking.dst IS NULL OR blockingtask.status NOT IN (%Ls)', 547 - ManiphestTaskStatus::getOpenStatusConstants()); 548 - } 519 + if ($this->hasOpenParents) { 520 + $join_type = 'JOIN'; 521 + } else { 522 + $join_type = 'LEFT JOIN'; 523 + } 549 524 550 - if ($this->blockedTasks === true) { 551 - $parts[] = qsprintf( 525 + $joins[] = qsprintf( 552 526 $conn, 553 - 'blocked.dst IS NOT NULL AND blockedtask.status IN (%Ls)', 554 - ManiphestTaskStatus::getOpenStatusConstants()); 555 - } else if ($this->blockedTasks === false) { 556 - $parts[] = qsprintf( 557 - $conn, 558 - 'blocked.dst IS NULL OR blockedtask.status NOT IN (%Ls)', 559 - ManiphestTaskStatus::getOpenStatusConstants()); 527 + '%Q %T e_parent 528 + ON e_parent.src = task.phid 529 + AND e_parent.type = %d 530 + %Q %T parent 531 + ON e_parent.dst = parent.phid 532 + AND parent.status IN (%Ls)', 533 + $join_type, 534 + $edge_table, 535 + $parent_type, 536 + $join_type, 537 + $task_table, 538 + $open_statuses); 560 539 } 561 540 562 - return '('.implode(') OR (', $parts).')'; 563 - } 541 + if ($this->hasOpenSubtasks !== null) { 542 + $subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST; 564 543 565 - protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) { 566 - $edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE; 544 + if ($this->hasOpenSubtasks) { 545 + $join_type = 'JOIN'; 546 + } else { 547 + $join_type = 'LEFT JOIN'; 548 + } 567 549 568 - $joins = array(); 569 - 570 - if ($this->shouldJoinBlockingTasks()) { 571 550 $joins[] = qsprintf( 572 - $conn_r, 573 - 'LEFT JOIN %T blocking ON blocking.src = task.phid '. 574 - 'AND blocking.type = %d '. 575 - 'LEFT JOIN %T blockingtask ON blocking.dst = blockingtask.phid', 576 - $edge_table, 577 - ManiphestTaskDependedOnByTaskEdgeType::EDGECONST, 578 - id(new ManiphestTask())->getTableName()); 579 - } 580 - if ($this->shouldJoinBlockedTasks()) { 581 - $joins[] = qsprintf( 582 - $conn_r, 583 - 'LEFT JOIN %T blocked ON blocked.src = task.phid '. 584 - 'AND blocked.type = %d '. 585 - 'LEFT JOIN %T blockedtask ON blocked.dst = blockedtask.phid', 551 + $conn, 552 + '%Q %T e_subtask 553 + ON e_subtask.src = task.phid 554 + AND e_subtask.type = %d 555 + %Q %T subtask 556 + ON e_subtask.dst = subtask.phid 557 + AND subtask.status IN (%Ls)', 558 + $join_type, 586 559 $edge_table, 587 - ManiphestTaskDependsOnTaskEdgeType::EDGECONST, 588 - id(new ManiphestTask())->getTableName()); 560 + $subtask_type, 561 + $join_type, 562 + $task_table, 563 + $open_statuses); 589 564 } 590 565 591 566 if ($this->subscriberPHIDs !== null) { 592 567 $joins[] = qsprintf( 593 - $conn_r, 568 + $conn, 594 569 'JOIN %T e_ccs ON e_ccs.src = task.phid '. 595 570 'AND e_ccs.type = %s '. 596 571 'AND e_ccs.dst in (%Ls)', ··· 604 579 $ignore_group_phids = $this->getIgnoreGroupedProjectPHIDs(); 605 580 if ($ignore_group_phids) { 606 581 $joins[] = qsprintf( 607 - $conn_r, 582 + $conn, 608 583 'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src 609 584 AND projectGroup.type = %d 610 585 AND projectGroup.dst NOT IN (%Ls)', ··· 613 588 $ignore_group_phids); 614 589 } else { 615 590 $joins[] = qsprintf( 616 - $conn_r, 591 + $conn, 617 592 'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src 618 593 AND projectGroup.type = %d', 619 594 $edge_table, 620 595 PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 621 596 } 622 597 $joins[] = qsprintf( 623 - $conn_r, 598 + $conn, 624 599 'LEFT JOIN %T projectGroupName 625 600 ON projectGroup.dst = projectGroupName.indexedObjectPHID', 626 601 id(new ManiphestNameIndex())->getTableName()); 627 602 break; 628 603 } 629 604 630 - $joins[] = parent::buildJoinClauseParts($conn_r); 605 + $joins[] = parent::buildJoinClauseParts($conn); 631 606 632 607 return $joins; 633 608 } 634 609 635 610 protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 636 - $joined_multiple_rows = $this->shouldJoinBlockingTasks() || 637 - $this->shouldJoinBlockedTasks() || 638 - ($this->shouldGroupQueryResultRows()); 611 + $joined_multiple_rows = 612 + ($this->hasOpenParents !== null) || 613 + ($this->hasOpenSubtasks !== null) || 614 + $this->shouldGroupQueryResultRows(); 639 615 640 616 $joined_project_name = ($this->groupBy == self::GROUP_PROJECT); 641 617 ··· 651 627 return ''; 652 628 } 653 629 } 630 + 631 + 632 + protected function buildHavingClauseParts(AphrontDatabaseConnection $conn) { 633 + $having = parent::buildHavingClauseParts($conn); 634 + 635 + if ($this->hasOpenParents !== null) { 636 + if (!$this->hasOpenParents) { 637 + $having[] = qsprintf( 638 + $conn, 639 + 'COUNT(parent.phid) = 0'); 640 + } 641 + } 642 + 643 + if ($this->hasOpenSubtasks !== null) { 644 + if (!$this->hasOpenSubtasks) { 645 + $having[] = qsprintf( 646 + $conn, 647 + 'COUNT(subtask.phid) = 0'); 648 + } 649 + } 650 + 651 + return $having; 652 + } 653 + 654 654 655 655 /** 656 656 * Return project PHIDs which we should ignore when grouping tasks by
+16 -14
src/applications/maniphest/query/ManiphestTaskSearchEngine.php
··· 77 77 ->setLabel(pht('Contains Words')) 78 78 ->setKey('fulltext'), 79 79 id(new PhabricatorSearchThreeStateField()) 80 - ->setLabel(pht('Blocking')) 81 - ->setKey('blocking') 80 + ->setLabel(pht('Open Parents')) 81 + ->setKey('hasParents') 82 + ->setAliases(array('blocking')) 82 83 ->setOptions( 83 84 pht('(Show All)'), 84 - pht('Show Only Tasks Blocking Other Tasks'), 85 - pht('Hide Tasks Blocking Other Tasks')), 85 + pht('Show Only Tasks With Open Parents'), 86 + pht('Show Only Tasks Without Open Parents')), 86 87 id(new PhabricatorSearchThreeStateField()) 87 - ->setLabel(pht('Blocked')) 88 - ->setKey('blocked') 88 + ->setLabel(pht('Open Subtasks')) 89 + ->setKey('hasSubtasks') 90 + ->setAliases(array('blocked')) 89 91 ->setOptions( 90 92 pht('(Show All)'), 91 - pht('Show Only Task Blocked By Other Tasks'), 92 - pht('Hide Tasks Blocked By Other Tasks')), 93 + pht('Show Only Tasks With Open Subtasks'), 94 + pht('Show Only Tasks Without Open Subtasks')), 93 95 id(new PhabricatorSearchSelectField()) 94 96 ->setLabel(pht('Group By')) 95 97 ->setKey('group') ··· 121 123 'statuses', 122 124 'priorities', 123 125 'fulltext', 124 - 'blocking', 125 - 'blocked', 126 + 'hasParents', 127 + 'hasSubtasks', 126 128 'group', 127 129 'order', 128 130 'ids', ··· 182 184 $query->withDateModifiedBefore($map['modifiedEnd']); 183 185 } 184 186 185 - if ($map['blocking'] !== null) { 186 - $query->withBlockingTasks($map['blocking']); 187 + if ($map['hasParents'] !== null) { 188 + $query->withOpenParents($map['hasParents']); 187 189 } 188 190 189 - if ($map['blocked'] !== null) { 190 - $query->withBlockedTasks($map['blocked']); 191 + if ($map['hasSubtasks'] !== null) { 192 + $query->withOpenSubtasks($map['hasSubtasks']); 191 193 } 192 194 193 195 if (strlen($map['fulltext'])) {