Files
Ebbe BaßandClaude Sonnet 5.5 3fd8f27127 feat: add profile edit
Profile page at /profile with first name, last name, display name, telephone and mobile read from AD and shown with the username and e-mail. Only changed fields are written through the directory adapter in one transaction with the audit entry (field names only); numbers are accepted in any common notation and stored as +country code and digits; names are required. Plain-language errors per field, German text and tests; review findings logged in the plan.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FocPJ75XPma7TcuYEMy8zm
2026-10-19 09:03:24 +02:00

70 lines
1.8 KiB
Python

"""The left menu, declared once. An entry is shown only to users who may open its page."""
from __future__ import annotations
from dataclasses import dataclass
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
@dataclass(frozen=True)
class Entry:
label: object # lazy translated string
url_name: str
@dataclass(frozen=True)
class Group:
title: object | None
role: str | None # None: everyone; otherwise "publisher" or "admin"
entries: tuple[Entry, ...]
MENU = (
Group(
None,
None,
(
Entry(_("Home"), "home"),
Entry(_("Profile"), "profile"),
Entry(_("Change password"), "password"),
),
),
Group(
_("Publishing"),
"publisher",
(Entry(_("Manage announcements"), "publish-announcements"),),
),
Group(
_("Administration"),
"admin",
(
Entry(_("Appearance"), "admin-appearance"),
Entry(_("Settings"), "admin-settings"),
Entry(_("Audit log"), "admin-audit"),
Entry(_("Status"), "admin-status"),
),
),
)
def _allowed(group: Group, portal_user) -> bool:
if group.role is None:
return True
return bool(getattr(portal_user, f"is_{group.role}", False))
def for_user(portal_user, current_path: str) -> list[dict]:
"""The groups this user sees, each entry with its address and whether it is current."""
groups = []
for group in MENU:
if not _allowed(group, portal_user):
continue
entries = []
for entry in group.entries:
url = reverse(entry.url_name)
entries.append({"label": entry.label, "url": url, "current": url == current_path})
groups.append({"title": group.title, "entries": entries})
return groups