Commit Graph

85 Commits

Author SHA1 Message Date
fs
be8bf496cb test: add architecture contract tests for layering, sessions, and asset versioning
New contracts enforce repository layer isolation, no HTML in services,
no DB calls in views, module session key prefixes, and asset versioning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:15:41 +01:00
fs
555199b217 fix: PHPStan and test strictness improvements
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>
2026-03-22 14:15:36 +01:00
fs
141f0a0155 refactor: extract shared code from bin/ scripts and remove legacy scheduler runner
- Extract ModuleManifestLoader for standalone manifest loading (used by
  module-deactivate and ValidateCommand)
- Extract SqlStatementParser, ModuleMigrationRepository, and
  ModuleMigrationService from bin/module-migrate.php to enforce strict
  layering (SQL in repository, orchestration in service)
- Delete bin/scheduler-run.php (fully superseded by bin/console scheduler:run)
- Update docs and test fixtures to reference bin/console

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:45:16 +01:00
fs
975651b183 fix: eliminate $_SESSION drift in SessionProvider and add notification UI states
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>
2026-03-20 22:49:03 +01:00
fs
1f0b1caf54 feat: module-owned i18n with merge-at-boot translation loading
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>
2026-03-19 22:08:34 +01:00
fs
ae74fc3251 Stabilize service suite hygiene 2026-03-19 20:35:15 +01:00
fs
b873efc973 Strengthen settings gateway tests 2026-03-19 20:32:34 +01:00
fs
56c3e30f43 Strengthen service workflow tests 2026-03-19 20:28:04 +01:00
fs
f7b03926b0 Clarify test suite taxonomy 2026-03-19 20:22:10 +01:00
fs
011d662dfc Drop redundant crypto gateway tests 2026-03-19 20:17:11 +01:00
fs
72c7f1b337 Tighten service test signal 2026-03-19 20:15:48 +01:00
fs
28008c008c Trim remember me delegation tests 2026-03-19 20:04:14 +01:00
fs
12bc58dffa chore: remove legacy tools/codex-skills (moved to .agents/skills), split filter drawer tests, clean up stale docs
- tools/codex-skills/ removed — skills now live in .agents/skills/
- docs/reference-agents-*.md removed — replaced by .agents/workflow.md
- FilterDrawerContractTest split into Runtime + Template contract tests
- bin scripts and .gitignore updated for .agents/ paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:59:08 +01:00
fs
c89501bbed Remove low-value service delegation tests 2026-03-19 19:46:07 +01:00
fs
5c7a1148ac Consolidate settings session policy tests 2026-03-19 19:45:16 +01:00
fs
bc72fa50a6 Consolidate settings normalization tests 2026-03-19 19:43:55 +01:00
fs
83aadb3535 Deduplicate crypto gateway tests 2026-03-19 19:36:17 +01:00
fs
8cd21f3c8a fix: permission sync was deactivating core permissions, not just module-owned ones
deactivateOrphaned() treated ALL is_system=1 permissions as module-owned.
Core seed permissions (users.view, tenants.view, etc.) are also is_system=1,
so they were incorrectly deactivated — breaking admin panel access.

Fix: scan all module manifests on disk (enabled or not) to build the set of
module-owned permission keys. Only deactivate permissions whose key appears
in that set. Core seed permissions are never touched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:32:36 +01:00
fs
e40cba23a1 Split list titlebar contracts 2026-03-19 19:29:45 +01:00
fs
ba1186c525 refactor: console improvements — auto-discovery, centralized bootstrap, per-command help
- ConsoleApplication.discoverCommands() scans lib/Console/Commands/ automatically;
  adding a new command no longer requires editing bin/console
- Command base class provides bootstrapApp(), bootstrapModuleApp(), requireBinScript()
  and projectRoot() helpers — eliminates fragile 4-level relative require_once paths
