Differential D20628 Diff 49228 src/applications/project/controller/PhabricatorProjectBoardViewController.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/project/controller/PhabricatorProjectBoardViewController.php
| <?php | <?php | ||||
| final class PhabricatorProjectBoardViewController | final class PhabricatorProjectBoardViewController | ||||
| extends PhabricatorProjectBoardController { | extends PhabricatorProjectBoardController { | ||||
| const BATCH_EDIT_ALL = 'all'; | const BATCH_EDIT_ALL = 'all'; | ||||
| private $queryKey; | |||||
| private $sortKey; | |||||
| private $showHidden; | |||||
| public function shouldAllowPublic() { | public function shouldAllowPublic() { | ||||
| return true; | return true; | ||||
| } | } | ||||
| public function handleRequest(AphrontRequest $request) { | public function handleRequest(AphrontRequest $request) { | ||||
| $viewer = $request->getUser(); | $viewer = $request->getUser(); | ||||
| $response = $this->loadProject(); | $response = $this->loadProject(); | ||||
| if ($response) { | if ($response) { | ||||
| return $response; | return $response; | ||||
| } | } | ||||
| $project = $this->getProject(); | $project = $this->getProject(); | ||||
| $state = $this->getViewState(); | |||||
| $this->readRequestState(); | $board_uri = $project->getWorkboardURI(); | ||||
| $board_uri = $this->getApplicationURI('board/'.$project->getID().'/'); | |||||
| $search_engine = id(new ManiphestTaskSearchEngine()) | $search_engine = id(new ManiphestTaskSearchEngine()) | ||||
| ->setViewer($viewer) | ->setViewer($viewer) | ||||
| ->setBaseURI($board_uri) | ->setBaseURI($board_uri) | ||||
| ->setIsBoardView(true); | ->setIsBoardView(true); | ||||
| if ($request->isFormPost() | if ($request->isFormPost() | ||||
| && !$request->getBool('initialize') | && !$request->getBool('initialize') | ||||
| Show All 9 Lines | if ($request->isFormPost() | ||||
| ->setWidth(AphrontDialogView::WIDTH_FULL) | ->setWidth(AphrontDialogView::WIDTH_FULL) | ||||
| ->setTitle(pht('Advanced Filter')) | ->setTitle(pht('Advanced Filter')) | ||||
| ->appendChild($filter_form->buildLayoutView()) | ->appendChild($filter_form->buildLayoutView()) | ||||
| ->setErrors($search_engine->getErrors()) | ->setErrors($search_engine->getErrors()) | ||||
| ->setSubmitURI($board_uri) | ->setSubmitURI($board_uri) | ||||
| ->addSubmitButton(pht('Apply Filter')) | ->addSubmitButton(pht('Apply Filter')) | ||||
| ->addCancelButton($board_uri); | ->addCancelButton($board_uri); | ||||
| } | } | ||||
| return id(new AphrontRedirectResponse())->setURI( | |||||
| $this->getURIWithState( | |||||
| $search_engine->getQueryResultsPageURI($saved->getQueryKey()))); | |||||
| } | |||||
| $query_key = $this->getDefaultFilter($project); | |||||
| $request_query = $request->getStr('filter'); | $query_key = $saved->getQueryKey(); | ||||
| if (strlen($request_query)) { | $results_uri = $search_engine->getQueryResultsPageURI($query_key); | ||||
| $query_key = $request_query; | $results_uri = $state->newURI($results_uri); | ||||
| } | |||||
| $uri_query = $request->getURIData('queryKey'); | return id(new AphrontRedirectResponse())->setURI($results_uri); | ||||
| if (strlen($uri_query)) { | |||||
| $query_key = $uri_query; | |||||
| } | } | ||||
| $this->queryKey = $query_key; | $query_key = $state->getQueryKey(); | ||||
amckinley: I'm pretty sure this is now an unneeded repeat of line 49? | |||||
Done Inline ActionsOh, they're not always the same ($saved vs $state) and line 49 moves out of the controller somewhere down the line. epriestley: Oh, they're not always the same (`$saved` vs `$state`) and line 49 moves out of the controller… | |||||
| $custom_query = null; | $custom_query = null; | ||||
| if ($search_engine->isBuiltinQuery($query_key)) { | if ($search_engine->isBuiltinQuery($query_key)) { | ||||
| $saved = $search_engine->buildSavedQueryFromBuiltin($query_key); | $saved = $search_engine->buildSavedQueryFromBuiltin($query_key); | ||||
| } else { | } else { | ||||
| $saved = id(new PhabricatorSavedQueryQuery()) | $saved = id(new PhabricatorSavedQueryQuery()) | ||||
| ->setViewer($viewer) | ->setViewer($viewer) | ||||
| ->withQueryKeys(array($query_key)) | ->withQueryKeys(array($query_key)) | ||||
| ▲ Show 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | if ($batch_edit) { | ||||
| } | } | ||||
| $batch_tasks = array_select_keys($tasks, $batch_task_phids); | $batch_tasks = array_select_keys($tasks, $batch_task_phids); | ||||
| } else { | } else { | ||||
| $batch_tasks = $task_can_edit_map; | $batch_tasks = $task_can_edit_map; | ||||
| } | } | ||||
| if (!$batch_tasks) { | if (!$batch_tasks) { | ||||
| $cancel_uri = $this->getURIWithState($board_uri); | $cancel_uri = $state->newWorkboardURI(); | ||||
| return $this->newDialog() | return $this->newDialog() | ||||
| ->setTitle(pht('No Editable Tasks')) | ->setTitle(pht('No Editable Tasks')) | ||||
| ->appendParagraph( | ->appendParagraph( | ||||
| pht( | pht( | ||||
| 'The selected column contains no visible tasks which you '. | 'The selected column contains no visible tasks which you '. | ||||
| 'have permission to edit.')) | 'have permission to edit.')) | ||||
| ->addCancelButton($board_uri); | ->addCancelButton($board_uri); | ||||
| } | } | ||||
| Show All 31 Lines | if (strlen($move_id)) { | ||||
| foreach ($move_task_phids as $key => $move_task_phid) { | foreach ($move_task_phids as $key => $move_task_phid) { | ||||
| if (empty($task_can_edit_map[$move_task_phid])) { | if (empty($task_can_edit_map[$move_task_phid])) { | ||||
| unset($move_task_phids[$key]); | unset($move_task_phids[$key]); | ||||
| } | } | ||||
| } | } | ||||
| $move_tasks = array_select_keys($tasks, $move_task_phids); | $move_tasks = array_select_keys($tasks, $move_task_phids); | ||||
| $cancel_uri = $this->getURIWithState($board_uri); | $cancel_uri = $state->newWorkboardURI(); | ||||
| if (!$move_tasks) { | if (!$move_tasks) { | ||||
| return $this->newDialog() | return $this->newDialog() | ||||
| ->setTitle(pht('No Movable Tasks')) | ->setTitle(pht('No Movable Tasks')) | ||||
| ->appendParagraph( | ->appendParagraph( | ||||
| pht( | pht( | ||||
| 'The selected column contains no visible tasks which you '. | 'The selected column contains no visible tasks which you '. | ||||
| 'have permission to move.')) | 'have permission to move.')) | ||||
| ▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | $board = id(new PHUIWorkboardView()) | ||||
| array( | array( | ||||
| 'boardPHID' => $project->getPHID(), | 'boardPHID' => $project->getPHID(), | ||||
| )); | )); | ||||
| $visible_columns = array(); | $visible_columns = array(); | ||||
| $column_phids = array(); | $column_phids = array(); | ||||
| $visible_phids = array(); | $visible_phids = array(); | ||||
| foreach ($columns as $column) { | foreach ($columns as $column) { | ||||
| if (!$this->showHidden) { | if (!$state->getShowHidden()) { | ||||
| if ($column->isHidden()) { | if ($column->isHidden()) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| $proxy = $column->getProxy(); | $proxy = $column->getProxy(); | ||||
| if ($proxy && !$proxy->isMilestone()) { | if ($proxy && !$proxy->isMilestone()) { | ||||
| // TODO: For now, don't show subproject columns because we can't | // TODO: For now, don't show subproject columns because we can't | ||||
| ▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | foreach ($visible_columns as $column_phid => $column) { | ||||
| $column_templates[] = array( | $column_templates[] = array( | ||||
| 'columnPHID' => $column_phid, | 'columnPHID' => $column_phid, | ||||
| 'effects' => $drop_effects, | 'effects' => $drop_effects, | ||||
| 'cardPHIDs' => $card_phids, | 'cardPHIDs' => $card_phids, | ||||
| 'triggerPreviewEffect' => $preview_effect, | 'triggerPreviewEffect' => $preview_effect, | ||||
| ); | ); | ||||
| } | } | ||||
| $order_key = $this->sortKey; | $order_key = $state->getOrder(); | ||||
| $ordering_map = PhabricatorProjectColumnOrder::getEnabledOrders(); | $ordering_map = PhabricatorProjectColumnOrder::getEnabledOrders(); | ||||
| $ordering = id(clone $ordering_map[$order_key]) | $ordering = id(clone $ordering_map[$order_key]) | ||||
| ->setViewer($viewer); | ->setViewer($viewer); | ||||
| $headers = $ordering->getHeadersForObjects($all_tasks); | $headers = $ordering->getHeadersForObjects($all_tasks); | ||||
| $headers = mpull($headers, 'toDictionary'); | $headers = mpull($headers, 'toDictionary'); | ||||
| Show All 17 Lines | public function handleRequest(AphrontRequest $request) { | ||||
| $behavior_config = array( | $behavior_config = array( | ||||
| 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'), | 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'), | ||||
| 'uploadURI' => '/file/dropupload/', | 'uploadURI' => '/file/dropupload/', | ||||
| 'coverURI' => $this->getApplicationURI('cover/'), | 'coverURI' => $this->getApplicationURI('cover/'), | ||||
| 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(), | 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(), | ||||
| 'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(), | 'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(), | ||||
| 'boardPHID' => $project->getPHID(), | 'boardPHID' => $project->getPHID(), | ||||
| 'order' => $this->sortKey, | 'order' => $state->getOrder(), | ||||
| 'orders' => $order_maps, | 'orders' => $order_maps, | ||||
| 'headers' => $headers, | 'headers' => $headers, | ||||
| 'headerKeys' => $header_keys, | 'headerKeys' => $header_keys, | ||||
| 'templateMap' => $templates, | 'templateMap' => $templates, | ||||
| 'orderMaps' => $vector_map, | 'orderMaps' => $vector_map, | ||||
| 'propertyMaps' => $properties, | 'propertyMaps' => $properties, | ||||
| 'columnTemplates' => $column_templates, | 'columnTemplates' => $column_templates, | ||||
| 'boardID' => $board_id, | 'boardID' => $board_id, | ||||
| 'projectPHID' => $project->getPHID(), | 'projectPHID' => $project->getPHID(), | ||||
| 'preloadSounds' => $sounds, | 'preloadSounds' => $sounds, | ||||
| ); | ); | ||||
| $this->initBehavior('project-boards', $behavior_config); | $this->initBehavior('project-boards', $behavior_config); | ||||
| $sort_menu = $this->buildSortMenu( | $sort_menu = $this->buildSortMenu( | ||||
| $viewer, | $viewer, | ||||
| $project, | $project, | ||||
| $this->sortKey, | $state->getOrder(), | ||||
| $ordering_map); | $ordering_map); | ||||
| $filter_menu = $this->buildFilterMenu( | $filter_menu = $this->buildFilterMenu( | ||||
| $viewer, | $viewer, | ||||
| $project, | $project, | ||||
| $custom_query, | $custom_query, | ||||
| $search_engine, | $search_engine, | ||||
| $query_key); | $query_key); | ||||
| $manage_menu = $this->buildManageMenu($project, $this->showHidden); | $manage_menu = $this->buildManageMenu($project, $state->getShowHidden()); | ||||
| $header_link = phutil_tag( | $header_link = phutil_tag( | ||||
| 'a', | 'a', | ||||
| array( | array( | ||||
| 'href' => $this->getApplicationURI('profile/'.$project->getID().'/'), | 'href' => $this->getApplicationURI('profile/'.$project->getID().'/'), | ||||
| ), | ), | ||||
| $project->getName()); | $project->getName()); | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if ($background !== null) { | ||||
| $page->addClass($background_color_class); | $page->addClass($background_color_class); | ||||
| } else { | } else { | ||||
| $page->addClass('phui-workboard-no-color'); | $page->addClass('phui-workboard-no-color'); | ||||
| } | } | ||||
| return $page; | return $page; | ||||
| } | } | ||||
| private function readRequestState() { | |||||
| $request = $this->getRequest(); | |||||
| $project = $this->getProject(); | |||||
| $this->showHidden = $request->getBool('hidden'); | |||||
| $sort_key = $this->getDefaultSort($project); | |||||
| $request_sort = $request->getStr('order'); | |||||
| if ($this->isValidSort($request_sort)) { | |||||
| $sort_key = $request_sort; | |||||
| } | |||||
| $this->sortKey = $sort_key; | |||||
| } | |||||
| private function getDefaultSort(PhabricatorProject $project) { | |||||
| $default_sort = $project->getDefaultWorkboardSort(); | |||||
| if ($this->isValidSort($default_sort)) { | |||||
| return $default_sort; | |||||
| } | |||||
| return PhabricatorProjectColumnNaturalOrder::ORDERKEY; | |||||
| } | |||||
| private function getDefaultFilter(PhabricatorProject $project) { | |||||
| $default_filter = $project->getDefaultWorkboardFilter(); | |||||
| if (strlen($default_filter)) { | |||||
| return $default_filter; | |||||
| } | |||||
| return 'open'; | |||||
| } | |||||
| private function isValidSort($sort) { | |||||
| $map = PhabricatorProjectColumnOrder::getEnabledOrders(); | |||||
| return isset($map[$sort]); | |||||
| } | |||||
| private function buildSortMenu( | private function buildSortMenu( | ||||
| PhabricatorUser $viewer, | PhabricatorUser $viewer, | ||||
| PhabricatorProject $project, | PhabricatorProject $project, | ||||
| $sort_key, | $sort_key, | ||||
| array $ordering_map) { | array $ordering_map) { | ||||
| $base_uri = $this->getURIWithState(); | $state = $this->getViewState(); | ||||
| $base_uri = $state->newWorkboardURI(); | |||||
| $items = array(); | $items = array(); | ||||
| foreach ($ordering_map as $key => $ordering) { | foreach ($ordering_map as $key => $ordering) { | ||||
| // TODO: It would be desirable to build a real "PHUIIconView" here, but | // TODO: It would be desirable to build a real "PHUIIconView" here, but | ||||
| // the pathway for threading that through all the view classes ends up | // the pathway for threading that through all the view classes ends up | ||||
| // being fairly complex, since some callers read the icon out of other | // being fairly complex, since some callers read the icon out of other | ||||
| // views. For now, just stick with a string. | // views. For now, just stick with a string. | ||||
| $ordering_icon = $ordering->getMenuIconIcon(); | $ordering_icon = $ordering->getMenuIconIcon(); | ||||
| ▲ Show 20 Lines • Show All 158 Lines • ▼ Show 20 Lines | final class PhabricatorProjectBoardViewController | ||||
| } | } | ||||
| private function buildManageMenu( | private function buildManageMenu( | ||||
| PhabricatorProject $project, | PhabricatorProject $project, | ||||
| $show_hidden) { | $show_hidden) { | ||||
| $request = $this->getRequest(); | $request = $this->getRequest(); | ||||
| $viewer = $request->getUser(); | $viewer = $request->getUser(); | ||||
| $state = $this->getViewState(); | |||||
| $id = $project->getID(); | $id = $project->getID(); | ||||
| $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); | $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); | ||||
| $add_uri = $this->getApplicationURI("board/{$id}/edit/"); | $add_uri = $this->getApplicationURI("board/{$id}/edit/"); | ||||
| $can_edit = PhabricatorPolicyFilter::hasCapability( | $can_edit = PhabricatorPolicyFilter::hasCapability( | ||||
| $viewer, | $viewer, | ||||
| Show All 13 Lines | private function buildManageMenu( | ||||
| $manage_items[] = id(new PhabricatorActionView()) | $manage_items[] = id(new PhabricatorActionView()) | ||||
| ->setIcon('fa-exchange') | ->setIcon('fa-exchange') | ||||
| ->setName(pht('Reorder Columns')) | ->setName(pht('Reorder Columns')) | ||||
| ->setHref($reorder_uri) | ->setHref($reorder_uri) | ||||
| ->setDisabled(!$can_edit) | ->setDisabled(!$can_edit) | ||||
| ->setWorkflow(true); | ->setWorkflow(true); | ||||
| if ($show_hidden) { | if ($show_hidden) { | ||||
| $hidden_uri = $this->getURIWithState() | $hidden_uri = $state->newWorkboardURI() | ||||
| ->removeQueryParam('hidden'); | ->removeQueryParam('hidden'); | ||||
| $hidden_icon = 'fa-eye-slash'; | $hidden_icon = 'fa-eye-slash'; | ||||
| $hidden_text = pht('Hide Hidden Columns'); | $hidden_text = pht('Hide Hidden Columns'); | ||||
| } else { | } else { | ||||
| $hidden_uri = $this->getURIWithState() | $hidden_uri = $state->newWorkboardURI() | ||||
| ->replaceQueryParam('hidden', 'true'); | ->replaceQueryParam('hidden', 'true'); | ||||
| $hidden_icon = 'fa-eye'; | $hidden_icon = 'fa-eye'; | ||||
| $hidden_text = pht('Show Hidden Columns'); | $hidden_text = pht('Show Hidden Columns'); | ||||
| } | } | ||||
| $manage_items[] = id(new PhabricatorActionView()) | $manage_items[] = id(new PhabricatorActionView()) | ||||
| ->setIcon($hidden_icon) | ->setIcon($hidden_icon) | ||||
| ->setName($hidden_text) | ->setName($hidden_text) | ||||
| ▲ Show 20 Lines • Show All 259 Lines • ▼ Show 20 Lines | |||||
| * the rest of the board state persistent. If no URI is provided, this method | * the rest of the board state persistent. If no URI is provided, this method | ||||
| * starts with the request URI. | * starts with the request URI. | ||||
| * | * | ||||
| * @param string|null URI to add state parameters to. | * @param string|null URI to add state parameters to. | ||||
| * @param bool True to explicitly include all state. | * @param bool True to explicitly include all state. | ||||
| * @return PhutilURI URI with state parameters. | * @return PhutilURI URI with state parameters. | ||||
| */ | */ | ||||
| private function getURIWithState($base = null, $force = false) { | private function getURIWithState($base = null, $force = false) { | ||||
| $project = $this->getProject(); | |||||
| if ($base === null) { | if ($base === null) { | ||||
| $base = $this->getRequest()->getPath(); | $base = $this->getProject()->getWorkboardURI(); | ||||
| } | |||||
| $base = new PhutilURI($base); | |||||
| if ($force || ($this->sortKey != $this->getDefaultSort($project))) { | |||||
| if ($this->sortKey !== null) { | |||||
| $base->replaceQueryParam('order', $this->sortKey); | |||||
| } else { | |||||
| $base->removeQueryParam('order'); | |||||
| } | |||||
| } else { | |||||
| $base->removeQueryParam('order'); | |||||
| } | |||||
| if ($force || ($this->queryKey != $this->getDefaultFilter($project))) { | |||||
| if ($this->queryKey !== null) { | |||||
| $base->replaceQueryParam('filter', $this->queryKey); | |||||
| } else { | |||||
| $base->removeQueryParam('filter'); | |||||
| } | |||||
| } else { | |||||
| $base->removeQueryParam('filter'); | |||||
| } | |||||
| if ($this->showHidden) { | |||||
| $base->replaceQueryParam('hidden', 'true'); | |||||
| } else { | |||||
| $base->removeQueryParam('hidden'); | |||||
| } | } | ||||
| return $base; | return $this->getViewState()->newURI($base, $force); | ||||
| } | } | ||||
| private function buildInitializeContent(PhabricatorProject $project) { | private function buildInitializeContent(PhabricatorProject $project) { | ||||
| $request = $this->getRequest(); | $request = $this->getRequest(); | ||||
| $viewer = $this->getViewer(); | $viewer = $this->getViewer(); | ||||
| $type = $request->getStr('initialize-type'); | $type = $request->getStr('initialize-type'); | ||||
| ▲ Show 20 Lines • Show All 160 Lines • Show Last 20 Lines | |||||
I'm pretty sure this is now an unneeded repeat of line 49?