├── LICENSE.md ├── README.md ├── _config └── groupedcmsmenu.yml ├── client └── dist │ └── css │ └── GroupedCmsMenu.css ├── composer.json ├── src └── Admin │ └── GroupedCmsMenu.php └── templates ├── Includes └── GroupedCmsMenu │ └── MenuIcon.ss └── SilverStripe └── Admin └── Includes └── LeftAndMain_MenuList.ss /LICENSE.md: -------------------------------------------------------------------------------- 1 | # SilverStripe Grouped CMS Menu Module License 2 | 3 | Copyright © 20010, Symbiote PTY LTD - www.symbiote.com.au 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in thebvdocumentation and/or 13 | other materials provided with the distribution. 14 | * Neither the name of SilverStripe nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SilverStripe Grouped CMS Menu 2 | 3 | This module allows you to group CMS menu items into nested lists which expand when hovered over. This is useful when 4 | there are so many CMS menu items that screen space becomes an issue. 5 | 6 | Previous versions are available through the appropriate branch. 7 | 8 | ## Basic Usage 9 | 10 | In order to group CMS menu items together, define your menu groups in a `config.yml` file. 11 | 12 | In the example below, CMSMain (Pages) and AssetAdmin (Files & Images) are grouped 13 | together under a "Content" heading. 14 | 15 | ```yml 16 | SilverStripe\Admin\LeftAndMain: 17 | menu_groups: 18 | Content: 19 | children: 20 | - SilverStripe-CMS-Controllers-CMSPagesController 21 | - SilverStripe-AssetAdmin-Controller-AssetAdmin 22 | ``` 23 | 24 | ## Sort order 25 | 26 | The items in each grouped menu will follow the order you set in your YML. The groups 27 | themselves will be inserted in the menu with a priority of 0, with other menu items 28 | appearing above or below depending on their existing priority. 29 | You can change the priority of a menu group like this: 30 | 31 | ```yml 32 | SilverStripe\Admin\LeftAndMain: 33 | menu_groups: 34 | Other: 35 | priority: -500 36 | children: 37 | - SilverStripe-Reports-ReportAdmin 38 | - SilverStripe-Admin-SecurityAdmin 39 | ``` 40 | 41 | Or you can "group" items by themselves to make any menu item follow the order you set in your configuration: 42 | 43 | ```yml 44 | SilverStripe\Admin\LeftAndMain: 45 | menu_groups: 46 | SilverStripe\CMS\Controllers\CMSPagesController: 47 | children: 48 | - SilverStripe-CMS-Controllers-CMSPagesController 49 | Other: 50 | children: 51 | - SilverStripe-Reports-ReportAdmin 52 | - SilverStripe-Admin-SecurityAdmin 53 | ``` 54 | 55 | When you have larger menus, and/or multiple modules combining to the same menu, this may require something more consistent. In which case, you may sort your grouped menus alphabetically. 56 | 57 | ```yml 58 | SilverStripe\Admin\LeftAndMain: 59 | menu_groups: 60 | SilverStripe\CMS\Controllers\CMSPagesController: 61 | children: 62 | - SilverStripe-CMS-Controllers-CMSPagesController 63 | Other: 64 | children: 65 | - SilverStripe-Reports-ReportAdmin 66 | - SilverStripe-Admin-SecurityAdmin 67 | menu_groups_alphabetical_sorting: true 68 | ``` 69 | 70 | ## Group icons 71 | 72 | You can add a CSS class to groups for the purpose of adding an icon. The class name will be prefixed with 'font-icon-'. 73 | In the example below the same icon used for the Pages menu item will be used for the Content group: 74 | 75 | ```yml 76 | SilverStripe\Admin\LeftAndMain: 77 | menu_groups: 78 | Content: 79 | icon: 'sitemap' 80 | children: 81 | - SilverStripe-CMS-Controllers-CMSPagesController 82 | - SilverStripe-AssetAdmin-Controller-AssetAdmin; 83 | ``` 84 | 85 | ## Translating group labels 86 | 87 | A group label may be translated by providing a translation key as below (using 88 | the 'Other' group from above as an example) 89 | 90 | ``` 91 | langcode: 92 | GroupedCmsMenuLabel: 93 | Other: 'translated text' 94 | ``` 95 | 96 | If the group label has spaces, these will be converted to underscores for the 97 | key 98 | 99 | ``` 100 | langcode: 101 | GroupedCmsMenuLabel: 102 | Other_Label: 'translated text' 103 | ``` 104 | 105 | ## Requirements 106 | 107 | * SilverStripe 4+ and 5+ (See other branches for compatibility with older versions) 108 | 109 | ## Project Links 110 | 111 | * [GitHub Project Page](https://github.com/symbiote/silverstripe-grouped-cms-menu) 112 | * [Issue Tracker](https://github.com/symbiote/silverstripe-grouped-cms-menu/issues) 113 | 114 | ## Credits 115 | 116 | * A massive thanks to Russ Michell ([phptek](https://github.com/phptek)) for upgrading this module to be SS4 compatible! 117 | -------------------------------------------------------------------------------- /_config/groupedcmsmenu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Name: grouped-cms-menu 3 | After: 'framework/*, cms/*' 4 | --- 5 | 6 | SilverStripe\Admin\LeftAndMain: 7 | extensions: 8 | - Symbiote\GroupedCmsMenu\Admin\GroupedCmsMenu 9 | -------------------------------------------------------------------------------- /client/dist/css/GroupedCmsMenu.css: -------------------------------------------------------------------------------- 1 | .cms-menu__list li ul.group { 2 | margin: 0 0 0 15px; 3 | padding: 0; 4 | } 5 | 6 | .cms-menu__list li ul.group, 7 | .cms-menu__list li ul.group li { 8 | list-style: none; 9 | } 10 | 11 | .cms-menu__list li ul.group li a { 12 | min-height: 35px; 13 | } 14 | 15 | .cms-menu__list li ul.group li a .text { 16 | padding-left: 8px; 17 | } 18 | 19 | .cms-menu__list li a .toggle-children-icon, 20 | .cms-menu__list li a .toggle-children .toggle-children-icon { 21 | background-image: none !important; 22 | top: 35% !important; 23 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symbiote/silverstripe-grouped-cms-menu", 3 | "description": "Allows you to group CMS menu items.", 4 | "type": "silverstripe-vendormodule", 5 | "homepage": "https://github.com/symbiote/silverstripe-grouped-cms-menu", 6 | "keywords": ["silverstripe", "menu", "module", "cms", "group", "grouped", "grouping"], 7 | "license": "BSD-3-Clause", 8 | "authors": [ 9 | { 10 | "name": "Marcus Nyeholt", 11 | "homepage": "https://github.com/nyeholt" 12 | }, 13 | { 14 | "name": "Russ Michell", 15 | "homepage": "https://github.com/phptek" 16 | } 17 | ], 18 | "require": { 19 | "silverstripe/admin": "^1.3 || ^2.0" 20 | }, 21 | "extra": { 22 | "expose": [ 23 | "client" 24 | ], 25 | "branch-alias": { 26 | "dev-master": "4.1.x-dev" 27 | } 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Symbiote\\GroupedCmsMenu\\Admin\\": "src/Admin/" 32 | } 33 | }, 34 | "replace": { 35 | "silverstripe-australia/grouped-cms-menu": "self.version" 36 | }, 37 | "minimum-stability": "dev" 38 | } 39 | -------------------------------------------------------------------------------- /src/Admin/GroupedCmsMenu.php: -------------------------------------------------------------------------------- 1 | getOwner()->MainMenu(); 55 | $result = ArrayList::create(); 56 | $config = $this->getOwner()->config(); 57 | $groupSettings = $config->get('menu_groups'); 58 | $itemsToGroup = []; 59 | $groupSort = 0; 60 | $itemSort = 0; 61 | 62 | foreach ($groupSettings as $groupName => $menuItems) { 63 | if (!count($menuItems['children'])) { 64 | continue; 65 | } 66 | 67 | foreach ($menuItems['children'] as $key => $menuItem) { 68 | $itemsToGroup[$menuItem] = [ 69 | 'Group' => $groupName, 70 | 'Priority' => (array_key_exists('priority', $groupSettings[$groupName])) ? $groupSettings[$groupName]['priority'] : $groupSort, 71 | 'SortOrder' => $itemSort 72 | ]; 73 | $itemSort++; 74 | } 75 | 76 | $groupSort--; 77 | } 78 | 79 | foreach ($items as $item) { 80 | $code = $item->Code; 81 | 82 | if (array_key_exists($code, $itemsToGroup)) { 83 | $item->Group = $itemsToGroup[$code]['Group']; 84 | $item->Priority = $itemsToGroup[$code]['Priority']; 85 | $item->SortOrder = $itemsToGroup[$code]['SortOrder']; 86 | } else { 87 | $item->Group = $code; 88 | $item->Priority = is_numeric($item->MenuItem->priority) ? $item->MenuItem->priority : -1; 89 | $item->SortOrder = 0; 90 | } 91 | } 92 | 93 | $groupedList = GroupedList::create($items->sort(['Priority' => 'DESC']))->groupBy('Group'); 94 | $menuIconStyling = ''; 95 | foreach ($groupedList as $group => $children) { 96 | if (count($children)) { 97 | $active = false; 98 | 99 | foreach ($children as $child) { 100 | if ($child->LinkingMode == 'current') { 101 | $active = true; 102 | } 103 | } 104 | 105 | $code = str_replace(' ', '_', $group); 106 | $class = str_replace('-', '\\', $code); 107 | $iconClass = null; 108 | $menuIcon = LeftAndMain::menu_icon_for_class($class); 109 | 110 | if (!empty($menuIcon)) { 111 | $menuIconStyling .= $menuIcon; 112 | } else { 113 | $iconClass = $this->getIcon($group, $code); 114 | } 115 | 116 | $result->push(ArrayData::create([ 117 | 'Title' => $this->getTitle($group, $code), 118 | 'IconClass' => $iconClass, 119 | 'HasCSSIcon' => strtolower($code), 120 | 'Code' => DBField::create_field(DBText::class, $code), 121 | 'Link' => $children->first()->Link, 122 | 'LinkingMode' => $active ? 'current' : 'link', 123 | 'Children' => $this->filterChildren($children), 124 | ])); 125 | } else { 126 | $result->push($children->first()); 127 | } 128 | } 129 | 130 | if ($menuIconStyling) { 131 | Requirements::customCSS($menuIconStyling); 132 | } 133 | 134 | return $result; 135 | } 136 | 137 | /** 138 | * Return the {@link LeftAndMain} subclass' `$menu_title` for each Level 1 139 | * menu item, or use the partial name of each child item from `$menu_groups` 140 | * config. 141 | * 142 | * @param string $group 143 | * @param string $code 144 | * @return string 145 | */ 146 | public function getTitle($group, $code) 147 | { 148 | $class = str_replace('-', '\\', $code); 149 | 150 | if (class_exists($class)) { 151 | return LeftAndMain::menu_title($class); 152 | } 153 | 154 | return _t('GroupedCmsMenuLabel.' . $code, $group); 155 | } 156 | 157 | /** 158 | * Return the {@link LeftAndMain} subclass' `$menu_icon_class` or use the 159 | * name of the heading as per the `$menu_groups` setting in config. 160 | * 161 | * @param string $group 162 | * @param string $code 163 | * @return string 164 | */ 165 | public function getIcon($group, $code) 166 | { 167 | $class = str_replace('-', '\\', $code); 168 | $groupSettings = $this->getOwner()->config()->get('menu_groups'); 169 | 170 | if (class_exists($class)) { 171 | return $class::create()->config()->get('menu_icon_class'); 172 | } 173 | 174 | return 'font-icon-' . (!empty($groupSettings[$group]['icon']) ? $groupSettings[$group]['icon'] : ''); 175 | } 176 | 177 | /** 178 | * Ensure what we pass to $Children in the include template is accurate. We only 179 | * want to indicate to it that children should be shown groups declared in config. 180 | * 181 | * @param SS_List $children 182 | * @return ArrayList 183 | */ 184 | public function filterChildren(SS_List $children) 185 | { 186 | // Only deal with children if we've explicitly instructed our classes as such 187 | $config = $this->getOwner()->config(); 188 | $groupSettings = $config->get('menu_groups'); 189 | $filtered = ArrayList::create(); 190 | $menuIconStyling = ''; 191 | foreach ($children as $child) { 192 | foreach ($groupSettings as $group => $candidates) { 193 | $candidates = str_replace('-', '\\', $candidates['children']); 194 | $class = $child->MenuItem->controller; 195 | $iconClass = null; 196 | $menuIcon = LeftAndMain::menu_icon_for_class($class); 197 | 198 | if (!empty($menuIcon)) { 199 | $menuIconStyling .= $menuIcon; 200 | } else { 201 | $iconClass = $this->getIcon($group, $class); 202 | } 203 | 204 | if (in_array($class, $candidates)) { 205 | $menuItem = $class::create(); 206 | $menuItem->setField('ChildTitle', $this->getTitle($group, $class)); 207 | $menuItem->setField('Code', str_replace('\\', '-', $class)); 208 | $menuItem->setField('IconClass', $iconClass); 209 | $menuItem->setField('HasCSSIcon', strtolower(Convert::raw2htmlname(str_replace('\\', '-', $class)))); 210 | $menuItem->setField('LinkingMode', $child->LinkingMode); 211 | $filtered->push($menuItem); 212 | } 213 | } 214 | } 215 | 216 | if ($menuIconStyling) { 217 | Requirements::customCSS($menuIconStyling); 218 | } 219 | 220 | return $config->get('menu_groups_alphabetical_sorting') ? 221 | $filtered->sort('ChildTitle') : 222 | $filtered->sort('SortOrder'); 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /templates/Includes/GroupedCmsMenu/MenuIcon.ss: -------------------------------------------------------------------------------- 1 | <% if $IconClass %> 2 | 3 | <% else_if $HasCSSIcon %> 4 |   5 | <% else %> 6 |   7 | <% end_if %> -------------------------------------------------------------------------------- /templates/SilverStripe/Admin/Includes/LeftAndMain_MenuList.ss: -------------------------------------------------------------------------------- 1 | 23 | --------------------------------------------------------------------------------