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

at recaptime-dev/main 291 lines 7.7 kB view raw
1<?php 2 3final class PhabricatorWorkboardViewState 4 extends Phobject { 5 6 private $viewer; 7 private $project; 8 private $requestState = array(); 9 private $savedQuery; 10 private $searchEngine; 11 private $layoutEngine; 12 private $objects; 13 14 public function setProject(PhabricatorProject $project) { 15 $this->project = $project; 16 return $this; 17 } 18 19 public function getProject() { 20 return $this->project; 21 } 22 23 public function readFromRequest(AphrontRequest $request) { 24 if ($request->getExists('hidden')) { 25 $this->requestState['hidden'] = $request->getBool('hidden'); 26 } 27 28 if ($request->getExists('order')) { 29 $this->requestState['order'] = $request->getStr('order'); 30 } 31 32 // On some pathways, the search engine query key may be specified with 33 // either a "?filter=X" query parameter or with a "/query/X/" URI 34 // component. If both are present, the URI component is controlling. 35 36 // In particular, the "queryKey" URI parameter is used by 37 // "buildSavedQueryFromRequest()" when we are building custom board filters 38 // by invoking SearchEngine code. 39 40 if ($request->getExists('filter')) { 41 $this->requestState['filter'] = $request->getStr('filter'); 42 } 43 44 if (phutil_nonempty_string($request->getURIData('queryKey'))) { 45 $this->requestState['filter'] = $request->getURIData('queryKey'); 46 } 47 48 $this->viewer = $request->getViewer(); 49 50 return $this; 51 } 52 53 public function getViewer() { 54 return $this->viewer; 55 } 56 57 public function getSavedQuery() { 58 if ($this->savedQuery === null) { 59 $this->savedQuery = $this->newSavedQuery(); 60 } 61 62 return $this->savedQuery; 63 } 64 65 private function newSavedQuery() { 66 $search_engine = $this->getSearchEngine(); 67 $query_key = $this->getQueryKey(); 68 $viewer = $this->getViewer(); 69 70 if ($search_engine->isBuiltinQuery($query_key)) { 71 $saved_query = $search_engine->buildSavedQueryFromBuiltin($query_key); 72 } else { 73 $saved_query = id(new PhabricatorSavedQueryQuery()) 74 ->setViewer($viewer) 75 ->withQueryKeys(array($query_key)) 76 ->executeOne(); 77 } 78 79 return $saved_query; 80 } 81 82 public function getSearchEngine() { 83 if ($this->searchEngine === null) { 84 $this->searchEngine = $this->newSearchEngine(); 85 } 86 87 return $this->searchEngine; 88 } 89 90 private function newSearchEngine() { 91 $viewer = $this->getViewer(); 92 93 // TODO: This URI is not fully state-preserving, because "SearchEngine" 94 // does not preserve URI parameters when constructing some URIs at time of 95 // writing. 96 $board_uri = $this->getProject()->getWorkboardURI(); 97 98 return id(new ManiphestTaskSearchEngine()) 99 ->setViewer($viewer) 100 ->setBaseURI($board_uri) 101 ->setIsBoardView(true); 102 } 103 104 public function newWorkboardURI($path = null) { 105 $project = $this->getProject(); 106 $uri = urisprintf('%s%s', $project->getWorkboardURI(), $path); 107 return $this->newURI($uri); 108 } 109 110 public function newURI($path) { 111 $project = $this->getProject(); 112 $uri = new PhutilURI($path); 113 114 $request_order = $this->getOrder(); 115 $default_order = $this->getDefaultOrder(); 116 if ($request_order !== $default_order) { 117 $request_value = idx($this->requestState, 'order'); 118 if ($request_value !== null) { 119 $uri->replaceQueryParam('order', $request_value); 120 } else { 121 $uri->removeQueryParam('order'); 122 } 123 } else { 124 $uri->removeQueryParam('order'); 125 } 126 127 $request_query = $this->getQueryKey(); 128 $default_query = $this->getDefaultQueryKey(); 129 if ($request_query !== $default_query) { 130 $request_value = idx($this->requestState, 'filter'); 131 if ($request_value !== null) { 132 $uri->replaceQueryParam('filter', $request_value); 133 } else { 134 $uri->removeQueryParam('filter'); 135 } 136 } else { 137 $uri->removeQueryParam('filter'); 138 } 139 140 if ($this->getShowHidden()) { 141 $uri->replaceQueryParam('hidden', 'true'); 142 } else { 143 $uri->removeQueryParam('hidden'); 144 } 145 146 return $uri; 147 } 148 149 public function getShowHidden() { 150 $request_show = idx($this->requestState, 'hidden'); 151 152 if ($request_show !== null) { 153 return $request_show; 154 } 155 156 return false; 157 } 158 159 public function getOrder() { 160 $request_order = idx($this->requestState, 'order'); 161 if ($request_order !== null) { 162 if ($this->isValidOrder($request_order)) { 163 return $request_order; 164 } 165 } 166 167 return $this->getDefaultOrder(); 168 } 169 170 public function getQueryKey() { 171 $request_query = idx($this->requestState, 'filter'); 172 if (phutil_nonempty_string($request_query)) { 173 return $request_query; 174 } 175 176 return $this->getDefaultQueryKey(); 177 } 178 179 public function setQueryKey($query_key) { 180 $this->requestState['filter'] = $query_key; 181 return $this; 182 } 183 184 private function isValidOrder($order) { 185 $map = PhabricatorProjectColumnOrder::getEnabledOrders(); 186 return isset($map[$order]); 187 } 188 189 private function getDefaultOrder() { 190 $project = $this->getProject(); 191 192 $default_order = $project->getDefaultWorkboardSort(); 193 194 if ($this->isValidOrder($default_order)) { 195 return $default_order; 196 } 197 198 return PhabricatorProjectColumnNaturalOrder::ORDERKEY; 199 } 200 201 private function getDefaultQueryKey() { 202 $project = $this->getProject(); 203 204 $default_query = $project->getDefaultWorkboardFilter(); 205 206 if (phutil_nonempty_string($default_query)) { 207 return $default_query; 208 } 209 210 return 'open'; 211 } 212 213 public function getQueryParameters() { 214 return $this->requestState; 215 } 216 217 public function getLayoutEngine() { 218 if ($this->layoutEngine === null) { 219 $this->layoutEngine = $this->newLayoutEngine(); 220 } 221 return $this->layoutEngine; 222 } 223 224 private function newLayoutEngine() { 225 $project = $this->getProject(); 226 $viewer = $this->getViewer(); 227 228 $board_phid = $project->getPHID(); 229 $objects = $this->getObjects(); 230 231 // Regardless of display order, pass tasks to the layout engine in ID order 232 // so layout is consistent. 233 $objects = msort($objects, 'getID'); 234 235 $layout_engine = id(new PhabricatorBoardLayoutEngine()) 236 ->setViewer($viewer) 237 ->setObjectPHIDs(array_keys($objects)) 238 ->setBoardPHIDs(array($board_phid)) 239 ->setFetchAllBoards(true) 240 ->executeLayout(); 241 242 return $layout_engine; 243 } 244 245 public function getBoardContainerPHIDs() { 246 $project = $this->getProject(); 247 $viewer = $this->getViewer(); 248 249 $container_phids = array($project->getPHID()); 250 if ($project->getHasSubprojects() || $project->getHasMilestones()) { 251 $descendants = id(new PhabricatorProjectQuery()) 252 ->setViewer($viewer) 253 ->withAncestorProjectPHIDs($container_phids) 254 ->execute(); 255 foreach ($descendants as $descendant) { 256 $container_phids[] = $descendant->getPHID(); 257 } 258 } 259 260 return $container_phids; 261 } 262 263 public function getObjects() { 264 if ($this->objects === null) { 265 $this->objects = $this->newObjects(); 266 } 267 268 return $this->objects; 269 } 270 271 private function newObjects() { 272 $viewer = $this->getViewer(); 273 $saved_query = $this->getSavedQuery(); 274 $search_engine = $this->getSearchEngine(); 275 276 $container_phids = $this->getBoardContainerPHIDs(); 277 278 $task_query = $search_engine->buildQueryFromSavedQuery($saved_query) 279 ->setViewer($viewer) 280 ->withEdgeLogicPHIDs( 281 PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 282 PhabricatorQueryConstraint::OPERATOR_ANCESTOR, 283 array($container_phids)); 284 285 $tasks = $task_query->execute(); 286 $tasks = mpull($tasks, null, 'getPHID'); 287 288 return $tasks; 289 } 290 291}