Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
Knowledge Commons Works 1.0.0 documentation
Knowledge Commons Works 1.0.0 documentation
  • About
  • Feature Highlights
  • Changes
  • Development Roadmap
  • Known Issues
  • Administrator Guide
    • Content Moderation
    • Running Commands from the KCWorks Back End
    • File Storage Administration
    • Scheduled events
    • Vocabulary Management
    • Names Vocabulary Lifecycle
    • User Data Management
    • Organization Management
    • Collection hierarchy and subcollection requests
    • Record access: requests and grants
    • Record search
    • Export formats
  • Setting up KCWorks
    • Configuration of KCWorks
    • Installation
  • Developing KCWorks
    • KCWorks Architecture
    • Development Standards
    • Source Control and Versioning
    • Development Workflow
    • Automated Testing
    • How Tos
    • Menus (Flask-Menu)
    • Publishing admin docs to the knowledge base
    • Build Processes
    • Stats and Analytics
    • I18n
    • KCWorks Infrastructure
  • Reference
    • Metadata Schema, Vocabularies, and Identifiers
    • API
    • Outgoing Signals
    • CLI Commands
  • Customizations to InvenioRDM
  • In-depth Installation Instructions (NEEDS UPDATING)
Back to top
View this page

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:

  1. sort_menu_items_by_name — reorders items by a hard-coded name list (community_menu_order in the template). Items not in that list are appended in their existing order.

  2. filter_visible_community_menu_items — applies Flask-Menu visibility, permission checks, and KCWorks-specific gates (stats dashboard enabled, hide search when home exists, hide submit, 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

name

public attribute

Stable key for the node (e.g. "members", "stats"). Used in submenu("members") and in KCWorks filters via item.name.

parent

public attribute

Parent MenuNode, or None for the root.

_child_entries

private attribute

dict[str, MenuNode] of child nodes keyed by name. Source of truth for the tree.

children

read-only property

Sorted list of child MenuNode objects, ordered by each child’s order.

submenu(path, auto_create=True)

method

Navigate or create children. "communities.members" walks the tree; returns the target node.

list_path(from_path, to_path)

method

Returns the ancestor→descendant path as a list of nodes, or None if the paths are on unrelated branches.

Configuration (set via register())¶

These values are stored on private fields. register() is the supported way to set them initially.

Backing field

register() parameter

Use

_endpoint

endpoint

Flask endpoint name for url_for(...). Mutually exclusive with external_url.

_external_url

external_url

Static URL when there is no endpoint. Used when _endpoint is unset.

_text

text

Label shown in templates (item.text). Defaults to name on register.

_order

order

Sort key for children. Lower values appear earlier.

_expected_args

expected_args

URL argument names (e.g. ["pid_value"]) pulled from g._menu_kwargs when building url.

_endpoint_arguments_constructor

endpoint_arguments_constructor

Callable returning extra url_for keyword arguments.

_dynamic_list_constructor

dynamic_list_constructor

Callable returning a list of nodes to render instead of just self (for dynamic menu expansion).

_visible_when

visible_when

Callable; item is visible only if it returns a truthy value (and _text is set). Default: always visible.

_active_when

active_when

Callable deciding whether the item is “active”. Default: match endpoint, path prefix, or exact path.

**kwargs

e.g. icon, permissions

Arbitrary extra attributes via setattr. Re-registering with a different value for an existing kwarg raises RuntimeError.

Read-only computed properties¶

These are what templates and filters usually read.

Property

Use

text

Display label. Falls back to "MenuNode item not initialised" if _text is None.

url

Link target. Built from endpoint and arguments, or external_url, or "#".

order

Exposes _order (getter only; no setter).

visible

True if _text is not None and _visible_when() is truthy.

active

True if _active_when() considers this item to match the current request.

active_item

Deepest active node in this subtree, or self if active, else None.

dynamic_list

Result of _dynamic_list_constructor(), or [self] if unset.

Methods¶

Method

Use

register(...)

Configure or reconfigure a node. Updates backing fields in place.

hide()

Sets _visible_when to always-false so the item never shows.

has_active_child(recursive=True)

Whether any descendant is active.

has_visible_child(recursive=True)

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

permissions

Permission key checked in filter_visible_community_menu_items (e.g. "can_read", "can_update"). True means no permission gate.

icon

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

.submenu("name").register(...)

Works from finalize_app after other extensions.

Change text, endpoint, visible_when

Re-call .register(...) on that submenu

Supported API.

Change order

register(order=...) or item._order = ...

May not affect display; template name-order list wins.

Change permissions after registration

Direct assignment (item.permissions = ...)

Re-register() with a different value errors.

Hide an item

.hide() or template filter

Filter may also remove items regardless of visible.

Change displayed tab order

Edit community_menu_order in header template

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.

Next
Publishing admin docs to the knowledge base
Previous
How Tos
Copyright © 2026, Mesh Research
Made with Sphinx and @pradyunsg's Furo
On this page
  • Menus (Flask-Menu)
    • Accessing the menu
    • Registering items in KCWorks
    • KCWorks community header pipeline
    • MenuNode reference
      • Identity and tree structure
      • Configuration (set via register())
      • Read-only computed properties
      • Methods
      • Request-time URL building
      • KCWorks-specific extra attributes
    • Modifying items after registration
      • Via register() (supported API)
      • Via direct mutation (post-registration tweaks)
      • What you can and cannot do
    • Mental model