1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-18 02:31:10 +01:00

Enhance Maniphest custom queries

Summary:
Improve the custom query interface:

  - Allow search for tasks not in projects.
  - Allow search for tasks with no projects.
  - Allow custom search to include author/owner constraints.

Test Plan: Searched for various sorts of tasks.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran, epriestley

Maniphest Tasks: T911

Differential Revision: https://secure.phabricator.com/D1722
This commit is contained in:
epriestley 2012-02-28 21:08:02 -08:00
parent 280d7cd294
commit 4117cdbdfb
5 changed files with 171 additions and 45 deletions

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,5 +22,6 @@
final class ManiphestTaskOwner extends ManiphestConstants { final class ManiphestTaskOwner extends ManiphestConstants {
const OWNER_UP_FOR_GRABS = 'PHID-!!!!-UP-FOR-GRABS'; const OWNER_UP_FOR_GRABS = 'PHID-!!!!-UP-FOR-GRABS';
const PROJECT_NO_PROJECT = 'PHID-!!!!-NO-PROJECT';
} }

View file

@ -29,6 +29,12 @@ class ManiphestTaskListController extends ManiphestController {
$this->view = idx($data, 'view'); $this->view = idx($data, 'view');
} }
private function getArrToStrList($key) {
$arr = $this->getRequest()->getArr($key);
$arr = implode(',', $arr);
return nonempty($arr, null);
}
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
@ -37,18 +43,15 @@ class ManiphestTaskListController extends ManiphestController {
if ($request->isFormPost()) { if ($request->isFormPost()) {
// Redirect to GET so URIs can be copy/pasted. // Redirect to GET so URIs can be copy/pasted.
$user_phids = $request->getArr('set_users');
$proj_phids = $request->getArr('set_projects');
$task_ids = $request->getStr('set_tasks'); $task_ids = $request->getStr('set_tasks');
$user_phids = implode(',', $user_phids);
$proj_phids = implode(',', $proj_phids);
$user_phids = nonempty($user_phids, null);
$proj_phids = nonempty($proj_phids, null);
$task_ids = nonempty($task_ids, null); $task_ids = nonempty($task_ids, null);
$uri = $request->getRequestURI() $uri = $request->getRequestURI()
->alter('users', $user_phids) ->alter('users', $this->getArrToStrList('set_users'))
->alter('projects', $proj_phids) ->alter('projects', $this->getArrToStrList('set_projects'))
->alter('xprojects', $this->getArrToStrList('set_xprojects'))
->alter('owners', $this->getArrToStrList('set_owners'))
->alter('authors', $this->getArrToStrList('set_authors'))
->alter('tasks', $task_ids); ->alter('tasks', $task_ids);
return id(new AphrontRedirectResponse())->setURI($uri); return id(new AphrontRedirectResponse())->setURI($uri);
@ -66,7 +69,8 @@ class ManiphestTaskListController extends ManiphestController {
$nav->addFilter('alltriage', 'Need Triage'); $nav->addFilter('alltriage', 'Need Triage');
$nav->addFilter('all', 'All Tasks'); $nav->addFilter('all', 'All Tasks');
$nav->addSpacer(); $nav->addSpacer();
$nav->addFilter('custom', 'Custom'); $nav->addLabel('Custom');
$nav->addFilter('custom', 'Custom Query');
$this->view = $nav->selectFilter($this->view, 'action'); $this->view = $nav->selectFilter($this->view, 'action');
@ -81,21 +85,12 @@ class ManiphestTaskListController extends ManiphestController {
list($grouping, $group_control) = $this->renderGroupLinks(); list($grouping, $group_control) = $this->renderGroupLinks();
list($order, $order_control) = $this->renderOrderLinks(); list($order, $order_control) = $this->renderOrderLinks();
$user_phids = $request->getStr('users'); $user_phids = $request->getStrList('users');
if (strlen($user_phids)) { $project_phids = $request->getStrList('projects');
$user_phids = explode(',', $user_phids); $exclude_project_phids = $request->getStrList('xprojects');
} else {
$user_phids = array($user->getPHID());
}
$project_phids = $request->getStr('projects');
if (strlen($project_phids)) {
$project_phids = explode(',', $project_phids);
} else {
$project_phids = array();
}
$task_ids = $request->getStrList('tasks'); $task_ids = $request->getStrList('tasks');
$owner_phids = $request->getStrList('owners');
$author_phids = $request->getStrList('authors');
$page = $request->getInt('page'); $page = $request->getInt('page');
$page_size = self::DEFAULT_PAGE_SIZE; $page_size = self::DEFAULT_PAGE_SIZE;
@ -104,15 +99,18 @@ class ManiphestTaskListController extends ManiphestController {
$query->setQuery('<<maniphest>>'); $query->setQuery('<<maniphest>>');
$query->setParameters( $query->setParameters(
array( array(
'view' => $this->view, 'view' => $this->view,
'userPHIDs' => $user_phids, 'userPHIDs' => $user_phids,
'projectPHIDs' => $project_phids, 'projectPHIDs' => $project_phids,
'taskIDs' => $task_ids, 'excludeProjectPHIDs' => $exclude_project_phids,
'group' => $grouping, 'ownerPHIDs' => $owner_phids,
'order' => $order, 'authorPHIDs' => $author_phids,
'offset' => $page, 'taskIDs' => $task_ids,
'limit' => $page_size, 'group' => $grouping,
'status' => $status_map, 'order' => $order,
'offset' => $page,
'limit' => $page_size,
'status' => $status_map,
)); ));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
@ -145,6 +143,28 @@ class ManiphestTaskListController extends ManiphestController {
->setLabel('Task IDs') ->setLabel('Task IDs')
->setValue(join(',', $task_ids)) ->setValue(join(',', $task_ids))
); );
$tokens = array();
foreach ($owner_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/searchowner/')
->setName('set_owners')
->setLabel('Owners')
->setValue($tokens));
$tokens = array();
foreach ($author_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setName('set_authors')
->setLabel('Authors')
->setValue($tokens));
} }
$tokens = array(); $tokens = array();
@ -153,11 +173,24 @@ class ManiphestTaskListController extends ManiphestController {
} }
$form->appendChild( $form->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/projects/') ->setDatasource('/typeahead/common/searchproject/')
->setName('set_projects') ->setName('set_projects')
->setLabel('Projects') ->setLabel('Projects')
->setValue($tokens)); ->setValue($tokens));
if ($this->view == 'custom') {
$tokens = array();
foreach ($exclude_project_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/projects/')
->setName('set_xprojects')
->setLabel('Exclude Projects')
->setValue($tokens));
}
$form $form
->appendChild($status_control) ->appendChild($status_control)
->appendChild($group_control) ->appendChild($group_control)
@ -266,11 +299,28 @@ class ManiphestTaskListController extends ManiphestController {
$user_phids = $search_query->getParameter('userPHIDs', array()); $user_phids = $search_query->getParameter('userPHIDs', array());
$project_phids = $search_query->getParameter('projectPHIDs', array()); $project_phids = $search_query->getParameter('projectPHIDs', array());
$task_ids = $search_query->getParameter('taskIDs', array()); $task_ids = $search_query->getParameter('taskIDs', array());
$xproject_phids = $search_query->getParameter(
'excludeProjectPHIDs',
array());
$owner_phids = $search_query->getParameter('ownerPHIDs', array());
$author_phids = $search_query->getParameter('authorPHIDs', array());
$query = new ManiphestTaskQuery(); $query = new ManiphestTaskQuery();
$query->withProjects($project_phids); $query->withProjects($project_phids);
$query->withTaskIDs($task_ids); $query->withTaskIDs($task_ids);
if ($xproject_phids) {
$query->withoutProjects($xproject_phids);
}
if ($owner_phids) {
$query->withOwners($owner_phids);
}
if ($author_phids) {
$query->withAuthors($author_phids);
}
$status = $search_query->getParameter('status', 'all'); $status = $search_query->getParameter('status', 'all');
if (!empty($status['open']) && !empty($status['closed'])) { if (!empty($status['open']) && !empty($status['closed'])) {
$query->withStatus(ManiphestTaskQuery::STATUS_ANY); $query->withStatus(ManiphestTaskQuery::STATUS_ANY);
@ -330,7 +380,13 @@ class ManiphestTaskListController extends ManiphestController {
$total_row_count = $query->getRowCount(); $total_row_count = $query->getRowCount();
$handle_phids = mpull($data, 'getOwnerPHID'); $handle_phids = mpull($data, 'getOwnerPHID');
$handle_phids = array_merge($handle_phids, $project_phids, $user_phids); $handle_phids = array_merge(
$handle_phids,
$project_phids,
$user_phids,
$xproject_phids,
$owner_phids,
$author_phids);
$handles = id(new PhabricatorObjectHandleData($handle_phids)) $handles = id(new PhabricatorObjectHandleData($handle_phids))
->loadHandles(); ->loadHandles();

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,8 +29,10 @@ final class ManiphestTaskQuery {
private $ownerPHIDs = array(); private $ownerPHIDs = array();
private $includeUnowned = null; private $includeUnowned = null;
private $projectPHIDs = array(); private $projectPHIDs = array();
private $xprojectPHIDs = array();
private $subscriberPHIDs = array(); private $subscriberPHIDs = array();
private $anyProject = false; private $anyProject = false;
private $includeNoProject = null;
private $status = 'status-any'; private $status = 'status-any';
const STATUS_ANY = 'status-any'; const STATUS_ANY = 'status-any';
@ -83,10 +85,22 @@ final class ManiphestTaskQuery {
} }
public function withProjects(array $projects) { public function withProjects(array $projects) {
$this->includeNoProject = false;
foreach ($projects as $k => $phid) {
if ($phid == ManiphestTaskOwner::PROJECT_NO_PROJECT) {
$this->includeNoProject = true;
unset($projects[$k]);
}
}
$this->projectPHIDs = $projects; $this->projectPHIDs = $projects;
return $this; return $this;
} }
public function withoutProjects(array $projects) {
$this->xprojectPHIDs = $projects;
return $this;
}
public function withStatus($status) { public function withStatus($status) {
$this->status = $status; $this->status = $status;
return $this; return $this;
@ -160,6 +174,7 @@ final class ManiphestTaskQuery {
$where[] = $this->buildOwnerWhereClause($conn); $where[] = $this->buildOwnerWhereClause($conn);
$where[] = $this->buildSubscriberWhereClause($conn); $where[] = $this->buildSubscriberWhereClause($conn);
$where[] = $this->buildProjectWhereClause($conn); $where[] = $this->buildProjectWhereClause($conn);
$where[] = $this->buildXProjectWhereClause($conn);
$where = array_filter($where); $where = array_filter($where);
if ($where) { if ($where) {
@ -170,6 +185,7 @@ final class ManiphestTaskQuery {
$join = array(); $join = array();
$join[] = $this->buildProjectJoinClause($conn); $join[] = $this->buildProjectJoinClause($conn);
$join[] = $this->buildXProjectJoinClause($conn);
$join[] = $this->buildSubscriberJoinClause($conn); $join[] = $this->buildSubscriberJoinClause($conn);
$join = array_filter($join); $join = array_filter($join);
@ -195,7 +211,7 @@ final class ManiphestTaskQuery {
$group = 'GROUP BY task.id'; $group = 'GROUP BY task.id';
if (!$this->anyProject) { if (!$this->anyProject) {
$count = ', COUNT(1) projectCount'; $count = ', COUNT(project.projectPHID) projectCount';
$having = qsprintf( $having = qsprintf(
$conn, $conn,
'HAVING projectCount = %d', 'HAVING projectCount = %d',
@ -320,28 +336,63 @@ final class ManiphestTaskQuery {
} }
private function buildProjectWhereClause($conn) { private function buildProjectWhereClause($conn) {
if (!$this->projectPHIDs) { if (!$this->projectPHIDs && !$this->includeNoProject) {
return null; return null;
} }
return qsprintf( $parts = array();
$conn, if ($this->projectPHIDs) {
'project.projectPHID IN (%Ls)', $parts[] = qsprintf(
$this->projectPHIDs); $conn,
'project.projectPHID in (%Ls)',
$this->projectPHIDs);
}
if ($this->includeNoProject) {
$parts[] = qsprintf(
$conn,
'project.projectPHID IS NULL');
}
return '('.implode(') OR (', $parts).')';
} }
private function buildProjectJoinClause($conn) { private function buildProjectJoinClause($conn) {
if (!$this->projectPHIDs) { if (!$this->projectPHIDs && !$this->includeNoProject) {
return null; return null;
} }
$project_dao = new ManiphestTaskProject(); $project_dao = new ManiphestTaskProject();
return qsprintf( return qsprintf(
$conn, $conn,
'JOIN %T project ON project.taskPHID = task.phid', '%Q JOIN %T project ON project.taskPHID = task.phid',
($this->includeNoProject ? 'LEFT' : ''),
$project_dao->getTableName()); $project_dao->getTableName());
} }
private function buildXProjectWhereClause($conn) {
if (!$this->xprojectPHIDs) {
return null;
}
return qsprintf(
$conn,
'xproject.projectPHID IS NULL');
}
private function buildXProjectJoinClause($conn) {
if (!$this->xprojectPHIDs) {
return null;
}
$project_dao = new ManiphestTaskProject();
return qsprintf(
$conn,
'LEFT JOIN %T xproject ON xproject.taskPHID = task.phid
AND xproject.projectPHID IN (%Ls)',
$project_dao->getTableName(),
$this->xprojectPHIDs);
}
private function buildSubscriberJoinClause($conn) { private function buildSubscriberJoinClause($conn) {
if (!$this->subscriberPHIDs) { if (!$this->subscriberPHIDs) {
return null; return null;

View file

@ -117,6 +117,11 @@ class PhabricatorObjectHandleData {
$handle->setFullName('upforgrabs (Up For Grabs)'); $handle->setFullName('upforgrabs (Up For Grabs)');
$handle->setComplete(true); $handle->setComplete(true);
break; break;
case ManiphestTaskOwner::PROJECT_NO_PROJECT:
$handle->setName('No Project');
$handle->setFullName('noproject (No Project)');
$handle->setComplete(true);
break;
default: default:
$handle->setName('Foul Magicks'); $handle->setName('Foul Magicks');
break; break;

View file

@ -1,7 +1,7 @@
<?php <?php
/* /*
* Copyright 2011 Facebook, Inc. * Copyright 2012 Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,11 +36,16 @@ class PhabricatorTypeaheadCommonDatasourceController
$need_packages = false; $need_packages = false;
$need_upforgrabs = false; $need_upforgrabs = false;
$need_arcanist_projects = false; $need_arcanist_projects = false;
$need_noproject = false;
switch ($this->type) { switch ($this->type) {
case 'searchowner': case 'searchowner':
$need_users = true; $need_users = true;
$need_upforgrabs = true; $need_upforgrabs = true;
break; break;
case 'searchproject':
$need_projs = true;
$need_noproject = true;
break;
case 'users': case 'users':
$need_users = true; $need_users = true;
break; break;
@ -77,6 +82,14 @@ class PhabricatorTypeaheadCommonDatasourceController
); );
} }
if ($need_noproject) {
$data[] = array(
'noproject (No Project)',
null,
ManiphestTaskOwner::PROJECT_NO_PROJECT,
);
}
if ($need_users) { if ($need_users) {
$columns = array( $columns = array(
'isSystemAgent', 'isSystemAgent',