diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -36,6 +36,11 @@ private $statuses; private $priorities; + private $dependencies; + const DEPENDENCY_BLOCKING = 'dependency-blocking'; + const DEPENDENCY_NOT_BLOCKING = 'dependency-notblocking'; + const DEPENDENCY_BLOCKED = 'dependency-blocked'; + const DEPENDENCY_NOT_BLOCKED = 'dependency-notblocked'; private $groupBy = 'group-none'; const GROUP_NONE = 'group-none'; @@ -53,6 +58,9 @@ private $needSubscriberPHIDs; private $needProjectPHIDs; + private $joinBlockingDependencies = false; + private $joinBlockedDependencies = false; + const DEFAULT_PAGE_SIZE = 1000; public function withAuthors(array $authors) { @@ -131,6 +139,11 @@ return $this; } + public function withDependencies(array $dependencies) { + $this->dependencies = $dependencies; + return $this; + } + public function withSubscribers(array $subscribers) { $this->subscriberPHIDs = $subscribers; return $this; @@ -207,6 +220,7 @@ $where[] = $this->buildStatusWhereClause($conn); $where[] = $this->buildStatusesWhereClause($conn); $where[] = $this->buildPrioritiesWhereClause($conn); + $where[] = $this->buildDependenciesWhereClause($conn); $where[] = $this->buildAuthorWhereClause($conn); $where[] = $this->buildOwnerWhereClause($conn); $where[] = $this->buildProjectWhereClause($conn); @@ -520,6 +534,78 @@ $fulltext_results); } + private function buildDependenciesWhereClause( + AphrontDatabaseConnection $conn) { + + if (!$this->dependencies) { + return null; + } + + // selecting both e.g. "blocking" and "not blocking" means its best + // to do nothing + $where_blocking = false; + $where_blocked = false; + foreach ($this->dependencies as $dependency) { + switch ($dependency) { + case self::DEPENDENCY_BLOCKING: + case self::DEPENDENCY_NOT_BLOCKING: + $where_blocking = !$where_blocking; + break; + case self::DEPENDENCY_BLOCKED: + case self::DEPENDENCY_NOT_BLOCKED: + $where_blocked = !$where_blocked; + break; + } + } + + $this->joinBlockingDependencies = $where_blocking; + $this->joinBlockedDependencies = $where_blocked; + if (!$where_blocking && !$where_blocked) { + return null; + } + + $parts = array(); + foreach ($this->dependencies as $dependency) { + switch ($dependency) { + case self::DEPENDENCY_BLOCKING: + if (!$where_blocking) { + continue; + } + $parts[] = qsprintf( + $conn, + 'blocking.dst IS NOT NULL'); + break; + case self::DEPENDENCY_NOT_BLOCKING: + if (!$where_blocking) { + continue; + } + $parts[] = qsprintf( + $conn, + 'blocking.dst IS NULL'); + break; + case self::DEPENDENCY_BLOCKED: + if (!$where_blocked) { + continue; + } + $parts[] = qsprintf( + $conn, + 'blocked.dst IS NOT NULL'); + break; + case self::DEPENDENCY_NOT_BLOCKED: + if (!$where_blocked) { + continue; + } + $parts[] = qsprintf( + $conn, + 'blocked.dst IS NULL'); + break; + } + } + + print_r($parts); + return '('.implode(') OR (', $parts).')'; + } + private function buildProjectWhereClause(AphrontDatabaseConnection $conn) { if (!$this->projectPHIDs && !$this->includeNoProject) { return null; @@ -699,6 +785,25 @@ PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); } + // Note: the dependency join logic is updated as a result of + // @{method:buildDependenciesWhereClause}. + if ($this->joinBlockingDependencies) { + $joins[] = qsprintf( + $conn_r, + 'LEFT JOIN %T blocking ON blocking.src = task.phid '. + 'AND blocking.type = %d', + $edge_table, + ManiphestTaskDependedOnByTaskEdgeType::EDGECONST); + } + if ($this->joinBlockedDependencies) { + $joins[] = qsprintf( + $conn_r, + 'LEFT JOIN %T blocked ON blocked.src = task.phid '. + 'AND blocked.type = %d', + $edge_table, + ManiphestTaskDependsOnTaskEdgeType::EDGECONST); + } + if ($this->anyProjectPHIDs || $this->anyUserProjectPHIDs) { $joins[] = qsprintf( $conn_r, diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php --- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php +++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php @@ -67,6 +67,10 @@ 'priorities', $this->readListFromRequest($request, 'priorities')); + $saved->setParameter( + 'dependencies', + $this->readListFromRequest($request, 'dependencies')); + $saved->setParameter('group', $request->getStr('group')); $saved->setParameter('order', $request->getStr('order')); @@ -152,6 +156,11 @@ $query->withPriorities($priorities); } + $dependencies = $saved->getParameter('dependencies'); + if ($dependencies) { + $query->withDependencies($dependencies); + } + $this->applyOrderByToQuery( $query, $this->getOrderValues(), @@ -302,6 +311,27 @@ isset($priorities[$pri])); } + $dependencies_control = id(new AphrontFormCheckboxControl()) + ->setLabel(pht('Dependencies')); + $dependencies = array( + ManiphestTaskQuery::DEPENDENCY_BLOCKING => + pht('Blocking other tasks'), + ManiphestTaskQuery::DEPENDENCY_NOT_BLOCKING => + pht('Not blocking other tasks'), + ManiphestTaskQuery::DEPENDENCY_BLOCKED => + pht('Blocked by other tasks'), + ManiphestTaskQuery::DEPENDENCY_NOT_BLOCKED => + pht('Not blocked by other tasks'), + ); + $dependency_values = $saved->getParameter('dependencies', array()); + foreach ($dependencies as $type => $label) { + $dependencies_control->addCheckbox( + 'dependencies[]', + $type, + $label, + in_array($type, $dependency_values)); + } + $ids = $saved->getParameter('ids', array()); $builtin_orders = $this->getOrderOptions(); @@ -377,7 +407,8 @@ ->setLabel(pht('Contains Words')) ->setValue($saved->getParameter('fulltext'))) ->appendChild($status_control) - ->appendChild($priority_control); + ->appendChild($priority_control) + ->appendChild($dependencies_control); if (!$this->getIsBoardView()) { $form