mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
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
This commit is contained in:
parent
2e41c85cc9
commit
c715b42f36
2 changed files with 100 additions and 98 deletions
|
@ -20,6 +20,8 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
private $subpriorityMin;
|
||||
private $subpriorityMax;
|
||||
private $bridgedObjectPHIDs;
|
||||
private $hasOpenParents;
|
||||
private $hasOpenSubtasks;
|
||||
|
||||
private $fullTextSearch = '';
|
||||
|
||||
|
@ -51,8 +53,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
|
||||
private $needSubscriberPHIDs;
|
||||
private $needProjectPHIDs;
|
||||
private $blockingTasks;
|
||||
private $blockedTasks;
|
||||
|
||||
public function withAuthors(array $authors) {
|
||||
$this->authorPHIDs = $authors;
|
||||
|
@ -151,34 +151,16 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* True returns tasks that are blocking other tasks only.
|
||||
* False returns tasks that are not blocking other tasks only.
|
||||
* Null returns tasks regardless of blocking status.
|
||||
*/
|
||||
public function withBlockingTasks($mode) {
|
||||
$this->blockingTasks = $mode;
|
||||
public function withOpenSubtasks($value) {
|
||||
$this->hasOpenSubtasks = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function shouldJoinBlockingTasks() {
|
||||
return $this->blockingTasks !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True returns tasks that are blocked by other tasks only.
|
||||
* False returns tasks that are not blocked by other tasks only.
|
||||
* Null returns tasks regardless of blocked by status.
|
||||
*/
|
||||
public function withBlockedTasks($mode) {
|
||||
$this->blockedTasks = $mode;
|
||||
public function withOpenParents($value) {
|
||||
$this->hasOpenParents = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function shouldJoinBlockedTasks() {
|
||||
return $this->blockedTasks !== null;
|
||||
}
|
||||
|
||||
public function withDateCreatedBefore($date_created_before) {
|
||||
$this->dateCreatedBefore = $date_created_before;
|
||||
return $this;
|
||||
|
@ -335,7 +317,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
$where[] = $this->buildStatusWhereClause($conn);
|
||||
$where[] = $this->buildDependenciesWhereClause($conn);
|
||||
$where[] = $this->buildOwnerWhereClause($conn);
|
||||
$where[] = $this->buildFullTextWhereClause($conn);
|
||||
|
||||
|
@ -526,71 +507,65 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$fulltext_results);
|
||||
}
|
||||
|
||||
private function buildDependenciesWhereClause(
|
||||
AphrontDatabaseConnection $conn) {
|
||||
|
||||
if (!$this->shouldJoinBlockedTasks() &&
|
||||
!$this->shouldJoinBlockingTasks()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = array();
|
||||
if ($this->blockingTasks === true) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocking.dst IS NOT NULL AND blockingtask.status IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
} else if ($this->blockingTasks === false) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocking.dst IS NULL OR blockingtask.status NOT IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
}
|
||||
|
||||
if ($this->blockedTasks === true) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocked.dst IS NOT NULL AND blockedtask.status IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
} else if ($this->blockedTasks === false) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocked.dst IS NULL OR blockedtask.status NOT IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
}
|
||||
|
||||
return '('.implode(') OR (', $parts).')';
|
||||
}
|
||||
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$open_statuses = ManiphestTaskStatus::getOpenStatusConstants();
|
||||
$edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
|
||||
$task_table = $this->newResultObject()->getTableName();
|
||||
|
||||
$joins = array();
|
||||
if ($this->hasOpenParents !== null) {
|
||||
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
|
||||
|
||||
if ($this->hasOpenParents) {
|
||||
$join_type = 'JOIN';
|
||||
} else {
|
||||
$join_type = 'LEFT JOIN';
|
||||
}
|
||||
|
||||
if ($this->shouldJoinBlockingTasks()) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'LEFT JOIN %T blocking ON blocking.src = task.phid '.
|
||||
'AND blocking.type = %d '.
|
||||
'LEFT JOIN %T blockingtask ON blocking.dst = blockingtask.phid',
|
||||
$conn,
|
||||
'%Q %T e_parent
|
||||
ON e_parent.src = task.phid
|
||||
AND e_parent.type = %d
|
||||
%Q %T parent
|
||||
ON e_parent.dst = parent.phid
|
||||
AND parent.status IN (%Ls)',
|
||||
$join_type,
|
||||
$edge_table,
|
||||
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST,
|
||||
id(new ManiphestTask())->getTableName());
|
||||
$parent_type,
|
||||
$join_type,
|
||||
$task_table,
|
||||
$open_statuses);
|
||||
}
|
||||
if ($this->shouldJoinBlockedTasks()) {
|
||||
|
||||
if ($this->hasOpenSubtasks !== null) {
|
||||
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
|
||||
|
||||
if ($this->hasOpenSubtasks) {
|
||||
$join_type = 'JOIN';
|
||||
} else {
|
||||
$join_type = 'LEFT JOIN';
|
||||
}
|
||||
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'LEFT JOIN %T blocked ON blocked.src = task.phid '.
|
||||
'AND blocked.type = %d '.
|
||||
'LEFT JOIN %T blockedtask ON blocked.dst = blockedtask.phid',
|
||||
$conn,
|
||||
'%Q %T e_subtask
|
||||
ON e_subtask.src = task.phid
|
||||
AND e_subtask.type = %d
|
||||
%Q %T subtask
|
||||
ON e_subtask.dst = subtask.phid
|
||||
AND subtask.status IN (%Ls)',
|
||||
$join_type,
|
||||
$edge_table,
|
||||
ManiphestTaskDependsOnTaskEdgeType::EDGECONST,
|
||||
id(new ManiphestTask())->getTableName());
|
||||
$subtask_type,
|
||||
$join_type,
|
||||
$task_table,
|
||||
$open_statuses);
|
||||
}
|
||||
|
||||
if ($this->subscriberPHIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T e_ccs ON e_ccs.src = task.phid '.
|
||||
'AND e_ccs.type = %s '.
|
||||
'AND e_ccs.dst in (%Ls)',
|
||||
|
@ -604,7 +579,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$ignore_group_phids = $this->getIgnoreGroupedProjectPHIDs();
|
||||
if ($ignore_group_phids) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
|
||||
AND projectGroup.type = %d
|
||||
AND projectGroup.dst NOT IN (%Ls)',
|
||||
|
@ -613,29 +588,30 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$ignore_group_phids);
|
||||
} else {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
|
||||
AND projectGroup.type = %d',
|
||||
$edge_table,
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
}
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'LEFT JOIN %T projectGroupName
|
||||
ON projectGroup.dst = projectGroupName.indexedObjectPHID',
|
||||
id(new ManiphestNameIndex())->getTableName());
|
||||
break;
|
||||
}
|
||||
|
||||
$joins[] = parent::buildJoinClauseParts($conn_r);
|
||||
$joins[] = parent::buildJoinClauseParts($conn);
|
||||
|
||||
return $joins;
|
||||
}
|
||||
|
||||
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
|
||||
$joined_multiple_rows = $this->shouldJoinBlockingTasks() ||
|
||||
$this->shouldJoinBlockedTasks() ||
|
||||
($this->shouldGroupQueryResultRows());
|
||||
$joined_multiple_rows =
|
||||
($this->hasOpenParents !== null) ||
|
||||
($this->hasOpenSubtasks !== null) ||
|
||||
$this->shouldGroupQueryResultRows();
|
||||
|
||||
$joined_project_name = ($this->groupBy == self::GROUP_PROJECT);
|
||||
|
||||
|
@ -652,6 +628,30 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected function buildHavingClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$having = parent::buildHavingClauseParts($conn);
|
||||
|
||||
if ($this->hasOpenParents !== null) {
|
||||
if (!$this->hasOpenParents) {
|
||||
$having[] = qsprintf(
|
||||
$conn,
|
||||
'COUNT(parent.phid) = 0');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasOpenSubtasks !== null) {
|
||||
if (!$this->hasOpenSubtasks) {
|
||||
$having[] = qsprintf(
|
||||
$conn,
|
||||
'COUNT(subtask.phid) = 0');
|
||||
}
|
||||
}
|
||||
|
||||
return $having;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return project PHIDs which we should ignore when grouping tasks by
|
||||
* project. For example, if a user issues a query like:
|
||||
|
|
|
@ -77,19 +77,21 @@ final class ManiphestTaskSearchEngine
|
|||
->setLabel(pht('Contains Words'))
|
||||
->setKey('fulltext'),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Blocking'))
|
||||
->setKey('blocking')
|
||||
->setLabel(pht('Open Parents'))
|
||||
->setKey('hasParents')
|
||||
->setAliases(array('blocking'))
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Tasks Blocking Other Tasks'),
|
||||
pht('Hide Tasks Blocking Other Tasks')),
|
||||
pht('Show Only Tasks With Open Parents'),
|
||||
pht('Show Only Tasks Without Open Parents')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Blocked'))
|
||||
->setKey('blocked')
|
||||
->setLabel(pht('Open Subtasks'))
|
||||
->setKey('hasSubtasks')
|
||||
->setAliases(array('blocked'))
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Task Blocked By Other Tasks'),
|
||||
pht('Hide Tasks Blocked By Other Tasks')),
|
||||
pht('Show Only Tasks With Open Subtasks'),
|
||||
pht('Show Only Tasks Without Open Subtasks')),
|
||||
id(new PhabricatorSearchSelectField())
|
||||
->setLabel(pht('Group By'))
|
||||
->setKey('group')
|
||||
|
@ -121,8 +123,8 @@ final class ManiphestTaskSearchEngine
|
|||
'statuses',
|
||||
'priorities',
|
||||
'fulltext',
|
||||
'blocking',
|
||||
'blocked',
|
||||
'hasParents',
|
||||
'hasSubtasks',
|
||||
'group',
|
||||
'order',
|
||||
'ids',
|
||||
|
@ -182,12 +184,12 @@ final class ManiphestTaskSearchEngine
|
|||
$query->withDateModifiedBefore($map['modifiedEnd']);
|
||||
}
|
||||
|
||||
if ($map['blocking'] !== null) {
|
||||
$query->withBlockingTasks($map['blocking']);
|
||||
if ($map['hasParents'] !== null) {
|
||||
$query->withOpenParents($map['hasParents']);
|
||||
}
|
||||
|
||||
if ($map['blocked'] !== null) {
|
||||
$query->withBlockedTasks($map['blocked']);
|
||||
if ($map['hasSubtasks'] !== null) {
|
||||
$query->withOpenSubtasks($map['hasSubtasks']);
|
||||
}
|
||||
|
||||
if (strlen($map['fulltext'])) {
|
||||
|
|
Loading…
Reference in a new issue