1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-22 21:40:55 +01:00

Improve filtering and sorting options for Maniphest.

Summary:

Test Plan:

Reviewers:

CC:
This commit is contained in:
epriestley 2011-02-11 10:28:37 -08:00
parent 33a9019a89
commit 0d1da3df15
7 changed files with 444 additions and 68 deletions

View file

@ -63,7 +63,7 @@ celerity_register_resource_map(array(
), ),
'aphront-side-nav-view-css' => 'aphront-side-nav-view-css' =>
array( array(
'uri' => '/res/0fc0545c/rsrc/css/aphront/side-nav-view.css', 'uri' => '/res/09b7eb85/rsrc/css/aphront/side-nav-view.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -199,7 +199,7 @@ celerity_register_resource_map(array(
), ),
'maniphest-task-summary-css' => 'maniphest-task-summary-css' =>
array( array(
'uri' => '/res/bed1edf0/rsrc/css/application/maniphest/task-summary.css', 'uri' => '/res/19a3c543/rsrc/css/application/maniphest/task-summary.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -208,7 +208,7 @@ celerity_register_resource_map(array(
), ),
'maniphest-transaction-detail-css' => 'maniphest-transaction-detail-css' =>
array( array(
'uri' => '/res/443acf25/rsrc/css/application/maniphest/transaction-detail.css', 'uri' => '/res/658912c5/rsrc/css/application/maniphest/transaction-detail.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -217,7 +217,7 @@ celerity_register_resource_map(array(
), ),
'phabricator-core-buttons-css' => 'phabricator-core-buttons-css' =>
array( array(
'uri' => '/res/fe74ba44/rsrc/css/core/buttons.css', 'uri' => '/res/ee35ffe1/rsrc/css/core/buttons.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -444,7 +444,7 @@ celerity_register_resource_map(array(
), array ( ), array (
'packages' => 'packages' =>
array ( array (
'8cbb3650' => '4f907a28' =>
array ( array (
'name' => 'core.pkg.css', 'name' => 'core.pkg.css',
'symbols' => 'symbols' =>
@ -463,7 +463,7 @@ celerity_register_resource_map(array(
11 => 'phabricator-remarkup-css', 11 => 'phabricator-remarkup-css',
12 => 'syntax-highlighting-css', 12 => 'syntax-highlighting-css',
), ),
'uri' => '/res/pkg/8cbb3650/core.pkg.css', 'uri' => '/res/pkg/4f907a28/core.pkg.css',
'type' => 'css', 'type' => 'css',
), ),
'4b8af7b5' => '4b8af7b5' =>
@ -500,19 +500,19 @@ celerity_register_resource_map(array(
), ),
'reverse' => 'reverse' =>
array ( array (
'phabricator-core-css' => '8cbb3650', 'phabricator-core-css' => '4f907a28',
'phabricator-core-buttons-css' => '8cbb3650', 'phabricator-core-buttons-css' => '4f907a28',
'phabricator-standard-page-view' => '8cbb3650', 'phabricator-standard-page-view' => '4f907a28',
'aphront-dialog-view-css' => '8cbb3650', 'aphront-dialog-view-css' => '4f907a28',
'aphront-form-view-css' => '8cbb3650', 'aphront-form-view-css' => '4f907a28',
'aphront-panel-view-css' => '8cbb3650', 'aphront-panel-view-css' => '4f907a28',
'aphront-side-nav-view-css' => '8cbb3650', 'aphront-side-nav-view-css' => '4f907a28',
'aphront-table-view-css' => '8cbb3650', 'aphront-table-view-css' => '4f907a28',
'aphront-tokenizer-control-css' => '8cbb3650', 'aphront-tokenizer-control-css' => '4f907a28',
'aphront-typeahead-control-css' => '8cbb3650', 'aphront-typeahead-control-css' => '4f907a28',
'phabricator-directory-css' => '8cbb3650', 'phabricator-directory-css' => '4f907a28',
'phabricator-remarkup-css' => '8cbb3650', 'phabricator-remarkup-css' => '4f907a28',
'syntax-highlighting-css' => '8cbb3650', 'syntax-highlighting-css' => '4f907a28',
'differential-core-view-css' => '4b8af7b5', 'differential-core-view-css' => '4b8af7b5',
'differential-changeset-view-css' => '4b8af7b5', 'differential-changeset-view-css' => '4b8af7b5',
'differential-revision-detail-css' => '4b8af7b5', 'differential-revision-detail-css' => '4b8af7b5',

View file

@ -28,23 +28,23 @@ class ManiphestTaskListController extends ManiphestController {
$views = array( $views = array(
'Your Tasks', 'Your Tasks',
'action' => 'Action Required', 'action' => 'Assigned',
// 'activity' => 'Recently Active',
// 'closed' => 'Recently Closed',
'created' => 'Created', 'created' => 'Created',
'triage' => 'Need Triage', 'triage' => 'Need Triage',
// 'touched' => 'Touched',
'<hr />', '<hr />',
'All Open Tasks', 'All Tasks',
'alltriage' => 'Need Triage', 'alltriage' => 'Need Triage',
'unassigned' => 'Unassigned', 'unassigned' => 'Unassigned',
'allopen' => 'All Open', 'all' => 'All Tasks',
); );
if (empty($views[$this->view])) { if (empty($views[$this->view])) {
$this->view = 'action'; $this->view = 'action';
} }
$tasks = $this->loadTasks(); $request = $this->getRequest();
$uri = $request->getRequestURI();
$nav = new AphrontSideNavView(); $nav = new AphrontSideNavView();
foreach ($views as $view => $name) { foreach ($views as $view => $name) {
@ -55,11 +55,12 @@ class ManiphestTaskListController extends ManiphestController {
array(), array(),
$name)); $name));
} else { } else {
$uri->setPath('/maniphest/view/'.$view.'/');
$nav->addNavItem( $nav->addNavItem(
phutil_render_tag( phutil_render_tag(
'a', 'a',
array( array(
'href' => '/maniphest/view/'.$view.'/', 'href' => $uri,
'class' => ($this->view == $view) 'class' => ($this->view == $view)
? 'aphront-side-nav-selected' ? 'aphront-side-nav-selected'
: null, : null,
@ -68,21 +69,62 @@ class ManiphestTaskListController extends ManiphestController {
} }
} }
$handle_phids = mpull($tasks, 'getOwnerPHID'); list($status_map, $status_links) = $this->renderStatusLinks();
$handles = id(new PhabricatorObjectHandleData($handle_phids)) list($grouping, $group_links) = $this->renderGroupLinks();
->loadHandles(); list($order, $order_links) = $this->renderOrderLinks();
$task_list = new ManiphestTaskListView(); list($tasks, $handles) = $this->loadTasks(
$task_list->setTasks($tasks); array(
$task_list->setHandles($handles); 'status' => $status_map,
'group' => $grouping,
'order' => $order,
));
require_celerity_resource('maniphest-task-summary-css');
$nav->appendChild( $nav->appendChild(
'<div style="text-align: right; padding: 1em 1em 0;">'. '<div class="maniphest-basic-search-view">'.
'<a href="/maniphest/task/create/" class="green button">'. '<div class="maniphest-basic-search-actions">'.
'Create New Task'. '<a href="/maniphest/task/create/" class="green button">'.
'</a>'. 'Create New Task'.
'</a>'.
'</div>'.
'<div class="maniphest-basic-search-options">'.
'<table class="maniphest-basic-search-options-table">'.
'<tr><th>Status:</th><td>'.$status_links.'</td></tr>'.
'<tr><th>Group:</th><td>'.$group_links.'</td></tr>'.
'<tr><th>Order:</th><td>'.$order_links.'</td></tr>'.
'</table>'.
'</div>'.
'<div style="clear: both;"></div>'.
'</div>'); '</div>');
$nav->appendChild($task_list);
$have_tasks = false;
foreach ($tasks as $group => $list) {
if (count($list)) {
$have_tasks = true;
break;
}
}
if (!$have_tasks) {
$nav->appendChild(
'<h1 class="maniphest-task-group-header">'.
'No matching tasks.'.
'</h1>');
} else {
foreach ($tasks as $group => $list) {
$task_list = new ManiphestTaskListView();
$task_list->setTasks($list);
$task_list->setHandles($handles);
$nav->appendChild(
'<h1 class="maniphest-task-group-header">'.
phutil_escape_html($group).
'</h1>');
$nav->appendChild($task_list);
}
}
return $this->buildStandardPageResponse( return $this->buildStandardPageResponse(
$nav, $nav,
@ -91,39 +133,288 @@ class ManiphestTaskListController extends ManiphestController {
)); ));
} }
private function loadTasks() { private function loadTasks(array $dict) {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$phids = array($user->getPHID()); $phids = array($user->getPHID());
switch ($this->view) { $task = new ManiphestTask();
case 'action':
return id(new ManiphestTask())->loadAllWhere( $argv = array();
'ownerPHID in (%Ls) AND status = 0',
$phids); $status = $dict['status'];
case 'created': if (!empty($status['open']) && !empty($status['closed'])) {
return id(new ManiphestTask())->loadAllWhere( $status_clause = '1 = 1';
'authorPHID in (%Ls) AND status = 0', } else if (!empty($status['open'])) {
$phids); $status_clause = 'status = %d';
case 'triage': $argv[] = 0;
return id(new ManiphestTask())->loadAllWhere( } else {
'ownerPHID in (%Ls) and status = %d', $status_clause = 'status > %d';
$phids, $argv[] = 0;
ManiphestTaskPriority::PRIORITY_TRIAGE);
case 'alltriage':
return id(new ManiphestTask())->loadAllWhere(
'status = %d',
ManiphestTaskPriority::PRIORITY_TRIAGE);
case 'unassigned':
return id(new ManiphestTask())->loadAllWhere(
'ownerPHID IS NULL');
case 'allopen':
return id(new ManiphestTask())->loadAllWhere(
'status = 0');
} }
return array(); $extra_clause = '1 = 1';
switch ($this->view) {
case 'action':
$extra_clause = 'ownerPHID in (%Ls)';
$argv[] = $phids;
break;
case 'created':
$extra_clause = 'authorPHID in (%Ls)';
$argv[] = $phids;
break;
case 'triage':
$extra_clause = 'ownerPHID in (%Ls) AND status = %d';
$argv[] = $phids;
$argv[] = ManiphestTaskPriority::PRIORITY_TRIAGE;
break;
case 'alltriage':
$extra_clause = 'status = %d';
$argv[] = ManiphestTaskPriority::PRIORITY_TRIAGE;
break;
case 'unassigned':
$extra_clause = 'ownerPHID is NULL';
break;
case 'all':
break;
}
switch ($dict['order']) {
case 'priority':
$order = 'priority DESC, dateModified DESC';
break;
case 'created':
$order = 'id DESC';
break;
default:
$order = 'dateModified DESC';
break;
}
$sql = "({$status_clause}) AND ({$extra_clause}) ORDER BY {$order}";
array_unshift($argv, $sql);
$data = call_user_func_array(array($task, 'loadAllWhere'), $argv);
$handle_phids = mpull($data, 'getOwnerPHID');
$handles = id(new PhabricatorObjectHandleData($handle_phids))
->loadHandles();
switch ($dict['group']) {
case 'priority':
$data = mgroup($data, 'getPriority');
krsort($data);
$out = array();
foreach ($data as $pri => $tasks) {
$out[ManiphestTaskPriority::getTaskPriorityName($pri)] = $tasks;
}
$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;
}
return array($data, $handles);
}
public function renderStatusLinks() {
$request = $this->getRequest();
$sel = array(
'open' => false,
'closed' => false,
);
$status = $request->getStr('s');
if ($status == 'c') {
$sel['closed'] = true;
} else if ($status == 'oc') {
$sel['closed'] = true;
$sel['open'] = true;
} else {
$sel['open'] = true;
}
$just_one = (count(array_filter($sel)) == 1);
$flag_map = array(
'Open' => 'open',
'Closed' => 'closed',
);
$button_names = array(
'Open' => 'o',
'Closed' => 'c',
);
$base_flags = array();
foreach ($flag_map as $name => $key) {
$base_flags[$button_names[$name]] = $sel[$key];
}
foreach ($button_names as $name => $letter) {
$flags = $base_flags;
$flags[$letter] = !$flags[$letter];
$button_names[$name] = implode('', array_keys(array_filter($flags)));
}
$uri = $request->getRequestURI();
$links = array();
foreach ($button_names as $name => $value) {
$selected = $sel[$flag_map[$name]];
$fixed = ($selected && $just_one);
$more = null;
if ($fixed) {
$href = null;
$more .= ' toggle-fixed';
} else {
$href = $uri->alter('s', $value);
}
if ($selected) {
$more .= ' toggle-selected';
}
$links[] = phutil_render_tag(
'a',
array(
'href' => $href,
'class' => 'toggle'.$more,
),
$name);
}
$status_links = implode("\n", $links);
return array($sel, $status_links);
}
public function renderOrderLinks() {
$request = $this->getRequest();
$order = $request->getStr('o');
$orders = array(
'u' => 'updated',
'c' => 'created',
'p' => 'priority',
);
if (empty($orders[$order])) {
$order = 'u';
}
$order_by = $orders[$order];
$order_names = array(
'Updated' => 'u',
'Created' => 'c',
'Priority' => 'p',
);
$uri = $request->getRequestURI();
$links = array();
foreach ($order_names as $name => $param_key) {
if ($param_key == $order) {
$more = ' toggle-selected toggle-fixed';
$href = null;
} else {
$more = null;
$href = $uri->alter('o', $param_key);
}
$links[] = phutil_render_tag(
'a',
array(
'class' => 'toggle'.$more,
'href' => $href,
),
$name);
}
$order_links = implode("\n", $links);
return array($order_by, $order_links);
}
public function renderGroupLinks() {
$request = $this->getRequest();
$group = $request->getStr('g');
$groups = array(
'n' => 'none',
'p' => 'priority',
's' => 'status',
'o' => 'owner',
);
if (empty($groups[$group])) {
$group = 'n';
}
$group_by = $groups[$group];
$group_names = array(
'None' => 'n',
'Priority' => 'p',
'Owner' => 'o',
'Status' => 's',
);
$uri = $request->getRequestURI();
$links = array();
foreach ($group_names as $name => $param_key) {
if ($param_key == $group) {
$more = ' toggle-selected toggle-fixed';
$href = null;
} else {
$more = null;
$href = $uri->alter('g', $param_key);
}
$links[] = phutil_render_tag(
'a',
array(
'class' => 'toggle'.$more,
'href' => $href,
),
$name);
}
$group_links = implode("\n", $links);
return array($group_by, $group_links);
} }

View file

@ -7,10 +7,12 @@
phutil_require_module('phabricator', 'applications/maniphest/constants/priority'); phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/controller/base'); phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/maniphest/storage/task'); phutil_require_module('phabricator', 'applications/maniphest/storage/task');
phutil_require_module('phabricator', 'applications/maniphest/view/tasklist'); phutil_require_module('phabricator', 'applications/maniphest/view/tasklist');
phutil_require_module('phabricator', 'applications/phid/handle/data'); phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'view/layout/sidenav'); phutil_require_module('phabricator', 'view/layout/sidenav');
phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'markup');

View file

@ -42,8 +42,15 @@ class ManiphestTaskSummaryView extends AphrontView {
'<td class="maniphest-task-number">'. '<td class="maniphest-task-number">'.
'T'.$task->getID(). 'T'.$task->getID().
'</td>'. '</td>'.
'<td class="maniphest-task-status">'.
($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN
? 'Open'
: 'Closed').
'</td>'.
'<td class="maniphest-task-owner">'. '<td class="maniphest-task-owner">'.
$handles[$task->getOwnerPHID()]->renderLink(). ($task->getOwnerPHID()
? $handles[$task->getOwnerPHID()]->renderLink()
: '<em>None</em>').
'</td>'. '</td>'.
'<td class="maniphest-task-name">'. '<td class="maniphest-task-name">'.
phutil_render_tag( phutil_render_tag(

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/maniphest/constants/priority'); phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'infrastructure/celerity/api'); phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'view/base'); phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/utils'); phutil_require_module('phabricator', 'view/utils');

View file

@ -11,7 +11,7 @@
} }
.maniphest-task-summary td { .maniphest-task-summary td {
padding: 4px 1%; padding: 4px 0.5%;
background: #dfdfdf; background: #dfdfdf;
white-space: nowrap; white-space: nowrap;
} }
@ -19,7 +19,14 @@
.maniphest-task-summary td.maniphest-task-number { .maniphest-task-summary td.maniphest-task-number {
font-weight: bold; font-weight: bold;
color: #444444; color: #444444;
width: 8%; width: 1%;
min-width: 80px;
}
.maniphest-task-summary td.maniphest-task-status {
width: 6%;
text-align: center;
min-width: 75px;
} }
.maniphest-task-summary td.maniphest-task-owner { .maniphest-task-summary td.maniphest-task-owner {
@ -29,15 +36,52 @@
.maniphest-task-summary td.maniphest-task-name { .maniphest-task-summary td.maniphest-task-name {
overflow: hidden; overflow: hidden;
font-weight: bold; font-weight: bold;
width: 49%;
} }
.maniphest-task-summary td.maniphest-task-priority { .maniphest-task-summary td.maniphest-task-priority {
text-align: center; text-align: center;
width: 11%; width: 11%;
min-width: 120px;
} }
.maniphest-task-summary td.maniphest-task-updated { .maniphest-task-summary td.maniphest-task-updated {
text-align: center; text-align: left;
width: 11%; width: 11%;
min-width: 120px;
}
.maniphest-basic-search-view {
background: #888888;
border-bottom: 1px solid #bbbbbb;
}
.maniphest-basic-search-actions {
float: right;
width: 200px;
padding: 10px;
}
.maniphest-basic-search-options {
margin-right: 225px;
background: #f0f0f0;
padding: 6px 0;
}
.maniphest-basic-search-options-table th {
padding: 6px 4px;
text-align: right;
width: 100px;
font-weight: bold;
color: #333333;
}
.maniphest-basic-search-options-table td {
padding: 6px 4px;
}
.maniphest-task-group-header {
font-size: 18px;
margin: 1.5em 14px 0;
border-bottom: 1px solid #dddddd;
} }

View file

@ -148,3 +148,34 @@ button.link {
button.link:hover { button.link:hover {
text-decoration: underline; text-decoration: underline;
} }
a.toggle {
background: #d3d3d3;
padding: 2px 6px 3px;
font-weight: bold;
margin-right: 4px;
color: #333333;
border-radius: 8px;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
text-decoration: none;
}
a.toggle:hover {
background: #b0b0b0;
color: white;
}
a.toggle-selected {
background: #909090;
color: white;
}
a.toggle-fixed {
cursor: default;
}
a.toggle-fixed:hover {
background: #909090;
}