1
0
fs 5378209fed refactor(departments-edit): migrate to actionEditContext (step 2)
Pilot migration of pages/admin/departments/edit($id).php onto the
actionEditContext aggregator introduced in step 1. The CONTEXT-stage
vorspiel (lookup → authorize → tenant-scope → can_view_page → viewAuth)
collapses into a single declarative call; the POST branch (CSRF →
SUBMIT-authorize → can_update gate → service call → PRG) stays
callsite-specific as planned.

Three deliberate touches beyond a 1:1 lift:

* Additive aggregator extension: actionEditContext gains an optional
  notFoundFlashScopeKey arg so the dedup scope-key 'department_not_found'
  is preserved without widening the frozen actionResolveModelOrFail
  building-block signature. Pattern is documented as the
  forward-compatibility mechanism for future cluster migrations.
* t() consistency: the not-found message now flows through t() via the
  aggregator. To avoid a partial-translation mix, Flash::success calls
  for 'Department updated' (×2) are also wrapped — German users now see
  fully translated messages instead of a German/English mix.
* Defensive scope consumption: the action now consults
  $tenantScope['scope'] before falling through to the strict-mode
  fallback. The Departments policy never emits can_manage_all_tenants
  today (so behavior is identical), but the action is now resilient to
  future policies that might.

New ActionContextCsrfPairingContractTest enforces actionRequireCsrf()
before any POST body access for actions that use the aggregators —
preventing CSRF-pairing regressions during the cluster-wide rollout
(step 3). The step-1 testNoProductionCallSitesYet guard is removed,
since departments-edit is now the first legitimate caller; the new
pairing test takes over its protective role with a more substantive
guarantee.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:52:27 +02:00
2026-04-24 21:53:14 +02:00
2026-02-04 23:31:53 +01:00
2026-02-04 23:31:53 +01:00
2026-02-04 23:31:53 +01:00

CoreCore (MintyPHP)

CoreCore ist eine mandantenfaehige Admin-Anwendung auf Basis von MintyPHP. Der Fokus liegt auf klarer Rollen-/Rechtestruktur, Tenant-Scope und einer konsistenten Admin-UI.

Funktionsumfang

  • Mandantenverwaltung (inkl. Branding/Farbe/Favicon)
  • Abteilungen pro Mandant
  • Benutzerverwaltung mit Rollen, Sichtbarkeit und Stammdaten
  • Rollen- und Berechtigungsverwaltung
  • Adressbuch (tenant-scoped, read-only Profilansicht)
  • Globale Suche (Admin + Address Book)
  • Mail-Versand (SMTP/PHPMailer) inkl. Mail-Log
  • Onboarding-PDF fuer Zugangsdaten (Single inline, Bulk als ZIP)
  • Passwort-Reset und Remember-Login-Tokens
  • Geplante Aufgaben (Scheduler mit Ausfuehrungshistorie)

Tech Stack

  • PHP 8.5 (Runtime im Docker-Container)
  • MintyPHP Core
  • MariaDB 11
  • Memcached
  • Nginx
  • Grid.js (Tabellen)
  • PHPMailer
  • Dompdf (PDF Rendering)
  • endroid/qr-code (QR-Code Rendering)

Schnellstart (Docker)

  1. Umgebungsdatei anlegen
cp .env.example .env
  1. Config-Template fuer den App-Bootstrap anlegen
cp config/config.php.example config/config.php
  1. Container starten
docker compose up --build -d
  1. App oeffnen
  1. Container stoppen
docker compose down

Alternativ ueber den Dev-Orchestrator:

cp config/config.php.example config/config.php
bin/dev init
bin/dev up
bin/dev down
bin/dev console list
bin/dev qa

Produktion (Docker)

Es gibt eine getrennte Produktions-Compose:

  • Dev: /docker-compose.yml
  • Prod: /docker-compose.prod.yml

Grundschritte:

  1. .env auf Produktionswerte setzen (siehe /.env.prod.example)
  2. Domain und TLS in /docker/nginx/prod.conf anpassen
  3. Zertifikate unter /docker/nginx/certs/ bereitstellen
  4. Starten:
docker compose -f docker-compose.prod.yml up --build -d

Details stehen in:

  • /docs/howto-docker-lokal.md
  • /docs/howto-docker-produktiv.md
  • /docs/explanation-docker-betrieb.md (Übersicht)

Seed-Daten

Die Initialisierung liegt in /db/init/init.sql.

Standard-Demo-User:

  • E-Mail: demo@user.com
  • Passwort: Demo123
  • Tenant: MusterMandant
  • Abteilung: Musterabteilung
  • Rollen: Admin, User

Initiale DB-Settings (Seed):

  • app_title -> CoreCore
  • app_locale -> de
  • app_theme -> light
  • app_theme_user -> 1
  • app_registration -> 1 (aktiv)
  • app_primary_color -> #105433
  • api_token_default_ttl_days -> 90
  • api_token_max_ttl_days -> 365
  • api_cors_allowed_origins -> http://localhost:8080, http://127.0.0.1:8080
  • session_idle_timeout_minutes -> 30
  • session_absolute_timeout_hours -> 8
  • user_inactivity_deactivate_days -> 180
  • user_inactivity_delete_days -> 365
  • default_tenant_id -> MusterMandant
  • default_department_id -> Abteilungscode MUSTER (Musterabteilung)
  • default_role_id -> Rolle USER (User)
  • frontend_telemetry_enabled -> 1
  • frontend_telemetry_sample_rate -> 0.2
  • frontend_telemetry_allowed_events -> ajax_error,warn_once

Projektstruktur

  • /config Konfiguration (Routen, Assets, Themes, Module + ENV-Bootstrap)
  • /db/init Datenbankschema + Seeds
  • /lib Backend-Code (Repository, Service, Http, Support)
  • /pages Action-Layer + Views pro Route
  • /templates Layouts + wiederverwendbare Partials
  • /web oeffentliche Assets (CSS/JS/Vendor)
  • /i18n Uebersetzungen
  • /tests PHPUnit-Tests
  • /docs Projekt-Dokumentation

Architektur-Kurzregeln

  • Keine DB-Zugriffe in .phtml-Views.
  • SQL bleibt in Repositories.
  • Geschaeftslogik bleibt in Services.
  • pages/.../*.php fungiert als Action-/Controller-Schicht.
  • Service-Aufloesung in Actions/Helpern ueber app(Foo::class).
  • new ...Factory() nur im Composition Root (web/index.php + core/App/registerContainer.php) oder in *Factory.php.
  • pages/**/data().php sind GET-only (gridRequireGetRequest()) und normalisieren limit/offset/order/dir ueber Grid-Helper.
  • Permissions + Tenant-Scope werden in Actions/Services erzwungen.
  • Wiederkehrende UI-Bloecke als Partials zentralisieren.

UI-Konventionen

Edit-Seiten verwenden einheitliche Tab-Struktur:

  • erster Tab: Master data
  • letzter Tab: Visibility
  • optional letzter Tab: Danger zone

User-Organisation:

  • Departments werden tenantweise angezeigt (ein Multi-Select je ausgewaehltem Tenant).
  • Nach Tenant-Aenderungen im selben Formular werden Department-Optionen nach Speichern/Reload aktualisiert (kein Live-Update).

Zentrale Partials:

  • /templates/partials/app-details-titlebar.phtml
  • /templates/partials/app-visibility-status-field.phtml
  • /templates/partials/app-danger-zone-delete-field.phtml
  • /templates/partials/app-details-aside-audit.phtml
  • /templates/partials/app-details-aside-ids.phtml

Onboarding-PDF (User)

  • Permission: users.access_pdf
  • Single: admin/users/access-pdf/{uuid} (inline im neuen Tab)
  • Bulk: admin/users/access-pdf-bulk (POST, ZIP mit einer PDF pro User)
  • Hard limit: maximal 100 User pro Bulk-Request
  • Benoetigte Extensions: ext-zip, ext-dom, ext-mbstring
  • Bestehende Installationen: Schema-/Permission-Updates als idempotentes SQL-Update ausfuehren

