@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove ManiphestTaskListController

Summary:
Fixes T3273. Fixes T3088. Fixes T2992. Fixes T1163. Fixes T1008. Ref T3241. Ref T1386. Ref T418. Ref T2625. Ref T603.

Replaces task list with one powered by ApplicationSearch.

Test Plan:
After many days of work, the task search interface now looks exactly the same as it did before.

{F58114}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran, chad

Maniphest Tasks: T418, T603, T1008, T1163, T1386, T2625, T2992, T3088, T3241, T3273

Differential Revision: https://secure.phabricator.com/D6979

+1 -981
-2
src/__phutil_library_map__.php
··· 719 719 'ManiphestTaskDetailController' => 'applications/maniphest/controller/ManiphestTaskDetailController.php', 720 720 'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php', 721 721 'ManiphestTaskExtensions' => 'applications/maniphest/extensions/ManiphestTaskExtensions.php', 722 - 'ManiphestTaskListController' => 'applications/maniphest/controller/ManiphestTaskListController.php', 723 722 'ManiphestTaskListControllerPro' => 'applications/maniphest/controller/ManiphestTaskListControllerPro.php', 724 723 'ManiphestTaskListView' => 'applications/maniphest/view/ManiphestTaskListView.php', 725 724 'ManiphestTaskMailReceiver' => 'applications/maniphest/mail/ManiphestTaskMailReceiver.php', ··· 2791 2790 'ManiphestTaskDescriptionPreviewController' => 'ManiphestController', 2792 2791 'ManiphestTaskDetailController' => 'ManiphestController', 2793 2792 'ManiphestTaskEditController' => 'ManiphestController', 2794 - 'ManiphestTaskListController' => 'ManiphestController', 2795 2793 'ManiphestTaskListControllerPro' => 2796 2794 array( 2797 2795 0 => 'ManiphestController',
+1 -3
src/applications/maniphest/application/PhabricatorApplicationManiphest.php
··· 50 50 return array( 51 51 '/T(?P<id>[1-9]\d*)' => 'ManiphestTaskDetailController', 52 52 '/maniphest/' => array( 53 - '' => 'ManiphestTaskListController', 54 - 'query/(?:(?P<queryKey>[^/]+)/)?' => 'ManiphestTaskListControllerPro', 55 - 'view/(?P<view>\w+)/' => 'ManiphestTaskListController', 53 + '(?:query/(?P<queryKey>[^/]+)/)?' => 'ManiphestTaskListControllerPro', 56 54 'report/(?:(?P<view>\w+)/)?' => 'ManiphestReportController', 57 55 'batch/' => 'ManiphestBatchEditController', 58 56 'task/' => array(
-976
src/applications/maniphest/controller/ManiphestTaskListController.php
··· 1 - <?php 2 - 3 - /** 4 - * @group maniphest 5 - */ 6 - final class ManiphestTaskListController extends ManiphestController { 7 - 8 - const DEFAULT_PAGE_SIZE = 1000; 9 - 10 - private $view; 11 - 12 - public function willProcessRequest(array $data) { 13 - $this->view = idx($data, 'view'); 14 - } 15 - 16 - private function getArrToStrList($key) { 17 - $arr = $this->getRequest()->getArr($key); 18 - $arr = implode(',', $arr); 19 - return nonempty($arr, null); 20 - } 21 - 22 - public function processRequest() { 23 - 24 - $request = $this->getRequest(); 25 - $user = $request->getUser(); 26 - 27 - if ($request->isFormPost()) { 28 - // Redirect to GET so URIs can be copy/pasted. 29 - 30 - $task_ids = $request->getStr('set_tasks'); 31 - $task_ids = nonempty($task_ids, null); 32 - 33 - $search_text = $request->getStr('set_search'); 34 - 35 - $min_priority = $request->getInt('set_lpriority'); 36 - 37 - $max_priority = $request->getInt('set_hpriority'); 38 - 39 - $uri = $request->getRequestURI() 40 - ->alter('users', $this->getArrToStrList('set_users')) 41 - ->alter('projects', $this->getArrToStrList('set_projects')) 42 - ->alter('aprojects', $this->getArrToStrList('set_aprojects')) 43 - ->alter('useraprojects', $this->getArrToStrList('set_useraprojects')) 44 - ->alter('xprojects', $this->getArrToStrList('set_xprojects')) 45 - ->alter('owners', $this->getArrToStrList('set_owners')) 46 - ->alter('authors', $this->getArrToStrList('set_authors')) 47 - ->alter('lpriority', $min_priority) 48 - ->alter('hpriority', $max_priority) 49 - ->alter('tasks', $task_ids) 50 - ->alter('search', $search_text); 51 - 52 - return id(new AphrontRedirectResponse())->setURI($uri); 53 - } 54 - 55 - $nav = $this->buildBaseSideNav(); 56 - 57 - $has_filter = array( 58 - 'action' => true, 59 - 'created' => true, 60 - 'subscribed' => true, 61 - 'triage' => true, 62 - 'projecttriage' => true, 63 - 'projectall' => true, 64 - ); 65 - 66 - $query = null; 67 - $key = $request->getStr('key'); 68 - if (!$key && !$this->view) { 69 - if ($this->getDefaultQuery()) { 70 - $key = $this->getDefaultQuery()->getQueryKey(); 71 - } 72 - } 73 - 74 - if ($key) { 75 - $query = id(new PhabricatorSearchQuery())->loadOneWhere( 76 - 'queryKey = %s', 77 - $key); 78 - } 79 - 80 - // If the user is running a saved query, load query parameters from that 81 - // query. Otherwise, build a new query object from the HTTP request. 82 - 83 - if ($query) { 84 - $nav->selectFilter('Q:'.$query->getQueryKey(), 'custom'); 85 - $this->view = 'custom'; 86 - } else { 87 - $this->view = $nav->selectFilter($this->view, 'action'); 88 - $query = $this->buildQueryFromRequest(); 89 - } 90 - 91 - // Execute the query. 92 - 93 - list($tasks, $handles, $total_count) = self::loadTasks( 94 - $query, 95 - $user); 96 - 97 - // Extract information we need to render the filters from the query. 98 - 99 - $search_text = $query->getParameter('fullTextSearch'); 100 - 101 - $user_phids = $query->getParameter('userPHIDs', array()); 102 - $task_ids = $query->getParameter('taskIDs', array()); 103 - $owner_phids = $query->getParameter('ownerPHIDs', array()); 104 - $author_phids = $query->getParameter('authorPHIDs', array()); 105 - $project_phids = $query->getParameter('projectPHIDs', array()); 106 - $any_project_phids = $query->getParameter( 107 - 'anyProjectPHIDs', 108 - array()); 109 - $any_user_project_phids = $query->getParameter( 110 - 'anyUserProjectPHIDs', 111 - array()); 112 - $exclude_project_phids = $query->getParameter( 113 - 'excludeProjectPHIDs', 114 - array()); 115 - $low_priority = $query->getParameter('lowPriority'); 116 - $high_priority = $query->getParameter('highPriority'); 117 - 118 - $page_size = $query->getParameter('limit'); 119 - $page = $query->getParameter('offset'); 120 - 121 - $q_status = $query->getParameter('status'); 122 - $q_group = $query->getParameter('group'); 123 - $q_order = $query->getParameter('order'); 124 - 125 - $form = id(new AphrontFormView()) 126 - ->setUser($user) 127 - ->setAction( 128 - $request->getRequestURI() 129 - ->alter('key', null) 130 - ->alter( 131 - $this->getStatusRequestKey(), 132 - $this->getStatusRequestValue($q_status)) 133 - ->alter( 134 - $this->getOrderRequestKey(), 135 - $this->getOrderRequestValue($q_order)) 136 - ->alter( 137 - $this->getGroupRequestKey(), 138 - $this->getGroupRequestValue($q_group))); 139 - 140 - if (isset($has_filter[$this->view])) { 141 - $tokens = array(); 142 - foreach ($user_phids as $phid) { 143 - $tokens[$phid] = $handles[$phid]->getFullName(); 144 - } 145 - $form->appendChild( 146 - id(new AphrontFormTokenizerControl()) 147 - ->setDatasource('/typeahead/common/searchowner/') 148 - ->setName('set_users') 149 - ->setLabel(pht('Users')) 150 - ->setValue($tokens)); 151 - } 152 - 153 - if ($this->view == 'custom') { 154 - $form->appendChild( 155 - id(new AphrontFormTextControl()) 156 - ->setName('set_search') 157 - ->setLabel(pht('Search')) 158 - ->setValue($search_text)); 159 - $form->appendChild( 160 - id(new AphrontFormTextControl()) 161 - ->setName('set_tasks') 162 - ->setLabel(pht('Task IDs')) 163 - ->setValue(join(',', $task_ids))); 164 - 165 - $tokens = array(); 166 - foreach ($owner_phids as $phid) { 167 - $tokens[$phid] = $handles[$phid]->getFullName(); 168 - } 169 - $form->appendChild( 170 - id(new AphrontFormTokenizerControl()) 171 - ->setDatasource('/typeahead/common/searchowner/') 172 - ->setName('set_owners') 173 - ->setLabel(pht('Owners')) 174 - ->setValue($tokens)); 175 - 176 - $tokens = array(); 177 - foreach ($author_phids as $phid) { 178 - $tokens[$phid] = $handles[$phid]->getFullName(); 179 - } 180 - $form->appendChild( 181 - id(new AphrontFormTokenizerControl()) 182 - ->setDatasource('/typeahead/common/authors/') 183 - ->setName('set_authors') 184 - ->setLabel(pht('Authors')) 185 - ->setValue($tokens)); 186 - } 187 - 188 - $tokens = array(); 189 - foreach ($project_phids as $phid) { 190 - $tokens[$phid] = $handles[$phid]->getFullName(); 191 - } 192 - if ($this->view != 'projectall' && $this->view != 'projecttriage') { 193 - 194 - $caption = null; 195 - if ($this->view == 'custom') { 196 - $caption = pht('Find tasks in ALL of these projects ("AND" query).'); 197 - } 198 - 199 - $form->appendChild( 200 - id(new AphrontFormTokenizerControl()) 201 - ->setDatasource('/typeahead/common/searchproject/') 202 - ->setName('set_projects') 203 - ->setLabel(pht('Projects')) 204 - ->setCaption($caption) 205 - ->setValue($tokens)); 206 - } 207 - 208 - if ($this->view == 'custom') { 209 - $atokens = array(); 210 - foreach ($any_project_phids as $phid) { 211 - $atokens[$phid] = $handles[$phid]->getFullName(); 212 - } 213 - $form->appendChild( 214 - id(new AphrontFormTokenizerControl()) 215 - ->setDatasource('/typeahead/common/projects/') 216 - ->setName('set_aprojects') 217 - ->setLabel(pht('Any Projects')) 218 - ->setCaption(pht('Find tasks in ANY of these projects ("OR" query).')) 219 - ->setValue($atokens)); 220 - 221 - $tokens = array(); 222 - foreach ($any_user_project_phids as $phid) { 223 - $tokens[$phid] = $handles[$phid]->getFullName(); 224 - } 225 - $form->appendChild( 226 - id(new AphrontFormTokenizerControl()) 227 - ->setDatasource('/typeahead/common/users/') 228 - ->setName('set_useraprojects') 229 - ->setLabel(pht('Any User Projects')) 230 - ->setCaption( 231 - pht('Find tasks in ANY of these users\' projects ("OR" query).')) 232 - ->setValue($tokens)); 233 - 234 - $tokens = array(); 235 - foreach ($exclude_project_phids as $phid) { 236 - $tokens[$phid] = $handles[$phid]->getFullName(); 237 - } 238 - $form->appendChild( 239 - id(new AphrontFormTokenizerControl()) 240 - ->setDatasource('/typeahead/common/projects/') 241 - ->setName('set_xprojects') 242 - ->setLabel(pht('Exclude Projects')) 243 - ->setCaption(pht('Find tasks NOT in any of these projects.')) 244 - ->setValue($tokens)); 245 - 246 - $priority = ManiphestTaskPriority::getLowestPriority(); 247 - if ($low_priority !== null) { 248 - $priority = $low_priority; 249 - } 250 - 251 - $form->appendChild( 252 - id(new AphrontFormSelectControl()) 253 - ->setLabel(pht('Min Priority')) 254 - ->setName('set_lpriority') 255 - ->setValue($priority) 256 - ->setOptions(array_reverse( 257 - ManiphestTaskPriority::getTaskPriorityMap(), true))); 258 - 259 - $priority = ManiphestTaskPriority::getHighestPriority(); 260 - if ($high_priority !== null) { 261 - $priority = $high_priority; 262 - } 263 - 264 - $form->appendChild( 265 - id(new AphrontFormSelectControl()) 266 - ->setLabel(pht('Max Priority')) 267 - ->setName('set_hpriority') 268 - ->setValue($priority) 269 - ->setOptions(ManiphestTaskPriority::getTaskPriorityMap())); 270 - 271 - } 272 - 273 - $form 274 - ->appendChild($this->renderStatusControl($q_status)) 275 - ->appendChild($this->renderGroupControl($q_group)) 276 - ->appendChild($this->renderOrderControl($q_order)); 277 - 278 - $submit = id(new AphrontFormSubmitControl()) 279 - ->setValue(pht('Filter Tasks')); 280 - 281 - // Only show "Save..." for novel queries which have some kind of query 282 - // parameters set. 283 - if ($this->view === 'custom' 284 - && empty($key) 285 - && $request->getRequestURI()->getQueryParams()) { 286 - $submit->addCancelButton( 287 - '/maniphest/custom/edit/?key='.$query->getQueryKey(), 288 - pht('Save Custom Query...')); 289 - } 290 - 291 - $form->appendChild($submit); 292 - 293 - $create_uri = new PhutilURI('/maniphest/task/create/'); 294 - if ($project_phids) { 295 - // If we have project filters selected, use them as defaults for task 296 - // creation. 297 - $create_uri->setQueryParam('projects', implode(';', $project_phids)); 298 - } 299 - 300 - $filter = new AphrontListFilterView(); 301 - if (empty($key)) { 302 - $filter->appendChild($form); 303 - } 304 - 305 - $have_tasks = false; 306 - foreach ($tasks as $group => $list) { 307 - if (count($list)) { 308 - $have_tasks = true; 309 - break; 310 - } 311 - } 312 - 313 - require_celerity_resource('maniphest-task-summary-css'); 314 - 315 - $list_container = new AphrontNullView(); 316 - $list_container->appendChild(hsprintf( 317 - '<div class="maniphest-list-container">')); 318 - 319 - if (!$have_tasks) { 320 - $no_tasks = pht('No matching tasks.'); 321 - $list_container->appendChild(hsprintf( 322 - '<h1 class="maniphest-task-group-header">'. 323 - '%s'. 324 - '</h1>', 325 - $no_tasks)); 326 - $result_count = null; 327 - } else { 328 - $pager = new AphrontPagerView(); 329 - $pager->setURI($request->getRequestURI(), 'offset'); 330 - $pager->setPageSize($page_size); 331 - $pager->setOffset($page); 332 - $pager->setCount($total_count); 333 - 334 - $cur = ($pager->getOffset() + 1); 335 - $max = min($pager->getOffset() + $page_size, $total_count); 336 - $tot = $total_count; 337 - 338 - $results = pht('Displaying tasks %s - %s of %s.', 339 - number_format($cur), 340 - number_format($max), 341 - number_format($tot)); 342 - $result_count = phutil_tag( 343 - 'div', 344 - array( 345 - 'class' => 'maniphest-total-result-count' 346 - ), 347 - $results); 348 - 349 - $selector = new AphrontNullView(); 350 - 351 - $group = $query->getParameter('group'); 352 - $order = $query->getParameter('order'); 353 - $is_draggable = 354 - ($order == 'priority') && 355 - ($group == 'none' || $group == 'priority'); 356 - 357 - $lists = array(); 358 - foreach ($tasks as $group => $list) { 359 - $task_list = new ManiphestTaskListView(); 360 - $task_list->setShowBatchControls(true); 361 - if ($is_draggable) { 362 - $task_list->setShowSubpriorityControls(true); 363 - } 364 - $task_list->setUser($user); 365 - $task_list->setTasks($list); 366 - $task_list->setHandles($handles); 367 - 368 - $count = number_format(count($list)); 369 - 370 - $header = 371 - javelin_tag( 372 - 'h1', 373 - array( 374 - 'class' => 'maniphest-task-group-header', 375 - 'sigil' => 'task-group', 376 - 'meta' => array( 377 - 'priority' => head($list)->getPriority(), 378 - ), 379 - ), 380 - $group.' ('.$count.')'); 381 - 382 - $lists[] = 383 - phutil_tag( 384 - 'div', 385 - array( 386 - 'class' => 'maniphest-task-group' 387 - ), 388 - array( 389 - $header, 390 - $task_list, 391 - )); 392 - } 393 - 394 - $selector->appendChild($lists); 395 - $selector->appendChild($this->renderBatchEditor($query)); 396 - 397 - $list_container->appendChild($selector); 398 - $list_container->appendChild($pager); 399 - 400 - Javelin::initBehavior( 401 - 'maniphest-subpriority-editor', 402 - array( 403 - 'uri' => '/maniphest/subpriority/', 404 - )); 405 - } 406 - 407 - $nav->appendChild($filter); 408 - $nav->appendChild($result_count); 409 - $nav->appendChild($list_container); 410 - 411 - $title = pht('Task List'); 412 - 413 - $crumbs = $this->buildApplicationCrumbs() 414 - ->addCrumb( 415 - id(new PhabricatorCrumbView()) 416 - ->setName($title)) 417 - ->addAction( 418 - id(new PHUIListItemView()) 419 - ->setHref($this->getApplicationURI('/task/create/')) 420 - ->setName(pht('Create Task')) 421 - ->setIcon('create')); 422 - 423 - $nav->setCrumbs($crumbs); 424 - 425 - return $this->buildApplicationPage( 426 - $nav, 427 - array( 428 - 'title' => $title, 429 - 'device' => true, 430 - )); 431 - } 432 - 433 - public static function loadTasks( 434 - PhabricatorSearchQuery $search_query, 435 - PhabricatorUser $viewer) { 436 - 437 - $any_project = false; 438 - $search_text = $search_query->getParameter('fullTextSearch'); 439 - $user_phids = $search_query->getParameter('userPHIDs', array()); 440 - $task_ids = $search_query->getParameter('taskIDs', array()); 441 - $project_phids = $search_query->getParameter('projectPHIDs', array()); 442 - $any_project_phids = $search_query->getParameter( 443 - 'anyProjectPHIDs', 444 - array()); 445 - $any_user_project_phids = $search_query->getParameter( 446 - 'anyUserProjectPHIDs', 447 - array()); 448 - $xproject_phids = $search_query->getParameter( 449 - 'excludeProjectPHIDs', 450 - array()); 451 - $owner_phids = $search_query->getParameter('ownerPHIDs', array()); 452 - $author_phids = $search_query->getParameter('authorPHIDs', array()); 453 - 454 - $low_priority = $search_query->getParameter('lowPriority'); 455 - $low_priority = coalesce($low_priority, 456 - ManiphestTaskPriority::getLowestPriority()); 457 - $high_priority = $search_query->getParameter('highPriority'); 458 - $high_priority = coalesce($high_priority, 459 - ManiphestTaskPriority::getHighestPriority()); 460 - 461 - $query = new ManiphestTaskQuery(); 462 - $query->setViewer($viewer); 463 - $query->withIDs($task_ids); 464 - 465 - if ($project_phids) { 466 - $query->withAllProjects($project_phids); 467 - } 468 - 469 - if ($xproject_phids) { 470 - $query->withoutProjects($xproject_phids); 471 - } 472 - 473 - if ($any_project_phids) { 474 - $query->withAnyProjects($any_project_phids); 475 - } 476 - 477 - if ($owner_phids) { 478 - $query->withOwners($owner_phids); 479 - } 480 - 481 - if ($author_phids) { 482 - $query->withAuthors($author_phids); 483 - } 484 - 485 - if ($any_user_project_phids) { 486 - $query->setViewer($viewer); 487 - $query->withAnyUserProjects($any_user_project_phids); 488 - } 489 - 490 - $status = $search_query->getParameter('status', 'all'); 491 - if (!empty($status['open']) && !empty($status['closed'])) { 492 - $query->withStatus(ManiphestTaskQuery::STATUS_ANY); 493 - } else if (!empty($status['open'])) { 494 - $query->withStatus(ManiphestTaskQuery::STATUS_OPEN); 495 - } else { 496 - $query->withStatus(ManiphestTaskQuery::STATUS_CLOSED); 497 - } 498 - 499 - switch ($search_query->getParameter('view')) { 500 - case 'action': 501 - $query->withOwners($user_phids); 502 - break; 503 - case 'created': 504 - $query->withAuthors($user_phids); 505 - break; 506 - case 'subscribed': 507 - $query->withSubscribers($user_phids); 508 - break; 509 - case 'triage': 510 - $query->withOwners($user_phids); 511 - $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); 512 - break; 513 - case 'alltriage': 514 - $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); 515 - break; 516 - case 'all': 517 - break; 518 - case 'projecttriage': 519 - $query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE); 520 - break; 521 - case 'projectall': 522 - break; 523 - case 'custom': 524 - $query->withPrioritiesBetween($low_priority, $high_priority); 525 - break; 526 - } 527 - 528 - $query->withFullTextSearch($search_text); 529 - 530 - $order_map = array( 531 - 'priority' => ManiphestTaskQuery::ORDER_PRIORITY, 532 - 'created' => ManiphestTaskQuery::ORDER_CREATED, 533 - 'title' => ManiphestTaskQuery::ORDER_TITLE, 534 - ); 535 - $query->setOrderBy( 536 - idx( 537 - $order_map, 538 - $search_query->getParameter('order'), 539 - ManiphestTaskQuery::ORDER_MODIFIED)); 540 - 541 - $group_map = array( 542 - 'priority' => ManiphestTaskQuery::GROUP_PRIORITY, 543 - 'owner' => ManiphestTaskQuery::GROUP_OWNER, 544 - 'status' => ManiphestTaskQuery::GROUP_STATUS, 545 - 'project' => ManiphestTaskQuery::GROUP_PROJECT, 546 - ); 547 - $query->setGroupBy( 548 - idx( 549 - $group_map, 550 - $search_query->getParameter('group'), 551 - ManiphestTaskQuery::GROUP_NONE)); 552 - 553 - $query->setCalculateRows(true); 554 - $query->setLimit($search_query->getParameter('limit')); 555 - $query->setOffset($search_query->getParameter('offset')); 556 - 557 - $data = $query->execute(); 558 - $total_row_count = $query->getRowCount(); 559 - 560 - $project_group_phids = array(); 561 - if ($search_query->getParameter('group') == 'project') { 562 - foreach ($data as $task) { 563 - foreach ($task->getProjectPHIDs() as $phid) { 564 - $project_group_phids[] = $phid; 565 - } 566 - } 567 - } 568 - 569 - $handle_phids = mpull($data, 'getOwnerPHID'); 570 - $handle_phids = array_merge( 571 - $handle_phids, 572 - $project_phids, 573 - $user_phids, 574 - $xproject_phids, 575 - $owner_phids, 576 - $author_phids, 577 - $project_group_phids, 578 - $any_project_phids, 579 - $any_user_project_phids, 580 - array_mergev(mpull($data, 'getProjectPHIDs'))); 581 - $handles = id(new PhabricatorHandleQuery()) 582 - ->setViewer($viewer) 583 - ->withPHIDs($handle_phids) 584 - ->execute(); 585 - 586 - switch ($search_query->getParameter('group')) { 587 - case 'priority': 588 - $data = mgroup($data, 'getPriority'); 589 - 590 - // If we have invalid priorities, they'll all map to "???". Merge 591 - // arrays to prevent them from overwriting each other. 592 - 593 - $out = array(); 594 - foreach ($data as $pri => $tasks) { 595 - $out[ManiphestTaskPriority::getTaskPriorityName($pri)][] = $tasks; 596 - } 597 - foreach ($out as $pri => $tasks) { 598 - $out[$pri] = array_mergev($tasks); 599 - } 600 - $data = $out; 601 - 602 - break; 603 - case 'status': 604 - $data = mgroup($data, 'getStatus'); 605 - 606 - $out = array(); 607 - foreach ($data as $status => $tasks) { 608 - $out[ManiphestTaskStatus::getTaskStatusFullName($status)] = $tasks; 609 - } 610 - 611 - $data = $out; 612 - break; 613 - case 'owner': 614 - $data = mgroup($data, 'getOwnerPHID'); 615 - 616 - $out = array(); 617 - foreach ($data as $phid => $tasks) { 618 - if ($phid) { 619 - $out[$handles[$phid]->getFullName()] = $tasks; 620 - } else { 621 - $out['Unassigned'] = $tasks; 622 - } 623 - } 624 - 625 - $data = $out; 626 - ksort($data); 627 - 628 - // Move "Unassigned" to the top of the list. 629 - if (isset($data['Unassigned'])) { 630 - $data = array('Unassigned' => $out['Unassigned']) + $out; 631 - } 632 - break; 633 - case 'project': 634 - $data = mgroup($data, 'getGroupByProjectPHID'); 635 - 636 - $out = array(); 637 - foreach ($data as $phid => $tasks) { 638 - $name = pht('No Project'); 639 - if ($phid) { 640 - $handle = idx($handles, $phid); 641 - if ($handle) { 642 - $name = $handles[$phid]->getFullName(); 643 - } 644 - } 645 - $out[$name] = $tasks; 646 - } 647 - $data = $out; 648 - break; 649 - default: 650 - $data = array( 651 - 'Tasks' => $data, 652 - ); 653 - break; 654 - } 655 - 656 - return array($data, $handles, $total_row_count); 657 - } 658 - 659 - private function renderBatchEditor(PhabricatorSearchQuery $search_query) { 660 - $user = $this->getRequest()->getUser(); 661 - 662 - Javelin::initBehavior( 663 - 'maniphest-batch-selector', 664 - array( 665 - 'selectAll' => 'batch-select-all', 666 - 'selectNone' => 'batch-select-none', 667 - 'submit' => 'batch-select-submit', 668 - 'status' => 'batch-select-status-cell', 669 - 'idContainer' => 'batch-select-id-container', 670 - 'formID' => 'batch-select-form', 671 - )); 672 - 673 - $select_all = javelin_tag( 674 - 'a', 675 - array( 676 - 'href' => '#', 677 - 'mustcapture' => true, 678 - 'class' => 'grey button', 679 - 'id' => 'batch-select-all', 680 - ), 681 - pht('Select All')); 682 - 683 - $select_none = javelin_tag( 684 - 'a', 685 - array( 686 - 'href' => '#', 687 - 'mustcapture' => true, 688 - 'class' => 'grey button', 689 - 'id' => 'batch-select-none', 690 - ), 691 - pht('Clear Selection')); 692 - 693 - $submit = phutil_tag( 694 - 'button', 695 - array( 696 - 'id' => 'batch-select-submit', 697 - 'disabled' => 'disabled', 698 - 'class' => 'disabled', 699 - ), 700 - pht("Batch Edit Selected \xC2\xBB")); 701 - 702 - $export = javelin_tag( 703 - 'a', 704 - array( 705 - 'href' => '/maniphest/export/'.$search_query->getQueryKey().'/', 706 - 'class' => 'grey button', 707 - ), 708 - pht('Export to Excel')); 709 - 710 - $hidden = phutil_tag( 711 - 'div', 712 - array( 713 - 'id' => 'batch-select-id-container', 714 - ), 715 - ''); 716 - 717 - $editor = hsprintf( 718 - '<div class="maniphest-batch-editor">'. 719 - '<div class="batch-editor-header">%s</div>'. 720 - '<table class="maniphest-batch-editor-layout">'. 721 - '<tr>'. 722 - '<td>%s%s</td>'. 723 - '<td>%s</td>'. 724 - '<td id="batch-select-status-cell">%s</td>'. 725 - '<td class="batch-select-submit-cell">%s%s</td>'. 726 - '</tr>'. 727 - '</table>'. 728 - '</div>', 729 - pht('Batch Task Editor'), 730 - $select_all, 731 - $select_none, 732 - $export, 733 - '', 734 - $submit, 735 - $hidden); 736 - 737 - $editor = phabricator_form( 738 - $user, 739 - array( 740 - 'method' => 'POST', 741 - 'action' => '/maniphest/batch/', 742 - 'id' => 'batch-select-form', 743 - ), 744 - $editor); 745 - 746 - return $editor; 747 - } 748 - 749 - private function buildQueryFromRequest() { 750 - $request = $this->getRequest(); 751 - $user = $request->getUser(); 752 - 753 - $status = $this->getStatusValueFromRequest(); 754 - $group = $this->getGroupValueFromRequest(); 755 - $order = $this->getOrderValueFromRequest(); 756 - 757 - $user_phids = $request->getStrList( 758 - 'users', 759 - array($user->getPHID())); 760 - 761 - if ($this->view == 'projecttriage' || $this->view == 'projectall') { 762 - $projects = id(new PhabricatorProjectQuery()) 763 - ->setViewer($user) 764 - ->withMemberPHIDs($user_phids) 765 - ->execute(); 766 - $any_project_phids = mpull($projects, 'getPHID'); 767 - $any_user_project_phids = array(); 768 - } else { 769 - $any_project_phids = $request->getStrList('aprojects'); 770 - $any_user_project_phids = $request->getStrList('useraprojects'); 771 - } 772 - 773 - $project_phids = $request->getStrList('projects'); 774 - $exclude_project_phids = $request->getStrList('xprojects'); 775 - $task_ids = $request->getStrList('tasks'); 776 - 777 - if ($task_ids) { 778 - // We only need the integer portion of each task ID, so get rid of any 779 - // non-numeric elements 780 - $numeric_task_ids = array(); 781 - 782 - foreach ($task_ids as $task_id) { 783 - $task_id = preg_replace('/\D+/', '', $task_id); 784 - if (!empty($task_id)) { 785 - $numeric_task_ids[] = $task_id; 786 - } 787 - } 788 - 789 - if (empty($numeric_task_ids)) { 790 - $numeric_task_ids = array(null); 791 - } 792 - 793 - $task_ids = $numeric_task_ids; 794 - } 795 - 796 - $owner_phids = $request->getStrList('owners'); 797 - $author_phids = $request->getStrList('authors'); 798 - 799 - $search_string = $request->getStr('search'); 800 - 801 - $low_priority = $request->getInt('lpriority'); 802 - $high_priority = $request->getInt('hpriority'); 803 - 804 - $page = $request->getInt('offset'); 805 - $page_size = self::DEFAULT_PAGE_SIZE; 806 - 807 - $query = new PhabricatorSearchQuery(); 808 - $query->setQuery('<<maniphest>>'); 809 - $query->setParameters( 810 - array( 811 - 'fullTextSearch' => $search_string, 812 - 'view' => $this->view, 813 - 'userPHIDs' => $user_phids, 814 - 'projectPHIDs' => $project_phids, 815 - 'anyProjectPHIDs' => $any_project_phids, 816 - 'anyUserProjectPHIDs' => $any_user_project_phids, 817 - 'excludeProjectPHIDs' => $exclude_project_phids, 818 - 'ownerPHIDs' => $owner_phids, 819 - 'authorPHIDs' => $author_phids, 820 - 'taskIDs' => $task_ids, 821 - 'lowPriority' => $low_priority, 822 - 'highPriority' => $high_priority, 823 - 'group' => $group, 824 - 'order' => $order, 825 - 'offset' => $page, 826 - 'limit' => $page_size, 827 - 'status' => $status, 828 - )); 829 - 830 - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 831 - $query->save(); 832 - unset($unguarded); 833 - 834 - return $query; 835 - } 836 - 837 - /* -( Toggle Button Controls )--------------------------------------------- 838 - 839 - These are a giant mess since we have several different values: the request 840 - key (GET param used in requests), the request value (short names used in 841 - requests to keep URIs readable), and the query value (complex value stored in 842 - the query). 843 - 844 - */ 845 - 846 - private function getStatusValueFromRequest() { 847 - $map = $this->getStatusMap(); 848 - $val = $this->getRequest()->getStr($this->getStatusRequestKey()); 849 - return idx($map, $val, head($map)); 850 - } 851 - 852 - private function getGroupValueFromRequest() { 853 - $map = $this->getGroupMap(); 854 - $val = $this->getRequest()->getStr($this->getGroupRequestKey()); 855 - return idx($map, $val, head($map)); 856 - } 857 - 858 - private function getOrderValueFromRequest() { 859 - $map = $this->getOrderMap(); 860 - $val = $this->getRequest()->getStr($this->getOrderRequestKey()); 861 - return idx($map, $val, head($map)); 862 - } 863 - 864 - private function getStatusRequestKey() { 865 - return 's'; 866 - } 867 - 868 - private function getGroupRequestKey() { 869 - return 'g'; 870 - } 871 - 872 - private function getOrderRequestKey() { 873 - return 'o'; 874 - } 875 - 876 - private function getStatusRequestValue($value) { 877 - return array_search($value, $this->getStatusMap()); 878 - } 879 - 880 - private function getGroupRequestValue($value) { 881 - return array_search($value, $this->getGroupMap()); 882 - } 883 - 884 - private function getOrderRequestValue($value) { 885 - return array_search($value, $this->getOrderMap()); 886 - } 887 - 888 - private function getStatusMap() { 889 - return array( 890 - 'o' => array( 891 - 'open' => true, 892 - ), 893 - 'c' => array( 894 - 'closed' => true, 895 - ), 896 - 'oc' => array( 897 - 'open' => true, 898 - 'closed' => true, 899 - ), 900 - ); 901 - } 902 - 903 - private function getGroupMap() { 904 - return array( 905 - 'p' => 'priority', 906 - 'o' => 'owner', 907 - 's' => 'status', 908 - 'j' => 'project', 909 - 'n' => 'none', 910 - ); 911 - } 912 - 913 - private function getOrderMap() { 914 - return array( 915 - 'p' => 'priority', 916 - 'u' => 'updated', 917 - 'c' => 'created', 918 - 't' => 'title', 919 - ); 920 - } 921 - 922 - private function getStatusButtonMap() { 923 - return array( 924 - 'o' => pht('Open'), 925 - 'c' => pht('Closed'), 926 - 'oc' => pht('All'), 927 - ); 928 - } 929 - 930 - private function getGroupButtonMap() { 931 - return array( 932 - 'p' => pht('Priority'), 933 - 'o' => pht('Owner'), 934 - 's' => pht('Status'), 935 - 'j' => pht('Project'), 936 - 'n' => pht('None'), 937 - ); 938 - } 939 - 940 - private function getOrderButtonMap() { 941 - return array( 942 - 'p' => pht('Priority'), 943 - 'u' => pht('Updated'), 944 - 'c' => pht('Created'), 945 - 't' => pht('Title'), 946 - ); 947 - } 948 - 949 - public function renderStatusControl($value) { 950 - $request = $this->getRequest(); 951 - return id(new AphrontFormToggleButtonsControl()) 952 - ->setLabel(pht('Status')) 953 - ->setValue($this->getStatusRequestValue($value)) 954 - ->setBaseURI($request->getRequestURI(), $this->getStatusRequestKey()) 955 - ->setButtons($this->getStatusButtonMap()); 956 - } 957 - 958 - public function renderOrderControl($value) { 959 - $request = $this->getRequest(); 960 - return id(new AphrontFormToggleButtonsControl()) 961 - ->setLabel(pht('Order')) 962 - ->setValue($this->getOrderRequestValue($value)) 963 - ->setBaseURI($request->getRequestURI(), $this->getOrderRequestKey()) 964 - ->setButtons($this->getOrderButtonMap()); 965 - } 966 - 967 - public function renderGroupControl($value) { 968 - $request = $this->getRequest(); 969 - return id(new AphrontFormToggleButtonsControl()) 970 - ->setLabel(pht('Group')) 971 - ->setValue($this->getGroupRequestValue($value)) 972 - ->setBaseURI($request->getRequestURI(), $this->getGroupRequestKey()) 973 - ->setButtons($this->getGroupButtonMap()); 974 - } 975 - 976 - }