2011-06-30 01:16:33 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query tasks by specific criteria. This class uses the higher-performance
|
|
|
|
* but less-general Maniphest indexes to satisfy queries.
|
|
|
|
*/
|
2014-07-10 00:12:48 +02:00
|
|
|
final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-04-04 19:29:40 +02:00
|
|
|
private $taskIDs = array();
|
|
|
|
private $taskPHIDs = array();
|
|
|
|
private $authorPHIDs = array();
|
|
|
|
private $ownerPHIDs = array();
|
|
|
|
private $includeUnowned = null;
|
|
|
|
private $projectPHIDs = array();
|
|
|
|
private $xprojectPHIDs = array();
|
|
|
|
private $subscriberPHIDs = array();
|
|
|
|
private $anyProjectPHIDs = array();
|
|
|
|
private $anyUserProjectPHIDs = array();
|
|
|
|
private $includeNoProject = null;
|
2013-09-12 22:03:39 +02:00
|
|
|
private $dateCreatedAfter;
|
|
|
|
private $dateCreatedBefore;
|
2014-03-17 23:53:07 +01:00
|
|
|
private $dateModifiedAfter;
|
|
|
|
private $dateModifiedBefore;
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2012-06-15 23:09:49 +02:00
|
|
|
private $fullTextSearch = '';
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
private $status = 'status-any';
|
|
|
|
const STATUS_ANY = 'status-any';
|
|
|
|
const STATUS_OPEN = 'status-open';
|
|
|
|
const STATUS_CLOSED = 'status-closed';
|
2012-06-29 18:17:19 +02:00
|
|
|
const STATUS_RESOLVED = 'status-resolved';
|
|
|
|
const STATUS_WONTFIX = 'status-wontfix';
|
|
|
|
const STATUS_INVALID = 'status-invalid';
|
|
|
|
const STATUS_SPITE = 'status-spite';
|
|
|
|
const STATUS_DUPLICATE = 'status-duplicate';
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
private $statuses;
|
2013-09-10 20:54:17 +02:00
|
|
|
private $priorities;
|
2011-06-30 01:16:33 +02:00
|
|
|
|
|
|
|
private $groupBy = 'group-none';
|
|
|
|
const GROUP_NONE = 'group-none';
|
|
|
|
const GROUP_PRIORITY = 'group-priority';
|
|
|
|
const GROUP_OWNER = 'group-owner';
|
|
|
|
const GROUP_STATUS = 'group-status';
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
const GROUP_PROJECT = 'group-project';
|
2011-06-30 01:16:33 +02:00
|
|
|
|
|
|
|
private $orderBy = 'order-modified';
|
|
|
|
const ORDER_PRIORITY = 'order-priority';
|
|
|
|
const ORDER_CREATED = 'order-created';
|
|
|
|
const ORDER_MODIFIED = 'order-modified';
|
2012-08-02 23:21:13 +02:00
|
|
|
const ORDER_TITLE = 'order-title';
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2014-12-11 01:27:30 +01:00
|
|
|
private $needSubscriberPHIDs;
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
const DEFAULT_PAGE_SIZE = 1000;
|
|
|
|
|
|
|
|
public function withAuthors(array $authors) {
|
|
|
|
$this->authorPHIDs = $authors;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-07-21 21:05:28 +02:00
|
|
|
public function withIDs(array $ids) {
|
2013-07-25 02:31:49 +02:00
|
|
|
$this->taskIDs = $ids;
|
2013-07-21 21:05:28 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function withPHIDs(array $phids) {
|
|
|
|
$this->taskPHIDs = $phids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
public function withOwners(array $owners) {
|
|
|
|
$this->includeUnowned = false;
|
|
|
|
foreach ($owners as $k => $phid) {
|
2013-05-27 22:32:46 +02:00
|
|
|
if ($phid == ManiphestTaskOwner::OWNER_UP_FOR_GRABS || $phid === null) {
|
2011-06-30 01:16:33 +02:00
|
|
|
$this->includeUnowned = true;
|
|
|
|
unset($owners[$k]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->ownerPHIDs = $owners;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-10-05 00:31:04 +02:00
|
|
|
public function withAllProjects(array $projects) {
|
2012-02-29 06:08:02 +01:00
|
|
|
$this->includeNoProject = false;
|
|
|
|
foreach ($projects as $k => $phid) {
|
|
|
|
if ($phid == ManiphestTaskOwner::PROJECT_NO_PROJECT) {
|
|
|
|
$this->includeNoProject = true;
|
|
|
|
unset($projects[$k]);
|
|
|
|
}
|
|
|
|
}
|
2011-06-30 01:16:33 +02:00
|
|
|
$this->projectPHIDs = $projects;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-05-20 20:42:05 +02:00
|
|
|
/**
|
|
|
|
* Add an additional "all projects" constraint to existing filters.
|
|
|
|
*
|
|
|
|
* This is used by boards to supplement queries.
|
|
|
|
*
|
2014-07-23 02:03:09 +02:00
|
|
|
* @param list<phid> List of project PHIDs to add to any existing constraint.
|
2014-05-20 20:42:05 +02:00
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function addWithAllProjects(array $projects) {
|
|
|
|
if ($this->projectPHIDs === null) {
|
|
|
|
$this->projectPHIDs = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->withAllProjects(array_merge($this->projectPHIDs, $projects));
|
|
|
|
}
|
|
|
|
|
2012-02-29 06:08:02 +01:00
|
|
|
public function withoutProjects(array $projects) {
|
|
|
|
$this->xprojectPHIDs = $projects;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
public function withStatus($status) {
|
|
|
|
$this->status = $status;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
public function withStatuses(array $statuses) {
|
|
|
|
$this->statuses = $statuses;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:54:17 +02:00
|
|
|
public function withPriorities(array $priorities) {
|
|
|
|
$this->priorities = $priorities;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-07-07 19:24:49 +02:00
|
|
|
public function withSubscribers(array $subscribers) {
|
|
|
|
$this->subscriberPHIDs = $subscribers;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-06-15 23:09:49 +02:00
|
|
|
public function withFullTextSearch($fulltext_search) {
|
|
|
|
$this->fullTextSearch = $fulltext_search;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
public function setGroupBy($group) {
|
|
|
|
$this->groupBy = $group;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setOrderBy($order) {
|
|
|
|
$this->orderBy = $order;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-10-05 00:30:51 +02:00
|
|
|
public function withAnyProjects(array $projects) {
|
|
|
|
$this->anyProjectPHIDs = $projects;
|
2011-07-07 22:50:56 +02:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-04-04 19:29:40 +02:00
|
|
|
public function withAnyUserProjects(array $users) {
|
|
|
|
$this->anyUserProjectPHIDs = $users;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:03:39 +02:00
|
|
|
public function withDateCreatedBefore($date_created_before) {
|
|
|
|
$this->dateCreatedBefore = $date_created_before;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function withDateCreatedAfter($date_created_after) {
|
|
|
|
$this->dateCreatedAfter = $date_created_after;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-03-17 23:53:07 +01:00
|
|
|
public function withDateModifiedBefore($date_modified_before) {
|
|
|
|
$this->dateModifiedBefore = $date_modified_before;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function withDateModifiedAfter($date_modified_after) {
|
|
|
|
$this->dateModifiedAfter = $date_modified_after;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-12-11 01:27:30 +01:00
|
|
|
public function needSubscriberPHIDs($bool) {
|
|
|
|
$this->needSubscriberPHIDs = $bool;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-09-10 16:53:27 +02:00
|
|
|
public function loadPage() {
|
2013-09-12 22:03:14 +02:00
|
|
|
// TODO: (T603) It is possible for a user to find the PHID of a project
|
|
|
|
// they can't see, then query for tasks in that project and deduce the
|
|
|
|
// identity of unknown/invisible projects. Before we allow the user to
|
|
|
|
// execute a project-based PHID query, we should verify that they
|
|
|
|
// can see the project.
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
$task_dao = new ManiphestTask();
|
|
|
|
$conn = $task_dao->establishConnection('r');
|
|
|
|
|
|
|
|
$where = array();
|
2011-12-02 16:30:20 +01:00
|
|
|
$where[] = $this->buildTaskIDsWhereClause($conn);
|
2012-11-01 18:47:45 +01:00
|
|
|
$where[] = $this->buildTaskPHIDsWhereClause($conn);
|
2011-06-30 01:16:33 +02:00
|
|
|
$where[] = $this->buildStatusWhereClause($conn);
|
2013-09-10 20:07:34 +02:00
|
|
|
$where[] = $this->buildStatusesWhereClause($conn);
|
2013-09-10 20:54:17 +02:00
|
|
|
$where[] = $this->buildPrioritiesWhereClause($conn);
|
2011-06-30 01:16:33 +02:00
|
|
|
$where[] = $this->buildAuthorWhereClause($conn);
|
|
|
|
$where[] = $this->buildOwnerWhereClause($conn);
|
|
|
|
$where[] = $this->buildProjectWhereClause($conn);
|
2012-10-05 00:30:51 +02:00
|
|
|
$where[] = $this->buildAnyProjectWhereClause($conn);
|
2013-04-04 19:29:40 +02:00
|
|
|
$where[] = $this->buildAnyUserProjectWhereClause($conn);
|
2012-02-29 06:08:02 +01:00
|
|
|
$where[] = $this->buildXProjectWhereClause($conn);
|
2012-06-15 23:09:49 +02:00
|
|
|
$where[] = $this->buildFullTextWhereClause($conn);
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-09-12 22:03:39 +02:00
|
|
|
if ($this->dateCreatedAfter) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn,
|
2014-07-21 15:44:35 +02:00
|
|
|
'task.dateCreated >= %d',
|
2013-09-12 22:03:39 +02:00
|
|
|
$this->dateCreatedAfter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->dateCreatedBefore) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn,
|
2014-07-21 15:44:35 +02:00
|
|
|
'task.dateCreated <= %d',
|
2013-09-12 22:03:39 +02:00
|
|
|
$this->dateCreatedBefore);
|
|
|
|
}
|
|
|
|
|
2014-03-17 23:53:07 +01:00
|
|
|
if ($this->dateModifiedAfter) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn,
|
2014-07-21 15:44:35 +02:00
|
|
|
'task.dateModified >= %d',
|
2014-03-17 23:53:07 +01:00
|
|
|
$this->dateModifiedAfter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->dateModifiedBefore) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn,
|
2014-07-21 15:44:35 +02:00
|
|
|
'task.dateModified <= %d',
|
2014-03-17 23:53:07 +01:00
|
|
|
$this->dateModifiedBefore);
|
|
|
|
}
|
|
|
|
|
2013-09-13 16:13:06 +02:00
|
|
|
$where[] = $this->buildPagingClause($conn);
|
|
|
|
|
2012-10-05 00:30:51 +02:00
|
|
|
$where = $this->formatWhereClause($where);
|
2011-06-30 01:16:33 +02:00
|
|
|
|
|
|
|
$having = '';
|
|
|
|
$count = '';
|
2011-07-07 22:50:56 +02:00
|
|
|
|
2012-10-05 00:30:51 +02:00
|
|
|
if (count($this->projectPHIDs) > 1) {
|
|
|
|
// We want to treat the query as an intersection query, not a union
|
|
|
|
// query. We sum the project count and require it be the same as the
|
|
|
|
// number of projects we're searching for.
|
|
|
|
|
2014-07-18 00:42:30 +02:00
|
|
|
$count = ', COUNT(project.dst) projectCount';
|
2012-10-05 00:30:51 +02:00
|
|
|
$having = qsprintf(
|
|
|
|
$conn,
|
|
|
|
'HAVING projectCount = %d',
|
|
|
|
count($this->projectPHIDs));
|
2011-06-30 01:16:33 +02:00
|
|
|
}
|
|
|
|
|
2013-09-10 16:53:27 +02:00
|
|
|
$order = $this->buildCustomOrderClause($conn);
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-09-10 16:53:27 +02:00
|
|
|
// TODO: Clean up this nonstandardness.
|
|
|
|
if (!$this->getLimit()) {
|
|
|
|
$this->setLimit(self::DEFAULT_PAGE_SIZE);
|
|
|
|
}
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
$group_column = '';
|
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_PROJECT:
|
|
|
|
$group_column = qsprintf(
|
|
|
|
$conn,
|
|
|
|
', projectGroupName.indexedObjectPHID projectGroupPHID');
|
|
|
|
break;
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
$rows = queryfx_all(
|
2011-06-30 01:16:33 +02:00
|
|
|
$conn,
|
2013-09-25 22:44:36 +02:00
|
|
|
'SELECT task.* %Q %Q FROM %T task %Q %Q %Q %Q %Q %Q',
|
2011-06-30 01:16:33 +02:00
|
|
|
$count,
|
2013-09-12 22:08:25 +02:00
|
|
|
$group_column,
|
2011-06-30 01:16:33 +02:00
|
|
|
$task_dao->getTableName(),
|
2013-09-12 22:08:25 +02:00
|
|
|
$this->buildJoinsClause($conn),
|
2011-06-30 01:16:33 +02:00
|
|
|
$where,
|
2013-09-12 22:08:25 +02:00
|
|
|
$this->buildGroupClause($conn),
|
2011-06-30 01:16:33 +02:00
|
|
|
$having,
|
|
|
|
$order,
|
2013-09-10 16:53:27 +02:00
|
|
|
$this->buildLimitClause($conn));
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_PROJECT:
|
|
|
|
$data = ipull($rows, null, 'id');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$data = $rows;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
$tasks = $task_dao->loadAllFromArray($data);
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_PROJECT:
|
|
|
|
$results = array();
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
$task = clone $tasks[$row['id']];
|
|
|
|
$task->attachGroupByProjectPHID($row['projectGroupPHID']);
|
|
|
|
$results[] = $task;
|
|
|
|
}
|
|
|
|
$tasks = $results;
|
|
|
|
break;
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $tasks;
|
2011-06-30 01:16:33 +02:00
|
|
|
}
|
|
|
|
|
2013-09-13 16:32:57 +02:00
|
|
|
protected function willFilterPage(array $tasks) {
|
|
|
|
if ($this->groupBy == self::GROUP_PROJECT) {
|
|
|
|
// We should only return project groups which the user can actually see.
|
|
|
|
$project_phids = mpull($tasks, 'getGroupByProjectPHID');
|
|
|
|
$projects = id(new PhabricatorProjectQuery())
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
->withPHIDs($project_phids)
|
|
|
|
->execute();
|
|
|
|
$projects = mpull($projects, null, 'getPHID');
|
|
|
|
|
|
|
|
foreach ($tasks as $key => $task) {
|
2014-05-16 17:47:06 +02:00
|
|
|
if (!$task->getGroupByProjectPHID()) {
|
|
|
|
// This task is either not in any projects, or only in projects
|
|
|
|
// which we're ignoring because they're being queried for explicitly.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-09-13 16:32:57 +02:00
|
|
|
if (empty($projects[$task->getGroupByProjectPHID()])) {
|
|
|
|
unset($tasks[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $tasks;
|
|
|
|
}
|
|
|
|
|
2014-07-18 00:42:53 +02:00
|
|
|
protected function didFilterPage(array $tasks) {
|
2014-12-11 01:27:30 +01:00
|
|
|
$phids = mpull($tasks, 'getPHID');
|
|
|
|
|
2014-07-18 00:42:53 +02:00
|
|
|
// TODO: Eventually, we should make this optional and introduce a
|
|
|
|
// needProjectPHIDs() method, but for now there's a lot of code which
|
|
|
|
// assumes the data is always populated.
|
|
|
|
|
|
|
|
$edge_query = id(new PhabricatorEdgeQuery())
|
2014-12-11 01:27:30 +01:00
|
|
|
->withSourcePHIDs($phids)
|
2014-07-18 00:42:53 +02:00
|
|
|
->withEdgeTypes(
|
|
|
|
array(
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
|
|
|
));
|
|
|
|
$edge_query->execute();
|
|
|
|
|
|
|
|
foreach ($tasks as $task) {
|
2014-12-11 01:27:30 +01:00
|
|
|
$project_phids = $edge_query->getDestinationPHIDs(
|
|
|
|
array($task->getPHID()));
|
|
|
|
$task->attachProjectPHIDs($project_phids);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->needSubscriberPHIDs) {
|
|
|
|
$subscriber_sets = id(new PhabricatorSubscribersQuery())
|
|
|
|
->withObjectPHIDs($phids)
|
|
|
|
->execute();
|
|
|
|
foreach ($tasks as $task) {
|
|
|
|
$subscribers = idx($subscriber_sets, $task->getPHID(), array());
|
|
|
|
$task->attachSubscriberPHIDs($subscribers);
|
|
|
|
}
|
2014-07-18 00:42:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $tasks;
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildTaskIDsWhereClause(AphrontDatabaseConnection $conn) {
|
2011-12-02 16:30:20 +01:00
|
|
|
if (!$this->taskIDs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'id in (%Ld)',
|
|
|
|
$this->taskIDs);
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildTaskPHIDsWhereClause(AphrontDatabaseConnection $conn) {
|
|
|
|
if (!$this->taskPHIDs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'phid in (%Ls)',
|
|
|
|
$this->taskPHIDs);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildStatusWhereClause(AphrontDatabaseConnection $conn) {
|
2012-06-29 18:17:19 +02:00
|
|
|
static $map = array(
|
|
|
|
self::STATUS_RESOLVED => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED,
|
|
|
|
self::STATUS_WONTFIX => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX,
|
|
|
|
self::STATUS_INVALID => ManiphestTaskStatus::STATUS_CLOSED_INVALID,
|
|
|
|
self::STATUS_SPITE => ManiphestTaskStatus::STATUS_CLOSED_SPITE,
|
|
|
|
self::STATUS_DUPLICATE => ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE,
|
|
|
|
);
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
switch ($this->status) {
|
|
|
|
case self::STATUS_ANY:
|
|
|
|
return null;
|
|
|
|
case self::STATUS_OPEN:
|
2014-03-25 21:47:42 +01:00
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
Use string constants, not integer constants, to represent task status internally
Summary:
Ref T1812. I think integer constants are going to be confusing and error prone for users to interact with. For example, because we use 0-5, adding a second "open" status like "needs verification" without disrupting the existing statuses would require users to define a status with, e.g., constant `6`, but order it between constants `0` and `1`. And if they later remove statuses, they need to avoid reusing existing constants.
Instead, use more manageable string constants like "open", "resolved", etc.
We must migrate three tables:
- The task table itself, to update task status.
- The transaction table, to update historic status changes.
- The saved query table, to update saved queries which specify status sets.
Test Plan:
- Saved a query with complicated status filters.
- Ran migrations.
- Looked at the query, at existing tasks, and at task transactions.
- Forced migrations to run again to verify idempotentcy/safety.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T1812
Differential Revision: https://secure.phabricator.com/D8583
2014-03-25 21:58:14 +01:00
|
|
|
'status IN (%Ls)',
|
2014-03-25 21:47:42 +01:00
|
|
|
ManiphestTaskStatus::getOpenStatusConstants());
|
2011-06-30 01:16:33 +02:00
|
|
|
case self::STATUS_CLOSED:
|
2014-03-25 21:47:42 +01:00
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
Use string constants, not integer constants, to represent task status internally
Summary:
Ref T1812. I think integer constants are going to be confusing and error prone for users to interact with. For example, because we use 0-5, adding a second "open" status like "needs verification" without disrupting the existing statuses would require users to define a status with, e.g., constant `6`, but order it between constants `0` and `1`. And if they later remove statuses, they need to avoid reusing existing constants.
Instead, use more manageable string constants like "open", "resolved", etc.
We must migrate three tables:
- The task table itself, to update task status.
- The transaction table, to update historic status changes.
- The saved query table, to update saved queries which specify status sets.
Test Plan:
- Saved a query with complicated status filters.
- Ran migrations.
- Looked at the query, at existing tasks, and at task transactions.
- Forced migrations to run again to verify idempotentcy/safety.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T1812
Differential Revision: https://secure.phabricator.com/D8583
2014-03-25 21:58:14 +01:00
|
|
|
'status IN (%Ls)',
|
2014-03-25 21:47:42 +01:00
|
|
|
ManiphestTaskStatus::getClosedStatusConstants());
|
2011-06-30 01:16:33 +02:00
|
|
|
default:
|
2012-06-29 18:17:19 +02:00
|
|
|
$constant = idx($map, $this->status);
|
|
|
|
if (!$constant) {
|
|
|
|
throw new Exception("Unknown status query '{$this->status}'!");
|
|
|
|
}
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
Use string constants, not integer constants, to represent task status internally
Summary:
Ref T1812. I think integer constants are going to be confusing and error prone for users to interact with. For example, because we use 0-5, adding a second "open" status like "needs verification" without disrupting the existing statuses would require users to define a status with, e.g., constant `6`, but order it between constants `0` and `1`. And if they later remove statuses, they need to avoid reusing existing constants.
Instead, use more manageable string constants like "open", "resolved", etc.
We must migrate three tables:
- The task table itself, to update task status.
- The transaction table, to update historic status changes.
- The saved query table, to update saved queries which specify status sets.
Test Plan:
- Saved a query with complicated status filters.
- Ran migrations.
- Looked at the query, at existing tasks, and at task transactions.
- Forced migrations to run again to verify idempotentcy/safety.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T1812
Differential Revision: https://secure.phabricator.com/D8583
2014-03-25 21:58:14 +01:00
|
|
|
'status = %s',
|
2012-06-29 18:17:19 +02:00
|
|
|
$constant);
|
2011-06-30 01:16:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
private function buildStatusesWhereClause(AphrontDatabaseConnection $conn) {
|
|
|
|
if ($this->statuses) {
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
Use string constants, not integer constants, to represent task status internally
Summary:
Ref T1812. I think integer constants are going to be confusing and error prone for users to interact with. For example, because we use 0-5, adding a second "open" status like "needs verification" without disrupting the existing statuses would require users to define a status with, e.g., constant `6`, but order it between constants `0` and `1`. And if they later remove statuses, they need to avoid reusing existing constants.
Instead, use more manageable string constants like "open", "resolved", etc.
We must migrate three tables:
- The task table itself, to update task status.
- The transaction table, to update historic status changes.
- The saved query table, to update saved queries which specify status sets.
Test Plan:
- Saved a query with complicated status filters.
- Ran migrations.
- Looked at the query, at existing tasks, and at task transactions.
- Forced migrations to run again to verify idempotentcy/safety.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T1812
Differential Revision: https://secure.phabricator.com/D8583
2014-03-25 21:58:14 +01:00
|
|
|
'status IN (%Ls)',
|
2013-09-10 20:07:34 +02:00
|
|
|
$this->statuses);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:54:17 +02:00
|
|
|
private function buildPrioritiesWhereClause(AphrontDatabaseConnection $conn) {
|
|
|
|
if ($this->priorities) {
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'priority IN (%Ld)',
|
|
|
|
$this->priorities);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildAuthorWhereClause(AphrontDatabaseConnection $conn) {
|
2011-06-30 01:16:33 +02:00
|
|
|
if (!$this->authorPHIDs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'authorPHID in (%Ls)',
|
|
|
|
$this->authorPHIDs);
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildOwnerWhereClause(AphrontDatabaseConnection $conn) {
|
2011-06-30 01:16:33 +02:00
|
|
|
if (!$this->ownerPHIDs) {
|
|
|
|
if ($this->includeUnowned === null) {
|
|
|
|
return null;
|
|
|
|
} else if ($this->includeUnowned) {
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'ownerPHID IS NULL');
|
|
|
|
} else {
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'ownerPHID IS NOT NULL');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->includeUnowned) {
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'ownerPHID IN (%Ls) OR ownerPHID IS NULL',
|
|
|
|
$this->ownerPHIDs);
|
|
|
|
} else {
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'ownerPHID IN (%Ls)',
|
|
|
|
$this->ownerPHIDs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildFullTextWhereClause(AphrontDatabaseConnection $conn) {
|
2013-09-12 22:03:05 +02:00
|
|
|
if (!strlen($this->fullTextSearch)) {
|
2012-06-15 23:09:49 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In doing a fulltext search, we first find all the PHIDs that match the
|
|
|
|
// fulltext search, and then use that to limit the rest of the search
|
2014-02-03 21:51:08 +01:00
|
|
|
$fulltext_query = id(new PhabricatorSavedQuery())
|
2014-05-16 21:20:13 +02:00
|
|
|
->setEngineClassName('PhabricatorSearchApplicationSearchEngine')
|
2014-02-03 21:51:08 +01:00
|
|
|
->setParameter('query', $this->fullTextSearch);
|
2013-09-24 19:50:56 +02:00
|
|
|
|
|
|
|
// NOTE: Setting this to something larger than 2^53 will raise errors in
|
|
|
|
// ElasticSearch, and billions of results won't fit in memory anyway.
|
|
|
|
$fulltext_query->setParameter('limit', 100000);
|
2014-07-24 00:05:46 +02:00
|
|
|
$fulltext_query->setParameter('type', ManiphestTaskPHIDType::TYPECONST);
|
2012-06-15 23:09:49 +02:00
|
|
|
|
|
|
|
$engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
|
|
|
|
$fulltext_results = $engine->executeSearch($fulltext_query);
|
|
|
|
|
2012-06-16 00:41:20 +02:00
|
|
|
if (empty($fulltext_results)) {
|
|
|
|
$fulltext_results = array(null);
|
|
|
|
}
|
|
|
|
|
2012-06-15 23:09:49 +02:00
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
|
|
|
'phid IN (%Ls)',
|
|
|
|
$fulltext_results);
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildProjectWhereClause(AphrontDatabaseConnection $conn) {
|
2012-02-29 06:08:02 +01:00
|
|
|
if (!$this->projectPHIDs && !$this->includeNoProject) {
|
2011-06-30 01:16:33 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-02-29 06:08:02 +01:00
|
|
|
$parts = array();
|
|
|
|
if ($this->projectPHIDs) {
|
|
|
|
$parts[] = qsprintf(
|
|
|
|
$conn,
|
2014-07-18 00:42:30 +02:00
|
|
|
'project.dst in (%Ls)',
|
2012-02-29 06:08:02 +01:00
|
|
|
$this->projectPHIDs);
|
|
|
|
}
|
|
|
|
if ($this->includeNoProject) {
|
|
|
|
$parts[] = qsprintf(
|
|
|
|
$conn,
|
2014-07-18 00:42:30 +02:00
|
|
|
'project.dst IS NULL');
|
2012-02-29 06:08:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return '('.implode(') OR (', $parts).')';
|
2011-06-30 01:16:33 +02:00
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildAnyProjectWhereClause(AphrontDatabaseConnection $conn) {
|
2012-10-05 00:30:51 +02:00
|
|
|
if (!$this->anyProjectPHIDs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
2014-07-18 00:42:30 +02:00
|
|
|
'anyproject.dst IN (%Ls)',
|
2012-10-05 00:30:51 +02:00
|
|
|
$this->anyProjectPHIDs);
|
|
|
|
}
|
|
|
|
|
2013-04-04 19:29:40 +02:00
|
|
|
private function buildAnyUserProjectWhereClause(
|
|
|
|
AphrontDatabaseConnection $conn) {
|
|
|
|
if (!$this->anyUserProjectPHIDs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$projects = id(new PhabricatorProjectQuery())
|
2013-09-11 21:27:28 +02:00
|
|
|
->setViewer($this->getViewer())
|
2013-04-04 19:29:40 +02:00
|
|
|
->withMemberPHIDs($this->anyUserProjectPHIDs)
|
|
|
|
->execute();
|
|
|
|
$any_user_project_phids = mpull($projects, 'getPHID');
|
2013-09-02 22:24:41 +02:00
|
|
|
if (!$any_user_project_phids) {
|
|
|
|
throw new PhabricatorEmptyQueryException();
|
|
|
|
}
|
2013-04-04 19:29:40 +02:00
|
|
|
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
2014-07-18 00:42:30 +02:00
|
|
|
'anyproject.dst IN (%Ls)',
|
2013-04-04 19:29:40 +02:00
|
|
|
$any_user_project_phids);
|
|
|
|
}
|
|
|
|
|
2012-11-01 18:47:45 +01:00
|
|
|
private function buildXProjectWhereClause(AphrontDatabaseConnection $conn) {
|
2012-02-29 06:08:02 +01:00
|
|
|
if (!$this->xprojectPHIDs) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsprintf(
|
|
|
|
$conn,
|
2014-07-18 00:42:30 +02:00
|
|
|
'xproject.dst IS NULL');
|
2012-02-29 06:08:02 +01:00
|
|
|
}
|
|
|
|
|
2013-09-10 16:53:27 +02:00
|
|
|
private function buildCustomOrderClause(AphrontDatabaseConnection $conn) {
|
2014-08-02 10:22:16 +02:00
|
|
|
$reverse = ($this->getBeforeID() xor $this->getReversePaging());
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
$order = array();
|
|
|
|
|
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_NONE:
|
|
|
|
break;
|
|
|
|
case self::GROUP_PRIORITY:
|
|
|
|
$order[] = 'priority';
|
|
|
|
break;
|
|
|
|
case self::GROUP_OWNER:
|
|
|
|
$order[] = 'ownerOrdering';
|
|
|
|
break;
|
|
|
|
case self::GROUP_STATUS:
|
|
|
|
$order[] = 'status';
|
|
|
|
break;
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
case self::GROUP_PROJECT:
|
2013-09-12 22:08:25 +02:00
|
|
|
$order[] = '<group.project>';
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
break;
|
2011-06-30 01:16:33 +02:00
|
|
|
default:
|
|
|
|
throw new Exception("Unknown group query '{$this->groupBy}'!");
|
|
|
|
}
|
|
|
|
|
2014-08-02 10:22:16 +02:00
|
|
|
$app_order = $this->buildApplicationSearchOrders($conn, $reverse);
|
|
|
|
|
|
|
|
if (!$app_order) {
|
|
|
|
switch ($this->orderBy) {
|
|
|
|
case self::ORDER_PRIORITY:
|
|
|
|
$order[] = 'priority';
|
|
|
|
$order[] = 'subpriority';
|
|
|
|
$order[] = 'dateModified';
|
|
|
|
break;
|
|
|
|
case self::ORDER_CREATED:
|
|
|
|
$order[] = 'id';
|
|
|
|
break;
|
|
|
|
case self::ORDER_MODIFIED:
|
|
|
|
$order[] = 'dateModified';
|
|
|
|
break;
|
|
|
|
case self::ORDER_TITLE:
|
|
|
|
$order[] = 'title';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception("Unknown order query '{$this->orderBy}'!");
|
|
|
|
}
|
2011-06-30 01:16:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$order = array_unique($order);
|
|
|
|
|
2014-08-02 10:22:16 +02:00
|
|
|
if (empty($order) && empty($app_order)) {
|
2011-06-30 01:16:33 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($order as $k => $column) {
|
|
|
|
switch ($column) {
|
2012-04-02 21:12:04 +02:00
|
|
|
case 'subpriority':
|
2011-06-30 01:16:33 +02:00
|
|
|
case 'ownerOrdering':
|
2012-08-02 23:21:13 +02:00
|
|
|
case 'title':
|
2013-09-13 16:13:06 +02:00
|
|
|
if ($reverse) {
|
|
|
|
$order[$k] = "task.{$column} DESC";
|
|
|
|
} else {
|
|
|
|
$order[$k] = "task.{$column} ASC";
|
|
|
|
}
|
2011-06-30 01:16:33 +02:00
|
|
|
break;
|
2013-09-12 22:08:25 +02:00
|
|
|
case '<group.project>':
|
|
|
|
// Put "No Project" at the end of the list.
|
2013-09-13 16:13:06 +02:00
|
|
|
if ($reverse) {
|
|
|
|
$order[$k] =
|
|
|
|
'projectGroupName.indexedObjectName IS NULL DESC, '.
|
|
|
|
'projectGroupName.indexedObjectName DESC';
|
|
|
|
} else {
|
|
|
|
$order[$k] =
|
|
|
|
'projectGroupName.indexedObjectName IS NULL ASC, '.
|
|
|
|
'projectGroupName.indexedObjectName ASC';
|
|
|
|
}
|
2013-09-12 22:08:25 +02:00
|
|
|
break;
|
2011-06-30 01:16:33 +02:00
|
|
|
default:
|
2013-09-13 16:13:06 +02:00
|
|
|
if ($reverse) {
|
|
|
|
$order[$k] = "task.{$column} ASC";
|
|
|
|
} else {
|
|
|
|
$order[$k] = "task.{$column} DESC";
|
|
|
|
}
|
2011-06-30 01:16:33 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-02 10:22:16 +02:00
|
|
|
if ($app_order) {
|
|
|
|
foreach ($app_order as $order_by) {
|
|
|
|
$order[] = $order_by;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($reverse) {
|
|
|
|
$order[] = 'task.id ASC';
|
|
|
|
} else {
|
|
|
|
$order[] = 'task.id DESC';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
return 'ORDER BY '.implode(', ', $order);
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
|
2014-07-18 00:42:30 +02:00
|
|
|
$edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
|
2011-06-30 01:16:33 +02:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
$joins = array();
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
if ($this->projectPHIDs || $this->includeNoProject) {
|
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2014-07-18 00:42:30 +02:00
|
|
|
'%Q JOIN %T project ON project.src = task.phid
|
|
|
|
AND project.type = %d',
|
2013-09-12 22:08:25 +02:00
|
|
|
($this->includeNoProject ? 'LEFT' : ''),
|
2014-07-18 00:42:30 +02:00
|
|
|
$edge_table,
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
if ($this->anyProjectPHIDs || $this->anyUserProjectPHIDs) {
|
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2014-07-18 00:42:30 +02:00
|
|
|
'JOIN %T anyproject ON anyproject.src = task.phid
|
|
|
|
AND anyproject.type = %d',
|
|
|
|
$edge_table,
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
2013-09-12 22:08:25 +02:00
|
|
|
}
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
if ($this->xprojectPHIDs) {
|
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2014-07-18 00:42:30 +02:00
|
|
|
'LEFT JOIN %T xproject ON xproject.src = task.phid
|
|
|
|
AND xproject.type = %d
|
|
|
|
AND xproject.dst IN (%Ls)',
|
|
|
|
$edge_table,
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
2013-09-12 22:08:25 +02:00
|
|
|
$this->xprojectPHIDs);
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
if ($this->subscriberPHIDs) {
|
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2014-12-11 01:27:30 +01:00
|
|
|
'JOIN %T e_ccs ON e_ccs.src = task.phid '.
|
|
|
|
'AND e_ccs.type = %s '.
|
|
|
|
'AND e_ccs.dst in (%Ls)',
|
|
|
|
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
|
|
|
PhabricatorEdgeConfig::TYPE_OBJECT_HAS_SUBSCRIBER,
|
|
|
|
$this->subscriberPHIDs);
|
2013-09-12 22:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_PROJECT:
|
|
|
|
$ignore_group_phids = $this->getIgnoreGroupedProjectPHIDs();
|
|
|
|
if ($ignore_group_phids) {
|
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2014-07-18 00:42:30 +02:00
|
|
|
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
|
|
|
|
AND projectGroup.type = %d
|
|
|
|
AND projectGroup.dst NOT IN (%Ls)',
|
|
|
|
$edge_table,
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
2013-09-12 22:08:25 +02:00
|
|
|
$ignore_group_phids);
|
|
|
|
} else {
|
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2014-07-18 00:42:30 +02:00
|
|
|
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
|
|
|
|
AND projectGroup.type = %d',
|
|
|
|
$edge_table,
|
|
|
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
2013-09-12 22:08:25 +02:00
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'LEFT JOIN %T projectGroupName
|
2014-07-18 00:42:30 +02:00
|
|
|
ON projectGroup.dst = projectGroupName.indexedObjectPHID',
|
2013-09-12 22:08:25 +02:00
|
|
|
id(new ManiphestNameIndex())->getTableName());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-09-17 01:03:09 +02:00
|
|
|
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
return implode(' ', $joins);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildGroupClause(AphrontDatabaseConnection $conn_r) {
|
2013-09-17 01:03:09 +02:00
|
|
|
$joined_multiple_rows = (count($this->projectPHIDs) > 1) ||
|
|
|
|
(count($this->anyProjectPHIDs) > 1) ||
|
|
|
|
($this->getApplicationSearchMayJoinMultipleRows());
|
2013-09-12 22:08:25 +02:00
|
|
|
|
|
|
|
$joined_project_name = ($this->groupBy == self::GROUP_PROJECT);
|
|
|
|
|
|
|
|
// If we're joining multiple rows, we need to group the results by the
|
|
|
|
// task IDs.
|
2013-09-17 01:03:09 +02:00
|
|
|
if ($joined_multiple_rows) {
|
2013-09-12 22:08:25 +02:00
|
|
|
if ($joined_project_name) {
|
2014-07-18 00:42:30 +02:00
|
|
|
return 'GROUP BY task.phid, projectGroup.dst';
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
} else {
|
2013-09-17 01:03:09 +02:00
|
|
|
return 'GROUP BY task.phid';
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
2013-09-12 22:08:25 +02:00
|
|
|
} else {
|
|
|
|
return '';
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
2013-09-12 22:08:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return project PHIDs which we should ignore when grouping tasks by
|
|
|
|
* project. For example, if a user issues a query like:
|
|
|
|
*
|
|
|
|
* Tasks in all projects: Frontend, Bugs
|
|
|
|
*
|
|
|
|
* ...then we don't show "Frontend" or "Bugs" groups in the result set, since
|
|
|
|
* they're meaningless as all results are in both groups.
|
|
|
|
*
|
|
|
|
* Similarly, for queries like:
|
|
|
|
*
|
|
|
|
* Tasks in any projects: Public Relations
|
|
|
|
*
|
|
|
|
* ...we ignore the single project, as every result is in that project. (In
|
|
|
|
* the case that there are several "any" projects, we do not ignore them.)
|
|
|
|
*
|
|
|
|
* @return list<phid> Project PHIDs which should be ignored in query
|
|
|
|
* construction.
|
|
|
|
*/
|
|
|
|
private function getIgnoreGroupedProjectPHIDs() {
|
|
|
|
$phids = array();
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
if ($this->projectPHIDs) {
|
|
|
|
$phids[] = $this->projectPHIDs;
|
|
|
|
}
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
if (count($this->anyProjectPHIDs) == 1) {
|
|
|
|
$phids[] = $this->anyProjectPHIDs;
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
|
|
|
|
2013-09-12 22:08:25 +02:00
|
|
|
// Maybe we should also exclude the "excludeProjectPHIDs"? It won't
|
|
|
|
// impact the results, but we might end up with a better query plan.
|
|
|
|
// Investigate this on real data? This is likely very rare.
|
|
|
|
|
|
|
|
return array_mergev($phids);
|
Add "Group by: Project" to Maniphest
Summary:
Allow tasks to be grouped by project. Since this is many-to-many and we're a little deficient on indexes for doing this on the database, we pull all matching tasks and group them in PHP. This shouldn't be a huge issue for any existing installs, though, and we can add keys when we run into one.
- When a task is in multiple projects, it appears under multiple headers.
- When a query has a task filter, those projects are omitted from the grouping (they'd always show everything, which isn't useful). Notably, if you search for "Differential", you can now see "Bugs", "Feature Requests", etc.
Test Plan: Selected "Group by: Project".
Reviewers: btrahan, Josereyes
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T923
Differential Revision: https://secure.phabricator.com/D1953
2012-03-20 03:47:34 +01:00
|
|
|
}
|
|
|
|
|
2013-09-13 16:13:06 +02:00
|
|
|
private function loadCursorObject($id) {
|
|
|
|
$results = id(new ManiphestTaskQuery())
|
2013-09-22 01:23:44 +02:00
|
|
|
->setViewer($this->getPagingViewer())
|
2013-09-13 16:13:06 +02:00
|
|
|
->withIDs(array((int)$id))
|
|
|
|
->execute();
|
|
|
|
return head($results);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getPagingValue($result) {
|
|
|
|
$id = $result->getID();
|
|
|
|
|
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_NONE:
|
|
|
|
return $id;
|
|
|
|
case self::GROUP_PRIORITY:
|
|
|
|
return $id.'.'.$result->getPriority();
|
|
|
|
case self::GROUP_OWNER:
|
2013-09-13 16:32:57 +02:00
|
|
|
return rtrim($id.'.'.$result->getOwnerPHID(), '.');
|
2013-09-13 16:13:06 +02:00
|
|
|
case self::GROUP_STATUS:
|
|
|
|
return $id.'.'.$result->getStatus();
|
|
|
|
case self::GROUP_PROJECT:
|
2013-09-13 16:32:57 +02:00
|
|
|
return rtrim($id.'.'.$result->getGroupByProjectPHID(), '.');
|
2013-09-13 16:13:06 +02:00
|
|
|
default:
|
|
|
|
throw new Exception("Unknown group query '{$this->groupBy}'!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
|
|
|
|
$default = parent::buildPagingClause($conn_r);
|
|
|
|
|
|
|
|
$before_id = $this->getBeforeID();
|
|
|
|
$after_id = $this->getAfterID();
|
|
|
|
|
|
|
|
if (!$before_id && !$after_id) {
|
|
|
|
return $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
$cursor_id = nonempty($before_id, $after_id);
|
|
|
|
$cursor_parts = explode('.', $cursor_id, 2);
|
|
|
|
$task_id = $cursor_parts[0];
|
|
|
|
$group_id = idx($cursor_parts, 1);
|
|
|
|
|
|
|
|
$cursor = $this->loadCursorObject($task_id);
|
|
|
|
if (!$cursor) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$columns = array();
|
|
|
|
|
|
|
|
switch ($this->groupBy) {
|
|
|
|
case self::GROUP_NONE:
|
|
|
|
break;
|
|
|
|
case self::GROUP_PRIORITY:
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.priority',
|
|
|
|
'value' => (int)$group_id,
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case self::GROUP_OWNER:
|
|
|
|
$columns[] = array(
|
2013-09-13 16:32:57 +02:00
|
|
|
'name' => '(task.ownerOrdering IS NULL)',
|
|
|
|
'value' => (int)(strlen($group_id) ? 0 : 1),
|
|
|
|
'type' => 'int',
|
2013-09-13 16:13:06 +02:00
|
|
|
);
|
2013-09-13 16:32:57 +02:00
|
|
|
if ($group_id) {
|
|
|
|
$paging_users = id(new PhabricatorPeopleQuery())
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
->withPHIDs(array($group_id))
|
|
|
|
->execute();
|
|
|
|
if (!$paging_users) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.ownerOrdering',
|
|
|
|
'value' => head($paging_users)->getUsername(),
|
|
|
|
'type' => 'string',
|
|
|
|
'reverse' => true,
|
|
|
|
);
|
|
|
|
}
|
2013-09-13 16:13:06 +02:00
|
|
|
break;
|
|
|
|
case self::GROUP_STATUS:
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.status',
|
Use string constants, not integer constants, to represent task status internally
Summary:
Ref T1812. I think integer constants are going to be confusing and error prone for users to interact with. For example, because we use 0-5, adding a second "open" status like "needs verification" without disrupting the existing statuses would require users to define a status with, e.g., constant `6`, but order it between constants `0` and `1`. And if they later remove statuses, they need to avoid reusing existing constants.
Instead, use more manageable string constants like "open", "resolved", etc.
We must migrate three tables:
- The task table itself, to update task status.
- The transaction table, to update historic status changes.
- The saved query table, to update saved queries which specify status sets.
Test Plan:
- Saved a query with complicated status filters.
- Ran migrations.
- Looked at the query, at existing tasks, and at task transactions.
- Forced migrations to run again to verify idempotentcy/safety.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T1812
Differential Revision: https://secure.phabricator.com/D8583
2014-03-25 21:58:14 +01:00
|
|
|
'value' => $group_id,
|
|
|
|
'type' => 'string',
|
2013-09-13 16:13:06 +02:00
|
|
|
);
|
|
|
|
break;
|
|
|
|
case self::GROUP_PROJECT:
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => '(projectGroupName.indexedObjectName IS NULL)',
|
|
|
|
'value' => (int)(strlen($group_id) ? 0 : 1),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
2013-09-13 16:32:57 +02:00
|
|
|
if ($group_id) {
|
|
|
|
$paging_projects = id(new PhabricatorProjectQuery())
|
|
|
|
->setViewer($this->getViewer())
|
|
|
|
->withPHIDs(array($group_id))
|
|
|
|
->execute();
|
|
|
|
if (!$paging_projects) {
|
|
|
|
return null;
|
|
|
|
}
|
2013-09-13 16:13:06 +02:00
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'projectGroupName.indexedObjectName',
|
2013-09-13 16:32:57 +02:00
|
|
|
'value' => head($paging_projects)->getName(),
|
2013-09-13 16:13:06 +02:00
|
|
|
'type' => 'string',
|
|
|
|
'reverse' => true,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception("Unknown group query '{$this->groupBy}'!");
|
|
|
|
}
|
|
|
|
|
2014-08-02 10:22:16 +02:00
|
|
|
$app_columns = $this->buildApplicationSearchPagination($conn_r, $cursor);
|
|
|
|
if ($app_columns) {
|
|
|
|
$columns = array_merge($columns, $app_columns);
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.id',
|
|
|
|
'value' => (int)$cursor->getID(),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
switch ($this->orderBy) {
|
|
|
|
case self::ORDER_PRIORITY:
|
|
|
|
if ($this->groupBy != self::GROUP_PRIORITY) {
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.priority',
|
|
|
|
'value' => (int)$cursor->getPriority(),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
}
|
2013-09-13 16:13:06 +02:00
|
|
|
$columns[] = array(
|
2014-08-02 10:22:16 +02:00
|
|
|
'name' => 'task.subpriority',
|
|
|
|
'value' => (int)$cursor->getSubpriority(),
|
2013-09-13 16:13:06 +02:00
|
|
|
'type' => 'int',
|
2014-08-02 10:22:16 +02:00
|
|
|
'reverse' => true,
|
2013-09-13 16:13:06 +02:00
|
|
|
);
|
2014-08-02 10:22:16 +02:00
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.dateModified',
|
|
|
|
'value' => (int)$cursor->getDateModified(),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case self::ORDER_CREATED:
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.id',
|
|
|
|
'value' => (int)$cursor->getID(),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case self::ORDER_MODIFIED:
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.dateModified',
|
|
|
|
'value' => (int)$cursor->getDateModified(),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case self::ORDER_TITLE:
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.title',
|
|
|
|
'value' => $cursor->getTitle(),
|
|
|
|
'type' => 'string',
|
|
|
|
);
|
|
|
|
$columns[] = array(
|
|
|
|
'name' => 'task.id',
|
|
|
|
'value' => $cursor->getID(),
|
|
|
|
'type' => 'int',
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception("Unknown order query '{$this->orderBy}'!");
|
|
|
|
}
|
2013-09-13 16:13:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->buildPagingClauseFromMultipleColumns(
|
|
|
|
$conn_r,
|
|
|
|
$columns,
|
|
|
|
array(
|
|
|
|
'reversed' => (bool)($before_id xor $this->getReversePaging()),
|
|
|
|
));
|
|
|
|
}
|
2013-09-12 22:08:25 +02:00
|
|
|
|
2013-09-17 01:03:09 +02:00
|
|
|
protected function getApplicationSearchObjectPHIDColumn() {
|
|
|
|
return 'task.phid';
|
|
|
|
}
|
|
|
|
|
Lock policy queries to their applications
Summary:
While we mostly have reasonable effective object accessibility when you lock a user out of an application, it's primarily enforced at the controller level. Users can still, e.g., load the handles of objects they can't actually see. Instead, lock the queries to the applications so that you can, e.g., never load a revision if you don't have access to Differential.
This has several parts:
- For PolicyAware queries, provide an application class name method.
- If the query specifies a class name and the user doesn't have permission to use it, fail the entire query unconditionally.
- For handles, simplify query construction and count all the PHIDs as "restricted" so we get a UI full of "restricted" instead of "unknown" handles.
Test Plan:
- Added a unit test to verify I got all the class names right.
- Browsed around, logged in/out as a normal user with public policies on and off.
- Browsed around, logged in/out as a restricted user with public policies on and off. With restrictions, saw all traces of restricted apps removed or restricted.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D7367
2013-10-22 02:20:27 +02:00
|
|
|
public function getQueryApplicationClass() {
|
2014-07-23 02:03:09 +02:00
|
|
|
return 'PhabricatorManiphestApplication';
|
Lock policy queries to their applications
Summary:
While we mostly have reasonable effective object accessibility when you lock a user out of an application, it's primarily enforced at the controller level. Users can still, e.g., load the handles of objects they can't actually see. Instead, lock the queries to the applications so that you can, e.g., never load a revision if you don't have access to Differential.
This has several parts:
- For PolicyAware queries, provide an application class name method.
- If the query specifies a class name and the user doesn't have permission to use it, fail the entire query unconditionally.
- For handles, simplify query construction and count all the PHIDs as "restricted" so we get a UI full of "restricted" instead of "unknown" handles.
Test Plan:
- Added a unit test to verify I got all the class names right.
- Browsed around, logged in/out as a normal user with public policies on and off.
- Browsed around, logged in/out as a restricted user with public policies on and off. With restrictions, saw all traces of restricted apps removed or restricted.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D7367
2013-10-22 02:20:27 +02:00
|
|
|
}
|
|
|
|
|
2011-06-30 01:16:33 +02:00
|
|
|
}
|