- Per-command --help support via optional usage() method on Command
- Docker-compose scheduler updated to use bin/console scheduler:run
- Old bin scripts now emit deprecation hint to stderr when called directly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:16:36 +01:00
fs
f7787465f7 Split aside navigation contracts 2026-03-19 19:13:51 +01:00
fs
115723fcd6 Split auth login accessibility contracts 2026-03-19 19:12:25 +01:00
fs
2de0039d4f Split status taxonomy contracts 2026-03-19 19:11:03 +01:00
fs
5582a55208 Split security logging contracts 2026-03-19 19:08:28 +01:00
fs
0bacf2a7a6 Split auth help link contracts 2026-03-19 19:05:29 +01:00
fs
522705dd33 Split detail page contracts 2026-03-19 19:04:28 +01:00
fs
f6046c9168 Split detail action policy contracts 2026-03-19 19:03:10 +01:00
fs
cc653cb4cd Split detail validation contracts 2026-03-19 19:02:04 +01:00
fs
95ab6b8c6c Split module registry contracts 2026-03-19 19:00:29 +01:00
fs
475524bfd4 Rename module extraction contracts 2026-03-19 18:57:03 +01:00
fs
b9f566c49f Split meta architecture contracts 2026-03-19 18:52:36 +01:00
fs
ac9cc4955a Split public page contracts 2026-03-19 18:48:16 +01:00
fs
98520dcc6a Split filter drawer contracts 2026-03-19 18:41:08 +01:00
fs
0f8fff8daf Split frontend runtime contracts 2026-03-19 18:34:02 +01:00
fs
ee5fdaaff7 Split module structure contracts 2026-03-19 18:30:14 +01:00
fs
1e993e470c Refactor architecture test contracts 2026-03-19 18:25:19 +01:00
fs
ef72b34c40 feat: module architecture improvements — session keys, dependency graph, event dispatcher, deactivation hooks
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>
2026-03-19 18:20:13 +01:00
fs
cb27fe090d refactor: simplify module platform — drop custom autoloader, layout_capabilities indirection, and absolute template paths
- Replace ModuleAutoloader with Composer ClassLoader (addPsr4 via spl_autoload_functions)
- Eliminate layout_capabilities mapping; UI slots reference ability strings directly
- Resolve relative template paths in ModuleRegistry instead of baking __DIR__ into manifests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 08:26:45 +01:00
fs
c328067aa6 refactor: align addressbook module to MintyPHP\Module\AddressBook namespace
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>
2026-03-18 22:20:54 +01:00
fs
4871c6032e feat: extract bookmarks as standalone module with MintyPHP\Module\Bookmarks namespace
Moves bookmarks from core hardcoding into modules/bookmarks/ as a
fully self-contained module, following the same pattern as addressbook.

Module contributions via platform slots:
- aside.tab_panel: bookmark sidebar panel
- topbar.right_item: bookmark toggle button
- layout.body_end_template: bookmark/group dialogs
- layout.head_style: bookmark CSS (form + sidebar)
- runtime.component: bookmark-save and bookmark-panel (phase: late)
- search.resource_item: bookmarks in global search (user-scoped via {{userId}})

Backend fully in module namespace (MintyPHP\Module\Bookmarks\*):
- Service: BookmarkService, BookmarkServicesFactory, BookmarkRepositoryFactory
- Repository: BookmarkRepository, BookmarkGroupRepository, BookmarkNavigationRepository
- Support: BookmarkUrlNormalizer
- Providers: BookmarksSessionProvider, BookmarksLayoutProvider, BookmarksSearchProvider

Core cleanup:
- Removed all bookmark-specific markup from core templates
- Removed core DI registrations for bookmark services
- Removed core bookmark pages, JS, CSS
- AuthSessionTenantContextService delegates to module SessionProvider

Architecture guards:
- NoBookmarksHardcodingTest: verifies zero bookmark references in core
- testModuleClassesUseModuleNamespace: prevents core namespace leakage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:20:20 +01:00
fs
c7b8fd516a feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.

New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering

New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
  module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
  FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00
