diff --git a/docs/conf.py b/docs/conf.py
index a179fe70..f76e5ffe 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -319,6 +319,7 @@
"https://docs.python.org/": None,
"flask": ("http://flask.pocoo.org/docs/latest/", None),
"flask_menu": ("https://flask-menu.readthedocs.io/en/latest/", None),
+ "flask_breadcrumbs": ("https://flask-breadcrumbs.readthedocs.io/en/latest/", None),
"invenio_assets": ("https://invenio-assets.readthedocs.io/en/latest/", None),
}
diff --git a/invenio_theme/__init__.py b/invenio_theme/__init__.py
index 9575315f..b9231bc8 100644
--- a/invenio_theme/__init__.py
+++ b/invenio_theme/__init__.py
@@ -15,9 +15,9 @@
`_ HTML, CSS and JS framework.
- `Error handlers` - for showing user-friendly 401, 402, 404, and 500 error
pages.
-- `Menu` - for basic site navigation using `Flask-Menu
- `_
-
+- `Menu and breadcrumbs` - for basic site navigation using `Flask-Menu
+ `_ and `Flask-Breadcrumbs
+ `_.
Initialization
@@ -151,7 +151,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~
The header template (``invenio_theme/header.html``) is reponsible for rendering
the navigation bar (including logo, search bar, menu and login/sign up
-buttons) and flash messages
+buttons), flash messages and the breadcrumbs.
Change the template by updating
:data:`invenio_theme.config.THEME_HEADER_TEMPLATE`.
@@ -165,6 +165,8 @@
* ``flashmessages`` - Displays small notification messages such as
"Successfully logged in."
+* ``breadcrumbs`` - Displays the breadcrumbs.
+
Header login section template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The header login template (``invenio_theme/header_login.html``) is responsible
@@ -437,6 +439,33 @@ def init_config(app):
To read more about the creation and usage of menus, see :mod:`~flask_menu`.
+Breadcrumbs
+~~~~~~~~~~~
+Breadcrumbs works similar to menus, just use the
+:func:`~flask_breadcrumbs.register_breadcrumb` instead.
+
+>>> from flask_breadcrumbs import register_breadcrumb
+
+Using this decorator, you can specify the position of the view in a
+hierarchical manner, as well as the title in the breadcrumb. By default, the
+current breadcrumb is displayed inside the ``page_header`` block in the base
+template.
+
+>>> @app.route('/part1')
+... @register_breadcrumb(app, '.', 'Index')
+... def part1():
+... return ""
+>>> @app.route('/part2')
+... @register_breadcrumb(app, '.p2', 'Part 2')
+... def part2():
+... return ""
+>>> @app.route('/part3')
+... @register_breadcrumb(app, '.p2.p3', 'Part 3')
+... def part3():
+... return ""
+
+To learn more about the usage of breadcrumbs, see :mod:`~flask_breadcrumbs`.
+
User settings
~~~~~~~~~~~~~
If your module allows your users to configure some settings, you can provide
@@ -459,6 +488,8 @@ def init_config(app):
menu.submenu(".settings.item1").register("settings_item_1", "Item 1", order=2)
@app.route('/settings/')
+ @register_breadcrumb(app, '.settings', 'Settings page')
+ @register_menu(app, 'settings.item1', 'Item 1', order=2)
def settings_item_1():
return render_template('invenio_foo/foo_settings.html')
diff --git a/invenio_theme/config.py b/invenio_theme/config.py
index e32e40b5..4e6a08e4 100644
--- a/invenio_theme/config.py
+++ b/invenio_theme/config.py
@@ -112,6 +112,9 @@
THEME_SEARCH_ENDPOINT = "/search"
"""The endpoint for the search bar."""
+THEME_BREADCRUMB_ROOT_ENDPOINT = ""
+"""The endpoint for the Home view in the breadcrumbs."""
+
THEME_SITENAME = _("Invenio")
"""The name of the site to be used on the header and as a title."""
diff --git a/invenio_theme/ext.py b/invenio_theme/ext.py
index e37c7875..92e0f8ee 100644
--- a/invenio_theme/ext.py
+++ b/invenio_theme/ext.py
@@ -9,6 +9,8 @@
"""Invenio standard theme."""
+from flask import Blueprint
+from flask_breadcrumbs import Breadcrumbs
from flask_menu import Menu
from . import config
@@ -24,7 +26,9 @@ def __init__(self, app=None, **kwargs):
:param app: An instance of :class:`~flask.Flask`.
:param \**kwargs: Keyword arguments are passed to ``init_app`` method.
"""
- self.app = None
+ self.menu_ext = Menu()
+ self.menu = None
+ self.breadcrumbs = Breadcrumbs()
if app:
self.app = app
@@ -40,6 +44,42 @@ def init_app(self, app, **kwargs):
self.menu_ext = Menu(app)
app.context_processor(lambda: {"current_theme_icons": self.icons})
+ # Initialize extensions
+ self.menu_ext.init_app(app)
+ self.menu = app.extensions["menu"]
+ self.breadcrumbs.init_app(app)
+
+ # Register blueprint in order to register template and static folder.
+ app.register_blueprint(
+ Blueprint(
+ "invenio_theme",
+ __name__,
+ template_folder="templates",
+ static_folder="static",
+ )
+ )
+
+ # Register frontpage blueprint if enabled.
+ if app.config["THEME_FRONTPAGE"]:
+ app.register_blueprint(blueprint)
+
+ # Initialize breadcrumbs.
+ item = self.menu.submenu("breadcrumbs")
+ item.register(app.config["THEME_BREADCRUMB_ROOT_ENDPOINT"], _("Home"))
+
+ # Register errors handlers.
+ app.register_error_handler(401, unauthorized)
+ app.register_error_handler(403, insufficient_permissions)
+ app.register_error_handler(404, page_not_found)
+ app.register_error_handler(429, too_many_requests)
+ app.register_error_handler(500, internal_error)
+
+ # Register context processor
+ @app.context_processor
+ def _theme_icon_ctx_processor():
+ from invenio_theme.proxies import current_theme_icons
+
+ return dict(current_theme_icons=current_theme_icons)
# Save reference to self on object
app.extensions["invenio-theme"] = self
diff --git a/setup.cfg b/setup.cfg
index 2419d7af..4a25dad8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -28,6 +28,7 @@ python_requires = >=3.7
zip_safe = False
install_requires =
Flask-Menu>=1.0.0
+ Flask-Breadcrumbs>=0.4.0
invenio-assets>=1.2.7
invenio-base>=1.2.5
invenio-i18n>=2.0.0
diff --git a/tests/test_invenio_theme.py b/tests/test_invenio_theme.py
index 8c0da25c..75c6c6f2 100644
--- a/tests/test_invenio_theme.py
+++ b/tests/test_invenio_theme.py
@@ -140,6 +140,7 @@ def test_header_template_blocks(app):
"brand",
"navbar_inner",
"navbar_right",
+ "breadcrumbs",
"flashmessages",
"navbar_nav",
"navbar_search",