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?