fs
c364e2b46d feat: introduce module system and extract address book as first module (MODULAR-MONOLITH-V1-001)
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>
2026-03-18 15:43:25 +01:00
fs
d9805c45d3 fix: address code review findings for bookmark feature (H1–L5)
Fix broken tests (H1), align interface signatures with implementations (H3),
remove dead code (H4), add missing input guard (M1), replace raw $_SESSION
access with SessionStoreInterface (M2), sync update script schema (M4),
use shared getAppBase utility (L2), document fragment stripping (L3),
and apply app- CSS class prefix convention (L5).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 22:58:07 +01:00
fs
9688848401 Global Boomarks 2026-03-14 21:45:58 +01:00
fs
a27b6a96ff fix: dismiss stale session-timeout flash on auto-login, resolve PHPStan and i18n issues
- Add Flash::dismissByKey() to clear flash messages by key+scope
- Dismiss 'session_timeout' flash after successful remember-me auto-login
  so the message doesn't persist through manual logout
- Suppress PHPStan false positives for dynamically defined MINTY_ALLOW_OUTPUT
- Remove unnecessary ?? [] fallbacks on preg_match_all results in tests
- Add missing i18n key 'No active roles are configured yet.'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:56:19 +01:00
fs
e1c211069e fix(session): preserve intended URL across session timeout with remember-me
Session timeout redirected to /de/login?return_to=... but the remember-me
cookie was not cleared by logoutDueToSessionTimeout(). On the next request,
autoLoginFromCookie() silently re-logged the user in, causing the MintyPHP
router to resolve the login route to pages/index().php (dashboard) instead
of the login action — the intended URL redirect logic never executed.

Fix: intercept ?return_to= immediately after successful auto-login in
index.php and redirect to the sanitized route path. Also harden the login
flow with hidden return_to form fields and POST body fallback for cases
without remember-me.

Additional improvements in this commit:
- Convert stored REQUEST_URI to router-compatible path (strip leading
  slash and locale prefix) via IntendedUrlService::toRoutePath()
- Consolidate duplicate CSRF data attributes into shared data-csrf-key
  and data-csrf-token on <html> element
- Add tenant branding (logo) to auth pages via appAuthLogoUrl() helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 18:28:13 +01:00
fs
add5cca222 settings cache 2026-03-13 14:38:58 +01:00
fs
04995e3712 feat(session): preserve intended URL across session timeout and add expiry warning dialog
When a session expires, the user's current URL is now captured and restored
after re-authentication (via remember-me cookie or manual login), instead of
always redirecting to the dashboard. A JavaScript session warning dialog
appears ~2 minutes before idle timeout, allowing users to extend their session
with a single click. Includes open-redirect prevention via URL validation,
Microsoft SSO callback support, and 33 unit tests.

Refs: SESSION-REDIRECT-PRESERVE-001

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:28:49 +01:00
fs
d4978d1504 test(services): add 88 unit tests for 7 critical service classes
RateLimiterServiceTest (17): hit/isBlocked/reset/registerFailure, fail-open,
sliding window, scope normalization.
PermissionServiceTest (24): userHas, getUserPermissions, cache hit/miss/refresh,
clearUserCache, CRUD, system permission protection.
RoleServiceTest (9): createFromAdmin, updateFromAdmin, deleteByUuid,
Admin role protection, duplicate code rejection.
TenantServiceTest (8): CRUD, department-dependent deletion blocking.
DepartmentServiceTest (14): listPaged scope filtering, groupActiveByTenantIds,
createFromAdmin, deleteByUuid, syncTenants.
MailServiceTest (8): send logging, MailerException handling, template metadata.
UserLifecycleServiceTest (8): advisory locking, deactivation, deletion,
snapshot failure skip, privileged user exclusion, manual trigger type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:57:54 +01:00
fs
f6777113ec test(gateways): add 27 unit tests for 3 security-critical gateway classes
AuthCryptoGatewayTest (10 tests): encrypt/decrypt round-trip, unique IVs,
Crypto payload format verification (GR-SEC-005), error handling.
SettingsCryptoGatewayTest (9 tests): interface compliance, round-trip,
AES-256-GCM format, tampered ciphertext handling.
OidcHttpGatewayTest (8 tests): success, 401/500, network error, malformed JSON.

Plan amended: PermissionGateway removed (does not exist in codebase).
SC-002 amended: payload format verification accepted as Crypto delegation proof.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:57:33 +01:00