Additional menu fields 🧩#
cs.megamenu ships one behavior with a menu field (IEditableMenu) and a
viewlet for the main navigation. You can define additional menu fields in your
own behavior and reuse the editor widget, validator, resolver, linkintegrity
retriever, and viewlet base class.
The code in this guide is an example for a separate add-on (myaddon). It is
not part of cs.megamenu; copy and adapt it in your own package.
To render your new field with custom HTML, see Custom menu rendering after defining it.
Use a distinct field name and behavior for each menu. The examples below add a
footer_menu field.
Define the field and behavior#
Create a behavior schema with a JSONField that uses MENU_JSON_SCHEMA, and
apply the menu widget to that field with plone.autoform.directives.widget:
# myaddon/behaviors/footer.py
from cs.megamenu.schema import MENU_JSON_SCHEMA
from cs.megamenu.widgets.menu import MegaMenuFieldWidget
from plone.autoform import directives
from plone.autoform.interfaces import IFormFieldProvider
from plone.schema import JSONField
from plone.supermodel import model
from zope.interface import provider
@provider(IFormFieldProvider)
class IFooterMenu(model.Schema):
"""Adds a footer menu field."""
directives.widget("footer_menu", MegaMenuFieldWidget)
footer_menu = JSONField(
title="Footer menu",
description="Menu rendered in the site footer.",
required=False,
schema=MENU_JSON_SCHEMA,
)
Register the behavior in your package’s ZCML. Remember the plone namespace
and the plone.behavior meta include:
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:plone="http://namespaces.plone.org/plone"
>
<include package="plone.behavior" file="meta.zcml" />
<plone:behavior
name="myaddon.footer_menu"
title="Footer menu"
description="Adds a footer menu field."
provides=".behaviors.footer.IFooterMenu"
/>
</configure>
Enable the behavior on the content types in your profile, for example:
<object meta_type="Dexterity FTI" name="Plone Site">
<property name="behaviors" purge="false">
<element value="myaddon.footer_menu" />
</property>
</object>
The schema-only behavior also acts as the behavior marker, so the object
provides IFooterMenu when the behavior is enabled.
Register the validator#
The semantic validator is reusable. cs.megamenu already registers the
MenuValidator adapter; add a discriminator for your field so it is selected
for footer_menu as well:
# myaddon/validators/menu.py
from cs.megamenu.validators.menu import MenuValidator
from myaddon.behaviors.footer import IFooterMenu
from z3c.form import validator
validator.WidgetValidatorDiscriminators(
MenuValidator,
field=IFooterMenu["footer_menu"],
)
Import this module from your package so the discriminator is registered.
Resolve the menu#
get_menu() accepts the field name and behavior:
from cs.megamenu.menu import get_menu
from myaddon.behaviors.footer import IFooterMenu
menu, owner = get_menu(
context,
field_name="footer_menu",
behavior=IFooterMenu,
)
See Custom menu rendering for the return contract,
filter_items(), and rendering examples.
Track link integrity#
The default retriever reads the menu field. Subclass it for your field and
register the adapter for your behavior:
# myaddon/linkintegrity.py
from cs.megamenu.linkintegrity.menu import MenuRetriever
from myaddon.behaviors.footer import IFooterMenu
from plone.app.linkintegrity.interfaces import IRetriever
from zope.component import adapter
from zope.interface import implementer
@implementer(IRetriever)
@adapter(IFooterMenu)
class FooterMenuRetriever(MenuRetriever):
field_name = "footer_menu"
Register it conditionally so your package still works without
plone.app.linkintegrity:
<!-- myaddon/linkintegrity.zcml -->
<configure xmlns="http://namespaces.zope.org/zope">
<adapter factory=".linkintegrity.FooterMenuRetriever" />
</configure>
<!-- myaddon/configure.zcml -->
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:zcml="http://namespaces.zope.org/zcml"
>
<include
file="linkintegrity.zcml"
zcml:condition="installed plone.app.linkintegrity"
/>
</configure>
Render with the built-in MegaMenuViewlet#
Subclass MegaMenuViewlet and point it at your field and behavior:
# myaddon/viewlets/footer.py
from cs.megamenu.viewlets.menu import MegaMenuViewlet
from myaddon.behaviors.footer import IFooterMenu
class FooterMenuViewlet(MegaMenuViewlet):
menu_field = "footer_menu"
menu_behavior = IFooterMenu
This reuses the built-in Barceloneta markup, resolution, permission filtering, and icon rendering. For fully custom HTML, use the standalone viewlet pattern in Custom menu rendering.
Register it under a new viewlet name and the manager used by your template, for example:
<browser:viewlet
name="myaddon.footer_menu"
manager="plone.app.layout.viewlets.interfaces.IPortalFooter"
class=".viewlets.footer.FooterMenuViewlet"
permission="zope2.View"
layer="myaddon.interfaces.IBrowserLayer"
/>
Add the browser namespace to the ZCML root
(xmlns:browser="http://namespaces.zope.org/browser").
Checklist#
The field name must match in
directives.widget(),get_menu(), the retriever’sfield_name, and the viewlet’smenu_field.Use a separate behavior and field for each menu.
Enable the behavior on the content types where the menu should be available.
Import the validator module so its field discriminator is registered.
Include the linkintegrity retriever only when
plone.app.linkintegrityis installed.