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

Add UI for alternate board ordering rules

Summary:
Ref T4807. This doesn't actually do anything yet, but adds a dropdown menu for choosing an ordering and gets all the UI working correctly.

This also fixes a bug where column hidden state wouldn't persist across filter changes.

(I won't land this until it does something, but the next diff will probably be a mess so this seemed like a clean place to sever things.)

Test Plan:
{F187114}

- Altered sort ordering.
- Altered hidden state and filters, verified all states persisted correctly.
- Added `phlog()` to edit/create and move controllers and verified they receive sort information.

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: swisspol, chad, epriestley

Maniphest Tasks: T4807

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

+145 -26
+10 -10
resources/celerity/map.php
··· 415 415 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => 'fe9a552f', 416 416 'rsrc/js/application/ponder/behavior-votebox.js' => '4e9b766b', 417 417 'rsrc/js/application/projects/behavior-boards-dropdown.js' => '0ec56e1d', 418 - 'rsrc/js/application/projects/behavior-project-boards.js' => '21171a56', 418 + 'rsrc/js/application/projects/behavior-project-boards.js' => 'f47fa23b', 419 419 'rsrc/js/application/projects/behavior-project-create.js' => '065227cc', 420 420 'rsrc/js/application/projects/behavior-reorder-columns.js' => '09eee344', 421 421 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', ··· 639 639 'javelin-behavior-policy-control' => 'f3fef818', 640 640 'javelin-behavior-policy-rule-editor' => 'fe9a552f', 641 641 'javelin-behavior-ponder-votebox' => '4e9b766b', 642 - 'javelin-behavior-project-boards' => '21171a56', 642 + 'javelin-behavior-project-boards' => 'f47fa23b', 643 643 'javelin-behavior-project-create' => '065227cc', 644 644 'javelin-behavior-refresh-csrf' => '7814b593', 645 645 'javelin-behavior-releeph-preview-branch' => 'b2b4fbaf', ··· 982 982 '1ffb3a9c' => array( 983 983 'javelin-util', 984 984 'javelin-magical-init', 985 - ), 986 - '21171a56' => array( 987 - 'javelin-behavior', 988 - 'javelin-dom', 989 - 'javelin-util', 990 - 'javelin-stratcom', 991 - 'javelin-workflow', 992 - 'phabricator-draggable-list', 993 985 ), 994 986 '2290aeef' => array( 995 987 'javelin-install', ··· 1851 1843 'phuix-action-list-view', 1852 1844 'phuix-action-view', 1853 1845 'javelin-workflow', 1846 + ), 1847 + 'f47fa23b' => array( 1848 + 'javelin-behavior', 1849 + 'javelin-dom', 1850 + 'javelin-util', 1851 + 'javelin-stratcom', 1852 + 'javelin-workflow', 1853 + 'phabricator-draggable-list', 1854 1854 ), 1855 1855 'f51afce0' => array( 1856 1856 'javelin-behavior',
+1
src/__phutil_library_map__.php
··· 3831 3831 'PassphraseCredential' => array( 3832 3832 'PassphraseDAO', 3833 3833 'PhabricatorPolicyInterface', 3834 + 'PhabricatorDestructibleInterface', 3834 3835 ), 3835 3836 'PassphraseCredentialControl' => 'AphrontFormControl', 3836 3837 'PassphraseCredentialCreateController' => 'PassphraseController',
+3
src/applications/maniphest/controller/ManiphestTaskEditController.php
··· 11 11 public function processRequest() { 12 12 $request = $this->getRequest(); 13 13 $user = $request->getUser(); 14 + 14 15 $response_type = $request->getStr('responseType', 'task'); 16 + $order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER); 15 17 16 18 $can_edit_assign = $this->hasApplicationCapability( 17 19 ManiphestEditAssignCapability::CAPABILITY); ··· 531 533 ->setUser($user) 532 534 ->addHiddenInput('template', $template_id) 533 535 ->addHiddenInput('responseType', $response_type) 536 + ->addHiddenInput('order', $order) 534 537 ->addHiddenInput('columnPHID', $request->getStr('columnPHID')); 535 538 536 539 if ($parent_task) {
+112 -7
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 8 8 private $handles; 9 9 private $queryKey; 10 10 private $filter; 11 + private $sortKey; 12 + private $showHidden; 11 13 12 14 public function shouldAllowPublic() { 13 15 return true; ··· 25 27 $viewer = $request->getUser(); 26 28 27 29 $show_hidden = $request->getBool('hidden'); 30 + $this->showHidden = $show_hidden; 28 31 29 32 $project = id(new PhabricatorProjectQuery()) 30 33 ->setViewer($viewer) ··· 41 44 42 45 $this->setProject($project); 43 46 $this->id = $project->getID(); 47 + 48 + $sort_key = $request->getStr('order'); 49 + switch ($sort_key) { 50 + case PhabricatorProjectColumn::ORDER_NATURAL: 51 + case PhabricatorProjectColumn::ORDER_PRIORITY: 52 + break; 53 + default: 54 + $sort_key = PhabricatorProjectColumn::DEFAULT_ORDER; 55 + break; 56 + } 57 + $this->sortKey = $sort_key; 44 58 45 59 $column_query = id(new PhabricatorProjectColumnQuery()) 46 60 ->setViewer($viewer) ··· 90 104 $saved = $engine->buildSavedQueryFromRequest($request); 91 105 $engine->saveQuery($saved); 92 106 return id(new AphrontRedirectResponse())->setURI( 93 - $engine->getQueryResultsPageURI($saved->getQueryKey())); 107 + $this->getURIWithState( 108 + $engine->getQueryResultsPageURI($saved->getQueryKey()))); 94 109 } 95 110 96 111 $query_key = $this->queryKey; 97 112 if (!$query_key) { 98 113 $query_key = 'open'; 99 114 } 115 + $this->queryKey = $query_key; 100 116 101 117 $custom_query = null; 102 118 if ($engine->isBuiltinQuery($query_key)) { ··· 180 196 'projectPHID' => $project->getPHID(), 181 197 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'), 182 198 'createURI' => '/maniphest/task/create/', 199 + 'order' => $this->sortKey, 183 200 )); 184 201 185 202 $this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks); ··· 234 251 Javelin::initBehavior( 235 252 'boards-dropdown', 236 253 array()); 254 + 255 + $sort_menu = $this->buildSortMenu( 256 + $viewer, 257 + $sort_key); 237 258 238 259 $filter_menu = $this->buildFilterMenu( 239 260 $viewer, ··· 256 277 ->setNoBackground(true) 257 278 ->setImage($project->getProfileImageURI()) 258 279 ->setImageURL($this->getApplicationURI('view/'.$project->getID().'/')) 280 + ->addActionLink($sort_menu) 259 281 ->addActionLink($filter_menu) 260 282 ->addActionLink($manage_menu) 261 283 ->setPolicyObject($project); ··· 274 296 )); 275 297 } 276 298 299 + private function buildSortMenu( 300 + PhabricatorUser $viewer, 301 + $sort_key) { 302 + 303 + $sort_icon = id(new PHUIIconView()) 304 + ->setIconFont('fa-sort-amount-asc bluegrey'); 305 + 306 + $named = array( 307 + PhabricatorProjectColumn::ORDER_NATURAL => pht('Natural'), 308 + PhabricatorProjectColumn::ORDER_PRIORITY => pht('Sort by Priority'), 309 + ); 310 + 311 + $base_uri = $this->getURIWithState(); 312 + 313 + $items = array(); 314 + foreach ($named as $key => $name) { 315 + $is_selected = ($key == $sort_key); 316 + if ($is_selected) { 317 + $active_order = $name; 318 + } 319 + 320 + $item = id(new PhabricatorActionView()) 321 + ->setIcon('fa-sort-amount-asc') 322 + ->setSelected($is_selected) 323 + ->setName($name); 324 + 325 + $uri = $base_uri->alter('order', $key); 326 + $item->setHref($uri); 327 + 328 + $items[] = $item; 329 + } 330 + 331 + $sort_menu = id(new PhabricatorActionListView()) 332 + ->setUser($viewer); 333 + foreach ($items as $item) { 334 + $sort_menu->addAction($item); 335 + } 336 + 337 + $sort_button = id(new PHUIButtonView()) 338 + ->setText(pht('Sort: %s', $active_order)) 339 + ->setIcon($sort_icon) 340 + ->setTag('a') 341 + ->setHref('#') 342 + ->addSigil('boards-dropdown-menu') 343 + ->setMetadata( 344 + array( 345 + 'items' => hsprintf('%s', $sort_menu), 346 + )); 347 + 348 + return $sort_button; 349 + } 277 350 private function buildFilterMenu( 278 351 PhabricatorUser $viewer, 279 352 $custom_query, ··· 314 387 ->setName($name); 315 388 316 389 if ($is_custom) { 317 - $item->setHref( 318 - $this->getApplicationURI( 319 - 'board/'.$this->id.'/filter/query/'.$key.'/')); 390 + $uri = $this->getApplicationURI( 391 + 'board/'.$this->id.'/filter/query/'.$key.'/'); 320 392 $item->setWorkflow(true); 321 393 } else { 322 - $item->setHref($engine->getQueryResultsPageURI($key)); 394 + $uri = $engine->getQueryResultsPageURI($key); 323 395 } 324 396 397 + $uri = $this->getURIWithState($uri); 398 + $item->setHref($uri); 399 + 325 400 $items[] = $item; 326 401 } 327 402 ··· 383 458 ->setWorkflow(true); 384 459 385 460 if ($show_hidden) { 386 - $hidden_uri = $request->getRequestURI() 461 + $hidden_uri = $this->getURIWithState() 387 462 ->setQueryParam('hidden', null); 388 463 $hidden_icon = 'fa-eye-slash'; 389 464 $hidden_text = pht('Hide Hidden Columns'); 390 465 } else { 391 - $hidden_uri = $request->getRequestURI() 466 + $hidden_uri = $this->getURIWithState() 392 467 ->setQueryParam('hidden', 'true'); 393 468 $hidden_icon = 'fa-eye'; 394 469 $hidden_text = pht('Show Hidden Columns'); ··· 444 519 445 520 return id(new AphrontDialogResponse()) 446 521 ->setDialog($dialog); 522 + } 523 + 524 + 525 + /** 526 + * Add current state parameters (like order and the visibility of hidden 527 + * columns) to a URI. 528 + * 529 + * This allows actions which toggle or adjust one piece of state to keep 530 + * the rest of the board state persistent. If no URI is provided, this method 531 + * starts with the request URI. 532 + * 533 + * @param string|null URI to add state parameters to. 534 + * @return PhutilURI URI with state parameters. 535 + */ 536 + private function getURIWithState($base = null) { 537 + if ($base === null) { 538 + $base = $this->getRequest()->getRequestURI(); 539 + } 540 + 541 + $base = new PhutilURI($base); 542 + 543 + if ($this->sortKey != PhabricatorProjectColumn::DEFAULT_ORDER) { 544 + $base->setQueryParam('order', $this->sortKey); 545 + } else { 546 + $base->setQueryParam('order', null); 547 + } 548 + 549 + $base->setQueryParam('hidden', $this->showHidden ? 'true' : null); 550 + 551 + return $base; 447 552 } 448 553 449 554 }
+4
src/applications/project/storage/PhabricatorProjectColumn.php
··· 9 9 const STATUS_ACTIVE = 0; 10 10 const STATUS_HIDDEN = 1; 11 11 12 + const DEFAULT_ORDER = 'natural'; 13 + const ORDER_NATURAL = 'natural'; 14 + const ORDER_PRIORITY = 'priority'; 15 + 12 16 protected $name; 13 17 protected $status; 14 18 protected $projectPHID;
+15 -9
webroot/rsrc/js/application/projects/behavior-project-boards.js
··· 81 81 data.beforePHID = before_phid; 82 82 } 83 83 84 + data.order = config.order; 85 + 84 86 var workflow = new JX.Workflow(config.moveURI, data) 85 87 .setHandler(function(response) { 86 88 onresponse(response, item, list); ··· 148 150 e.kill(); 149 151 var column = e.getNode('project-column'); 150 152 var request_data = { 151 - 'responseType' : 'card', 152 - 'columnPHID' : JX.Stratcom.getData(column).columnPHID }; 153 + responseType: 'card', 154 + columnPHID: JX.Stratcom.getData(column).columnPHID, 155 + order: config.order 156 + }; 153 157 new JX.Workflow(e.getNode('tag:a').href, request_data) 154 - .setHandler(JX.bind(null, onedit, column)) 155 - .start(); 158 + .setHandler(JX.bind(null, onedit, column)) 159 + .start(); 156 160 }); 157 161 158 162 JX.Stratcom.listen( ··· 162 166 e.kill(); 163 167 var column_phid = e.getNodeData('column-add-task').columnPHID; 164 168 var request_data = { 165 - 'responseType' : 'card', 166 - 'columnPHID' : column_phid, 167 - 'projects' : config.projectPHID }; 169 + responseType: 'card', 170 + columnPHID: column_phid, 171 + projects: config.projectPHID, 172 + order: config.order 173 + }; 168 174 var cols = JX.DOM.scry(JX.$(config.boardID), 'ul', 'project-column'); 169 175 var ii; 170 176 var column; ··· 175 181 } 176 182 } 177 183 new JX.Workflow(config.createURI, request_data) 178 - .setHandler(JX.bind(null, onedit, column)) 179 - .start(); 184 + .setHandler(JX.bind(null, onedit, column)) 185 + .start(); 180 186 }); 181 187 });