1
0
Files
breadcrumb-the-shire/docs/reference-notifications-module.md
fs 545bcc789b docs: replace ASCII umlaut fallbacks with proper äöüß across all .md
Most German docs were written with `ue`/`ae`/`oe`/`ss` ASCII fallbacks
(`fuer`, `aenderung`, `koennen`, `gemaess`) — relic from older toolchain
constraints. Replaced 237 occurrences across 38 .md files with proper
umlauts and ß so the docs read naturally.

Substitutions are constrained to plain prose: fenced code blocks
(```...```), inline code spans (`...`), markdown link targets `[..](..)`,
and HTML comments are preserved verbatim. Path references like
`docs/howto-erste-aenderung.md` keep their original (umlaut-replaced)
filenames — only the surrounding prose is normalised.

Tool: bin/fix-md-umlauts.py (idempotent — no-op on already-clean files).
Re-runnable if ASCII fallbacks creep back in via copy-paste.

Doc-link-check + doc-drift-check stay green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:57:22 +02:00

4.9 KiB

Notifications Modul (V1)

Letzte Aktualisierung: 2026-03-25

Ziel

Referenz für das Modul notifications: Event-Registrierung, Erzeugungsregeln, Guardrails, Frontend-Vertrag und Testbarkeit.

Scope v1

  • In-App Notifications (Bell + Dropdown), kein E-Mail-Channel.
  • Core-5 Eventtypen:
    • user.created
    • user.deleted
    • user.activated
    • user.deactivated
    • user.assignment_changed
  • Dedupe für diese Typen mit 30 Minuten Fenster.

Modul-Verkabelung

Kanonische Quelle: modules/notifications/module.php

  • event_listeners
    • mappt Eventtyp -> Listenerklasse + Methode handle.
  • ui_slots
    • topbar.right_item: Bell-Template
    • layout.head_style: Modul-CSS für Topbar + Dropdown
    • runtime.component: Bell-JS (initNotificationBell)
  • permissions
    • notifications.view
  • authorization_policies
    • NotificationsAuthorizationPolicy
  • layout_context_providers
    • liefert notifications.nav.unread_count in den Layout-Context
  • session_providers
    • synchronisiert module.notifications.unread_count aus DB

Event -> Notification Fluss

  1. Ein User-Lifecycle-Workflow dispatcht ein Domain-Event (z. B. user.activated).
  2. Der in module.php registrierte Listener verarbeitet das Payload.
  3. Listener ruft NotificationService auf.
  4. NotificationService orchestriert Empfaenger/Fan-out und Dedupe-Metadaten.
  5. NotificationRepository persistiert in user_notifications.
  6. Bell-Endpunkte lesen tenant-scoped Daten und aktualisieren den Session-unread_count.

Producer-Contract (für Core + andere Module)

Stabile Erzeugung erfolgt über:

  • MintyPHP\Module\Notifications\Service\NotificationMessage
  • MintyPHP\Module\Notifications\Service\NotificationService::createFromMessage(...)
  • NotificationService::createForTenantUsersFromMessage(...)
  • NotificationService::createForTenantAdminUsersFromMessage(...)

Beispiel (locale-neutral, mit i18n-Key):

use MintyPHP\Module\Notifications\Service\NotificationMessage;
use MintyPHP\Module\Notifications\Service\NotificationService;

$message = NotificationMessage::localized(
    'user.created',
    'New user: %s',
    [$displayName],
    null,
    [],
    'admin/users/edit/' . $uuid,
    ['user_id' => $userId, 'uuid' => $uuid]
);

$notificationService->createForTenantAdminUsersFromMessage(
    $tenantId,
    $message,
    $actorUserId,
    $allowedAdminSet
);

Plain/Fallback-Erzeugung bleibt möglich mit NotificationMessage::plain(...).

Datenvertrag Notification

Persistierter Kernvertrag:

  • type (string)
  • title (string)
  • body (nullable string)
  • link (nullable string)
  • data (nullable JSON)

Erweiterung für locale-neutrale Texte:

  • data.__message.title_key (string)
  • data.__message.title_params (array)
  • data.__message.body_key (string, optional)
  • data.__message.body_params (array, optional)

Wenn __message vorhanden ist, wird der Anzeigetext beim Lesen in der aktuellen Empfaenger-Locale gerendert (Fallback auf persistiertes title/body bleibt erhalten).

Dedupe-Metadaten (intern):

  • dedupe_fingerprint
  • dedupe_bucket
  • dedupe_until

Guardrails (Server)

  • Alle Bell-Endpunkte erzwingen:
    • Login
    • Ability notifications.view
  • POST-Endpunkte (mark-read, delete) prüfen CSRF.
  • Tenant-Scope wird serverseitig aus current_tenant erzwungen.
  • SQL ausschliesslich im Repository (NotificationRepository), Service nur Orchestrierung.

Guardrails (Frontend)

  • Endpunkte werden mit App-Base-URL gebaut (kein harter Root-Pfad).
  • Fehlerpfade laufen über zentrale Telemetrie (warn_once).
  • A11y-Klickzeilen im Dropdown sind lokal gestylt, damit globale [role="button"] Regeln nicht durchschlagen.
  • Default-details/summary-Chevron wird pro Komponente über data-summary-chevron="none" deaktiviert.

Template/Partial Best Practice

  • Core-Partials immer über templatePath('partials/...') einbinden.
  • Keine fragilen relativen Tiefenpfade auf Core-Templates verwenden.
  • Modulinterne Templates bleiben lokal relativ zum Modul.

CSS Token Best Practice (Module)

  • Modul-CSS soll auf Core-Variablen aufsetzen (z. B. --app-color, --app-muted-color, --app-dropdown-*, --app-action-danger-*).
  • Modul-spezifische Aliase sind ok (--app-notification-*), solange sie auf Core-Tokens mappen.
  • Harte Hex-Fallbacks nur als Ausnahme; Standard ist tokenbasiert.

Tests

Unit

  • Listener-Tests für alle Core-5 Typen:
    • Empfaengerregeln
    • Actor-Exclusion
    • ungultige/leere Payloads
  • Service-Tests für Dedupe (created vs suppressed)
  • Policy-Tests für notifications.view

Integration/Flow

  • End-to-End für create/delete/activate/deactivate/assignment-change.
  • Bell-Endpunkte mit Tenant-Scope und Permission-Prüfung.
  • Bulk-Flows dispatchen konsistent.

Manueller Smoke

  1. Zwei Admins im selben Tenant, einer als Actor.
  2. Actor führt User-Aktion aus.
  3. Zweiter Admin sieht Bell-Badge + Listeneintrag.
  4. Wiederholung innerhalb 30 Minuten erzeugt kein Duplikat.