Microsoft SSO (Tenant, Entra ID)

  • Zentraler Einstieg: login (optional mit Tenant-Hint login?tenant={tenant-slug})
  • OIDC-Endpunkte: auth/microsoft/start und auth/microsoft/callback
  • Tenant-Config liegt in tenant_auth_microsoft
  • Externe User-Linkung liegt in user_external_identities
  • Optionaler Profil-Sync pro Tenant:
    • sync_profile_on_login aktiviert Sync bei jedem Microsoft-Login
    • sync_profile_fields: first_name, last_name, phone, mobile, avatar
    • Default-Felder bei aktivem Sync ohne Auswahl: first_name,last_name
    • phone/mobile/avatar nutzen Microsoft Graph (User.Read)
  • Shared App Credentials werden in Settings gepflegt (optional Tenant-Overrides)
  • Permission fuer Tenant-SSO-Konfiguration: tenants.sso_manage
  • Bestandsinstallationen: Schema-/Permission-Updates als idempotentes SQL-Update ausfuehren
  • Wichtig: APP_CRYPTO_KEY in .env setzen, sonst koennen SSO-Secrets nicht gespeichert/verwendet werden

Asset- und Frontend-System

  • Core-CSS-Entrypoint: /web/css/core.css
  • CSS-Layer/Vendor-Basis: /web/css/app-layers.css
  • Vendor-Overrides: /web/css/vendor-overrides
  • Asset-Gruppen: /config/assets.php
  • Template-Styles: renderTemplateStyles('default'|'login')
  • Seiten-Styles per Action: Buffer::set('style_groups', ...)
  • CSS-Contract-Check: bin/css-contract-check.sh
  • JS-Bootstrap:
    • /web/js/app-boot.js (frueh, ohne Module)
    • /web/js/app-init.js (Standardseiten)
    • /web/js/app-login-init.js (Loginseiten)

Qualitaetschecks

Alle Befehle aus dem Projekt-Root ausfuehren.

PHPUnit

docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php

Gezielt:

docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/I18n/TranslationKeysTest.php
docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Repository/RepoQueryTest.php

PHPStan

docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress

Coding Style (PHP CS Fixer)

CI-Check (ohne Dateien zu aendern):

docker compose exec php vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose

Lokal automatisch formatieren:

docker compose exec php composer cs:fix

Frontend-JS Smoke-Check (ohne Node)

  • Browser-DevTools öffnen (Console + Preserve log)
  • Kernflows klicken: Users, Address-Book, Audit-Listen, Filter setzen/zurücksetzen
  • Keine JavaScript-Errors/Unhandled-Rejections in der Console

Dokumentation

  • /docs/index.md Dokumentationsstart mit chronologischem Lernpfad und Referenzbereichen
  • /docs/tutorial-02-systemueberblick.md Einstieg in Produktbild und Kernprinzipien
  • /docs/tutorial-03-setup-und-erster-login.md lokales Setup und erster Smoke-Test
  • /docs/reference-domain-glossar.md zentrale Begriffe fuer Tenant/Role/Scope
  • /docs/tutorial-04-architektur-request-flow.md Request-Flow und Schichtmodell
  • /docs/tutorial-07-security-tenant-scope.md Security-Grundlagen vor Feature-Arbeit
  • /docs/tutorial-08-api-erweitern.md verbindlicher API-Aenderungsablauf inkl. OpenAPI

Troubleshooting

  • Verhalten wirkt stale nach groesseren Refactors:
docker compose restart php nginx
  • Uebersetzungs-Keys fehlen:
docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests/I18n/TranslationKeysTest.php
  • Favicon/Logo aus Storage fehlt:
    • Symlink und Tenant-Kontext pruefen (web/favicon/tenants -> storage/tenants).

Lizenz

MIT (siehe /LICENSE.md)

Description
No description provided
Readme 6.8 MiB
Languages
PHP 70.9%
HTML 11.5%
JavaScript 10.4%
CSS 6.7%
Shell 0.4%
Other 0.1%