2011-02-08 10:53:59 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/*
|
2012-01-03 21:57:45 -08:00
|
|
|
* Copyright 2012 Facebook, Inc.
|
2011-02-08 10:53:59 -08:00
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-07-04 13:04:22 -07:00
|
|
|
/**
|
|
|
|
|
* @group maniphest
|
|
|
|
|
*/
|
2011-02-08 10:53:59 -08:00
|
|
|
class ManiphestTaskListController extends ManiphestController {
|
|
|
|
|
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
const DEFAULT_PAGE_SIZE = 1000;
|
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
private $view;
|
|
|
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
|
$this->view = idx($data, 'view');
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 21:08:02 -08:00
|
|
|
private function getArrToStrList($key) {
|
|
|
|
|
$arr = $this->getRequest()->getArr($key);
|
|
|
|
|
$arr = implode(',', $arr);
|
|
|
|
|
return nonempty($arr, null);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
public function processRequest() {
|
|
|
|
|
|
2011-04-11 02:03:30 -07:00
|
|
|
$request = $this->getRequest();
|
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
|
|
if ($request->isFormPost()) {
|
2011-06-29 16:16:33 -07:00
|
|
|
// Redirect to GET so URIs can be copy/pasted.
|
|
|
|
|
|
2011-12-02 07:30:20 -08:00
|
|
|
$task_ids = $request->getStr('set_tasks');
|
|
|
|
|
$task_ids = nonempty($task_ids, null);
|
2011-04-11 02:03:30 -07:00
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$uri = $request->getRequestURI()
|
2012-02-28 21:08:02 -08:00
|
|
|
->alter('users', $this->getArrToStrList('set_users'))
|
|
|
|
|
->alter('projects', $this->getArrToStrList('set_projects'))
|
|
|
|
|
->alter('xprojects', $this->getArrToStrList('set_xprojects'))
|
|
|
|
|
->alter('owners', $this->getArrToStrList('set_owners'))
|
|
|
|
|
->alter('authors', $this->getArrToStrList('set_authors'))
|
2011-12-02 07:30:20 -08:00
|
|
|
->alter('tasks', $task_ids);
|
2011-06-29 16:16:33 -07:00
|
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())->setURI($uri);
|
|
|
|
|
}
|
2011-04-11 02:03:30 -07:00
|
|
|
|
2011-12-20 14:36:54 -08:00
|
|
|
$nav = new AphrontSideNavFilterView();
|
|
|
|
|
$nav->setBaseURI(new PhutilURI('/maniphest/view/'));
|
|
|
|
|
$nav->addLabel('User Tasks');
|
|
|
|
|
$nav->addFilter('action', 'Assigned');
|
|
|
|
|
$nav->addFilter('created', 'Created');
|
|
|
|
|
$nav->addFilter('subscribed', 'Subscribed');
|
|
|
|
|
$nav->addFilter('triage', 'Need Triage');
|
|
|
|
|
$nav->addSpacer();
|
|
|
|
|
$nav->addLabel('All Tasks');
|
|
|
|
|
$nav->addFilter('alltriage', 'Need Triage');
|
|
|
|
|
$nav->addFilter('all', 'All Tasks');
|
|
|
|
|
$nav->addSpacer();
|
2012-02-28 21:08:02 -08:00
|
|
|
$nav->addLabel('Custom');
|
|
|
|
|
$nav->addFilter('custom', 'Custom Query');
|
2011-12-20 14:36:54 -08:00
|
|
|
|
|
|
|
|
$this->view = $nav->selectFilter($this->view, 'action');
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2011-04-11 02:03:30 -07:00
|
|
|
$has_filter = array(
|
|
|
|
|
'action' => true,
|
|
|
|
|
'created' => true,
|
2011-07-07 10:24:49 -07:00
|
|
|
'subscribed' => true,
|
2011-04-11 02:03:30 -07:00
|
|
|
'triage' => true,
|
|
|
|
|
);
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2012-02-27 12:59:05 -08:00
|
|
|
list($status_map, $status_control) = $this->renderStatusLinks();
|
|
|
|
|
list($grouping, $group_control) = $this->renderGroupLinks();
|
|
|
|
|
list($order, $order_control) = $this->renderOrderLinks();
|
2011-02-11 10:28:37 -08:00
|
|
|
|
2012-02-28 22:00:03 -08:00
|
|
|
$user_phids = $request->getStrList(
|
|
|
|
|
'users',
|
|
|
|
|
array($user->getPHID()));
|
2012-02-28 21:08:02 -08:00
|
|
|
$project_phids = $request->getStrList('projects');
|
|
|
|
|
$exclude_project_phids = $request->getStrList('xprojects');
|
2012-01-03 21:57:45 -08:00
|
|
|
$task_ids = $request->getStrList('tasks');
|
2012-02-28 21:08:02 -08:00
|
|
|
$owner_phids = $request->getStrList('owners');
|
|
|
|
|
$author_phids = $request->getStrList('authors');
|
2011-12-02 07:30:20 -08:00
|
|
|
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
$page = $request->getInt('page');
|
|
|
|
|
$page_size = self::DEFAULT_PAGE_SIZE;
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
$query = new PhabricatorSearchQuery();
|
|
|
|
|
$query->setQuery('<<maniphest>>');
|
|
|
|
|
$query->setParameters(
|
2011-02-11 10:28:37 -08:00
|
|
|
array(
|
2012-02-28 21:08:02 -08:00
|
|
|
'view' => $this->view,
|
|
|
|
|
'userPHIDs' => $user_phids,
|
|
|
|
|
'projectPHIDs' => $project_phids,
|
|
|
|
|
'excludeProjectPHIDs' => $exclude_project_phids,
|
|
|
|
|
'ownerPHIDs' => $owner_phids,
|
|
|
|
|
'authorPHIDs' => $author_phids,
|
|
|
|
|
'taskIDs' => $task_ids,
|
|
|
|
|
'group' => $grouping,
|
|
|
|
|
'order' => $order,
|
|
|
|
|
'offset' => $page,
|
|
|
|
|
'limit' => $page_size,
|
|
|
|
|
'status' => $status_map,
|
2011-02-11 10:28:37 -08:00
|
|
|
));
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
|
|
|
|
$query->save();
|
|
|
|
|
unset($unguarded);
|
|
|
|
|
|
|
|
|
|
list($tasks, $handles, $total_count) = self::loadTasks($query);
|
|
|
|
|
|
2011-04-03 15:50:06 -07:00
|
|
|
$form = id(new AphrontFormView())
|
2011-06-29 16:16:33 -07:00
|
|
|
->setUser($user)
|
|
|
|
|
->setAction($request->getRequestURI());
|
2011-04-11 02:03:30 -07:00
|
|
|
|
|
|
|
|
if (isset($has_filter[$this->view])) {
|
2011-06-29 16:16:33 -07:00
|
|
|
$tokens = array();
|
|
|
|
|
foreach ($user_phids as $phid) {
|
|
|
|
|
$tokens[$phid] = $handles[$phid]->getFullName();
|
|
|
|
|
}
|
2011-04-11 02:03:30 -07:00
|
|
|
$form->appendChild(
|
|
|
|
|
id(new AphrontFormTokenizerControl())
|
2011-05-28 14:13:12 -07:00
|
|
|
->setDatasource('/typeahead/common/searchowner/')
|
2011-06-29 16:16:33 -07:00
|
|
|
->setName('set_users')
|
|
|
|
|
->setLabel('Users')
|
|
|
|
|
->setValue($tokens));
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-02 07:30:20 -08:00
|
|
|
if ($this->view == 'custom') {
|
|
|
|
|
$form->appendChild(
|
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
|
->setName('set_tasks')
|
|
|
|
|
->setLabel('Task IDs')
|
|
|
|
|
->setValue(join(',', $task_ids))
|
|
|
|
|
);
|
2012-02-28 21:08:02 -08:00
|
|
|
|
|
|
|
|
$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));
|
2011-12-02 07:30:20 -08:00
|
|
|
}
|
|
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$tokens = array();
|
|
|
|
|
foreach ($project_phids as $phid) {
|
|
|
|
|
$tokens[$phid] = $handles[$phid]->getFullName();
|
2011-04-11 02:03:30 -07:00
|
|
|
}
|
2011-06-29 16:16:33 -07:00
|
|
|
$form->appendChild(
|
|
|
|
|
id(new AphrontFormTokenizerControl())
|
2012-02-28 21:08:02 -08:00
|
|
|
->setDatasource('/typeahead/common/searchproject/')
|
2011-06-29 16:16:33 -07:00
|
|
|
->setName('set_projects')
|
|
|
|
|
->setLabel('Projects')
|
|
|
|
|
->setValue($tokens));
|
2011-04-11 02:03:30 -07:00
|
|
|
|
2012-02-28 21:08:02 -08:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-11 02:03:30 -07:00
|
|
|
$form
|
2012-02-27 12:59:05 -08:00
|
|
|
->appendChild($status_control)
|
|
|
|
|
->appendChild($group_control)
|
|
|
|
|
->appendChild($order_control);
|
2011-04-03 15:50:06 -07:00
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$form->appendChild(
|
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
|
->setValue('Filter Tasks'));
|
|
|
|
|
|
2011-08-01 13:51:27 -07:00
|
|
|
$create_uri = new PhutilURI('/maniphest/task/create/');
|
|
|
|
|
if ($project_phids) {
|
|
|
|
|
// If we have project filters selected, use them as defaults for task
|
|
|
|
|
// creation.
|
|
|
|
|
$create_uri->setQueryParam('projects', implode(';', $project_phids));
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-03 15:50:06 -07:00
|
|
|
$filter = new AphrontListFilterView();
|
|
|
|
|
$filter->addButton(
|
|
|
|
|
phutil_render_tag(
|
|
|
|
|
'a',
|
|
|
|
|
array(
|
2011-08-01 13:51:27 -07:00
|
|
|
'href' => (string)$create_uri,
|
2011-04-03 15:50:06 -07:00
|
|
|
'class' => 'green button',
|
|
|
|
|
),
|
|
|
|
|
'Create New Task'));
|
|
|
|
|
$filter->appendChild($form);
|
|
|
|
|
|
|
|
|
|
$nav->appendChild($filter);
|
2011-02-11 10:28:37 -08:00
|
|
|
|
|
|
|
|
$have_tasks = false;
|
|
|
|
|
foreach ($tasks as $group => $list) {
|
|
|
|
|
if (count($list)) {
|
|
|
|
|
$have_tasks = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-03 15:50:06 -07:00
|
|
|
require_celerity_resource('maniphest-task-summary-css');
|
|
|
|
|
|
2011-02-11 10:28:37 -08:00
|
|
|
if (!$have_tasks) {
|
|
|
|
|
$nav->appendChild(
|
|
|
|
|
'<h1 class="maniphest-task-group-header">'.
|
|
|
|
|
'No matching tasks.'.
|
|
|
|
|
'</h1>');
|
|
|
|
|
} else {
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
$pager = new AphrontPagerView();
|
|
|
|
|
$pager->setURI($request->getRequestURI(), 'page');
|
|
|
|
|
$pager->setPageSize($page_size);
|
|
|
|
|
$pager->setOffset($page);
|
|
|
|
|
$pager->setCount($total_count);
|
|
|
|
|
|
|
|
|
|
$cur = ($pager->getOffset() + 1);
|
|
|
|
|
$max = min($pager->getOffset() + $page_size, $total_count);
|
|
|
|
|
$tot = $total_count;
|
|
|
|
|
|
|
|
|
|
$cur = number_format($cur);
|
|
|
|
|
$max = number_format($max);
|
|
|
|
|
$tot = number_format($tot);
|
|
|
|
|
|
|
|
|
|
$nav->appendChild(
|
|
|
|
|
'<div class="maniphest-total-result-count">'.
|
|
|
|
|
"Displaying tasks {$cur} - {$max} of {$tot}.".
|
|
|
|
|
'</div>');
|
|
|
|
|
|
2012-02-24 13:00:48 -08:00
|
|
|
$selector = new AphrontNullView();
|
|
|
|
|
|
2011-02-11 10:28:37 -08:00
|
|
|
foreach ($tasks as $group => $list) {
|
|
|
|
|
$task_list = new ManiphestTaskListView();
|
2012-02-24 13:00:48 -08:00
|
|
|
$task_list->setShowBatchControls(true);
|
Use phabricator_ time functions in more places
Summary:
Replace some more date() calls with locale-aware calls.
Also, at least on my system, the DateTimeZone / DateTime stuff didn't actually
work and always rendered in UTC. Fixed that.
Test Plan:
Viewed daemon console, differential revisions, files, and maniphest timestamps
in multiple timezones.
Reviewed By: toulouse
Reviewers: toulouse, fratrik, jungejason, aran, tuomaspelkonen
CC: aran, toulouse
Differential Revision: 530
2011-06-26 09:22:52 -07:00
|
|
|
$task_list->setUser($user);
|
2011-02-11 10:28:37 -08:00
|
|
|
$task_list->setTasks($list);
|
|
|
|
|
$task_list->setHandles($handles);
|
|
|
|
|
|
2011-06-13 20:03:44 -07:00
|
|
|
$count = number_format(count($list));
|
2012-02-24 13:00:48 -08:00
|
|
|
$selector->appendChild(
|
2011-02-11 10:28:37 -08:00
|
|
|
'<h1 class="maniphest-task-group-header">'.
|
2011-06-13 20:03:44 -07:00
|
|
|
phutil_escape_html($group).' ('.$count.')'.
|
2011-02-11 10:28:37 -08:00
|
|
|
'</h1>');
|
2012-02-24 13:00:48 -08:00
|
|
|
$selector->appendChild($task_list);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
|
2012-02-24 13:00:48 -08:00
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
$selector->appendChild($this->renderBatchEditor($query));
|
2012-02-24 13:00:48 -08:00
|
|
|
|
|
|
|
|
$selector = phabricator_render_form(
|
|
|
|
|
$user,
|
|
|
|
|
array(
|
|
|
|
|
'method' => 'POST',
|
|
|
|
|
'action' => '/maniphest/batch/',
|
|
|
|
|
),
|
|
|
|
|
$selector->render());
|
|
|
|
|
|
|
|
|
|
$nav->appendChild($selector);
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
$nav->appendChild($pager);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
2011-02-08 10:53:59 -08:00
|
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
|
|
|
|
$nav,
|
|
|
|
|
array(
|
|
|
|
|
'title' => 'Task List',
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
public static function loadTasks(PhabricatorSearchQuery $search_query) {
|
|
|
|
|
|
|
|
|
|
$user_phids = $search_query->getParameter('userPHIDs', array());
|
|
|
|
|
$project_phids = $search_query->getParameter('projectPHIDs', array());
|
|
|
|
|
$task_ids = $search_query->getParameter('taskIDs', array());
|
2012-02-28 21:08:02 -08:00
|
|
|
$xproject_phids = $search_query->getParameter(
|
|
|
|
|
'excludeProjectPHIDs',
|
|
|
|
|
array());
|
|
|
|
|
$owner_phids = $search_query->getParameter('ownerPHIDs', array());
|
|
|
|
|
$author_phids = $search_query->getParameter('authorPHIDs', array());
|
2011-02-08 10:53:59 -08:00
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$query = new ManiphestTaskQuery();
|
|
|
|
|
$query->withProjects($project_phids);
|
2011-12-02 07:30:20 -08:00
|
|
|
$query->withTaskIDs($task_ids);
|
2011-02-11 10:28:37 -08:00
|
|
|
|
2012-02-28 21:08:02 -08:00
|
|
|
if ($xproject_phids) {
|
|
|
|
|
$query->withoutProjects($xproject_phids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($owner_phids) {
|
|
|
|
|
$query->withOwners($owner_phids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($author_phids) {
|
|
|
|
|
$query->withAuthors($author_phids);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
$status = $search_query->getParameter('status', 'all');
|
2011-02-11 10:28:37 -08:00
|
|
|
if (!empty($status['open']) && !empty($status['closed'])) {
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withStatus(ManiphestTaskQuery::STATUS_ANY);
|
2011-02-11 10:28:37 -08:00
|
|
|
} else if (!empty($status['open'])) {
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
|
2011-02-11 10:28:37 -08:00
|
|
|
} else {
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withStatus(ManiphestTaskQuery::STATUS_CLOSED);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
switch ($search_query->getParameter('view')) {
|
2011-02-08 10:53:59 -08:00
|
|
|
case 'action':
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withOwners($user_phids);
|
2011-02-11 10:28:37 -08:00
|
|
|
break;
|
2011-02-08 10:53:59 -08:00
|
|
|
case 'created':
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withAuthors($user_phids);
|
2011-02-11 10:28:37 -08:00
|
|
|
break;
|
2011-07-07 10:24:49 -07:00
|
|
|
case 'subscribed':
|
|
|
|
|
$query->withSubscribers($user_phids);
|
|
|
|
|
break;
|
2011-02-08 10:53:59 -08:00
|
|
|
case 'triage':
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withOwners($user_phids);
|
|
|
|
|
$query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
|
2011-02-11 10:28:37 -08:00
|
|
|
break;
|
2011-02-09 16:29:46 -08:00
|
|
|
case 'alltriage':
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
|
2011-02-11 10:28:37 -08:00
|
|
|
break;
|
|
|
|
|
case 'all':
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$order_map = array(
|
|
|
|
|
'priority' => ManiphestTaskQuery::ORDER_PRIORITY,
|
|
|
|
|
'created' => ManiphestTaskQuery::ORDER_CREATED,
|
|
|
|
|
);
|
|
|
|
|
$query->setOrderBy(
|
|
|
|
|
idx(
|
|
|
|
|
$order_map,
|
2012-02-28 21:07:12 -08:00
|
|
|
$search_query->getParameter('order'),
|
2011-06-29 16:16:33 -07:00
|
|
|
ManiphestTaskQuery::ORDER_MODIFIED));
|
|
|
|
|
|
|
|
|
|
$group_map = array(
|
|
|
|
|
'priority' => ManiphestTaskQuery::GROUP_PRIORITY,
|
|
|
|
|
'owner' => ManiphestTaskQuery::GROUP_OWNER,
|
|
|
|
|
'status' => ManiphestTaskQuery::GROUP_STATUS,
|
|
|
|
|
);
|
|
|
|
|
$query->setGroupBy(
|
|
|
|
|
idx(
|
|
|
|
|
$group_map,
|
2012-02-28 21:07:12 -08:00
|
|
|
$search_query->getParameter('group'),
|
2011-06-29 16:16:33 -07:00
|
|
|
ManiphestTaskQuery::GROUP_NONE));
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$query->setCalculateRows(true);
|
2012-02-28 21:07:12 -08:00
|
|
|
$query->setLimit($search_query->getParameter('limit'));
|
|
|
|
|
$query->setOffset($search_query->getParameter('offset'));
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
|
2011-06-29 16:16:33 -07:00
|
|
|
$data = $query->execute();
|
|
|
|
|
$total_row_count = $query->getRowCount();
|
2011-02-11 10:28:37 -08:00
|
|
|
|
|
|
|
|
$handle_phids = mpull($data, 'getOwnerPHID');
|
2012-02-28 21:08:02 -08:00
|
|
|
$handle_phids = array_merge(
|
|
|
|
|
$handle_phids,
|
|
|
|
|
$project_phids,
|
|
|
|
|
$user_phids,
|
|
|
|
|
$xproject_phids,
|
|
|
|
|
$owner_phids,
|
|
|
|
|
$author_phids);
|
2011-02-11 10:28:37 -08:00
|
|
|
$handles = id(new PhabricatorObjectHandleData($handle_phids))
|
|
|
|
|
->loadHandles();
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
switch ($search_query->getParameter('group')) {
|
2011-02-11 10:28:37 -08:00
|
|
|
case 'priority':
|
|
|
|
|
$data = mgroup($data, 'getPriority');
|
|
|
|
|
krsort($data);
|
|
|
|
|
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
// If we have invalid priorities, they'll all map to "???". Merge
|
|
|
|
|
// arrays to prevent them from overwriting each other.
|
|
|
|
|
|
2011-02-11 10:28:37 -08:00
|
|
|
$out = array();
|
|
|
|
|
foreach ($data as $pri => $tasks) {
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
$out[ManiphestTaskPriority::getTaskPriorityName($pri)][] = $tasks;
|
|
|
|
|
}
|
|
|
|
|
foreach ($out as $pri => $tasks) {
|
|
|
|
|
$out[$pri] = array_mergev($tasks);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
$data = $out;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case 'status':
|
|
|
|
|
$data = mgroup($data, 'getStatus');
|
|
|
|
|
ksort($data);
|
|
|
|
|
|
|
|
|
|
$out = array();
|
|
|
|
|
foreach ($data as $status => $tasks) {
|
|
|
|
|
$out[ManiphestTaskStatus::getTaskStatusFullName($status)] = $tasks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $out;
|
|
|
|
|
break;
|
|
|
|
|
case 'owner':
|
|
|
|
|
$data = mgroup($data, 'getOwnerPHID');
|
|
|
|
|
|
|
|
|
|
$out = array();
|
|
|
|
|
foreach ($data as $phid => $tasks) {
|
|
|
|
|
if ($phid) {
|
|
|
|
|
$out[$handles[$phid]->getFullName()] = $tasks;
|
|
|
|
|
} else {
|
|
|
|
|
$out['Unassigned'] = $tasks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (isset($out['Unassigned'])) {
|
|
|
|
|
// If any tasks are unassigned, move them to the front of the list.
|
|
|
|
|
$data = array('Unassigned' => $out['Unassigned']) + $out;
|
|
|
|
|
} else {
|
|
|
|
|
$data = $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ksort($data);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
$data = array(
|
|
|
|
|
'Tasks' => $data,
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
Allow Maniphest to scale to a massive size
Summary:
Maniphest is missing some keys and some query strategy which will make it
cumbersome to manage more than a few tens of thousands of tasks.
Test Plan:
Handily manipulated 100k-scale task groups. Maniphest takes about 250ms to
select and render pages of 1,000 tasks and has no problem paging and filtering
them, etc. We should be good to scale to multiple millions of tasks with these
changes.
Reviewed By: gc3
Reviewers: fratrik, jungejason, aran, tuomaspelkonen, gc3
Commenters: jungejason
CC: anjali, aran, epriestley, gc3, jungejason
Differential Revision: 534
2011-06-26 18:50:17 -07:00
|
|
|
return array($data, $handles, $total_row_count);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function renderStatusLinks() {
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
|
|
Simplify Maniphest button filter toggle things
Summary:
Although these filters work pretty well, you still end up doing a double take
sometimes. Make the behavior simpler and more consistent by adding an "All"
button to "Open / Closed" so all three rows behave the same way (before, the top
row was toggleable but the other rows were select-only-one).
I played around with the styles a little bit too to try to make the selected
state more obvious.
sandra/anjali, let me know if this is good enough once it lands or if I should
go further in playing around with the styles and making it more clear.
Test Plan:
Filtered tasks with the various filter buttons, verified the task list
accurately represented the filters.
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: anjali, sandra, aran, epriestley, tuomaspelkonen
Differential Revision: 364
2011-05-28 13:28:56 -07:00
|
|
|
$statuses = array(
|
|
|
|
|
'o' => array('open' => true),
|
|
|
|
|
'c' => array('closed' => true),
|
|
|
|
|
'oc' => array('open' => true, 'closed' => true),
|
2011-02-11 10:28:37 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$status = $request->getStr('s');
|
Simplify Maniphest button filter toggle things
Summary:
Although these filters work pretty well, you still end up doing a double take
sometimes. Make the behavior simpler and more consistent by adding an "All"
button to "Open / Closed" so all three rows behave the same way (before, the top
row was toggleable but the other rows were select-only-one).
I played around with the styles a little bit too to try to make the selected
state more obvious.
sandra/anjali, let me know if this is good enough once it lands or if I should
go further in playing around with the styles and making it more clear.
Test Plan:
Filtered tasks with the various filter buttons, verified the task list
accurately represented the filters.
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: anjali, sandra, aran, epriestley, tuomaspelkonen
Differential Revision: 364
2011-05-28 13:28:56 -07:00
|
|
|
if (empty($statuses[$status])) {
|
|
|
|
|
$status = 'o';
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
|
2012-02-27 12:59:05 -08:00
|
|
|
$status_control = id(new AphrontFormToggleButtonsControl())
|
|
|
|
|
->setLabel('Status')
|
|
|
|
|
->setValue($status)
|
|
|
|
|
->setBaseURI($request->getRequestURI(), 's')
|
|
|
|
|
->setButtons(
|
|
|
|
|
array(
|
|
|
|
|
'o' => 'Open',
|
|
|
|
|
'c' => 'Closed',
|
|
|
|
|
'oc' => 'All',
|
|
|
|
|
));
|
2011-02-11 10:28:37 -08:00
|
|
|
|
2012-02-27 12:59:05 -08:00
|
|
|
return array($statuses[$status], $status_control);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function renderOrderLinks() {
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
|
|
|
|
|
|
$order = $request->getStr('o');
|
|
|
|
|
$orders = array(
|
|
|
|
|
'u' => 'updated',
|
|
|
|
|
'c' => 'created',
|
|
|
|
|
'p' => 'priority',
|
|
|
|
|
);
|
|
|
|
|
if (empty($orders[$order])) {
|
2011-02-18 21:57:34 -08:00
|
|
|
$order = 'p';
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
$order_by = $orders[$order];
|
|
|
|
|
|
2012-02-27 12:59:05 -08:00
|
|
|
$order_control = id(new AphrontFormToggleButtonsControl())
|
|
|
|
|
->setLabel('Order')
|
|
|
|
|
->setValue($order)
|
|
|
|
|
->setBaseURI($request->getRequestURI(), 'o')
|
|
|
|
|
->setButtons(
|
|
|
|
|
array(
|
|
|
|
|
'p' => 'Priority',
|
|
|
|
|
'u' => 'Updated',
|
|
|
|
|
'c' => 'Created',
|
|
|
|
|
));
|
2011-02-11 10:28:37 -08:00
|
|
|
|
2012-02-27 12:59:05 -08:00
|
|
|
return array($order_by, $order_control);
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function renderGroupLinks() {
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
|
|
|
|
|
|
$group = $request->getStr('g');
|
|
|
|
|
$groups = array(
|
|
|
|
|
'n' => 'none',
|
|
|
|
|
'p' => 'priority',
|
|
|
|
|
's' => 'status',
|
|
|
|
|
'o' => 'owner',
|
|
|
|
|
);
|
|
|
|
|
if (empty($groups[$group])) {
|
2011-02-18 21:57:34 -08:00
|
|
|
$group = 'p';
|
2011-02-11 10:28:37 -08:00
|
|
|
}
|
|
|
|
|
$group_by = $groups[$group];
|
|
|
|
|
|
Simplify Maniphest button filter toggle things
Summary:
Although these filters work pretty well, you still end up doing a double take
sometimes. Make the behavior simpler and more consistent by adding an "All"
button to "Open / Closed" so all three rows behave the same way (before, the top
row was toggleable but the other rows were select-only-one).
I played around with the styles a little bit too to try to make the selected
state more obvious.
sandra/anjali, let me know if this is good enough once it lands or if I should
go further in playing around with the styles and making it more clear.
Test Plan:
Filtered tasks with the various filter buttons, verified the task list
accurately represented the filters.
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: anjali, sandra, aran, epriestley, tuomaspelkonen
Differential Revision: 364
2011-05-28 13:28:56 -07:00
|
|
|
|
2012-02-27 12:59:05 -08:00
|
|
|
$group_control = id(new AphrontFormToggleButtonsControl())
|
|
|
|
|
->setLabel('Group')
|
|
|
|
|
->setValue($group)
|
|
|
|
|
->setBaseURI($request->getRequestURI(), 'g')
|
|
|
|
|
->setButtons(
|
2011-02-11 10:28:37 -08:00
|
|
|
array(
|
2012-02-27 12:59:05 -08:00
|
|
|
'p' => 'Priority',
|
|
|
|
|
'o' => 'Owner',
|
|
|
|
|
's' => 'Status',
|
|
|
|
|
'n' => 'None',
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
return array($group_by, $group_control);
|
2011-02-08 10:53:59 -08:00
|
|
|
}
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
private function renderBatchEditor(PhabricatorSearchQuery $search_query) {
|
2012-02-24 13:00:48 -08:00
|
|
|
Javelin::initBehavior(
|
|
|
|
|
'maniphest-batch-selector',
|
|
|
|
|
array(
|
|
|
|
|
'selectAll' => 'batch-select-all',
|
|
|
|
|
'selectNone' => 'batch-select-none',
|
|
|
|
|
'submit' => 'batch-select-submit',
|
|
|
|
|
'status' => 'batch-select-status-cell',
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$select_all = javelin_render_tag(
|
|
|
|
|
'a',
|
|
|
|
|
array(
|
|
|
|
|
'href' => '#',
|
|
|
|
|
'mustcapture' => true,
|
|
|
|
|
'class' => 'grey button',
|
|
|
|
|
'id' => 'batch-select-all',
|
|
|
|
|
),
|
|
|
|
|
'Select All');
|
|
|
|
|
|
|
|
|
|
$select_none = javelin_render_tag(
|
|
|
|
|
'a',
|
|
|
|
|
array(
|
|
|
|
|
'href' => '#',
|
|
|
|
|
'mustcapture' => true,
|
|
|
|
|
'class' => 'grey button',
|
|
|
|
|
'id' => 'batch-select-none',
|
|
|
|
|
),
|
|
|
|
|
'Clear Selection');
|
|
|
|
|
|
|
|
|
|
$submit = phutil_render_tag(
|
|
|
|
|
'button',
|
|
|
|
|
array(
|
|
|
|
|
'id' => 'batch-select-submit',
|
|
|
|
|
'disabled' => 'disabled',
|
|
|
|
|
'class' => 'disabled',
|
|
|
|
|
),
|
|
|
|
|
'Batch Edit Selected Tasks »');
|
|
|
|
|
|
2012-02-28 21:07:12 -08:00
|
|
|
$export = javelin_render_tag(
|
|
|
|
|
'a',
|
|
|
|
|
array(
|
|
|
|
|
'href' => '/maniphest/export/'.$search_query->getQueryKey().'/',
|
|
|
|
|
'class' => 'grey button',
|
|
|
|
|
),
|
|
|
|
|
'Export Tasks to Excel...');
|
|
|
|
|
|
2012-02-24 13:00:48 -08:00
|
|
|
return
|
|
|
|
|
'<div class="maniphest-batch-editor">'.
|
|
|
|
|
'<div class="batch-editor-header">Batch Task Editor</div>'.
|
|
|
|
|
'<table class="maniphest-batch-editor-layout">'.
|
|
|
|
|
'<tr>'.
|
|
|
|
|
'<td>'.
|
|
|
|
|
$select_all.
|
|
|
|
|
$select_none.
|
|
|
|
|
'</td>'.
|
2012-02-28 21:07:12 -08:00
|
|
|
'<td>'.
|
|
|
|
|
$export.
|
|
|
|
|
'</td>'.
|
2012-02-24 13:00:48 -08:00
|
|
|
'<td id="batch-select-status-cell">'.
|
|
|
|
|
'0 Selected Tasks'.
|
|
|
|
|
'</td>'.
|
|
|
|
|
'<td class="batch-select-submit-cell">'.$submit.'</td>'.
|
|
|
|
|
'</tr>'.
|
|
|
|
|
'</table>'.
|
|
|
|
|
'</table>';
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
}
|