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