website/thaliawebsite/templatetags/menu.py
"""Provides a template handler that renders the menu."""
from collections import defaultdict
from functools import cache
from django import template
from django.apps import apps
register = template.Library()
@cache
def collect_menus():
categories = defaultdict(list)
main_menu = []
for app in apps.get_app_configs():
if hasattr(app, "menu_items"):
menu = app.menu_items()
if "categories" in menu:
for category in menu["categories"]:
assert "key" in category
if category["name"] not in categories:
categories[category["name"]] = {"items": [], **category}
for item in menu["items"]:
assert "url" in item, item
if "category" not in item:
# Main item
main_menu.append(item)
else:
assert item["category"] in categories
categories[item["category"]]["items"].append(item)
for category in categories.values():
main_menu.append(
{
"submenu": sorted(
category["items"], key=lambda x: (x["key"], x["title"])
),
**category,
}
)
return sorted(main_menu, key=lambda x: (x["key"], x["title"]))
@register.inclusion_tag("menu/menu.html", takes_context=True)
def render_main_menu(context):
"""Render the main menu in this place.
Accounts for logged-in status and locale.
"""
path = None
if "request" in context:
path = context.get("request").path
main_menu = collect_menus()
for item in main_menu:
active = "url" in item and item["url"] == path
if not active and "submenu" in item:
for subitem in item["submenu"]:
if subitem["url"] == path:
subitem["active"] = True
active = True
else:
subitem["active"] = False
item["active"] = active
return {"menu": main_menu, "request": context.get("request")}
@cache
def collect_usermenu():
sections = defaultdict(list)
for app in apps.get_app_configs():
if hasattr(app, "user_menu_items"):
menu = app.user_menu_items()
if "sections" in menu:
for section in menu["sections"]:
assert "key" in section
if section["name"] not in sections:
sections[section["name"]] = {"items": [], **section}
for item in menu["items"]:
assert "url" in item, item
assert item["section"] in sections
sections[item["section"]]["items"].append(item)
sections = sections.values()
for section in sections:
section["items"] = sorted(
section["items"], key=lambda x: (x["key"], x["title"])
)
return sorted(sections, key=lambda x: (x["key"], x["name"]))
@register.inclusion_tag("menu/usermenu.html", takes_context=True)
def render_user_menu(context):
if "request" not in context or not context["request"].user.is_authenticated:
return {"authenticated": False}
request = context.get("request")
path = request.path
user_menu = collect_usermenu()
for section in user_menu:
# Create filtered copy of items that only shows 'enabled' items
section["submenu"] = list(
filter(
lambda item: "show" not in item or item["show"](request),
section["items"],
)
)
# Highlight active item
for item in section["submenu"]:
item["active"] = item["url"] == path
# Hide sections with no shown items
user_menu = filter(lambda section: len(section["submenu"]) > 0, user_menu)
return {"menu": user_menu, "request": request, "authenticated": True}