@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 `PhabricatorMenuView` to have items inserted in the middle

Summary:
Make `PhabricatorMenuView` more flexible, so callers can add items to the beginning/end/middle.

In particular, this allows event handlers to receive a $menu and call `addMenuItemToLabel('activity', ...)` or similar, for D4708.

Test Plan: Unit tests. Browsed site. Home page, Conpherence, and other pages with menus look correct.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

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

+232 -39
+2
src/__phutil_library_map__.php
··· 980 980 'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php', 981 981 'PhabricatorMenuItemView' => 'view/layout/PhabricatorMenuItemView.php', 982 982 'PhabricatorMenuView' => 'view/layout/PhabricatorMenuView.php', 983 + 'PhabricatorMenuViewTestCase' => 'view/layout/__tests__/PhabricatorMenuViewTestCase.php', 983 984 'PhabricatorMercurialGraphStream' => 'applications/repository/daemon/PhabricatorMercurialGraphStream.php', 984 985 'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php', 985 986 'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php', ··· 2398 2399 'PhabricatorMarkupCache' => 'PhabricatorCacheDAO', 2399 2400 'PhabricatorMenuItemView' => 'AphrontTagView', 2400 2401 'PhabricatorMenuView' => 'AphrontTagView', 2402 + 'PhabricatorMenuViewTestCase' => 'PhabricatorTestCase', 2401 2403 'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions', 2402 2404 'PhabricatorMetaMTAController' => 'PhabricatorController', 2403 2405 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
-1
src/applications/auth/application/PhabricatorApplicationAuth.php
··· 22 22 $item->setIcon('power'); 23 23 $item->setWorkflow(true); 24 24 $item->setHref('/logout/'); 25 - $item->setSortOrder(2.0); 26 25 $item->setSelected(($controller instanceof PhabricatorLogoutController)); 27 26 $items[] = $item; 28 27 }
-1
src/applications/diviner/application/PhabricatorApplicationDiviner.php
··· 44 44 $item->setName(pht('%s Help', $application->getName())); 45 45 $item->setIcon('help'); 46 46 $item->setHref($application->getHelpURI()); 47 - $item->setSortOrder(0.1); 48 47 $items[] = $item; 49 48 } 50 49
-1
src/applications/people/application/PhabricatorApplicationPeople.php
··· 58 58 $item = new PhabricatorMenuItemView(); 59 59 $item->setName($user->getUsername()); 60 60 $item->setHref('/p/'.$user->getUsername().'/'); 61 - $item->setSortOrder(0.0); 62 61 $item->addClass('phabricator-core-menu-item-profile'); 63 62 64 63 $classes = array(
-1
src/applications/settings/application/PhabricatorApplicationSettings.php
··· 44 44 $item->setIcon('settings'); 45 45 $item->setSelected($selected); 46 46 $item->setHref('/settings/'); 47 - $item->setSortOrder(0.90); 48 47 $items[] = $item; 49 48 } 50 49
+4 -1
src/view/layout/AphrontSideNavFilterView.php
··· 121 121 } 122 122 123 123 public function addCustomBlock($block) { 124 - $this->menu->appendChild($block); 124 + $this->menu->addMenuItem( 125 + id(new PhabricatorMenuItemView()) 126 + ->setType(PhabricatorMenuItemView::TYPE_CUSTOM) 127 + ->appendChild($block)); 125 128 return $this; 126 129 } 127 130
+1 -10
src/view/layout/PhabricatorMenuItemView.php
··· 6 6 const TYPE_SPACER = 'type-spacer'; 7 7 const TYPE_LABEL = 'type-label'; 8 8 const TYPE_BUTTON = 'type-button'; 9 + const TYPE_CUSTOM = 'type-custom'; 9 10 10 11 private $name; 11 12 private $href; 12 13 private $type = self::TYPE_LINK; 13 14 private $isExternal; 14 15 private $key; 15 - private $sortOrder = 1.0; 16 16 private $icon; 17 17 private $selected; 18 18 ··· 86 86 87 87 public function getIsExternal() { 88 88 return $this->isExternal; 89 - } 90 - 91 - public function setSortOrder($order) { 92 - $this->sortOrder = $order; 93 - return $this; 94 - } 95 - 96 - public function getSortOrder() { 97 - return $this->sortOrder; 98 89 } 99 90 100 91 protected function getTagName() {
+81 -3
src/view/layout/PhabricatorMenuView.php
··· 4 4 5 5 private $items = array(); 6 6 7 + protected function canAppendChild() { 8 + return false; 9 + } 10 + 7 11 public function newLabel($name) { 8 12 $item = id(new PhabricatorMenuItemView()) 9 13 ->setType(PhabricatorMenuItemView::TYPE_LABEL) ··· 37 41 } 38 42 39 43 public function addMenuItem(PhabricatorMenuItemView $item) { 40 - $key = $item->getKey(); 41 - $this->items[] = $item; 42 - $this->appendChild($item); 44 + return $this->addMenuItemAfter(null, $item); 45 + } 46 + 47 + public function addMenuItemAfter($key, PhabricatorMenuItemView $item) { 48 + if ($key === null) { 49 + $this->items[] = $item; 50 + return $this; 51 + } 52 + 53 + if (!$this->getItem($key)) { 54 + throw new Exception("No such key '{$key}' to add menu item after!"); 55 + } 43 56 57 + $result = array(); 58 + foreach ($this->items as $other) { 59 + $result[] = $other; 60 + if ($other->getKey() == $key) { 61 + $result[] = $item; 62 + } 63 + } 64 + 65 + $this->items = $result; 44 66 return $this; 45 67 } 46 68 69 + public function addMenuItemBefore($key, PhabricatorMenuItemView $item) { 70 + if ($key === null) { 71 + array_unshift($this->items, $item); 72 + return $this; 73 + } 74 + 75 + $this->requireKey($key); 76 + 77 + $result = array(); 78 + foreach ($this->items as $other) { 79 + if ($other->getKey() == $key) { 80 + $result[] = $item; 81 + } 82 + $result[] = $other; 83 + } 84 + 85 + $this->items = $result; 86 + return $this; 87 + } 88 + 89 + public function addMenuItemToLabel($key, PhabricatorMenuItemView $item) { 90 + $this->requireKey($key); 91 + 92 + $other = $this->getItem($key); 93 + if ($other->getType() != PhabricatorMenuItemView::TYPE_LABEL) { 94 + throw new Exception("Menu item '{$key}' is not a label!"); 95 + } 96 + 97 + $seen = false; 98 + $after = null; 99 + foreach ($this->items as $other) { 100 + if (!$seen) { 101 + if ($other->getKey() == $key) { 102 + $seen = true; 103 + } 104 + } else { 105 + if ($other->getType() == PhabricatorMenuItemView::TYPE_LABEL) { 106 + break; 107 + } 108 + } 109 + $after = $other->getKey(); 110 + } 111 + 112 + return $this->addMenuItemAfter($after, $item); 113 + } 114 + 115 + private function requireKey($key) { 116 + if (!$this->getItem($key)) { 117 + throw new Exception("No menu item with key '{$key}' exists!"); 118 + } 119 + } 120 + 47 121 public function getItem($key) { 48 122 $key = (string)$key; 49 123 ··· 82 156 return array( 83 157 'class' => 'phabricator-menu-view', 84 158 ); 159 + } 160 + 161 + protected function getTagContent() { 162 + return $this->renderSingleView($this->items); 85 163 } 86 164 }
+141
src/view/layout/__tests__/PhabricatorMenuViewTestCase.php
··· 1 + <?php 2 + 3 + final class PhabricatorMenuViewTestCase extends PhabricatorTestCase { 4 + 5 + public function testAppend() { 6 + $menu = $this->newABCMenu(); 7 + 8 + $this->assertMenuKeys( 9 + array( 10 + 'a', 11 + 'b', 12 + 'c', 13 + ), 14 + $menu); 15 + } 16 + 17 + public function testAppendAfter() { 18 + $menu = $this->newABCMenu(); 19 + 20 + $caught = null; 21 + try { 22 + $menu->addMenuItemAfter('x', $this->newLink('test1')); 23 + } catch (Exception $ex) { 24 + $caught = $ex; 25 + } 26 + $this->assertEqual(true, $caught instanceof Exception); 27 + 28 + $menu->addMenuItemAfter('a', $this->newLink('test2')); 29 + $menu->addMenuItemAfter(null, $this->newLink('test3')); 30 + $menu->addMenuItemAfter('a', $this->newLink('test4')); 31 + $menu->addMenuItemAfter('test3', $this->newLink('test5')); 32 + 33 + $this->assertMenuKeys( 34 + array( 35 + 'a', 36 + 'test4', 37 + 'test2', 38 + 'b', 39 + 'c', 40 + 'test3', 41 + 'test5', 42 + ), 43 + $menu); 44 + } 45 + 46 + public function testAppendBefore() { 47 + $menu = $this->newABCMenu(); 48 + 49 + $caught = null; 50 + try { 51 + $menu->addMenuItemBefore('x', $this->newLink('test1')); 52 + } catch (Exception $ex) { 53 + $caught = $ex; 54 + } 55 + $this->assertEqual(true, $caught instanceof Exception); 56 + 57 + $menu->addMenuItemBefore('b', $this->newLink('test2')); 58 + $menu->addMenuItemBefore(null, $this->newLink('test3')); 59 + $menu->addMenuItemBefore('a', $this->newLink('test4')); 60 + $menu->addMenuItemBefore('test3', $this->newLink('test5')); 61 + 62 + $this->assertMenuKeys( 63 + array( 64 + 'test5', 65 + 'test3', 66 + 'test4', 67 + 'a', 68 + 'test2', 69 + 'b', 70 + 'c', 71 + ), 72 + $menu); 73 + } 74 + 75 + public function testAppendLabel() { 76 + $menu = new PhabricatorMenuView(); 77 + $menu->addMenuItem($this->newLabel('fruit')); 78 + $menu->addMenuItem($this->newLabel('animals')); 79 + 80 + $caught = null; 81 + try { 82 + $menu->addMenuItemToLabel('x', $this->newLink('test1')); 83 + } catch (Exception $ex) { 84 + $caught = $ex; 85 + } 86 + $this->assertEqual(true, $caught instanceof Exception); 87 + 88 + $menu->addMenuItemToLabel('fruit', $this->newLink('apple')); 89 + $menu->addMenuItemToLabel('fruit', $this->newLink('banana')); 90 + 91 + $menu->addMenuItemToLabel('animals', $this->newLink('dog')); 92 + $menu->addMenuItemToLabel('animals', $this->newLink('cat')); 93 + 94 + $menu->addMenuItemToLabel('fruit', $this->newLink('cherry')); 95 + 96 + $this->assertMenuKeys( 97 + array( 98 + 'fruit', 99 + 'apple', 100 + 'banana', 101 + 'cherry', 102 + 'animals', 103 + 'dog', 104 + 'cat', 105 + ), 106 + $menu); 107 + } 108 + 109 + private function newLink($key) { 110 + return id(new PhabricatorMenuItemView()) 111 + ->setKey($key) 112 + ->setHref('#') 113 + ->setName('Link'); 114 + } 115 + 116 + private function newLabel($key) { 117 + return id(new PhabricatorMenuItemView()) 118 + ->setType(PhabricatorMenuItemView::TYPE_LABEL) 119 + ->setKey($key) 120 + ->setName('Label'); 121 + } 122 + 123 + private function newABCMenu() { 124 + $menu = new PhabricatorMenuView(); 125 + 126 + $menu->addMenuItem($this->newLink('a')); 127 + $menu->addMenuItem($this->newLink('b')); 128 + $menu->addMenuItem($this->newLink('c')); 129 + 130 + return $menu; 131 + } 132 + 133 + private function assertMenuKeys(array $expect, PhabricatorMenuView $menu) { 134 + $items = $menu->getItems(); 135 + $keys = mpull($items, 'getKey'); 136 + $keys = array_values($keys); 137 + 138 + $this->assertEqual($expect, $keys); 139 + } 140 + 141 + }
-17
src/view/page/menu/PhabricatorMainMenuIconView.php
··· 5 5 private $classes = array(); 6 6 private $href; 7 7 private $name; 8 - private $sortOrder = 0.5; 9 8 private $workflow; 10 9 private $style; 11 10 ··· 40 39 public function addStyle($style) { 41 40 $this->style = $style; 42 41 return $this; 43 - } 44 - 45 - /** 46 - * Provide a float, where 0.0 is the profile item and 1.0 is the logout 47 - * item. Normally you should pick something between the two. 48 - * 49 - * @param float Sort order. 50 - * @return this 51 - */ 52 - public function setSortOrder($sort_order) { 53 - $this->sortOrder = $sort_order; 54 - return $this; 55 - } 56 - 57 - public function getSortOrder() { 58 - return $this->sortOrder; 59 42 } 60 43 61 44 public function render() {
+3 -4
src/view/page/menu/PhabricatorMainMenuView.php
··· 164 164 $controller = $this->getController(); 165 165 166 166 $applications = PhabricatorApplication::getAllInstalledApplications(); 167 - $applications = msort($applications, 'getName'); 168 167 169 168 $core = array(); 170 169 $more = array(); ··· 184 183 if ($application->getApplicationGroup() == $group_core) { 185 184 $core[] = $item; 186 185 } else { 187 - $more[] = $item; 186 + $more[$application->getName()] = $item; 188 187 } 189 188 } 190 189 ··· 200 199 $view->addClass('phabricator-core-menu'); 201 200 202 201 $search = $this->renderSearch(); 203 - $view->appendChild($search); 202 + $view->addMenuItem($search); 204 203 205 204 $view 206 205 ->newLabel(pht('Home')) ··· 235 234 } 236 235 237 236 if ($actions) { 238 - $actions = msort($actions, 'getSortOrder'); 239 237 $view->addMenuItem( 240 238 id(new PhabricatorMenuItemView()) 241 239 ->addClass('phabricator-core-item-device') ··· 260 258 ->addClass('phabricator-core-item-device') 261 259 ->setType(PhabricatorMenuItemView::TYPE_LABEL) 262 260 ->setName(pht('More Applications'))); 261 + ksort($more); 263 262 foreach ($more as $item) { 264 263 $item->addClass('phabricator-core-item-device'); 265 264 $view->addMenuItem($item);