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

Allow profile menu items to be reordered

Summary: Ref T10054. Allows users to drag menu items to reorder them.

Test Plan: Reordered a project menu.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10054

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

+173 -1
+9
resources/celerity/map.php
··· 424 424 'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8', 425 425 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f', 426 426 'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43', 427 + 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072', 427 428 'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08', 428 429 'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f', 429 430 'rsrc/js/application/transactions/behavior-comment-actions.js' => '1f2fcaf8', ··· 663 664 'javelin-behavior-remarkup-preview' => '4b700e9e', 664 665 'javelin-behavior-reorder-applications' => '76b9fc3e', 665 666 'javelin-behavior-reorder-columns' => 'e1d25dfb', 667 + 'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072', 666 668 'javelin-behavior-repository-crossreference' => 'e5339c43', 667 669 'javelin-behavior-scrollbar' => '834a1173', 668 670 'javelin-behavior-search-reorder-queries' => 'e9581f08', ··· 1932 1934 ), 1933 1935 'e292eaf4' => array( 1934 1936 'javelin-install', 1937 + ), 1938 + 'e2e0a072' => array( 1939 + 'javelin-behavior', 1940 + 'javelin-stratcom', 1941 + 'javelin-workflow', 1942 + 'javelin-dom', 1943 + 'phabricator-draggable-list', 1935 1944 ), 1936 1945 'e379b58e' => array( 1937 1946 'javelin-behavior',
+1
src/applications/base/PhabricatorApplication.php
··· 642 642 '(?P<panelAction>view)/(?P<panelID>[^/]+)/' => $controller, 643 643 '(?P<panelAction>hide)/(?P<panelID>[^/]+)/' => $controller, 644 644 '(?P<panelAction>configure)/' => $controller, 645 + '(?P<panelAction>reorder)/' => $controller, 645 646 '(?P<panelAction>edit)/'.$edit_route => $controller, 646 647 '(?P<panelAction>new)/(?<panelKey>[^/]+)/'.$edit_route => $controller, 647 648 '(?P<panelAction>builtin)/(?<panelID>[^/]+)/'.$edit_route
+9
src/applications/search/editor/PhabricatorProfilePanelEditor.php
··· 15 15 $types = parent::getTransactionTypes(); 16 16 17 17 $types[] = PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY; 18 + $types[] = PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER; 18 19 19 20 return $types; 20 21 } ··· 27 28 case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY: 28 29 $key = $xaction->getMetadataValue('property.key'); 29 30 return $object->getPanelProperty($key, null); 31 + case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER: 32 + return $object->getPanelOrder(); 30 33 } 31 34 } 32 35 ··· 37 40 switch ($xaction->getTransactionType()) { 38 41 case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY: 39 42 return $xaction->getNewValue(); 43 + case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER: 44 + return (int)$xaction->getNewValue(); 40 45 } 41 46 } 42 47 ··· 50 55 $value = $xaction->getNewValue(); 51 56 $object->setPanelProperty($key, $value); 52 57 return; 58 + case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER: 59 + $object->setPanelOrder($xaction->getNewValue()); 60 + return; 53 61 } 54 62 55 63 return parent::applyCustomInternalTransaction($object, $xaction); ··· 61 69 62 70 switch ($xaction->getTransactionType()) { 63 71 case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY: 72 + case PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER: 64 73 return; 65 74 } 66 75
+98 -1
src/applications/search/engine/PhabricatorProfilePanelEngine.php
··· 91 91 $content = $this->buildPanelConfigureContent($panel_list); 92 92 $crumbs->addTextCrumb(pht('Configure Menu')); 93 93 break; 94 + case 'reorder': 95 + $content = $this->buildPanelReorderContent($panel_list); 96 + break; 94 97 case 'new': 95 98 $panel_key = $request->getURIData('panelKey'); 96 99 $content = $this->buildPanelNewContent($panel_key); ··· 204 207 $impl->setViewer($viewer); 205 208 } 206 209 210 + $panels = msort($panels, 'getSortKey'); 211 + 207 212 // Normalize keys since callers shouldn't rely on this array being 208 213 // partially keyed. 209 214 $panels = array_values($panels); ··· 305 310 return "/project/{$id}/panel/{$path}"; 306 311 } 307 312 313 + private function buildPanelReorderContent(array $panels) { 314 + $viewer = $this->getViewer(); 315 + $object = $this->getProfileObject(); 316 + 317 + PhabricatorPolicyFilter::requireCapability( 318 + $viewer, 319 + $object, 320 + PhabricatorPolicyCapability::CAN_EDIT); 321 + 322 + $controller = $this->getController(); 323 + $request = $controller->getRequest(); 324 + 325 + $request->validateCSRF(); 326 + 327 + $order = $request->getStrList('order'); 328 + 329 + $by_builtin = array(); 330 + $by_id = array(); 331 + 332 + foreach ($panels as $key => $panel) { 333 + $id = $panel->getID(); 334 + if ($id) { 335 + $by_id[$id] = $key; 336 + continue; 337 + } 338 + 339 + $builtin_key = $panel->getBuiltinKey(); 340 + if ($builtin_key) { 341 + $by_builtin[$builtin_key] = $key; 342 + continue; 343 + } 344 + } 345 + 346 + $key_order = array(); 347 + foreach ($order as $order_item) { 348 + if (isset($by_id[$order_item])) { 349 + $key_order[] = $by_id[$order_item]; 350 + continue; 351 + } 352 + if (isset($by_builtin[$order_item])) { 353 + $key_order[] = $by_builtin[$order_item]; 354 + continue; 355 + } 356 + } 357 + 358 + $panels = array_select_keys($panels, $key_order) + $panels; 359 + 360 + $type_order = 361 + PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER; 362 + 363 + $order = 1; 364 + foreach ($panels as $panel) { 365 + $xactions = array(); 366 + 367 + $xactions[] = id(new PhabricatorProfilePanelConfigurationTransaction()) 368 + ->setTransactionType($type_order) 369 + ->setNewValue($order); 370 + 371 + $editor = id(new PhabricatorProfilePanelEditor()) 372 + ->setContentSourceFromRequest($request) 373 + ->setActor($viewer) 374 + ->setContinueOnMissingFields(true) 375 + ->setContinueOnNoEffect(true) 376 + ->applyTransactions($panel, $xactions); 377 + 378 + $order++; 379 + } 380 + 381 + return id(new AphrontRedirectResponse()) 382 + ->setURI($this->getConfigureURI()); 383 + } 384 + 385 + 308 386 private function buildPanelConfigureContent(array $panels) { 309 387 $viewer = $this->getViewer(); 310 388 $object = $this->getProfileObject(); ··· 314 392 $object, 315 393 PhabricatorPolicyCapability::CAN_EDIT); 316 394 317 - $list = new PHUIObjectItemListView(); 395 + $list_id = celerity_generate_unique_node_id(); 396 + 397 + Javelin::initBehavior( 398 + 'reorder-profile-menu-items', 399 + array( 400 + 'listID' => $list_id, 401 + 'orderURI' => $this->getPanelURI('reorder/'), 402 + )); 403 + 404 + $list = id(new PHUIObjectItemListView()) 405 + ->setID($list_id); 406 + 318 407 foreach ($panels as $panel) { 319 408 $id = $panel->getID(); 320 409 $builtin_key = $panel->getBuiltinKey(); ··· 336 425 $item->addAttribute($type); 337 426 338 427 if ($can_edit) { 428 + $item 429 + ->setGrippable(true) 430 + ->addSigil('profile-menu-item') 431 + ->setMetadata( 432 + array( 433 + 'key' => nonempty($id, $builtin_key), 434 + )); 435 + 339 436 if ($id) { 340 437 $item->setHref($this->getPanelURI("edit/{$id}/")); 341 438 } else {
+14
src/applications/search/storage/PhabricatorProfilePanelConfiguration.php
··· 101 101 return $this->getPanel()->getDisplayName($this); 102 102 } 103 103 104 + public function getSortKey() { 105 + $order = $this->getPanelOrder(); 106 + if ($order === null) { 107 + $order = 'Z'; 108 + } else { 109 + $order = sprintf('%020d', $order); 110 + } 111 + 112 + return sprintf( 113 + '~%s%020d', 114 + $order, 115 + $this->getID()); 116 + } 117 + 104 118 105 119 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 106 120
+1
src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php
··· 4 4 extends PhabricatorApplicationTransaction { 5 5 6 6 const TYPE_PROPERTY = 'profilepanel.property'; 7 + const TYPE_ORDER = 'profilepanel.order'; 7 8 8 9 public function getApplicationName() { 9 10 return 'search';
+41
webroot/rsrc/js/application/search/behavior-reorder-profile-menu-items.js
··· 1 + /** 2 + * @provides javelin-behavior-reorder-profile-menu-items 3 + * @requires javelin-behavior 4 + * javelin-stratcom 5 + * javelin-workflow 6 + * javelin-dom 7 + * phabricator-draggable-list 8 + */ 9 + 10 + JX.behavior('reorder-profile-menu-items', function(config) { 11 + 12 + var root = JX.$(config.listID); 13 + 14 + var list = new JX.DraggableList('profile-menu-item', root) 15 + .setFindItemsHandler(function() { 16 + return JX.DOM.scry(root, 'li', 'profile-menu-item'); 17 + }); 18 + 19 + list.listen('didDrop', function(node) { 20 + var nodes = list.findItems(); 21 + var order = []; 22 + var key; 23 + for (var ii = 0; ii < nodes.length; ii++) { 24 + key = JX.Stratcom.getData(nodes[ii]).key; 25 + if (key) { 26 + order.push(key); 27 + } 28 + } 29 + 30 + list.lock(); 31 + JX.DOM.alterClass(node, 'drag-sending', true); 32 + 33 + new JX.Workflow(config.orderURI, {order: order.join()}) 34 + .setHandler(function() { 35 + JX.DOM.alterClass(node, 'drag-sending', false); 36 + list.unlock(); 37 + }) 38 + .start(); 39 + }); 40 + 41 + });