Action and Moderation Menus can be one of the most tedious development tasks, while also being critical to user experience.
For example, we may add support for pinning/unpinning content, then we need to remember to include the ability to pin/unpin that content in all the HTML templates. 3rd party developers add screens inside their applications, and then they need to add a link to the User Menu to make that accessible.
With Invision Community 4, this would require a template hook targeting fairly complex classes and children that was susceptible to structural changes to templates between versions.
In Invision Community 5, we have made significant changes to menu creation in order to streamline the process, and also to allow 3rd party developers better accessibility.
New Helper Classes
Menus are now built using helper classes, rather than relying on lengthy HTML templates. A menu is created by initializing a new Menu object.
$menu = new Menu( 'AccountMenu' );
We can then add elements to the menu.
$menu->addTitleField( 'menu_content' ); $menu->add( new Link( $member->url(), 'menu_profile', icon:'fa-solid fa-user' ) ); $menu->addSeparator(); $menu->add( new Link( Url::internal( "app=core&module=system&controller=markread", "front", "mark_site_as_read" )->csrf()->addRef( Request::i()->url() ), 'mark_site_read', dataAttributes: [ 'data-action' => 'markSiteRead', 'data-controller' => 'core.front.core.markRead' ], icon: 'fa-solid fa-eye' ) );
The most common menu element is the Link. This will generate the appropriate <li> element within the menu. You can define the URL, CSS class (default is ipsMenu_item), data attributes, icon, title, among other properties.
Titles and Separators are added using the Menu::addTitleField() and Menu::addSeparator() methods respectively.
You can also insert custom HTML into the menu using the Menu::addHtml() method.
In your HTML template, you would then simply display the menu object as a string.
{{if $menu->hasContent()}}
{$menu|raw}
{{endif}}
The menu templates will include the link that opens the menu, as well as the menu itself.
You'll notice that the above contains a call to the hasContent() method. This method will check if the menu object has any elements inside; if a user does not have permission to any of the elements, nothing will be displayed.
Content Menus
Almost all content items and comments (and reviews) require some kind of menu for moderation. Previously, this meant creating large chunks of redundant HTML code throughout the codebase. We've implemented \IPS\Content\Item::menu() and \IPS\Content\Comment::menu() to build these menus in a central location. The new methods include checks for whether a feature is in use and whether the user has permission to perform this action.
Example:
if( IPS::classUsesTrait( $this, 'IPS\Content\Pinnable' ) AND $this->canPin( $member ) ) { $links['pin'] = new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) ), 'pin' ); }
Now, the HTML template simply contains:
{$entry->menu()|raw}
(Note: Yes, these content menus can be extended using... you guessed it. UI Extensions.)
Other Menus
We have also migrated the following menus to the new structure:
- User Menu
- Mobile Menu
- Create Menu
Badges
Another area with redundant HTML was our content badges. For example, pinned topics will display an icon on both the topic list and the topic header. We have created helper classes for badges and badge icons to centralize this logic. Within the new Item::badges() method, we build an array of icons that will be shown.
if( IPS::classUsesTrait( $this, Featurable::class ) AND $this->isFeatured() ) { $return['featured'] = new Icon( Badge::BADGE_POSITIVE, 'fa-solid fa-star', Member::loggedIn()->language()->addToStack( 'featured' ) ); }
The above generates a badge icon with the badge type ipsBadge--positive (constants have been declared for all common types, but any class can be used), the star icon, and the "featured" language string as the tooltip.
In our template HTML, we now have
<div class='ipsBadges'> {{foreach $entry->badges() as $badge}}{$badge|raw}{{endforeach}} </div>
UserMenu Extension
Now that we've moved the menus into the codebase and out of the templates, we needed a way to allow 3rd party developers to insert their own menu items. We've introduced a new extension called UserMenu.
The UserMenu extension contains the following methods:
-
accountMenu
This method allows you to add menu elements to the user menu. The method includes a parameter to allow you to add elements in one of 3 places within the menu: content, settings, or logout. -
mobileMenu
Allows you to add elements to the mobile navigation menu. All elements are inserted before the "sign out" option. -
createMenu
Replaces the CreateMenu extension, which has been deprecated. -
accountSettingsLinks
Allows you to add links to the inner sidebar in the Account Settings overview tab (under the link for Notification Settings).
Note: No, this does not add tabs to the Account Settings page, but fear not, that's coming. (Not in the UI Extension.)
-
userNav
This method will insert HTML in the user bar, to the left of the Create Menu. -
mobileNav
Similar to the userNav method, but inserts HTML into the mobile navigation header.
What do you think of our changes to the Menus? Let us know in the comments below.
Recommended Comments