Remove NotificationRepositoryFactory, NotificationServicesFactory, and
NotificationRepositoryInterface — all single-consumer abstractions with
no polymorphism benefit. Simplify container registrar to wire services
directly. Extract shared listener logic (sanitizeTenantIds,
resolveDisplayName, resolveUuid) into NotificationListenerTrait,
eliminating duplication across 4 listeners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix 5 broken FQCN references (runtime errors) where core pages referenced
non-existent MintyPHP\Service\Audit\AuditMetadataEnricher and SystemAuditService.
Add AuditMetadataEnricherInterface with NullAuditMetadataEnricher fallback so
deactivating the audit module no longer crashes core edit pages.
Move audit search resources from core Search*Provider files into the module
via a new AuditSearchResourceProvider implementing the existing
SearchResourceProvider contract. Add module-specific purge permissions
(audit.api.purge, audit.imports.purge) replacing core ABILITY_ADMIN_SETTINGS_UPDATE.
Also fixes: session key prefix convention, hardcoded English string, $_SERVER
superglobal fallback, and manifest schema drift (i18n_path, group in ui_slots).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Routes in nav items, redirects, breadcrumbs, and JS data URLs all pointed
to the old admin/ prefix from before the module was restructured. Also
corrects use-statements still referencing core Domain\Taxonomy namespace
instead of Module\Audit\Domain.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Module JS files were using relative imports (../core/) which resolved
incorrectly when served from modules/audit/js/pages/ instead of the
original web/js/pages/. Changed to absolute paths (/js/core/, /js/pages/)
so they correctly resolve to the core JS utilities.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Module-contributed sidebar.admin_nav_item slots are now rendered as
complete <details><summary> nav groups appended to the admin panel,
matching the same pattern as core groups (Organization, Roles, etc.).
Fixes undefined $moduleNavItemSlots by reading layoutNav early.
Merges mail log into the module-contributed Audit & logs group when
both exist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Module pages must use a unique top-level directory (audit/) instead of
nesting under admin/ which collides with core's pages/admin/ during
module:build symlink creation. Routes still map admin/* URLs to the
audit/ page directory via manifest route targets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NotificationsAuthorizationPolicy was never registered in the container,
so ModuleClassResolver could not instantiate it (requires PermissionService).
This silently prevented the notifications.view permission from being granted,
hiding the bell icon in the topbar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refine bookmark create/edit and group create/edit dialogs to enterprise
quality: distinct titles per mode (Add bookmark vs Edit bookmark), URL
preview when creating, inline field validation with aria-invalid instead
of toast-only errors, loading spinner on submit, compact inline
new-group link, human-readable icon picker aria-labels, and aria-pressed
on selected icons.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the dedicated "Edit"/"Open" action button column from all list
pages. Instead, wrap the primary data column (name, description, subject,
etc.) in a clickable <a> link via the new rowInteraction.linkColumn option.
Double-click, Enter-key, and click-to-focus row navigation preserved.
Pages with explicit actions config are unaffected. Address-book opts out
of auto-wrapping (linkColumn: false) and renders the link in its own
formatter to avoid nested <a> with avatar lightbox.
Also fixes pre-existing XSS in insertActionColumn (unescaped href/label)
and updates typography test for --text-page-title token change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add bin/ scripts to PHPStan scanFiles, suppress property.onlyWritten for
by-ref test properties, add exhaustive default match arm, use explicit
expects() on mock stubs, fix data provider return type, and simplify
redundant null check in NotificationServiceTest.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set defaultTestSuite="CoreCore" to prevent 222 duplicate file warnings,
and replace deprecated isType() with isArray()/isString() in notification
listener tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass AppContainer to SessionProvider::clear() so all module providers use
SessionStoreInterface consistently instead of raw $_SESSION. Also add
loading and error states to the notification bell dropdown (GR-UI-014).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In-app notification system with topbar bell icon, dropdown panel,
mark-as-read/dismiss actions, and scheduled cleanup of old read
notifications. Includes event listeners for user.created and
user.deleted events, authorization policy, and full test coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each module can now ship its own i18n/ directory with translation files
(default_de.json, default_en.json). At boot time, ModuleI18nLoader
preloads core translations then merges module translations on top via
Reflection into I18n::$strings — the t() helper stays unchanged.
- Add i18nPath property to ModuleManifest
- Add ModuleI18nLoader with Reflection-based preload
- Extract 59 module-exclusive keys from core i18n into module files
(notifications: 8, bookmarks: 41, addressbook: 10)
- Expand TranslationKeysTest to scan modules/ and validate against
merged core + module translations
- Add ModuleI18nContractTest for per-module de/en parity and completeness
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Six targeted improvements to the modular monolith platform:
1. Fix AddressBook session key prefix to follow module.<id>.* convention
2. Move ADDRESS_BOOK_VIEW permission constant from core PermissionService into module
3. Add declarative JSON schema for module manifests (.agents/contracts/)
4. Add `requires` field with missing-dependency and circular-dependency detection
5. Add lightweight fire-and-forget event dispatcher (user.created/deleted/login/logout)
6. Add module deactivation hook interface and CLI script (bin/module-deactivate.php)
Includes 15 new architecture/unit tests covering all new functionality.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migrates addressbook service classes from core namespace
(MintyPHP\Service\AddressBook\*) to proper module namespace
(MintyPHP\Module\AddressBook\Service\*), consistent with the
bookmarks module pattern.
Moved to module:
- AddressBookService → modules/addressbook/lib/Module/AddressBook/Service/
- AddressBookServicesFactory → modules/addressbook/lib/Module/AddressBook/Service/
- AddressBookServiceTest → modules/addressbook/tests/Module/AddressBook/Service/
- Pages, templates, CSS, JS all in module directory
All module PHP classes now live under MintyPHP\Module\<Name>\*,
enforced by ModuleStructureContractTest::testModuleClassesUseModuleNamespace.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a module kernel (ModuleManifest, ModuleRegistry) that allows modules to
contribute routes, UI slots, search providers, layout context, session
lifecycle, and permissions. Modules are activated via config/modules.php or
APP_ENABLED_MODULES env variable. Conflicts (duplicate routes, permissions,
slot keys) cause a fail-fast with a clear error message.
Extract the address book from hardcoded Core integration points into the
first module (modules/addressbook/). The module provides:
- Aside icon-bar tab + People panel via UI slot system
- Global search resource via AddressBookSearchProvider
- Layout context data via AddressBookLayoutProvider
- Session lifecycle via AddressBookSessionProvider
Core cleanup removes address-book hardcodings from SearchSqlResourceProvider,
SearchUiMetaProvider, SearchItemMapperProvider, appBuildLayoutNavContext(),
and the aside templates. Permissions (ADDRESS_BOOK_VIEW) and business logic
(AddressBookService) remain in Core as they gate general user visibility.
Includes 38 new tests (894 total), PHPStan level 5 clean, and architecture
tests verifying zero hardcoded address-book references in search/templates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>