2013-09-10 19:04:26 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class ManiphestTaskSearchEngine
|
|
|
|
extends PhabricatorApplicationSearchEngine {
|
|
|
|
|
2013-09-17 01:03:09 +02:00
|
|
|
public function getCustomFieldObject() {
|
|
|
|
return new ManiphestTask();
|
|
|
|
}
|
|
|
|
|
2013-09-10 19:04:26 +02:00
|
|
|
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
|
|
|
$saved = new PhabricatorSavedQuery();
|
|
|
|
|
2013-09-10 19:44:42 +02:00
|
|
|
$saved->setParameter(
|
|
|
|
'assignedPHIDs',
|
|
|
|
$this->readUsersFromRequest($request, 'assigned'));
|
|
|
|
|
|
|
|
$saved->setParameter('withUnassigned', $request->getBool('withUnassigned'));
|
|
|
|
|
|
|
|
$saved->setParameter(
|
|
|
|
'authorPHIDs',
|
|
|
|
$this->readUsersFromRequest($request, 'authors'));
|
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
$saved->setParameter('statuses', $request->getArr('statuses'));
|
2013-09-10 20:54:17 +02:00
|
|
|
$saved->setParameter('priorities', $request->getArr('priorities'));
|
2013-09-12 23:48:52 +02:00
|
|
|
$saved->setParameter('group', $request->getStr('group'));
|
2013-09-10 20:07:34 +02:00
|
|
|
$saved->setParameter('order', $request->getStr('order'));
|
|
|
|
|
2013-09-10 20:54:17 +02:00
|
|
|
$ids = $request->getStrList('ids');
|
|
|
|
foreach ($ids as $key => $id) {
|
|
|
|
$id = trim($id, ' Tt');
|
|
|
|
if (!$id || !is_numeric($id)) {
|
|
|
|
unset($ids[$key]);
|
|
|
|
} else {
|
|
|
|
$ids[$key] = $id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$saved->setParameter('ids', $ids);
|
|
|
|
|
2013-09-12 22:03:05 +02:00
|
|
|
$saved->setParameter('fulltext', $request->getStr('fulltext'));
|
|
|
|
|
2013-09-12 22:03:14 +02:00
|
|
|
$saved->setParameter(
|
|
|
|
'allProjectPHIDs',
|
|
|
|
$request->getArr('allProjects'));
|
|
|
|
|
|
|
|
$saved->setParameter(
|
|
|
|
'withNoProject',
|
|
|
|
$request->getBool('withNoProject'));
|
|
|
|
|
|
|
|
$saved->setParameter(
|
|
|
|
'anyProjectPHIDs',
|
|
|
|
$request->getArr('anyProjects'));
|
|
|
|
|
|
|
|
$saved->setParameter(
|
|
|
|
'excludeProjectPHIDs',
|
|
|
|
$request->getArr('excludeProjects'));
|
|
|
|
|
|
|
|
$saved->setParameter(
|
|
|
|
'userProjectPHIDs',
|
|
|
|
$this->readUsersFromRequest($request, 'userProjects'));
|
|
|
|
|
2013-09-12 22:03:39 +02:00
|
|
|
$saved->setParameter('createdStart', $request->getStr('createdStart'));
|
|
|
|
$saved->setParameter('createdEnd', $request->getStr('createdEnd'));
|
|
|
|
|
Allow Maniphest queries to have configurable page sizes
Summary:
Current page size is `1000`. This is nice to have in some cases, but makes pages slower than necessary in others. Task lists are generally dominated by rendering costs.
For example, my default is "recent tasks", which just lists all tasks ordered by date created. Showing 100 tasks here instead of 1000 makes this several times faster without compromising utility.
I don't want to force the default to 100, though, since sometimes listing everything is quite useful and I think an advantage of Maniphest is that it generally deals reasonably well with large task sets.
(This `limit` property is actually read by the default implementation of `getPageSize()` in the parent class.)
Test Plan: Made queries with page sizes 1, 100, 12, 9, 3000, etc.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D6976
2013-09-13 17:44:00 +02:00
|
|
|
$limit = $request->getInt('limit');
|
|
|
|
if ($limit > 0) {
|
|
|
|
$saved->setParameter('limit', $limit);
|
|
|
|
}
|
|
|
|
|
2013-09-17 01:03:09 +02:00
|
|
|
$this->readCustomFieldsFromRequest($request, $saved);
|
|
|
|
|
2013-09-10 19:04:26 +02:00
|
|
|
return $saved;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
|
|
|
$query = id(new ManiphestTaskQuery());
|
|
|
|
|
2013-09-10 19:44:42 +02:00
|
|
|
$author_phids = $saved->getParameter('authorPHIDs');
|
|
|
|
if ($author_phids) {
|
|
|
|
$query->withAuthors($author_phids);
|
|
|
|
}
|
|
|
|
|
|
|
|
$with_unassigned = $saved->getParameter('withUnassigned');
|
|
|
|
if ($with_unassigned) {
|
|
|
|
$query->withOwners(array(null));
|
|
|
|
} else {
|
|
|
|
$assigned_phids = $saved->getParameter('assignedPHIDs', array());
|
|
|
|
if ($assigned_phids) {
|
|
|
|
$query->withOwners($assigned_phids);
|
|
|
|
}
|
|
|
|
}
|
2013-09-10 19:04:26 +02:00
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
$statuses = $saved->getParameter('statuses');
|
|
|
|
if ($statuses) {
|
|
|
|
$query->withStatuses($statuses);
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:54:17 +02:00
|
|
|
$priorities = $saved->getParameter('priorities');
|
|
|
|
if ($priorities) {
|
|
|
|
$query->withPriorities($priorities);
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
$order = $saved->getParameter('order');
|
|
|
|
$order = idx($this->getOrderValues(), $order);
|
|
|
|
if ($order) {
|
|
|
|
$query->setOrderBy($order);
|
|
|
|
} else {
|
|
|
|
$query->setOrderBy(head($this->getOrderValues()));
|
|
|
|
}
|
|
|
|
|
2013-09-12 23:48:52 +02:00
|
|
|
$group = $saved->getParameter('group');
|
|
|
|
$group = idx($this->getGroupValues(), $group);
|
|
|
|
if ($group) {
|
|
|
|
$query->setGroupBy($group);
|
|
|
|
} else {
|
|
|
|
$query->setGroupBy(head($this->getGroupValues()));
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:54:17 +02:00
|
|
|
$ids = $saved->getParameter('ids');
|
|
|
|
if ($ids) {
|
|
|
|
$query->withIDs($ids);
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:03:05 +02:00
|
|
|
$fulltext = $saved->getParameter('fulltext');
|
|
|
|
if (strlen($fulltext)) {
|
|
|
|
$query->withFullTextSearch($fulltext);
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:03:14 +02:00
|
|
|
$with_no_project = $saved->getParameter('withNoProject');
|
|
|
|
if ($with_no_project) {
|
|
|
|
$query->withAllProjects(array(ManiphestTaskOwner::PROJECT_NO_PROJECT));
|
|
|
|
} else {
|
|
|
|
$project_phids = $saved->getParameter('allProjectPHIDs');
|
|
|
|
if ($project_phids) {
|
|
|
|
$query->withAllProjects($project_phids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$any_project_phids = $saved->getParameter('anyProjectPHIDs');
|
|
|
|
if ($any_project_phids) {
|
|
|
|
$query->withAnyProjects($any_project_phids);
|
|
|
|
}
|
|
|
|
|
|
|
|
$exclude_project_phids = $saved->getParameter('excludeProjectPHIDs');
|
|
|
|
if ($exclude_project_phids) {
|
|
|
|
$query->withoutProjects($exclude_project_phids);
|
|
|
|
}
|
|
|
|
|
|
|
|
$user_project_phids = $saved->getParameter('userProjectPHIDs');
|
|
|
|
if ($user_project_phids) {
|
|
|
|
$query->withAnyUserProjects($user_project_phids);
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:03:39 +02:00
|
|
|
$start = $this->parseDateTime($saved->getParameter('createdStart'));
|
|
|
|
$end = $this->parseDateTime($saved->getParameter('createdEnd'));
|
|
|
|
|
|
|
|
if ($start) {
|
|
|
|
$query->withDateCreatedAfter($start);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($end) {
|
|
|
|
$query->withDateCreatedBefore($end);
|
|
|
|
}
|
|
|
|
|
2013-09-17 01:03:09 +02:00
|
|
|
$this->applyCustomFieldsToQuery($query, $saved);
|
|
|
|
|
2013-09-10 19:04:26 +02:00
|
|
|
return $query;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function buildSearchForm(
|
|
|
|
AphrontFormView $form,
|
2013-09-10 19:44:42 +02:00
|
|
|
PhabricatorSavedQuery $saved) {
|
|
|
|
|
|
|
|
$assigned_phids = $saved->getParameter('assignedPHIDs', array());
|
|
|
|
$author_phids = $saved->getParameter('authorPHIDs', array());
|
2013-09-12 22:03:14 +02:00
|
|
|
$all_project_phids = $saved->getParameter(
|
|
|
|
'allProjectPHIDs',
|
|
|
|
array());
|
|
|
|
$any_project_phids = $saved->getParameter(
|
|
|
|
'anyProjectPHIDs',
|
|
|
|
array());
|
|
|
|
$exclude_project_phids = $saved->getParameter(
|
|
|
|
'excludeProjectPHIDs',
|
|
|
|
array());
|
|
|
|
$user_project_phids = $saved->getParameter(
|
|
|
|
'userProjectPHIDs',
|
|
|
|
array());
|
|
|
|
|
|
|
|
$all_phids = array_merge(
|
|
|
|
$assigned_phids,
|
|
|
|
$author_phids,
|
|
|
|
$all_project_phids,
|
|
|
|
$any_project_phids,
|
|
|
|
$exclude_project_phids,
|
|
|
|
$user_project_phids);
|
2013-09-10 19:44:42 +02:00
|
|
|
|
|
|
|
if ($all_phids) {
|
|
|
|
$handles = id(new PhabricatorHandleQuery())
|
|
|
|
->setViewer($this->requireViewer())
|
|
|
|
->withPHIDs($all_phids)
|
|
|
|
->execute();
|
|
|
|
} else {
|
|
|
|
$handles = array();
|
|
|
|
}
|
|
|
|
|
2013-09-12 22:03:14 +02:00
|
|
|
$assigned_handles = array_select_keys($handles, $assigned_phids);
|
|
|
|
$author_handles = array_select_keys($handles, $author_phids);
|
|
|
|
$all_project_handles = array_select_keys($handles, $all_project_phids);
|
|
|
|
$any_project_handles = array_select_keys($handles, $any_project_phids);
|
|
|
|
$exclude_project_handles = array_select_keys(
|
|
|
|
$handles,
|
|
|
|
$exclude_project_phids);
|
|
|
|
$user_project_handles = array_select_keys($handles, $user_project_phids);
|
2013-09-10 19:44:42 +02:00
|
|
|
|
|
|
|
$with_unassigned = $saved->getParameter('withUnassigned');
|
2013-09-12 22:03:14 +02:00
|
|
|
$with_no_projects = $saved->getParameter('withNoProject');
|
2013-09-10 19:44:42 +02:00
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
$statuses = $saved->getParameter('statuses', array());
|
|
|
|
$statuses = array_fuse($statuses);
|
|
|
|
$status_control = id(new AphrontFormCheckboxControl())
|
|
|
|
->setLabel(pht('Status'));
|
|
|
|
foreach (ManiphestTaskStatus::getTaskStatusMap() as $status => $name) {
|
|
|
|
$status_control->addCheckbox(
|
|
|
|
'statuses[]',
|
|
|
|
$status,
|
|
|
|
$name,
|
|
|
|
isset($statuses[$status]));
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:54:17 +02:00
|
|
|
$priorities = $saved->getParameter('priorities', array());
|
|
|
|
$priorities = array_fuse($priorities);
|
|
|
|
$priority_control = id(new AphrontFormCheckboxControl())
|
|
|
|
->setLabel(pht('Priority'));
|
|
|
|
foreach (ManiphestTaskPriority::getTaskPriorityMap() as $pri => $name) {
|
|
|
|
$priority_control->addCheckbox(
|
|
|
|
'priorities[]',
|
|
|
|
$pri,
|
|
|
|
$name,
|
|
|
|
isset($priorities[$pri]));
|
|
|
|
}
|
|
|
|
|
|
|
|
$ids = $saved->getParameter('ids', array());
|
|
|
|
|
2013-09-10 19:44:42 +02:00
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setDatasource('/typeahead/common/accounts/')
|
|
|
|
->setName('assigned')
|
|
|
|
->setLabel(pht('Assigned To'))
|
2013-09-12 22:03:14 +02:00
|
|
|
->setValue($assigned_handles))
|
2013-09-10 19:44:42 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormCheckboxControl())
|
|
|
|
->addCheckbox(
|
|
|
|
'withUnassigned',
|
|
|
|
1,
|
|
|
|
pht('Show only unassigned tasks.'),
|
|
|
|
$with_unassigned))
|
2013-09-12 22:03:14 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setDatasource('/typeahead/common/projects/')
|
|
|
|
->setName('allProjects')
|
|
|
|
->setLabel(pht('In All Projects'))
|
|
|
|
->setValue($all_project_handles))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormCheckboxControl())
|
|
|
|
->addCheckbox(
|
|
|
|
'withNoProject',
|
|
|
|
1,
|
|
|
|
pht('Show only tasks with no projects.'),
|
|
|
|
$with_no_projects))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setDatasource('/typeahead/common/projects/')
|
|
|
|
->setName('anyProjects')
|
|
|
|
->setLabel(pht('In Any Project'))
|
|
|
|
->setValue($any_project_handles))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setDatasource('/typeahead/common/projects/')
|
|
|
|
->setName('excludeProjects')
|
|
|
|
->setLabel(pht('Not In Projects'))
|
|
|
|
->setValue($exclude_project_handles))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setDatasource('/typeahead/common/accounts/')
|
|
|
|
->setName('userProjects')
|
|
|
|
->setLabel(pht('In Users\' Projects'))
|
|
|
|
->setValue($user_project_handles))
|
2013-09-10 19:44:42 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
->setDatasource('/typeahead/common/accounts/')
|
|
|
|
->setName('authors')
|
|
|
|
->setLabel(pht('Authors'))
|
2013-09-12 22:03:14 +02:00
|
|
|
->setValue($author_handles))
|
2013-09-10 20:07:34 +02:00
|
|
|
->appendChild($status_control)
|
2013-09-10 20:54:17 +02:00
|
|
|
->appendChild($priority_control)
|
2013-09-12 23:48:52 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
|
|
|
->setName('group')
|
|
|
|
->setLabel(pht('Group By'))
|
|
|
|
->setValue($saved->getParameter('group'))
|
|
|
|
->setOptions($this->getGroupOptions()))
|
2013-09-10 20:07:34 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSelectControl())
|
|
|
|
->setName('order')
|
2013-09-12 23:48:52 +02:00
|
|
|
->setLabel(pht('Order By'))
|
2013-09-10 20:07:34 +02:00
|
|
|
->setValue($saved->getParameter('order'))
|
2013-09-10 20:54:17 +02:00
|
|
|
->setOptions($this->getOrderOptions()))
|
2013-09-12 22:03:05 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setName('fulltext')
|
|
|
|
->setLabel(pht('Contains Text'))
|
|
|
|
->setValue($saved->getParameter('fulltext')))
|
2013-09-10 20:54:17 +02:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setName('ids')
|
|
|
|
->setLabel(pht('Task IDs'))
|
|
|
|
->setValue(implode(', ', $ids)));
|
2013-09-12 22:03:39 +02:00
|
|
|
|
2013-09-17 01:03:09 +02:00
|
|
|
$this->appendCustomFieldsToForm($form, $saved);
|
|
|
|
|
2013-09-12 22:03:39 +02:00
|
|
|
$this->buildDateRange(
|
|
|
|
$form,
|
|
|
|
$saved,
|
|
|
|
'createdStart',
|
|
|
|
pht('Created After'),
|
|
|
|
'createdEnd',
|
|
|
|
pht('Created Before'));
|
Allow Maniphest queries to have configurable page sizes
Summary:
Current page size is `1000`. This is nice to have in some cases, but makes pages slower than necessary in others. Task lists are generally dominated by rendering costs.
For example, my default is "recent tasks", which just lists all tasks ordered by date created. Showing 100 tasks here instead of 1000 makes this several times faster without compromising utility.
I don't want to force the default to 100, though, since sometimes listing everything is quite useful and I think an advantage of Maniphest is that it generally deals reasonably well with large task sets.
(This `limit` property is actually read by the default implementation of `getPageSize()` in the parent class.)
Test Plan: Made queries with page sizes 1, 100, 12, 9, 3000, etc.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D6976
2013-09-13 17:44:00 +02:00
|
|
|
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setName('limit')
|
|
|
|
->setLabel(pht('Page Size'))
|
|
|
|
->setValue($saved->getParameter('limit', 100)));
|
2013-09-10 19:04:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getURI($path) {
|
|
|
|
return '/maniphest/'.$path;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getBuiltinQueryNames() {
|
|
|
|
$names = array();
|
|
|
|
|
|
|
|
if ($this->requireViewer()->isLoggedIn()) {
|
|
|
|
$names['assigned'] = pht('Assigned');
|
|
|
|
$names['authored'] = pht('Authored');
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
$names['open'] = pht('Open Tasks');
|
2013-09-10 19:04:26 +02:00
|
|
|
$names['all'] = pht('All Tasks');
|
|
|
|
|
|
|
|
return $names;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function buildSavedQueryFromBuiltin($query_key) {
|
|
|
|
|
|
|
|
$query = $this->newSavedQuery();
|
|
|
|
$query->setQueryKey($query_key);
|
|
|
|
|
2013-09-10 19:44:42 +02:00
|
|
|
$viewer_phid = $this->requireViewer()->getPHID();
|
|
|
|
|
2013-09-10 19:04:26 +02:00
|
|
|
switch ($query_key) {
|
|
|
|
case 'all':
|
|
|
|
return $query;
|
|
|
|
case 'assigned':
|
2013-09-10 20:07:34 +02:00
|
|
|
return $query
|
|
|
|
->setParameter('assignedPHIDs', array($viewer_phid))
|
|
|
|
->setParameter('statuses', array(ManiphestTaskStatus::STATUS_OPEN));
|
|
|
|
case 'open':
|
|
|
|
return $query
|
|
|
|
->setParameter('statuses', array(ManiphestTaskStatus::STATUS_OPEN));
|
2013-09-10 19:04:26 +02:00
|
|
|
case 'authored':
|
2013-09-10 20:07:34 +02:00
|
|
|
return $query
|
2013-09-13 01:58:09 +02:00
|
|
|
->setParameter('authorPHIDs', array($viewer_phid))
|
|
|
|
->setParameter('order', 'created')
|
|
|
|
->setParameter('group', 'none');
|
2013-09-10 19:04:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:07:34 +02:00
|
|
|
private function getOrderOptions() {
|
|
|
|
return array(
|
|
|
|
'priority' => pht('Priority'),
|
|
|
|
'updated' => pht('Date Updated'),
|
|
|
|
'created' => pht('Date Created'),
|
|
|
|
'title' => pht('Title'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getOrderValues() {
|
|
|
|
return array(
|
|
|
|
'priority' => ManiphestTaskQuery::ORDER_PRIORITY,
|
|
|
|
'updated' => ManiphestTaskQuery::ORDER_MODIFIED,
|
|
|
|
'created' => ManiphestTaskQuery::ORDER_CREATED,
|
|
|
|
'title' => ManiphestTaskQuery::ORDER_TITLE,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-09-12 23:48:52 +02:00
|
|
|
private function getGroupOptions() {
|
|
|
|
return array(
|
|
|
|
'priority' => pht('Priority'),
|
|
|
|
'assigned' => pht('Assigned'),
|
|
|
|
'status' => pht('Status'),
|
|
|
|
'project' => pht('Project'),
|
|
|
|
'none' => pht('None'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getGroupValues() {
|
|
|
|
return array(
|
|
|
|
'priority' => ManiphestTaskQuery::GROUP_PRIORITY,
|
|
|
|
'assigned' => ManiphestTaskQuery::GROUP_OWNER,
|
|
|
|
'status' => ManiphestTaskQuery::GROUP_STATUS,
|
|
|
|
'project' => ManiphestTaskQuery::GROUP_PROJECT,
|
|
|
|
'none' => ManiphestTaskQuery::GROUP_NONE,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-09-10 19:04:26 +02:00
|
|
|
}
|