Search by

heyday / silverstripe-menumanager

heyday

Allows complex menu management to be handled through the CMS when a simple tree structure is not enough.

Package info

github.com/WPP-Public/akqa-nz-silverstripe-menumanager

Type:silverstripe-vendormodule

pkg:composer/heyday/silverstripe-menumanager

Statistics

Installs: 323 061

Dependents: 13

Suggesters: 1

Stars: 35

Open Issues: 0

6.1.0 2026-09-22 23:13 UTC

README

The menu management module is for creating custom menu structures when the site tree hierarchy just won't do.

Editing a menu in the Menu Manager admin

Installation

composer require heyday/silverstripe-menumanager

Run sake db:build afterwards.

Usage

Creating a MenuSet

Open the Menus section and choose Add menu, then fill in the Settings tab. Publish the menu once you are happy with it.

A menu has two labels:

  • Title is what editors see, in the menu picker and everywhere else in the CMS. It is free text and safe to change at any time.
  • Name is the reference templates use, as in $MenuSet('MainMenu'). Spaces are stripped from it on save so we recommend UpperCamelCase.

A new menu starts without a name, so the first thing to do on the Settings tab is give it one. Menus listed under default_sets have a readonly name, because templates and configuration refer to them by it. Other menus keep their name editable, with a warning that changing it can break those references.

A menu listed under default_sets cannot be deleted either, because the site depends on it. Its title can be changed.

Heyday\MenuManager\MenuSet:
    default_sets:
        - Main
        - Footer

Usage in templates

$MenuItems returns the top level of the menu.

<% loop $MenuSet('YourMenuName').MenuItems %>
<a href="{$URL}" class="{$LinkingMode}">{$MenuTitle}</a>
<% end_loop %>

For a nested menu, descend into $Children on each link:

<% loop $MenuSet('YourMenuName').MenuItems %>
<a href="{$URL}" class="{$LinkingMode}">{$MenuTitle}</a>
<% if $Children %>
<ul>
    <% loop $Children %>
    <li><a href="{$URL}" class="{$LinkingMode}">{$MenuTitle}</a></li>
    <% end_loop %>
</ul>
<% end_if %>
<% end_loop %>

To loop through all MenuSets and their items:

<% loop $MenuSets %> <% loop $MenuItems %>
<a href="{$URL}" class="{$LinkingMode}">{$MenuTitle}</a>
<% end_loop %> <% end_loop %>

Optionally you can also limit the number of MenuSets and MenuItems that are looped through.

The example below will fetch the top 4 MenuSets (as seen in Menu Management), and the top 5 MenuItems for each:

<% loop $MenuSets.Limit(4) %> <% loop $MenuItems.Limit(5) %>
<a href="{$URL}" class="{$LinkingMode}">{$MenuTitle}</a>
<% end_loop %> <% end_loop %>

Limiting how deep a menu goes

Menus can be nested three levels deep. A design often has less room than that, such as a footer that only shows a heading and the links under it, so a menu can set its own limit with max_menu_depth, keyed by the menu's name:

Heyday\MenuManager\MenuSet:
    max_menu_depth:
        FooterMenu: 2
        FooterBottomMenu: 1

The depth counts top level links as 1, so:

  • 1 allows top level links only.
  • 2 allows links nested under top level links, but the + is not offered on a second level link, and nothing can be dragged or moved there.
  • 4 or more raises the limit above the default for that menu alone.
Heyday\MenuManager\TreeField\MenuItemTreeSource:
    max_depth: 3

Setting a limit does not change links that already sit deeper than it. They still show and render, but nothing more can be added below the limit, and they can only be moved somewhere that fits.

Drafts and publishing

Menus are versioned. Adding, editing, moving and reordering changes the draft only, and the tree marks anything not yet live: draft rows are italic and show an orange status dot, and the menu picker marks a menu that has never been published.

Publish menu sends the menu and all of its links live in one go. Unpublish takes it off the live site while keeping the draft. Deleting is immediate rather than staged: it archives the record, taking it off live too.

Previewing

Menus are previewable, so the site opens beside the tree in the CMS preview panel with draft and published toggles, the same as editing a page. A menu is not a page, so the preview opens the site's home page, which is where a menu is actually seen. Point it somewhere else per site:

Heyday\MenuManager\MenuSet:
    preview_url: 'https://example.com/about-us'

History and rollback

Each menu and each link has a History tab showing who changed it and when, with the option to roll back to an earlier version. History needs the silverstripe/versioned-admin module, which is installed as a dependency.

Anyone with MANAGE_MENU_SETS or MANAGE_MENU_ITEMS can see draft menus. That is configured through non_live_permissions.

Disable creating Menu Sets in the CMS

Sometimes the defined default_sets are all the menu's a project needs. You can disable the ability to create new Menu Sets in the CMS:

Heyday\MenuManager\MenuAdmin:
    enable_cms_create: false

Note: Non-default Menu Sets can still be deleted, to help tidy unwanted CMS content.

Enabling partial caching

Partial caching can be enabled with your menu to speed up rendering of your templates.

<% with $MenuSet('YourMenuName') %>
<% cached 'YourMenuNameCacheKey', $LastEdited, $MenuItems.max('LastEdited'), $MenuItems.count %>
<% if $MenuItems %>
<nav>
    <% loop $MenuItems %>
    <a href="{$URL}" class="{$LinkingMode}"> $MenuTitle.XML </a>
    <% end_loop %>
</nav>
<% end_if %>
<% end_cached %>
<% end_with %>

Subsite Support

If you're using SilverStripe Subsites, you can make MenuManager subsite aware via applying an extension to the MenuSet.

app/_config/menus.yml

Heyday\MenuManager\MenuSet:
    create_menu_sets_per_subsite: true
    extensions:
        - Heyday\MenuManager\Extensions\MenuSubsiteExtension
Heyday\MenuManager\MenuItem:
    extensions:
        - Heyday\MenuManager\Extensions\MenuSubsiteExtension

License

Menu Manager is licensed under an MIT license