@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.

Move workboard "filter" workflow to a separate controller

Summary:
Depends on D20629. Ref T4900. Currently, the "Advanced Filter..." workflow on workboards (where you build a custom query) is inline in the main board controller.

This is because the filter flow depends on some of the board view state: we want to start with the current filter applied to the board, and preserve other state after you change the filter.

Now that `ViewState` can handle state management, we can separate this stuff out pretty easily.

Test Plan:
- Changed filters on a board.
- Applied a custom filter to a board.
- Changed the ordering of a board, then applied a custom filter. Verified "Cancel" and "Apply Filter" both preserve the order state.
- Changed the ordering of a board, then applied a custom filter, intentionally making a mistake in configuring the filter by entering an invalid date. Saw a dialog with an error. After correcting the error, saw state preserved properly.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T4900

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

+131 -62
+2
src/__phutil_library_map__.php
··· 4160 4160 'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php', 4161 4161 'PhabricatorProjectBoardDefaultController' => 'applications/project/controller/PhabricatorProjectBoardDefaultController.php', 4162 4162 'PhabricatorProjectBoardDisableController' => 'applications/project/controller/PhabricatorProjectBoardDisableController.php', 4163 + 'PhabricatorProjectBoardFilterController' => 'applications/project/controller/PhabricatorProjectBoardFilterController.php', 4163 4164 'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php', 4164 4165 'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php', 4165 4166 'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php', ··· 10424 10425 'PhabricatorProjectBoardController' => 'PhabricatorProjectController', 10425 10426 'PhabricatorProjectBoardDefaultController' => 'PhabricatorProjectBoardController', 10426 10427 'PhabricatorProjectBoardDisableController' => 'PhabricatorProjectBoardController', 10428 + 'PhabricatorProjectBoardFilterController' => 'PhabricatorProjectBoardController', 10427 10429 'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController', 10428 10430 'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController', 10429 10431 'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
+2 -1
src/applications/project/application/PhabricatorProjectApplication.php
··· 66 66 'subprojects/(?P<id>[1-9]\d*)/' 67 67 => 'PhabricatorProjectSubprojectsController', 68 68 'board/(?P<id>[1-9]\d*)/'. 69 - '(?P<filter>filter/)?'. 70 69 '(?:query/(?P<queryKey>[^/]+)/)?' 71 70 => 'PhabricatorProjectBoardViewController', 72 71 'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController', ··· 92 91 => 'PhabricatorProjectBoardBackgroundController', 93 92 'default/(?P<target>[^/]+)/' 94 93 => 'PhabricatorProjectBoardDefaultController', 94 + 'filter/(?:query/(?P<queryKey>[^/]+)/)?' 95 + => 'PhabricatorProjectBoardFilterController', 95 96 ), 96 97 'column/' => array( 97 98 'remove/(?P<id>\d+)/' =>
+1 -1
src/applications/project/controller/PhabricatorProjectBoardController.php
··· 22 22 ->readFromRequest($request); 23 23 } 24 24 25 - final protected function newBoardDialog() { 25 + final protected function newWorkboardDialog() { 26 26 $dialog = $this->newDialog(); 27 27 28 28 $state = $this->getViewState();
+1 -1
src/applications/project/controller/PhabricatorProjectBoardDefaultController.php
··· 72 72 return id(new AphrontRedirectResponse())->setURI($board_uri); 73 73 } 74 74 75 - return $this->newBoardDialog() 75 + return $this->newWorkboardDialog() 76 76 ->setTitle($title) 77 77 ->appendChild($body) 78 78 ->addCancelButton($board_uri)
+56
src/applications/project/controller/PhabricatorProjectBoardFilterController.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectBoardFilterController 4 + extends PhabricatorProjectBoardController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getViewer(); 8 + 9 + $response = $this->loadProject(); 10 + if ($response) { 11 + return $response; 12 + } 13 + 14 + $project = $this->getProject(); 15 + $state = $this->getViewState(); 16 + $board_uri = $state->newWorkboardURI(); 17 + 18 + $search_engine = $state->getSearchEngine(); 19 + 20 + $is_submit = $request->isFormPost(); 21 + 22 + if ($is_submit) { 23 + $saved_query = $search_engine->buildSavedQueryFromRequest($request); 24 + $search_engine->saveQuery($saved_query); 25 + } else { 26 + $saved_query = $state->getSavedQuery(); 27 + if (!$saved_query) { 28 + return new Aphront404Response(); 29 + } 30 + } 31 + 32 + $filter_form = id(new AphrontFormView()) 33 + ->setUser($viewer); 34 + 35 + $search_engine->buildSearchForm($filter_form, $saved_query); 36 + 37 + $errors = $search_engine->getErrors(); 38 + 39 + if ($is_submit && !$errors) { 40 + $query_key = $saved_query->getQueryKey(); 41 + 42 + $state->setQueryKey($query_key); 43 + $board_uri = $state->newWorkboardURI(); 44 + 45 + return id(new AphrontRedirectResponse())->setURI($board_uri); 46 + } 47 + 48 + return $this->newWorkboardDialog() 49 + ->setWidth(AphrontDialogView::WIDTH_FULL) 50 + ->setTitle(pht('Advanced Filter')) 51 + ->appendChild($filter_form->buildLayoutView()) 52 + ->setErrors($errors) 53 + ->addSubmitButton(pht('Apply Filter')) 54 + ->addCancelButton($board_uri); 55 + } 56 + }
+8 -59
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 21 21 $state = $this->getViewState(); 22 22 $board_uri = $project->getWorkboardURI(); 23 23 24 - $search_engine = id(new ManiphestTaskSearchEngine()) 25 - ->setViewer($viewer) 26 - ->setBaseURI($board_uri) 27 - ->setIsBoardView(true); 28 - 29 - if ($request->isFormPost() 30 - && !$request->getBool('initialize') 31 - && !$request->getStr('move') 32 - && !$request->getStr('queryColumnID')) { 33 - $saved = $search_engine->buildSavedQueryFromRequest($request); 34 - $search_engine->saveQuery($saved); 35 - $filter_form = id(new AphrontFormView()) 36 - ->setUser($viewer); 37 - $search_engine->buildSearchForm($filter_form, $saved); 38 - if ($search_engine->getErrors()) { 39 - return $this->newDialog() 40 - ->setWidth(AphrontDialogView::WIDTH_FULL) 41 - ->setTitle(pht('Advanced Filter')) 42 - ->appendChild($filter_form->buildLayoutView()) 43 - ->setErrors($search_engine->getErrors()) 44 - ->setSubmitURI($board_uri) 45 - ->addSubmitButton(pht('Apply Filter')) 46 - ->addCancelButton($board_uri); 47 - } 48 - 49 - $query_key = $saved->getQueryKey(); 50 - $results_uri = $search_engine->getQueryResultsPageURI($query_key); 51 - $results_uri = $state->newURI($results_uri); 52 - 53 - return id(new AphrontRedirectResponse())->setURI($results_uri); 24 + $search_engine = $state->getSearchEngine(); 25 + $query_key = $state->getQueryKey(); 26 + $saved = $state->getSavedQuery(); 27 + if (!$saved) { 28 + return new Aphront404Response(); 54 29 } 55 30 56 - $query_key = $state->getQueryKey(); 57 - 58 - $custom_query = null; 59 - if ($search_engine->isBuiltinQuery($query_key)) { 60 - $saved = $search_engine->buildSavedQueryFromBuiltin($query_key); 31 + if ($saved->getID()) { 32 + $custom_query = $saved; 61 33 } else { 62 - $saved = id(new PhabricatorSavedQueryQuery()) 63 - ->setViewer($viewer) 64 - ->withQueryKeys(array($query_key)) 65 - ->executeOne(); 66 - 67 - if (!$saved) { 68 - return new Aphront404Response(); 69 - } 70 - 71 - $custom_query = $saved; 72 - } 73 - 74 - if ($request->getURIData('filter')) { 75 - $filter_form = id(new AphrontFormView()) 76 - ->setUser($viewer); 77 - $search_engine->buildSearchForm($filter_form, $saved); 78 - 79 - return $this->newDialog() 80 - ->setWidth(AphrontDialogView::WIDTH_FULL) 81 - ->setTitle(pht('Advanced Filter')) 82 - ->appendChild($filter_form->buildLayoutView()) 83 - ->setSubmitURI($board_uri) 84 - ->addSubmitButton(pht('Apply Filter')) 85 - ->addCancelButton($board_uri); 34 + $custom_query = null; 86 35 } 87 36 88 37 $task_query = $search_engine->buildQueryFromSavedQuery($saved);
+61
src/applications/project/state/PhabricatorWorkboardViewState.php
··· 3 3 final class PhabricatorWorkboardViewState 4 4 extends Phobject { 5 5 6 + private $viewer; 6 7 private $project; 7 8 private $requestState = array(); 9 + private $savedQuery; 10 + private $searchEngine; 8 11 9 12 public function setProject(PhabricatorProject $project) { 10 13 $this->project = $project; ··· 40 43 $this->requestState['filter'] = $request->getURIData('queryKey'); 41 44 } 42 45 46 + $this->viewer = $request->getViewer(); 47 + 43 48 return $this; 44 49 } 45 50 51 + public function getViewer() { 52 + return $this->viewer; 53 + } 54 + 55 + public function getSavedQuery() { 56 + if ($this->savedQuery === null) { 57 + $this->savedQuery = $this->newSavedQuery(); 58 + } 59 + 60 + return $this->savedQuery; 61 + } 62 + 63 + private function newSavedQuery() { 64 + $search_engine = $this->getSearchEngine(); 65 + $query_key = $this->getQueryKey(); 66 + $viewer = $this->getViewer(); 67 + 68 + if ($search_engine->isBuiltinQuery($query_key)) { 69 + $saved_query = $search_engine->buildSavedQueryFromBuiltin($query_key); 70 + } else { 71 + $saved_query = id(new PhabricatorSavedQueryQuery()) 72 + ->setViewer($viewer) 73 + ->withQueryKeys(array($query_key)) 74 + ->executeOne(); 75 + } 76 + 77 + return $saved_query; 78 + } 79 + 80 + public function getSearchEngine() { 81 + if ($this->searchEngine === null) { 82 + $this->searchEngine = $this->newSearchEngine(); 83 + } 84 + 85 + return $this->searchEngine; 86 + } 87 + 88 + private function newSearchEngine() { 89 + $viewer = $this->getViewer(); 90 + 91 + // TODO: This URI is not fully state-preserving, because "SearchEngine" 92 + // does not preserve URI parameters when constructing some URIs at time of 93 + // writing. 94 + $board_uri = $this->getProject()->getWorkboardURI(); 95 + 96 + return id(new ManiphestTaskSearchEngine()) 97 + ->setViewer($viewer) 98 + ->setBaseURI($board_uri) 99 + ->setIsBoardView(true); 100 + } 101 + 46 102 public function newWorkboardURI($path = null) { 47 103 $project = $this->getProject(); 48 104 $uri = urisprintf('%p%p', $project->getWorkboardURI(), $path); ··· 116 172 } 117 173 118 174 return $this->getDefaultQueryKey(); 175 + } 176 + 177 + public function setQueryKey($query_key) { 178 + $this->requestState['filter'] = $query_key; 179 + return $this; 119 180 } 120 181 121 182 private function isValidOrder($order) {