Menus (Flask-Menu)¶
KCWorks uses Flask-Menu to build navigation trees that Jinja2 templates render. Community header tabs, main navigation, and similar UI menus are all MenuNode instances registered at application startup and exposed to templates as current_menu.
This page documents the Flask-Menu MenuNode API and KCWorks-specific patterns for registering, customizing, filtering, and ordering community menu items.
Accessing the menu¶
The Flask-Menu extension registers current_menu as a Jinja2 global. In Python code, import the same proxy:
from flask_menu import current_menu
current_menu is the root MenuNode. Navigate the tree with dotted paths:
communities = current_menu.submenu("communities")
members = current_menu.submenu("communities.members")
In templates:
{% set items = current_menu.submenu('communities').children %}
children returns a sorted list of child MenuNode objects (sorted by each child’s order). It is recomputed on every access; you cannot assign to it.
Registering items in KCWorks¶
Extensions register menu items during init or finalize_app. KCWorks adds community header items in site/kcworks/ext.py (register_community_menu_items), which runs from finalize_app after other extensions have registered theirs.
current_menu.submenu("communities").submenu("contribute").register(
endpoint="invenio_app_rdm_records.deposit_create",
text=_("Contribute"),
order=70,
endpoint_arguments_constructor=deposit_args,
icon="plus",
permissions="can_submit_record",
)
Stock Invenio community items (requests, members, settings, etc.) are registered in invenio_communities.ext.register_menus. The stats tab is registered by invenio_stats_dashboard.
Call register() on a submenu path to create a new item or update an existing one in place. You do not need to reassign the parent submenu.
KCWorks community header pipeline¶
The community details header template (templates/semantic-ui/invenio_communities/details/header.html) does not render .children directly. It runs two KCWorks filters from site/kcworks/templates/template_filters.py:
sort_menu_items_by_name— reorders items by a hard-coded name list (community_menu_orderin the template). Items not in that list are appended in their existing order.filter_visible_community_menu_items— applies Flask-Menu visibility, permission checks, and KCWorks-specific gates (stats dashboard enabled, hidesearchwhenhomeexists, hidesubmit, etc.).
Because of step 1, changing order in register() or via _order mutation often does not change the displayed tab order. Update community_menu_order in the header template (or change the filter) when you care about display order.
MenuNode reference¶
Flask-Menu defines a single entry type: MenuNode (flask_menu.menu.MenuNode). There is no separate MenuItem class.
Identity and tree structure¶
Member |
Kind |
Use |
|---|---|---|
|
public attribute |
Stable key for the node (e.g. |
|
public attribute |
Parent |
|
private attribute |
|
|
read-only property |
Sorted list of child |
|
method |
Navigate or create children. |
|
method |
Returns the ancestor→descendant path as a list of nodes, or |
Configuration (set via register())¶
These values are stored on private fields. register() is the supported way to set them initially.
Backing field |
|
Use |
|---|---|---|
|
|
Flask endpoint name for |
|
|
Static URL when there is no endpoint. Used when |
|
|
Label shown in templates ( |
|
|
Sort key for |
|
|
URL argument names (e.g. |
|
|
Callable returning extra |
|
|
Callable returning a list of nodes to render instead of just |
|
|
Callable; item is visible only if it returns a truthy value (and |
|
|
Callable deciding whether the item is “active”. Default: match endpoint, path prefix, or exact path. |
|
e.g. |
Arbitrary extra attributes via |
Read-only computed properties¶
These are what templates and filters usually read.
Property |
Use |
|---|---|
|
Display label. Falls back to |
|
Link target. Built from |
|
Exposes |
|
|
|
|
|
Deepest active node in this subtree, or |
|
Result of |
Methods¶
Method |
Use |
|---|---|
|
Configure or reconfigure a node. Updates backing fields in place. |
|
Sets |
|
Whether any descendant is active. |
|
Whether any descendant is visible. |
Request-time URL building¶
Flask-Menu’s extension stores the current route’s view arguments on g._menu_kwargs during each request. When building url, only keys listed in _expected_args are passed to url_for. Community menu items therefore register expected_args=["pid_value"] so links include the current community slug.
KCWorks-specific extra attributes¶
These are not part of Flask-Menu itself. They are set via register(**kwargs) and used by KCWorks templates and filters.
Attribute |
Use in KCWorks |
|---|---|
|
Permission key checked in |
|
Semantic UI icon name for rendering (not used by Flask-Menu core). |
Modifying items after registration¶
A helper can mutate existing MenuNode objects in place. submenu(...) returns a reference to the node already stored in the tree; changes are visible the next time .children is read.
Via register() (supported API)¶
communities = current_menu.submenu("communities")
communities.submenu("members").register(order=25, text=_("Members"))
communities.submenu("submit").hide()
Re-calling register() updates _order, _text, _visible_when, and other standard fields. Custom kwargs such as permissions cannot be changed to a new value via re-register(); Flask-Menu raises RuntimeError if the value differs.
Via direct mutation (post-registration tweaks)¶
Public properties like order, text, and visible are read-only. Mutate the backing fields Flask-Menu actually stores:
item = current_menu.submenu("communities").submenu("members")
item._order = 25
item._text = _("Members")
item.permissions = "can_read" # custom attribute from register(**kwargs)
item._visible_when = my_visible_fn
Or call item.hide() instead of assigning _visible_when.
What you can and cannot do¶
Goal |
Approach |
Notes |
|---|---|---|
Add an item |
|
Works from |
Change text, endpoint, |
Re-call |
Supported API. |
Change |
|
May not affect display; template name-order list wins. |
Change |
Direct assignment ( |
Re- |
Hide an item |
|
Filter may also remove items regardless of |
Change displayed tab order |
Edit |
Primary control for community header order. |
Mental model¶
MenuNode tree
├── name, parent # structure
├── _child_entries # children dict
├── register() / hide() # configure
├── children, url, text, # read in templates
│ visible, active, order
└── custom attrs # icon, permissions, etc.
Read in templates and filters: name, parent, text, url, order, visible, active, children, active_item, dynamic_list.
Write via register(), hide(), or private fields: _order, _text, _endpoint, _visible_when, _active_when, and custom attributes such as permissions and icon.