From d8a4a4555c18faff05f23c57210ff479345f0f2b Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Wed, 4 Feb 2026 01:06:15 +0100 Subject: [PATCH] IDEA Extended --- .gitmodules | 3 + .planning/codebase/ARCHITECTURE.md | 231 +++++++++++ .planning/codebase/CONCERNS.md | 592 +++++++++++++++++++++++++++++ .planning/codebase/CONVENTIONS.md | 228 +++++++++++ .planning/codebase/INTEGRATIONS.md | 240 ++++++++++++ .planning/codebase/STACK.md | 126 ++++++ .planning/codebase/STRUCTURE.md | 248 ++++++++++++ .planning/codebase/TESTING.md | 467 +++++++++++++++++++++++ CLAUDE.md | 52 +++ README.md | 1 + golem15-wintercms-starter | 1 + 11 files changed, 2189 insertions(+) create mode 100644 .gitmodules create mode 100644 .planning/codebase/ARCHITECTURE.md create mode 100644 .planning/codebase/CONCERNS.md create mode 100644 .planning/codebase/CONVENTIONS.md create mode 100644 .planning/codebase/INTEGRATIONS.md create mode 100644 .planning/codebase/STACK.md create mode 100644 .planning/codebase/STRUCTURE.md create mode 100644 .planning/codebase/TESTING.md create mode 100644 CLAUDE.md create mode 100644 README.md create mode 160000 golem15-wintercms-starter diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cfd0e23 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "golem15-wintercms-starter"] + path = golem15-wintercms-starter + url = git@github.com:golem15com/wn-starter-app.git diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..db52b0c --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,231 @@ +# Architecture + +**Analysis Date:** 2026-02-04 + +## Pattern Overview + +**Overall:** Modular Layer-based MVC with Plugin System + +WinterCMS implements a **three-layer modular architecture** consisting of Core Modules (System, Backend, CMS) that form the framework foundation, with a Plugin system overlaid for extensibility. The framework uses Laravel 9.x as its base, with Winter's Storm library acting as a compatibility buffer. + +**Key Characteristics:** +- Laravel-based HTTP kernel with module-level routing +- CMS-first approach: frontend pages route through CMS module, backend admin routes through Backend module +- Plugin dependency resolution with automatic component/controller/model discovery +- Extensibility through plugin interdependencies and event-driven architecture +- YAML/JSON configuration for models, forms, lists, and behaviors +- Static theme system with file-based page, layout, and partial management + +## Layers + +**Core Modules (modules/ directory):** +- **System Module:** Base framework, plugin management, mail system, asset handling, error management +- **Backend Module:** Admin interface, form/list widgets, user role management, controller behaviors +- **CMS Module:** Frontend page rendering, theme management, component system, layout composition + +**Plugin Layer (plugins/ directory):** +- Vendor-namespaced plugins (winter/, golem15/) +- Each plugin extends and integrates with core modules +- Plugin dependencies declared via `$require` arrays in Plugin.php +- Core plugins: Apparatus (DI/scenarios), User (auth), PaymentGateway (commerce) +- Domain plugins: Blog, Menu, Translate, AI, Chat, etc. + +**Frontend/Theme Layer (themes/ directory):** +- Static theme files organized in theme directories +- Pages, layouts, partials stored as .htm files with Twig syntax +- Assets (CSS, JS, images) co-located with pages/layouts +- URL routing to pages via Theme manager and CMS controller + +## Data Flow + +**Frontend Request (Page View):** + +1. HTTP request to any URL arrives at `index.php` +2. Bootstrap loads application (`bootstrap/app.php`) +3. HTTP Kernel processes request through middleware +4. Laravel Router evaluates all registered routes +5. Backend/System routes handled by their controllers +6. CMS module catch-all route `{slug?}` routes to `Cms\Classes\CmsController@run()` +7. CMS Controller delegates to `Cms\Classes\Controller` (primary frontend controller) +8. Primary Controller: + - Looks up URL in Theme using `Cms\Classes\Router` + - Resolves Page, Layout, and Component objects from theme files + - Renders page through Twig template engine + - Components injected into page output via ComponentManager +9. Response sent back to client + +**Backend Request (Admin Interface):** + +1. HTTP request to `/backend/...` arrives +2. Backend Router matches `Backend\Classes\BackendController@run()` +3. BackendController parses remaining URL segments to determine target plugin/controller +4. Controller loaded from plugin (e.g., `Golem15\Blog\Controllers\Posts`) +5. Controller action determined by remaining URL parts +6. Behaviors attached (FormController, ListController, etc.) handle CRUD operations +7. Views rendered with form/list widgets +8. JSON/HTML response returned + +**Component Lifecycle:** + +1. Page YAML/Twig specifies component code (`{% component 'componentName' %}`) +2. ComponentManager resolves code to fully-qualified class name via codeMap +3. Component class instantiated with properties from page markup +4. Component's `onRun()` method called by page renderer +5. Component renders its partial template (HTM file) in page context +6. AJAX handlers on component (e.g., `onUpdateCart`) handled by Snowboard framework + +**State Management:** + +- Models: Database-backed via Winter\Storm\Database\Model (extends Laravel Eloquent) +- Sessions: Laravel session middleware, stored in `storage/framework/sessions/` +- Cache: Laravel cache (configurable drivers: file, redis, memcached) +- Parameters: System\Models\Parameter table for application-level configuration +- Translation: Text keys cached in parameters, loaded by Translate plugin + +## Key Abstractions + +**Plugin Architecture:** + +Purpose: Enables modular, independently-deployable features with dependency resolution +- Each plugin is a Laravel ServiceProvider extending `System\Classes\PluginBase` +- Plugin.php declares: `pluginDetails()`, `register()`, `boot()`, `registerComponents()`, `registerControllers()`, etc. +- Examples: `golem15-wintercms-starter/plugins/golem15/blog/Plugin.php`, `golem15-wintercms-starter/plugins/golem15/user/Plugin.php` +- Pattern: Plugins register services, components, controllers, and extend existing models + +**Component System:** + +Purpose: Reusable, composable page elements with their own controllers and views +- Components extend `Cms\Classes\ComponentBase` +- Register in plugin via `registerComponents()` returning [ClassName => 'code'] +- ComponentManager loads all registered components into codeMap +- Example: `golem15-wintercms-starter/modules/cms/components/SoftComponent.php` +- Pattern: Component renders partial, handles AJAX, injects CSS/JS + +**Theme/Page System:** + +Purpose: File-based, non-database-backed content management +- Theme directory contains pages/, layouts/, partials/ subdirectories +- Page class `Cms\Classes\Page` extends `CmsCompoundObject` - represents page file as object +- Page file format: URL metadata → YAML → Twig markup +- Page references layout (inherited structure) and components (functional blocks) +- Example page structure: `themes/demo/pages/home.htm` +- Pattern: Page objects loaded on-demand from disk, cached in memory + +**Backend Form/List System:** + +Purpose: YAML-driven admin CRUD interfaces +- Controllers use FormController and ListController behaviors +- forms.yaml/fields.yaml define form structure and validation +- lists.yaml/columns.yaml define table columns and filtering +- FormController handles create/read/update/delete operations +- Example files: `plugins/*/models/*/fields.yaml`, `plugins/*/models/*/columns.yaml` +- Pattern: Behaviors parse YAML and generate HTML forms/tables dynamically + +**Service Providers & Dependency Injection:** + +Purpose: Central registration of services and extensibility hooks +- Plugins register services in `register()` and `boot()` methods +- Services bound to Laravel's DI container via `$this->app->singleton()` +- Example: Apparatus framework registers DI container for component auto-wiring +- Pattern: Late binding allows plugin A to extend plugin B's services + +## Entry Points + +**Frontend (CMS Page Serving):** + +- Location: `golem15-wintercms-starter/modules/cms/classes/CmsController.php` (HTTP entry), `golem15-wintercms-starter/modules/cms/classes/Controller.php` (business logic) +- Triggers: Any URL not matching backend or other routes +- Responsibilities: + - Routes URL to page via Theme manager + - Loads Page, Layout, and Components + - Renders through Twig with component injection + - Returns HTML response + +**Backend (Admin Interface):** + +- Location: `golem15-wintercms-starter/modules/backend/classes/BackendController.php` +- Triggers: URLs prefixed with `/backend/` +- Responsibilities: + - Parses plugin.controller.action URL pattern + - Instantiates controller from plugin + - Applies middleware and behaviors + - Routes to controller action + - Renders admin interface + +**System Initialization:** + +- Location: `golem15-wintercms-starter/bootstrap/app.php`, `golem15-wintercms-starter/index.php` +- Triggers: Every HTTP request +- Responsibilities: + - Creates Laravel application instance + - Binds kernel, console kernel, exception handler + - Loads plugin manager and auto-discovers plugins + - Registers module routes + +**CLI Commands:** + +- Location: `golem15-wintercms-starter/artisan` (entry point), `golem15-wintercms-starter/modules/*/console/` (command classes) +- Triggers: `php artisan ` +- Responsibilities: + - Plugin scaffold commands + - Database migrations + - Asset management + - Cache management + - Payment gateway operations (via golem15/paymentgateway) + +## Error Handling + +**Strategy:** Exception-driven with error page rendering + +**Patterns:** +- All exceptions thrown as `SystemException`, `ApplicationException`, or Framework exceptions +- HTTP exceptions converted to error pages by Exception Handler (`modules/system/classes/ErrorHandler.php`) +- In production: Shows generic error page, logs details +- In debug mode: Shows full stack trace and context +- Model validation: Throws ApplicationException with validation messages +- Frontend: 404 errors trigger Page not found response +- Backend: 404 errors redirect to backend dashboard, show toast notifications + +## Cross-Cutting Concerns + +**Logging:** +- Uses Laravel's log facade (`Log::info()`, `Log::error()`) +- Stored in `storage/logs/` +- Configured in `config/app.php` and environment variables +- EventLog model (`modules/system/models/EventLog.php`) tracks application events +- RequestLog model tracks HTTP requests for debugging + +**Validation:** +- Model-level via `$rules` property (Laravel validation rules) +- Form widget validation via fields.yaml `validation` property +- Backend behaviors apply validation before save +- Errors returned as JSON validation responses in AJAX handlers + +**Authentication:** +- Winter Backend: Database-backed user with role-based access control +- Frontend API: JWT tokens (via golem15/user plugin) +- Middleware: `Backend\Middleware\AuthenticateBackend`, `User\Middleware\JwtAuth` +- Guards: `web` (session-based), `api` (JWT) + +## Extensibility Mechanisms + +**Model Extension:** +- Plugins extend models dynamically using `Model::extend(Closure)` +- Example: `User::extend(function($model) { $model->hasMany['payments'] = ...; })` +- Used for adding relationships, attributes, query scopes without modifying core + +**Event System:** +- Global events via `Event::listen()` and `Event::fire()` +- Module-level events: `system.route`, `cms.beforeRoute`, `cms.route`, `backend.beforeRoute` +- Model events: `model.saveInternal`, `model.afterCreate`, `model.afterUpdate`, `model.afterDelete` +- Custom events fired by plugins for extensibility + +**Behaviors:** +- Controllers implement behaviors via `$implement` array +- Backend behaviors: FormController, ListController, RelationController, ImportExportController +- Each behavior adds methods (e.g., `create`, `update`, `index`) to controller +- Example: `modules/backend/behaviors/FormController.php` + +--- + +*Architecture analysis: 2026-02-04* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 0000000..666bba8 --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -0,0 +1,592 @@ +# Codebase Concerns + +**Analysis Date:** 2026-02-04 + +## Overview + +This document identifies technical debt, architectural concerns, and improvement opportunities in the reference WinterCMS implementation that the Scala rewrite (SummerCMS) should address and avoid replicating. + +--- + +## Tech Debt + +### God Classes and Large Modules + +**Issue:** Multiple "Manager" classes exceed 1000+ lines, handling too many concerns in single files. + +**Files:** +- `modules/backend/widgets/Lists.php` (2056 lines) - UI list rendering and filtering logic combined +- `modules/backend/behaviors/RelationController.php` (1878 lines) - Relationship form handling +- `modules/cms/classes/Controller.php` (1635 lines) - CMS page controller with mixed concerns +- `modules/backend/widgets/Form.php` (1479 lines) - Form widget rendering and logic +- `modules/backend/widgets/MediaManager.php` (1379 lines) - File management UI +- `modules/system/classes/UpdateManager.php` (1147 lines) - Software updates and migrations +- `modules/system/classes/PluginManager.php` (1089 lines) - Plugin lifecycle management +- `modules/backend/widgets/Filter.php` (1129 lines) - List filtering UI + +**Impact:** Difficult to test, maintain, and extend. Tight coupling between UI rendering and business logic. Requires careful refactoring to separate concerns. + +**Fix approach for Scala rewrite:** +- Enforce smaller, single-responsibility modules +- Separate UI rendering from business logic +- Use composition over inheritance +- Implement clear layer boundaries + +### Incomplete Refactoring and TODO Comments + +**Issue:** Codebase contains numerous TODO/FIXME/HACK comments indicating incomplete work. + +**Found examples:** +- `modules/cms/console/ThemeList.php:53` - TODO: List everything in the marketplace (not just popular) +- `modules/cms/console/CreateTheme.php:156` - TODO: allow support for mix +- `modules/system/classes/UpdateManager.php:937` - @TODO: Refactor when marketplace API finalized +- `modules/system/classes/PluginManager.php:698` - @TODO: Limit this to only disabled flags +- `modules/system/console/CreateModel.php:139` - @TODO: Implement this +- `modules/backend/widgets/MediaManager.php:692` - @TODO: Improve support non-local disks +- `modules/backend/traits/UploadableWidget.php:53` - @TODO: Replace cms.storage system with real disks +- `modules/backend/formwidgets/DataTable.php:110,137` - TODO: provide streaming implementation +- `modules/backend/classes/Controller.php:210` - @TODO: Support detecting module controllers + +**Impact:** Features are incomplete or deferred. Maintenance burden increases as workarounds are left in place. + +**Fix approach:** SummerCMS should resolve these before shipping: finalize file storage abstraction, complete marketplace integration, implement proper streaming. + +### Plugin Dependency Management Complexity + +**Issue:** Plugin dependency resolution is complex and fragile, with multiple disabled states. + +**Files:** `modules/system/classes/PluginManager.php` + +**Current states:** +- `DISABLED_MISSING` - Plugin files missing +- `DISABLED_REPLACED` - Superseded by another plugin +- `DISABLED_REPLACEMENT_FAILED` - Replacement didn't work +- `DISABLED_MISSING_DEPENDENCIES` - Dependencies unavailable +- `DISABLED_REQUEST` - Explicitly disabled +- `DISABLED_BY_USER` - User disabled +- `DISABLED_BY_CONFIG` - Config disabled + +**Impact:** Complex error states difficult to diagnose. Plugin resolution order matters. Breaking changes in plugins cascade across installations. + +**Fix approach:** SummerCMS should implement: +- Cleaner dependency resolution (single source of truth) +- Better error reporting for dependency conflicts +- Version constraints on plugin dependencies +- Semantic versioning enforcement + +--- + +## Architecture & Design Issues + +### Tight Coupling: Global Service Locators + +**Issue:** Heavy use of PHP global functions and service locator pattern throughout. + +**Examples:** +- Global `App::` calls bypassing constructor injection +- `Cache::`, `Config::`, `Schema::`, `DB::` accessed globally +- `Lang::`, `View::`, `File::` static access +- No consistent dependency injection for cross-cutting concerns + +**Files:** `modules/system/classes/UpdateManager.php` (use App, Cache, Config, Schema at top) + +**Impact:** Difficult to test. Hidden dependencies. Hard to understand what a class needs. Tight coupling to container. + +**Fix approach for Scala rewrite:** +- Eliminate service locator pattern +- Enforce constructor injection +- Use dependency frameworks properly +- Make dependencies explicit + +### Security: Debug Mode Default + +**Issue:** Debug mode defaults to `true` in production-ready config. + +**Files:** +- `config/app.php:23` - `'debug' => env('APP_DEBUG', true)` +- `.env.example` - No APP_DEBUG value (defaults to true) + +**Impact:** Detailed stack traces exposed to unauthorized users. Reveals application structure, file paths, and database schema. + +**Risk:** Critical security issue if .env file not properly configured on deployment. + +**Fix approach:** +- Default to `false` in production-like environments +- Enforce explicit opt-in for debug mode +- Add warnings in logs when debug mode detected in production + +### Security: Insufficient CSRF and XSS Protections + +**Issue:** CSRF protection is optional and can be disabled. + +**Files:** `config/cms.php:406` - `'enableCsrfProtection' => env('ENABLE_CSRF', true)` + +**Problem:** While protected by default, the configuration is environment-dependent and could be accidentally disabled. + +**Related:** `modules/system/twig/FilterTokenParser.php:23` - Deprecated Twig filter tag still in use. + +**Fix approach:** +- Make CSRF protection mandatory, non-optional +- Enforce modern Twig syntax (apply tag instead of filter) +- Add runtime warnings for deprecated patterns + +### File Storage Abstraction Missing + +**Issue:** Media/file management lacks proper abstraction to non-local disks. + +**Files:** +- `modules/backend/widgets/MediaManager.php:692` - @TODO: Improve support non-local disks +- `modules/backend/traits/UploadableWidget.php:53` - @TODO: Replace cms.storage system with real disks + +**Impact:** Can't easily use S3, Azure, or other cloud storage. File operations tied to local filesystem. + +**Fix approach for Scala rewrite:** +- Abstract file storage behind clear interface +- Support cloud providers from day one +- No assumptions about local filesystem + +### Frontend-Backend Communication Constraints + +**Issue:** Snowboard framework (modern replacement for AJAX) has limitations. + +**Files:** Reference in `CLAUDE.md` + +**Constraints:** +- No `data-request-success` attribute support (removed due to eval() safety concerns) +- Must use JavaScript event handlers instead +- Reduces developer convenience for simple cases + +**Impact:** More verbose JavaScript required for simple AJAX interactions. Developers forced to use events instead of callbacks. + +**Fix approach for Scala rewrite:** +- Replace entire AJAX/frontend framework with modern alternative +- Use standard fetch API with proper security +- Consider GraphQL or REST API with type safety +- Avoid both eval-based callbacks AND the need for workarounds + +--- + +## Performance Bottlenecks + +### Inefficient ORM Usage & N+1 Query Problems + +**Issue:** Potential for N+1 queries in list rendering and relationship handling. + +**Files:** +- `modules/backend/widgets/Lists.php:2056` - Renders many model instances, lazy-loaded relations possible +- `modules/backend/behaviors/RelationController.php:1878` - Handles HasMany/BelongsToMany without query optimization visible + +**Impact:** Lists with thousands of records cause severe database load. Pagination alone doesn't fix root issue if relations aren't eagerly loaded. + +**Fix approach:** +- Require explicit eager loading declarations +- Add query logging/analysis tools +- Enforce eager load helpers at ORM level + +### Asset Compilation Overhead + +**Issue:** Multiple asset managers and build systems create complexity. + +**Files:** +- `modules/system/classes/asset/PackageManager.php` +- `modules/system/classes/asset/Vite.php` +- `modules/system/classes/asset/BundleManager.php` +- `modules/system/console/asset/mix/` - 5 separate Mix commands + +**Problem:** Asset pipeline has multiple entry points and unclear ordering. Vite and Mix both exist. + +**Impact:** Slow development builds. Confusion about which build system to use. Multiple cache invalidation points. + +**Fix approach:** Single, unified build system in Scala rewrite. Use standard tooling (Vite, esbuild). + +### Image Processing Blocking Operations + +**Issue:** ImageResizer (935 lines) performs blocking operations on upload/display. + +**Files:** `modules/system/classes/ImageResizer.php` + +**Problem:** Synchronous image resizing during request cycles. No async/queue-based resizing visible. + +**Impact:** Upload requests block on image processing. Slow response times for image-heavy uploads. + +**Fix approach:** +- Queue image processing jobs +- Lazy image generation +- Pre-generate common sizes + +--- + +## Fragile Areas + +### Plugin System Bootstrap Order Dependencies + +**Issue:** Plugin registration and boot order is implicit and error-prone. + +**Files:** `modules/system/classes/PluginManager.php:87-94` + +**Pattern:** +```php +protected $registered = false; +protected $booted = false; +``` + +**Problem:** +- Plugin initialization order matters but isn't explicit +- Circular dependencies possible +- No clear contract for what each phase can do +- Tests must carefully manage state + +**Impact:** Plugin bugs depend on load order. Hard to reproduce locally. + +**Safe modification:** +- Explicitly declare initialization phases +- Validate dependency graph before boot +- Error early if circular dependencies detected + +### Form Widget Cascading + +**Issue:** Complex form widget system with nested, interdependent behaviors. + +**Files:** +- `modules/backend/widgets/Form.php:1479` +- `modules/backend/formwidgets/RelationManager.php` +- `modules/backend/classes/FormField.php:732` + +**Problem:** +- Deep inheritance hierarchies +- Widget behavior customization via YAML configuration +- Custom field types require plugin code +- Relationship widgets have special handling mixed in + +**Impact:** Adding custom form fields is fragile. Requires deep understanding of widget hierarchy. Easy to break nested forms. + +**Safe modification:** +- Add comprehensive form widget tests +- Document widget lifecycle (init, render, process, save) +- Provide clear extension points + +### Configuration Cascading and Overrides + +**Issue:** Configuration system has multiple levels: hardcoded, env, config files, database. + +**Problem:** Unclear precedence. Database settings can override app config, making them fragile. + +**Files:** `modules/system/models/LogSetting.php`, `MailSetting.php`, etc. + +**Impact:** Configuration state spread across multiple sources. Debugging requires checking all levels. + +**Safe modification:** Use explicit configuration layers with clear precedence. + +--- + +## Scaling Limits + +### Database: Monolithic Schema Without Partitioning + +**Issue:** Single database schema stores everything. No multi-tenancy support visible. + +**Impact:** +- Can't shard by tenant +- Large installations get slow +- Backup/restore of entire system required + +**Fix approach for Scala rewrite:** +- Support multi-tenancy from day one +- Allow schema partitioning +- Enable sharding strategy + +### Memory Usage: Asset Bundling + +**Issue:** CombineAssets (900 lines) loads entire asset files into memory for bundling. + +**Files:** `modules/system/classes/CombineAssets.php` + +**Impact:** Large CSS/JS bundles cause memory spikes during generation. + +**Fix approach:** Stream-based asset bundling, not in-memory. + +### Plugin System Scalability + +**Issue:** Plugin loading uses filesystem iteration, not database registry. + +**Files:** `modules/system/classes/PluginManager.php` + +**Problem:** Every plugin discovery does `RecursiveDirectoryIterator` scans. + +**Impact:** Slow startup with many plugins (100+). No caching strategy visible for plugin list. + +**Fix approach:** Cache plugin registry, implement incremental discovery. + +--- + +## Dependencies at Risk + +### PHP Version Lock: 8.1 Minimum + +**Risk:** PHP 8.1 reaches EOL in November 2023. Platform pins to EOL version. + +**File:** `composer.json:32` - `"php": ">=8.1"` + +**Impact:** +- Security updates stop after EOL +- Hosting providers drop support +- New PHP features can't be used + +**Migration plan:** Move to PHP 8.2+ minimum for SummerCMS. Ensures 5+ years of support. + +### Laravel Version: 9.x (EOL) + +**Risk:** Laravel 9.x (latest in Winter) reaches EOL August 2024. + +**File:** `composer.json:37` - `"laravel/framework": "^9.1"` + +**Impact:** +- No new security patches +- Can't use Laravel 11+ features +- Dependency hell for new packages + +**Migration plan:** SummerCMS should target Laravel 11+ from start. + +### Storm Library Coupling + +**Risk:** Heavy reliance on Winter Storm library (custom Laravel wrapper). + +**Issue:** `winter/storm ~1.2.0` is single point of failure for Winter compatibility. + +**Impact:** +- Updating Laravel requires Storm update first +- Storm updates could break all WinterCMS installations +- Vendor lock-in to Winter ecosystem + +**Fix approach for Scala:** Eliminate need for adapters. Use upstream Laravel directly with minimal wrapping. + +### Deprecated Twig Features + +**Issue:** `modules/system/twig/FilterTokenParser.php:23` still supports deprecated filter tag. + +**Impact:** Codebase using old Twig syntax. Upgrade to Twig 3+ blocked. + +**Fix:** Migrate all filters to apply tag, remove support for filter tag. + +--- + +## Security Considerations + +### Configuration Exposure Risk + +**Issue:** Backend authentication with 5-year cookie lifetime. + +**File:** `config/cms.php:70` - `'backendForceRemember' => true` + +**Risk:** +- XSS vulnerability could steal 5-year session +- Lost/stolen devices have long-term access +- No option to enforce re-authentication for sensitive operations + +**Recommendation:** +- Default to false +- Implement role-based session timeout +- Require re-auth for sensitive operations (user creation, payment settings) + +### Service Worker Backend Control + +**Issue:** Backend service workers disabled by default but capability exists. + +**File:** `config/cms.php:475` - `'enableBackendServiceWorkers' => false` + +**Risk:** Service workers in backend could be hijacked for XSS. Configuration makes it possible to accidentally enable. + +**Recommendation:** Remove this feature entirely. Service workers should only run on frontend with proper scope isolation. + +### Path Traversal Protection Conditional + +**Issue:** Base directory restriction can be disabled. + +**File:** `config/cms.php:453` - `'restrictBaseDir' => env('RESTRICT_BASE_DIR', true)` + +**Risk:** If disabled, plugins could load templates from anywhere on filesystem (directory traversal). + +**Recommendation:** Make this non-configurable. Always enforce base directory restriction. + +### API Keys and Secrets Unencrypted + +**Issue:** UpdateManager stores API credentials as plaintext in database. + +**Files:** `modules/system/classes/UpdateManager.php:65-72` + +**Problem:** +```php +protected $key; +protected $secret; +``` + +**Risk:** Database breach exposes marketplace credentials. + +**Fix approach:** +- Encrypt sensitive config values in database +- Use AWS Secrets Manager / HashiCorp Vault for production + +--- + +## Missing Critical Features + +### API Authentication: JWT Without Standards + +**Issue:** Custom JWT guard implementation instead of using OAuth2/OpenID Connect. + +**Reference in CLAUDE.md:** User plugin implements custom `php-open-source-saver/jwt-auth` + +**Problem:** +- Custom auth = custom bugs +- Can't integrate with standard tools +- No SSO capability +- Mobile app authentication requires special handling + +**Fix approach for Scala:** +- Use OAuth2 / OpenID Connect standard +- Support multiple auth methods (password, social, SAML) +- Enable federation + +### API Documentation + +**Issue:** No visible API documentation or schema (GraphQL, OpenAPI). + +**Impact:** Third-party integrations difficult. API contracts not documented. + +**Fix approach:** Generate OpenAPI/Swagger docs automatically from code. + +### Structured Logging + +**Issue:** Logging uses Laravel Log facade (text-based), no structured logging. + +**Files:** `config/logging.php` + +**Problem:** Hard to query logs. No correlation IDs for request tracing. + +**Fix approach:** Implement structured JSON logging from day one. + +--- + +## Test Coverage Gaps + +### Integration Test Coverage Insufficient + +**Issue:** Tests are mostly unit-level. Limited integration testing for critical paths. + +**Files:** +- `modules/system/tests/` - 1061 PHP files total in modules +- Test files scattered without clear convention + +**Problem:** +- Plugin system integration tests weak +- Form widget interactions not thoroughly tested +- Asset pipeline integration missing tests +- Payment processing integration gaps (expected in payment plugin) + +**Priority:** HIGH + +**Recommendation:** +- Add comprehensive integration tests for plugin loading +- Test form widget cascading behavior +- Test asset compilation pipeline end-to-end +- Payment system transaction state machine fully tested + +### Security Testing Gaps + +**Issue:** No visible security test suite. + +**Missing:** +- CSRF token validation tests +- XSS injection prevention tests +- Path traversal protection tests +- Authentication bypass scenarios +- SQL injection protection tests + +**Priority:** HIGH + +**Recommendation:** +- Add OWASP Top 10 security tests +- Implement automated security scanning +- Penetration testing before major releases + +### Performance & Load Testing + +**Issue:** No load testing or performance benchmarks visible. + +**Problem:** +- Can't measure regressions +- Scaling limits unknown +- Database query performance unmonitored + +**Priority:** MEDIUM + +**Recommendation:** +- Add load testing harness for common operations +- Benchmark plugin loading times +- Profile database queries + +--- + +## Known Broken/Incomplete Areas + +### Marketplace Integration Incomplete + +**Issue:** Multiple TODO comments indicate marketplace API integration unfinished. + +**Files:** +- `modules/cms/console/ThemeList.php` - Lists only popular items +- `modules/system/classes/UpdateManager.php:937` - Awaiting finalized API + +**Impact:** Can't install all available plugins/themes. Can't update from marketplace. + +### Theme Marketplace + +**Issue:** Theme system expects marketplace but API contract undefined. + +**Files:** `modules/cms/classes/Theme.php:716` + +**Problem:** Theme discovery, installation, updates not fully working. + +**Fix approach:** Finalize marketplace API. Build complete plugin/theme discovery and installation. + +### Media Library Non-Local Storage + +**Issue:** Media manager designed for local filesystem. + +**Files:** `modules/backend/widgets/MediaManager.php` + +**Problem:** S3, Azure, GCS not properly supported. File operations assume local disk. + +**Fix approach:** Implement proper storage abstraction (Laravel Storage under the hood). + +--- + +## Recommendations for SummerCMS Scala Rewrite + +### Priority 1 (Must Fix) + +1. **Eliminate Service Locator Pattern** - Use dependency injection throughout +2. **Enforce Single Responsibility** - No classes > 500 lines (strict) +3. **Complete File Storage Abstraction** - Full cloud storage support from day one +4. **Modern Authentication** - OAuth2/OpenID Connect, not custom JWT +5. **Security Defaults** - Debug mode off by default, CSRF mandatory + +### Priority 2 (Should Fix) + +1. **Upgrade PHP/Framework** - Target PHP 8.3+, Laravel 11+ +2. **Structured Logging** - JSON logging with correlation IDs +3. **Comprehensive Testing** - >80% coverage, security tests required +4. **Multi-Tenancy Support** - Design architecture for SaaS from start +5. **API Documentation** - OpenAPI/Swagger generated from code + +### Priority 3 (Nice to Have) + +1. **Async Task Processing** - Built-in for long-running operations +2. **Real-time Collaboration** - WebSocket support for concurrent editing +3. **Performance Monitoring** - Built-in APM integration +4. **Advanced Caching** - Redis, Memcached strategies +5. **Plugin Marketplace** - Centralized discovery and updates + +--- + +*Concerns audit: 2026-02-04* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..7b88d49 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,228 @@ +# Coding Conventions + +**Analysis Date:** 2026-02-04 + +**Reference:** This document captures WinterCMS/Laravel PHP conventions that should guide the Scala rewrite. As SummerCMS is a greenfield Scala project, these patterns should be adapted to Scala idioms while maintaining architectural consistency. + +## Naming Patterns + +**Files:** +- **Classes:** PascalCase, one class per file (e.g., `User.php`, `AuthManager.php`, `FormController.php`) +- **Models:** Singular PascalCase (e.g., `User`, `UserRole`, `UserGroup` in `modules/backend/models/`) +- **Controllers:** Plural PascalCase with "Controller" suffix (e.g., `Users.php`, `AccessLogs.php` in `modules/backend/controllers/`) +- **Namespaces:** Correspond to directory structure with backslash path separators + +**Functions/Methods:** +- camelCase (e.g., `getFullNameAttribute()`, `sendInvitation()`, `getPersistCode()`) +- Helper methods in models use verb prefixes: `get*`, `set*`, `is*`, `has*` (e.g., `getAvatarThumb()`, `isSuperUser()`) +- Event hooks use specific naming: `afterCreate()`, `afterLogin()`, `afterUpdate()` (see `modules/backend/models/User.php` lines 129-158) +- Private methods are lowercase camelCase + +**Variables:** +- camelCase for all variable names (e.g., `$persist_code`, `$send_invite`, `$full_name`) +- Snake_case for database column names mapped to model attributes +- Configuration array keys use lowercase dot notation (e.g., `'cms.pluginsPath'`, `'database.default'`) + +**Types/Classes:** +- Model classes: Base classes in `modules/*/models/` (e.g., `Backend\Models\User` extends `Winter\Storm\Auth\Models\User as UserBase`) +- Controller classes: `Backend\Controllers\Users` implements behaviors +- Use fully qualified namespaced class names in type declarations + +**Constants:** +- UPPER_SNAKE_CASE for constants (convention follows Laravel) + +## Code Style + +**Formatting:** +- EditorConfig enforced via `.editorconfig` at repository root: + - End of line: LF + - Charset: UTF-8 + - Indent: 4 spaces + - Trim trailing whitespace: enabled + - Insert final newline: enabled + - Workflow files (`.github/workflows/`): 2-space indent + +**Spacing:** +- Opening braces on same line (e.g., `public function getData() {`) +- Closing braces on new line +- Method declarations with full type hints and documentation (see `modules/backend/models/User.php` lines 78-98) + +**Linting:** +- Tool: PHP CodeSniffer (squizlabs/php_codesniffer) via composer script +- Command: `composer sniff` - Checks PSR-1, PSR-2, PSR-4 standards +- Command: `composer lint` - Parallel lint checking with php-parallel-lint +- Excludes: vendor/, storage/, specific test fixtures + +**Documentation:** +- PSDoc style comments on all public methods with `@param`, `@return`, `@var` annotations +- Class-level doc blocks include package and author info: + ```php + /** + * Administrator user model + * + * @package winter\wn-backend-module + * @author Alexey Bobkov, Samuel Georges + */ + ``` +- Inline comments explain non-obvious logic (see `modules/backend/models/User.php` lines 89-95 option explanation) + +## Import Organization + +**Order (per file):** +1. PHP namespace declaration (no blank line) +2. Use statements grouped: + - Facades first (e.g., `use Backend\Facades\Backend;`) + - Models next (e.g., `use Backend\Models\UserGroup;`) + - Classes and utilities (e.g., `use Winter\Storm\Database\Traits\SoftDelete;`) +3. Blank line before class declaration + +**Example** (`modules/backend/models/User.php` lines 1-15): +```php + 'required|between:6,255|email|unique:backend_users', + 'login' => 'required|between:2,255|unique:backend_users', + 'password' => 'required:create|min:4|confirmed', + ]; + ``` + +## Logging + +**Framework:** Laravel native logging (via `Illuminate\Support\Facades\Log` or helpers) + +**Patterns:** +- No explicit logging calls in examined source files +- Framework routes all errors through Laravel's error handler +- Debug mode toggled via `.env` `APP_DEBUG` setting (defaults to true in config but should be false in production) +- Log configuration in `config/logging.php` + +**Observability:** +- Model lifecycle events fired via `Event::fire()` for observation/extension: + ```php + Event::fire('backend.user.login', [$this]); + ``` + +## Comments + +**When to Comment:** +- Explain "why", not "what" (code explains what it does) +- Document non-obvious algorithms or workarounds +- Mark uncertain logic with `@todo`, `@TODO` (seen throughout codebase) + +**Comment Styles:** +- Single-line: `// Comment` +- Multi-line: Block with `/** ... */` for docblocks only +- Inline: After code with `// Comment` + +**JSDoc/TSDoc:** +- PHP style with `@param`, `@return`, `@var`, `@throws` tags +- Event documentation uses `@event` tag with example usage (see `modules/backend/models/User.php` lines 147-158) +- Database column documentation via `@var` on property declarations + +## Function Design + +**Size:** +- Methods generally 10-40 lines (see examples in User model) +- Extracted helper methods for reusable logic +- Model lifecycle hooks are concise (afterCreate, afterLogin, etc.) + +**Parameters:** +- Type-hinted where applicable +- Favor explicit parameters over options arrays +- Options arrays used for backward compatibility (e.g., `getAvatarThumb($size = 25, $options = null)`) + +**Return Values:** +- Explicit return statements +- Type hints on return values in docblocks (though PHP 7.4+ allows native return types) +- Null returns used for "not found" cases + +## Module Design + +**Exports:** +- Classes explicitly imported via use statements +- No wildcard imports (`use Namespace\*;`) +- All public classes are explicitly declared + +**Barrel Files:** +- No barrel/index files observed in WinterCMS +- Direct class imports required + +**Class Properties:** +- Public properties for model relations and validation rules: + ```php + public $belongsToMany = [...]; + public $belongsTo = [...]; + public $rules = [...]; + public $implement = [...]; // In controllers + ``` +- Protected properties for internal state (e.g., `protected $table`, `protected $dates`) +- Magic properties documented via `@var` doc comments + +**Traits:** +- Used for cross-cutting concerns (e.g., `SoftDelete`, `UploadableWidget`) +- Applied within class body via `use` statement +- Traits encapsulate reusable behavior patterns + +## Event System + +**Framework:** Laravel Event system with plugin extension points + +**Patterns:** +- Events fired at significant lifecycle points (e.g., `backend.user.login`) +- Listeners registered via `Event::listen()` in plugin service providers +- Event documentation includes example listener code +- Used for non-breaking extensibility across plugins + +## Model Extension Pattern + +**Dynamic Extension:** +- Models extended via `extend()` method in plugin registration: + ```php + User::extend(function ($model) { + $model->hasMany['payments'] = [PaymentModel::class, 'key' => 'user_id']; + }); + ``` +- Allows plugins to add relations, validation, behaviors without modifying core models + +## Translation/Localization + +**Framework:** Winter's translation system + +**Pattern:** +- Backend labels use plugin namespace: `'plugin.namespace::lang.section.key'` +- Translation filter in views: `{{ 'String'|_ }}` +- Used for all user-facing strings (labels, help text, messages) + +--- + +*Convention analysis: 2026-02-04* +*Reference: WinterCMS PHP codebase patterns to inform Scala rewrite* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..6bd54e8 --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,240 @@ +# External Integrations + +**Analysis Date:** 2026-02-04 + +## APIs & External Services + +**Payment Processing:** +- Stripe - Payment provider for transactions + - SDK/Client: `stripe/stripe-php` (integrated via PgStripe plugin) + - Env vars: `STRIPE_API_KEY`, `STRIPE_SECRET_KEY` (configured in `plugins/golem15/pgstripe/`) + - Purpose: Card processing, payment authorization, subscription management via PaymentGateway system + +**AI Services:** +- OpenAI - LLM and chat completions + - SDK/Client: `openai-php/client` (integrated via AI plugin) + - Env vars: `OPENAI_API_KEY` + - Purpose: AI chat interfaces, prompt management, text generation + - Location: `plugins/golem15/ai/` + +- Perplexity - Alternative LLM provider + - SDK/Client: Native HTTP client integration + - Env vars: `PERPLEXITY_API_KEY` + - Purpose: Alternative AI engine for AI plugin + - Location: `plugins/golem15/ai/` + +**GitHub Integration:** +- GitHub API v3 - Repository and content management + - SDK/Client: GitHub REST API via HTTP client + - Env vars: `GITHUB_TOKEN` (optional, for private repos) + - Purpose: Git integration features (repo mirroring, CI/CD hooks) + - Location: `plugins/golem15/github/` + +**OAuth/Social Authentication:** +- Laravel Socialite - OAuth provider framework + - SDK/Client: `laravel/socialite` + - Configured via: `config/services.php` + - Purpose: Social login integration (Google, GitHub, etc.) for User plugin + - Location: `plugins/golem15/user/` + +## Data Storage + +**Databases:** +- MySQL 5.7+ (default in `.env.example`, `DB_CONNECTION=mysql`) + - Connection: `DB_HOST=127.0.0.1`, `DB_PORT=3306`, `DB_DATABASE=database`, `DB_USERNAME=root`, `DB_PASSWORD=` + - Client: Laravel Eloquent ORM (built into Laravel Framework) + - Config file: `config/database.php` + +- PostgreSQL 9.5+ (supported alternative) + - Connection: `DB_CONNECTION=pgsql` + - Client: Laravel Eloquent ORM + - Config file: `config/database.php` + +- SQLite (supported alternative, file-based) + - Connection: `DB_CONNECTION=sqlite` + - Default file: `storage/database.sqlite` + - Client: Laravel Eloquent ORM + - Config file: `config/database.php` + +- SQL Server (supported alternative) + - Connection: `DB_CONNECTION=sqlsrv` + - Client: Laravel Eloquent ORM + - Config file: `config/database.php` + +**File Storage:** +- AWS S3 (optional) + - Env vars: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `AWS_BUCKET` + - Client: AWS SDK for PHP + - Config: `config/filesystems.php`, can use S3 disk for file uploads + - Purpose: Cloud file storage and CDN delivery + +- Local filesystem (default) + - Storage paths: `storage/app/`, `storage/app/public/` + - Public access via symlink: `public/storage/` + - Generated via: `php artisan winter:mirror public --relative` + +**Caching:** +- File-based cache (default, `CACHE_DRIVER=file`) + - Location: `storage/framework/cache/` + +- Redis cache (optional, `CACHE_DRIVER=redis`) + - Connection: `REDIS_HOST=127.0.0.1`, `REDIS_PORT=6379`, `REDIS_PASSWORD=null` + - Config: `config/cache.php` and `config/database.php` (redis section) + - Purpose: Fast in-memory caching, session storage, queue backend + +- Memcached (optional, `CACHE_DRIVER=memcached`) + - Connection: `MEMCACHED_HOST=127.0.0.1`, `MEMCACHED_PORT=11211` + - Config: `config/cache.php` + +## Authentication & Identity + +**Auth Provider:** +- Custom JWT-based API authentication + - Implementation: PHP Open Source Saver JWT Auth (`php-open-source-saver/jwt-auth`) + - Config files: `config/jwt.php`, `config/auth.php` + - Purpose: Stateless API authentication for frontend-backend communication + - Location: `plugins/golem15/user/` + - Middleware: `jwt.auth` for protected routes, `jwt.refresh` for token refresh + +- Backend Admin Auth + - Implementation: Laravel session-based authentication + - Throttling: Enabled by default (5 attempts, 15-minute suspension) + - Config: `config/auth.php` (throttle settings) + +- OAuth Social Login (optional) + - Providers: Configurable via Laravel Socialite + - Integration: User plugin + +## Monitoring & Observability + +**Error Tracking:** +- Not detected in default stack +- Framework: Laravel uses default error logging to files/channels + +**Logs:** +- File-based logging (default) + - Location: `storage/logs/` + - Driver: `log` (Laravel default single/daily file rotation) + - Config: `config/logging.php` + +- Optional: Syslog, Papertrail, or other channels via `config/logging.php` + +**Debug Mode:** +- Enabled via `APP_DEBUG=true` in `.env` +- Winter CMS debugbar available via `debugbar` plugin (`plugins/winter/debugbar/`) +- WARNING: Always disable `APP_DEBUG=false` in production + +## CI/CD & Deployment + +**Hosting:** +- Not locked to specific platform +- Requirements: PHP 8.1+ environment with Composer +- Supports: Traditional VPS, managed hosting, containerized deployment + +**CI Pipeline:** +- Not detected in default stack +- Test execution: `composer test` or `php artisan winter:test` +- Linting: `composer lint` (parallel-lint for syntax) +- Code sniffing: `composer sniff` (PHPCS for PSR standards) + +**Scheduled Tasks:** +- Laravel Scheduler via cron job (requires cron setup) +- Cron entry: `* * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1` +- Scheduled commands (PaymentGateway plugin): + - `pg:cancel-sent-payments` - Daily + - `pg:cancel-created-payments` - Daily + - `pg:cancel-placed-orders` - Daily + - `pg:finish-orders` - Daily + - `pg:update-currencies` - Every 10 minutes +- User plugin scheduled tasks: + - `user:process-scheduled-deletions` - GDPR scheduled user deletion processing + +## Environment Configuration + +**Required env vars:** +- `APP_NAME` - Application display name +- `APP_KEY` - Laravel encryption key (generate via `php artisan winter:env`) +- `APP_DEBUG` - Debug mode (true for dev, false for production) +- `APP_URL` - Application URL for URL generation +- `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` - Database connection +- `CACHE_DRIVER` - Cache backend (file, redis, memcached) +- `SESSION_DRIVER` - Session storage (file, redis, database) +- `QUEUE_CONNECTION` - Queue backend (sync, redis, database, beanstalkd) +- `MAIL_MAILER` - Mail driver (log, smtp, mailgun, postmark, ses, sendmail) + +**Secrets location:** +- `.env` file in project root (generated from `.env.example`) +- Not committed to git (listed in `.gitignore`) +- AWS credentials: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` +- Mail credentials: `MAIL_USERNAME`, `MAIL_PASSWORD` +- Third-party API keys: `STRIPE_API_KEY`, `OPENAI_API_KEY`, `PERPLEXITY_API_KEY`, `GITHUB_TOKEN` +- Redis password: `REDIS_PASSWORD` + +## Webhooks & Callbacks + +**Incoming:** +- Payment webhooks from Stripe (handled by PgStripe plugin) + - Endpoint: Stripe event signature verification via Stripe SDK + - Events: payment.success, payment.failed, charge.refunded, subscription events + - Handler: `plugins/golem15/pgstripe/` webhook listeners + +- GitHub webhooks (optional, via GitHub plugin) + - Configurable push/pull request/issue event handlers + - Handler: `plugins/golem15/github/` + +**Outgoing:** +- Email notifications via configured mailer + - User registration confirmation + - Payment/order state change notifications (PaymentGateway plugin) + - Newsletter/notification system via Mail plugin + - Env vars: `MAIL_FROM_ADDRESS`, `MAIL_FROM_NAME` + +- Queue workers for async email + - Driver: `QUEUE_CONNECTION` (default: sync for dev, database/redis for production) + - Jobs table: `jobs` migration creates queue job storage + +## Message Queue System + +**Queue Backend:** +- Sync driver (default, development) - Processes jobs immediately in request lifecycle + - `QUEUE_CONNECTION=sync` + +- Database driver (production-ready) - Stores jobs in `jobs` table + - `QUEUE_CONNECTION=database` + - Table: `jobs` + - Requires: `php artisan queue:work` background worker + +- Redis driver (optimal for high throughput) + - `QUEUE_CONNECTION=redis` + - Connection: Uses REDIS config from `config/database.php` + +- Beanstalkd driver (alternative) + - `QUEUE_CONNECTION=beanstalkd` + - Host: `localhost`, configurable via queue.php + +## Session Management + +**Driver:** +- File-based (default, `SESSION_DRIVER=file`) + - Location: `storage/framework/sessions/` + +- Redis (optional, `SESSION_DRIVER=redis`) + - Connection: Uses REDIS config + +- Database (optional, `SESSION_DRIVER=database`) + - Table: `sessions` + +- Cookie (alternative, `SESSION_DRIVER=cookie`) + +## Real-Time Communication + +**WebSockets (via WebSockets plugin):** +- Framework: Pusher-compatible WebSocket support +- Plugin: `plugins/golem15/websockets/` +- Purpose: Real-time chat, notifications, live updates +- Config: Managed by WebSockets plugin +- Test command: `php artisan websockets:test-push` + +--- + +*Integration audit: 2026-02-04* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..b622e9b --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,126 @@ +# Technology Stack + +**Analysis Date:** 2026-02-04 + +## Languages + +**Primary:** +- PHP 8.1+ - Core application language for WinterCMS framework, all plugins, and controllers + +**Secondary:** +- Twig - Template engine for frontend themes and email templates +- JavaScript - Frontend interactions and AJAX handlers + +## Runtime + +**Environment:** +- PHP 8.1 or higher (required) +- Laravel 9.x runtime via Winter Storm abstraction layer + +**Package Manager:** +- Composer 2.x - PHP dependency management +- Lockfile: Present (composer.lock managed by Composer merge plugin) + +## Frameworks + +**Core:** +- Winter CMS 1.2.x - Complete CMS/CMF platform based on Laravel 9 +- Laravel Framework 9.1+ - HTTP framework and application foundation +- Winter Storm (~1.2.0) - Winter's buffer layer between Laravel and Winter plugins + +**Key Modules (Winter Core):** +- `winter/wn-system-module` - Core system functionality, models, migrations +- `winter/wn-backend-module` - Admin interface, backend forms, list views +- `winter/wn-cms-module` - Frontend CMS page management and publishing + +**Testing:** +- PHPUnit 9.5.8+ - Unit and functional testing +- Mockery 1.4.4+ - Mock object framework +- FakerPHP 1.9.2+ - Test data generation + +**Build/Dev:** +- Laravel Artisan - CLI framework for commands and migrations +- Wikimedia Composer Merge Plugin 2.1.0 - Automatic plugin composer.json merging + +## Key Dependencies + +**Critical:** +- `winter/storm` - Storm library providing DI container, filesystem abstraction, validation framework +- `laravel/framework` - HTTP kernel, routing, middleware, database ORM (Eloquent), queue system +- `php-open-source-saver/jwt-auth` - JWT token generation and validation for API authentication +- `keios/moneyright` - Financial calculations with precise decimal arithmetic (no floats) +- `keios/apparatus` - Scenario-based workflow engine, DI container enhancements, backend utilities +- `keios/laravel-apparatus` - Laravel integration for Apparatus framework + +**Infrastructure:** +- `wikimedia/composer-merge-plugin` - Enables modular plugin architecture with separate composer.json per plugin + +**Code Quality:** +- `squizlabs/php_codesniffer` - PSR-1/PSR-2/PSR-4 code standard checking +- `php-parallel-lint/php-parallel-lint` - Syntax checking +- `dms/phpunit-arraysubset-asserts` - Extended assertions for arrays in tests + +## Configuration + +**Environment:** +- `.env.example` template in project root +- Environment variables control all external service integration and deployment settings +- Runtime configuration values: + - `APP_NAME`, `APP_KEY`, `APP_DEBUG`, `APP_URL` - Application settings + - `DB_CONNECTION`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` - Database + - `CACHE_DRIVER`, `SESSION_DRIVER`, `QUEUE_CONNECTION` - Cache/session/queue backends + - `REDIS_HOST`, `REDIS_PASSWORD`, `REDIS_PORT` - Redis connection (optional) + - `MAIL_MAILER`, `MAIL_HOST`, `MAIL_PORT`, `MAIL_USERNAME`, `MAIL_PASSWORD` - Mail configuration + - `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `AWS_BUCKET` - AWS S3 (optional) + +**Build:** +- `composer.json` - Project dependencies and scripts +- `phpunit.xml` - PHPUnit testing configuration +- `phpcs.xml` - PHP CodeSniffer configuration +- `.editorconfig` - Editor formatting standards (PSR-compliant) +- `artisan` - Artisan CLI entry point script +- `index.php` - HTTP request entry point + +**Laravel Config Files (in `config/`):** +- `app.php` - Application name, debug mode, timezone +- `database.php` - Database connections (MySQL, PostgreSQL, SQLite, SQL Server, Redis) +- `cache.php` - Cache stores (file, Redis, Memcached, DynamoDB) +- `queue.php` - Queue backends (sync, database, beanstalkd, SQS, Redis) +- `mail.php` - Mail drivers (SMTP, Mailgun, Postmark, SES, Sendmail, Log) +- `auth.php` - Authentication throttling configuration +- `session.php` - Session configuration +- `filesystems.php` - Storage disk configuration +- `services.php` - Third-party service credentials (Mailgun, Postmark, AWS SES) +- `database.php` - Multiple database connection types + +**Plugin-Specific Config:** +- JWT configuration publishes to `config/jwt.php` and `config/auth.php` (User plugin) +- Each plugin manages its own configuration in `plugins/golem15/*/config/` + +## Platform Requirements + +**Development:** +- PHP 8.1+ installed and available in PATH (use `php-legacy` command per project CLAUDE.md) +- Composer 2.x +- MySQL 5.7+ OR PostgreSQL 9.5+ OR SQLite OR SQL Server (at least one required) +- Optional: Redis 5+ (for caching, sessions, queues) +- Optional: Memcached (for caching) + +**Production:** +- PHP 8.1+ with common extensions (PDO, OpenSSL, Fileinfo, Tokenizer, MBSTRING) +- MySQL 5.7+ OR PostgreSQL 9.5+ OR SQL Server database +- Redis (recommended for performance) +- Web server: Apache with mod_rewrite OR Nginx +- Cron job setup for Laravel scheduler (see CLAUDE.md) + +**Optional Services:** +- AWS S3 (for file storage, requires AWS SDK) +- Stripe (payment processing, requires stripe-php SDK) +- OpenAI API (AI integration, requires OpenAI PHP SDK) +- Perplexity API (AI integration alternative) +- Mailgun/Postmark/AWS SES (transactional email) +- GitHub API (Git integration via GitHub plugin) + +--- + +*Stack analysis: 2026-02-04* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..afe933f --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,248 @@ +# Codebase Structure + +**Analysis Date:** 2026-02-04 + +## Directory Layout + +``` +golem15-wintercms-starter/ # WinterCMS reference implementation +├── bootstrap/ # Application bootstrap +│ ├── app.php # Creates Laravel Application instance +│ ├── autoload.php # Composer autoloader +│ └── cache/ # Cached bootstrap files +├── modules/ # Core framework modules (not plugins) +│ ├── system/ # Core system functionality +│ ├── backend/ # Admin interface module +│ └── cms/ # Frontend CMS module +├── plugins/ # Plugin directory (git submodules) +│ ├── winter/ # Official Winter plugins +│ │ ├── pages/ # Pages plugin +│ │ ├── blocks/ # Content blocks plugin +│ │ ├── debugbar/ # Debug toolbar +│ │ └── ... +│ └── golem15/ # Golem15 custom plugins (19 total) +│ ├── apparatus/ # Foundation framework/DI +│ ├── user/ # Enhanced user auth + JWT +│ ├── paymentgateway/ # Payment processing +│ ├── blog/ # Blog system +│ ├── translate/ # Multilingual support +│ ├── ai/ # AI integration +│ ├── chat/ # Real-time chat +│ └── ... +├── themes/ # Static themes +│ └── demo/ # Demo theme +│ ├── pages/ # CMS pages (.htm files) +│ ├── layouts/ # Page layouts +│ ├── partials/ # Reusable partials +│ └── assets/ # CSS, JS, images +├── storage/ # Application storage +│ ├── logs/ # Log files +│ ├── framework/ # Framework cache/sessions/views +│ │ ├── sessions/ # User sessions +│ │ ├── views/ # Compiled Twig views +│ │ └── cache/ # Application cache +│ ├── temp/ # Temporary files +│ ├── cms/ # CMS-specific cache +│ └── app/ # Application uploads +│ ├── media/ # Media files +│ ├── uploads/ # User uploads +│ └── resized/ # Resized images +├── config/ # Application configuration +│ ├── app.php # Application config +│ ├── cms.php # CMS config +│ ├── database.php # Database config +│ ├── dev/ # Development overrides +│ └── testing/ # Testing overrides +├── index.php # Application entry point +├── artisan # Artisan CLI script +└── composer.json # Project dependencies +``` + +## Directory Purposes + +**bootstrap/:** +- Purpose: Application initialization and container setup +- Contains: App instance creation, autoloader configuration +- Key files: `bootstrap/app.php` (creates Winter\Storm\Foundation\Application), `bootstrap/autoload.php` (loads Composer) + +**modules/system/:** +- Purpose: Core framework functionality shared by all modules +- Contains: PluginBase, PluginManager, MailManager, ErrorHandler, UpdateManager, VersionManager, ImageResizer +- Structure: `classes/`, `models/`, `controllers/`, `database/`, `console/`, `lang/`, `tests/` +- Key classes: `System\Classes\PluginBase`, `System\Classes\PluginManager` + +**modules/backend/:** +- Purpose: Admin interface and CRUD backend +- Contains: Admin controllers, form/list widgets, user role management, behaviors +- Structure: `classes/`, `behaviors/`, `models/`, `controllers/`, `widgets/`, `formwidgets/`, `views/` +- Key classes: `Backend\Classes\BackendController`, `Backend\Classes\Controller`, `Backend\Behaviors\FormController` + +**modules/cms/:** +- Purpose: Frontend CMS - page rendering, theming, components +- Contains: Page/Layout/Component managers, theme management, frontend controllers +- Structure: `classes/`, `components/`, `models/`, `controllers/`, `formwidgets/`, `views/` +- Key classes: `Cms\Classes\Controller`, `Cms\Classes\Page`, `Cms\Classes\Layout`, `Cms\Classes\ComponentManager` + +**plugins/winter/ & plugins/golem15/:** +- Purpose: Modular plugin features registered as Laravel ServiceProviders +- Each plugin contains: `Plugin.php` (entry point), `controllers/`, `models/`, `components/`, `updates/` (migrations), `lang/` +- Plugin.php declares: name, description, author, dependencies ($require), registered components/controllers + +**themes/demo/:** +- Purpose: Static theme files - pages, layouts, and assets +- pages/: CMS pages (URL → page mapping) +- layouts/: Page layout templates with `{% component %}` tags +- partials/: Reusable Twig templates +- assets/: CSS, JavaScript, images + +**storage/:** +- Purpose: Runtime data, caches, and uploads +- logs/: Application logs (Laravel.log, system errors) +- framework/sessions/: User session data +- framework/views/: Compiled Twig templates +- cms/cache/: CMS-specific caches +- app/uploads/: User-uploaded files +- app/media/: Media library files + +**config/:** +- Purpose: Application configuration files +- app.php: Laravel application settings (logging, cache, etc.) +- cms.php: CMS-specific settings (backend URI, theme path, etc.) +- database.php: Database connection configuration +- dev/: Development environment overrides + +## Key File Locations + +**Entry Points:** +- `golem15-wintercms-starter/index.php`: HTTP entry point - loads bootstrap and runs kernel +- `golem15-wintercms-starter/artisan`: CLI entry point - loads kernel and console handler +- `golem15-wintercms-starter/bootstrap/app.php`: Creates application container +- `golem15-wintercms-starter/bootstrap/autoload.php`: Loads Composer autoloader + +**Core Framework Classes:** +- `golem15-wintercms-starter/modules/system/classes/PluginBase.php`: Base class for all plugins +- `golem15-wintercms-starter/modules/system/classes/PluginManager.php`: Discovers and loads plugins +- `golem15-wintercms-starter/modules/cms/classes/CmsController.php`: Frontend HTTP controller +- `golem15-wintercms-starter/modules/cms/classes/Controller.php`: Frontend business logic controller +- `golem15-wintercms-starter/modules/backend/classes/BackendController.php`: Backend HTTP controller +- `golem15-wintercms-starter/modules/backend/classes/Controller.php`: Backend business logic + +**Theme System:** +- `golem15-wintercms-starter/modules/cms/classes/Theme.php`: Theme loader and manager +- `golem15-wintercms-starter/modules/cms/classes/Page.php`: CMS page object +- `golem15-wintercms-starter/modules/cms/classes/Layout.php`: Layout template object +- `golem15-wintercms-starter/modules/cms/classes/ComponentManager.php`: Component registry + +**Backend Behaviors:** +- `golem15-wintercms-starter/modules/backend/behaviors/FormController.php`: Form CRUD behavior +- `golem15-wintercms-starter/modules/backend/behaviors/ListController.php`: List/table behavior +- `golem15-wintercms-starter/modules/backend/behaviors/RelationController.php`: Relation management +- `golem15-wintercms-starter/modules/backend/behaviors/ImportExportController.php`: Import/export + +**Routing:** +- `golem15-wintercms-starter/modules/cms/routes.php`: Frontend CMS routes +- `golem15-wintercms-starter/modules/backend/routes.php`: Backend admin routes +- `golem15-wintercms-starter/modules/system/routes.php`: System routes + +**Models:** +- `golem15-wintercms-starter/modules/system/models/Parameter.php`: Application parameters/settings +- `golem15-wintercms-starter/modules/system/models/MailTemplate.php`: Email templates +- `golem15-wintercms-starter/modules/system/models/EventLog.php`: Event logging + +## Naming Conventions + +**Files:** +- Controllers: CamelCase, singular (e.g., `Posts.php`, `Categories.php` in `plugins/*/controllers/`) +- Models: CamelCase, singular (e.g., `Post.php`, `Category.php` in `plugins/*/models/`) +- Components: CamelCase (e.g., `SearchBox.php`, `ProductCard.php` in `plugins/*/components/`) +- Views/Templates: snake_case with underscores for layout/partial files (e.g., `_toolbar.php`, `_list.php`) +- Behaviors: CamelCase with "Controller" suffix (e.g., `FormController.php`, `ListController.php`) +- Plugins: lowercase with namespace (e.g., `winter`, `golem15`) + +**Directories:** +- Module names: lowercase (system, cms, backend) +- Plugin vendor: lowercase (winter, golem15) +- Plugin names: lowercase (blog, user, apparatus) +- Class directories: lowercase plural (classes/, models/, controllers/, components/, views/, widgets/) + +**Classes and Namespaces:** +- All classes namespaced by module/plugin (e.g., `System\Classes\PluginBase`, `Cms\Classes\Page`, `Golem15\User\Models\User`) +- Plugins follow vendor\plugin pattern (e.g., `Golem15\Blog\Controllers\Posts`) +- Model classes singular (User, Post, Category) +- Manager classes suffix "Manager" (ComponentManager, ThemeManager) +- Controllers suffix "Controller" (BackendController, PostsController) + +## Where to Add New Code + +**New Plugin Feature (new controller, model, component):** +- Primary code: `plugins/golem15/{pluginname}/{models,controllers,components}/` +- Database schema: `plugins/golem15/{pluginname}/updates/` (migration files) +- Forms/lists config: `plugins/golem15/{pluginname}/models/{ModelName}/fields.yaml`, `columns.yaml` +- Views/templates: `plugins/golem15/{pluginname}/controllers/{ControllerName}/` (for backend views) +- Plugin entry: `plugins/golem15/{pluginname}/Plugin.php` (declare plugin, register components/controllers) + +**New Frontend Component:** +- Implementation: `plugins/golem15/{pluginname}/components/{ComponentName}.php` (extends ComponentBase) +- Partial template: `plugins/golem15/{pluginname}/components/{componentname}/default.htm` +- Register in Plugin.php: `registerComponents()` returns [ClassName => 'code'] +- Use on page: `{% component 'componentCode' %}` in page/layout Twig + +**New Backend CRUD Page:** +- Controller: `plugins/golem15/{pluginname}/controllers/{ModelName}.php` (extends Backend\Classes\Controller) +- Model: `plugins/golem15/{pluginname}/models/{ModelName}.php` (extends Winter\Storm\Database\Model) +- Forms: `plugins/golem15/{pluginname}/models/{modelname}/fields.yaml` +- Lists: `plugins/golem15/{pluginname}/models/{modelname}/columns.yaml` +- Views: `plugins/golem15/{pluginname}/controllers/{modelname}/{create,update,preview}.php` +- Register in Plugin.php: `registerControllers()` returns [ClassName => 'controller'] + +**Utilities/Helpers:** +- Shared service classes: `plugins/golem15/{pluginname}/classes/` +- Traits (shared behavior): `modules/{module}/traits/` or `plugins/{vendor}/{plugin}/traits/` +- Helper functions: `plugins/golem15/{pluginname}/helpers.php` (loaded in Plugin.php) + +**Configuration:** +- Plugin config: `plugins/golem15/{pluginname}/config/{name}.php` (published to app config/) +- Backend form widget: `plugins/golem15/{pluginname}/formwidgets/` +- Report widget: `plugins/golem15/{pluginname}/reportwidgets/` + +## Special Directories + +**storage/logs/:** +- Purpose: Application logging +- Generated: Yes (created at runtime) +- Committed: No (ignored in .gitignore) +- Contains: `laravel.log`, debug logs, error traces + +**storage/framework/:** +- Purpose: Framework caches, sessions, compiled views +- Generated: Yes (created at runtime) +- Committed: No (ignored in .gitignore) +- Subdirectories: `sessions/`, `views/`, `cache/` + +**storage/temp/ & storage/cms/cache/:** +- Purpose: Temporary files, CMS compilation caches +- Generated: Yes (created at runtime) +- Committed: No (ignored) +- Contents: Compiled page cache, asset combiner output + +**plugins/ (submodules):** +- Purpose: Git submodules for independent version control +- Generated: No (checked in as submodule references) +- Committed: References only (actual plugin code in separate repos) +- Update: `git submodule update --remote --merge` or `./scripts/update-submodules.sh` + +**bootstrap/cache/:** +- Purpose: Cached container, routes, config +- Generated: Yes (`php artisan config:cache`, `php artisan route:cache`) +- Committed: No (ignored) +- Clear: `php artisan cache:clear` + +**config/dev/ & config/testing/:** +- Purpose: Environment-specific config overrides +- Generated: No (committed) +- Committed: Yes (development/testing defaults) +- Used: In dev and testing environments via `.env` + +--- + +*Structure analysis: 2026-02-04* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..eda94a5 --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,467 @@ +# Testing Patterns + +**Analysis Date:** 2026-02-04 + +**Reference:** This document captures WinterCMS testing patterns (PHP and JavaScript) that should inform the Scala rewrite's test structure and conventions. + +## Test Framework + +**PHP Testing:** + +**Runner:** +- PHPUnit 9.5.8+ +- Config: `phpunit.xml` at root with module-specific configs in `modules/*/phpunit.xml` +- Bootstrap: `modules/system/tests/bootstrap/app.php` +- Base classes in `modules/system/tests/bootstrap/` + +**JavaScript Testing:** + +**Runner:** +- Jest +- Config: `modules/system/tests/js/jest.config.js` +- Uses jsdom environment (default behavior, Node.js runtime not specified) + +**Run Commands:** +```bash +composer test # Run all PHPUnit tests (stop on first failure) +composer lint # Check PHP syntax +composer sniff # Code standards check (PSR-1, PSR-2, PSR-4) +npm test # Run Jest tests (in js directory) +npm test -- --watch # Watch mode for JavaScript +``` + +## Test File Organization + +**PHP Tests:** + +**Location:** +- Tests co-located with source code in `modules/*/tests/` subdirectories +- Test subdirectories mirror source structure (e.g., `modules/backend/tests/widgets/`, `modules/backend/tests/models/`, `modules/backend/tests/classes/`) +- Module-level phpunit.xml files in each module + +**Naming:** +- Test files end with `Test.php` (e.g., `FormTest.php`, `AuthManagerTest.php`, `ImportModelTest.php`) +- Test class names: Source class name + `Test` suffix (e.g., `Form` → `FormTest`) +- Test methods start with `test` prefix (e.g., `testRestrictedFieldWithUserWithNoPermissions()`) + +**Structure:** +``` +modules/ +├── backend/ +│ ├── tests/ +│ │ ├── widgets/ +│ │ │ ├── FormTest.php +│ │ │ ├── ListsTest.php +│ │ │ └── FilterWidgetTest.php +│ │ ├── models/ +│ │ │ ├── ImportModelTest.php +│ │ │ └── ExportModelTest.php +│ │ ├── classes/ +│ │ │ ├── AuthManagerTest.php +│ │ │ └── WidgetManagerTest.php +│ │ ├── formwidgets/ +│ │ │ ├── CheckboxTest.php +│ │ │ └── ColorPickerTest.php +│ │ ├── fixtures/ +│ │ │ └── models/ +│ │ │ └── UserFixture.php +│ │ └── traits/ +│ │ └── WidgetMakerTest.php +│ └── phpunit.xml +``` + +**JavaScript Tests:** + +**Location:** +- Tests in `modules/system/tests/js/cases/` directory +- Structure mirrors feature organization +- Tests for Snowboard framework (AJAX library), form framework, and utilities + +**Naming:** +- Test files: `*.test.js` suffix (e.g., `DataAttribute.test.js`, `Snowboard.test.js`) +- Suite names: Descriptive string (e.g., "Data Attribute Request AJAX library") +- Test cases: Descriptive string starting with "can" or "should" (e.g., "can parse request data") + +**Structure:** +``` +modules/system/tests/js/ +├── jest.config.js +└── cases/ + ├── snowboard/ + │ ├── ajax/ + │ │ ├── DataAttribute.test.js + │ │ └── Request.test.js + │ ├── main/ + │ │ ├── Snowboard.test.js + │ │ └── PluginLoader.test.js + │ └── extras/ + │ └── DataConfig.test.js + └── framework/ + └── FormParent.test.js +``` + +## Test Structure + +**PHP Suite Organization:** + +**Base Test Case:** +```php +namespace System\Tests\Bootstrap; + +class TestCase extends \Illuminate\Foundation\Testing\TestCase +{ + public function createApplication() + { + // Creates Laravel application with testing configuration + } +} +``` + +**Plugin Test Case:** +- Extends base `TestCase` +- Auto-detects plugin being tested +- Automatically loads plugin dependencies +- Uses `InteractsWithAuthentication` trait +- Runs all migrations for test context + +**Example Test Structure** (`modules/backend/tests/classes/AuthManagerTest.php` lines 1-76): +```php +class AuthManagerTest extends TestCase +{ + protected AuthManager $instance; + protected $existingPermissions = []; + + public function setUp(): void + { + $this->createApplication(); + $this->instance = AuthManager::instance(); + $this->existingPermissions = $this->instance->listPermissions(); + + $this->instance->registerPermissions('Winter.TestCase', [ + 'test.permission_one' => [ + 'label' => 'Test Permission 1', + 'tab' => 'Test', + 'order' => 200 + ], + ]); + } + + protected function tearDown(): void + { + AuthManager::forgetInstance(); + } + + public function testListPermissions() + { + $permissions = $this->listNewPermissions(); + $this->assertCount(2, $permissions); + $this->assertEquals([ + 'test.permission_one', + 'test.permission_two' + ], $permissions); + } +} +``` + +**Patterns Observed:** + +**Setup/Teardown:** +- `setUp(): void` method runs before each test (creates fresh application state) +- `tearDown(): void` method cleans up singleton instances +- Database runs in-memory (SQLite `:memory:`) for test speed +- Fresh encryption key generated per test run +- All migrations run in setUp via `Artisan::call('winter:up')` + +**Assertion Patterns:** +- PHPUnit 9 assertions: `$this->assertCount()`, `$this->assertEquals()`, `$this->assertNull()`, `$this->assertNotNull()` +- Compatibility shims for PHPUnit 8/9 differences in `TestCase` base class (e.g., `assertFileNotExists()`, `assertRegExp()`) + +**Model Testing Pattern** (`modules/backend/tests/widgets/FormTest.php` lines 33-66): +```php +public function testRestrictedFieldWithUserWithNoPermissions() +{ + $user = new UserFixture; + $this->actingAs($user); + + $form = $this->restrictedFormFixture(); + $form->render(); + $this->assertNull($form->getField('testRestricted')); +} +``` + +## JavaScript Test Structure + +**Suite and Test Organization:** +```javascript +describe('Data Attribute Request AJAX library', function () { + it('can parse request data', function (done) { + FakeDom + .new() + .addScript([ + 'modules/system/assets/js/build/manifest.js', + 'modules/system/assets/js/snowboard/build/snowboard.vendor.js', + ]) + .render() + .then( + (dom) => { + const DataAttributeSingleton = dom.window.Snowboard.attributeRequest(); + + expect( + DataAttributeSingleton.parseData('{foo: "bar"}') + ).toEqual({ foo: 'bar' }); + + done(); + } + ); + }); +}); +``` + +**Patterns:** +- Uses FakeDom helper for DOM testing +- Scripts loaded explicitly (no auto-import) +- Async tests use `done()` callback (callback-style async) +- Expectations: `expect().toEqual()` for value comparison +- Jest global functions: `describe()`, `it()`, `jest.setTimeout()` + +## Mocking + +**PHP Mocking:** + +**Framework:** Mockery (mockery/mockery 1.4.4+) + +**Patterns:** +- Extends/uses reflection helpers in base `TestCase`: + ```php + protected static function callProtectedMethod($object, $name, $params = []) + protected static function getProtectedProperty($object, $name) + protected static function setProtectedProperty($object, $name, $value) + ``` +- Trait `InteractsWithAuthentication` provides auth mocking for controller tests +- Database transactions rolled back after each test (not explicitly visible but default Laravel behavior) + +**Authentication Mocking:** +```php +use Backend\Tests\Concerns\InteractsWithAuthentication; + +$user = new UserFixture; +$this->actingAs($user->withPermission('test.access_field', true)); +``` + +**What to Mock:** +- Database queries (use fixtures instead) +- External services (mail, events) +- Singleton instances (reset in tearDown) + +**What NOT to Mock:** +- Models and their relations (use fixtures) +- Framework components (use real instances) +- Business logic methods (test actual behavior) + +**JavaScript Mocking:** + +**Framework:** Jest built-in mocking + +**Patterns:** +- `clearMocks: true` in jest.config.js clears mock state before each test +- Mock creation via `jest.mock()` +- Used for module imports, timers, API calls + +## Fixtures and Factories + +**PHP Test Fixtures:** + +**Location:** +- `modules/backend/tests/fixtures/models/UserFixture.php` +- Located in `fixtures/` subdirectory parallel to test files + +**Test Data Pattern:** +```php +class UserFixture extends User +{ + public function withPermission($permission, $value = true) + { + $this->permissions[$permission] = $value; + return $this; + } +} +``` + +**Usage:** +- Fixtures are model subclasses that don't persist +- Can build chains of factory methods +- Used for controller/widget tests requiring user context +- No factory library (plain class extension) + +**Database Fixtures:** +- Run migrations in test setup to create schema +- Use model factories for initial test data where needed + +## Coverage + +**Requirements:** +- Coverage tracking configured in `phpunit.xml` but not enforced +- Coverage collected from `./modules/` directory +- Excludes routes, migrations, test fixtures + +**View Coverage:** +```bash +# Generate coverage report (requires xdebug) +phpunit --coverage-html=coverage/ +``` + +**Report Configuration** (`phpunit.xml` lines 14-26): +```xml + + + ./modules/ + + + ./modules/backend/routes.php + ./modules/cms/routes.php + ./modules/system/routes.php + ./modules/backend/database + ./modules/cms/database + ./modules/system/database + + +``` + +## Test Types + +**Unit Tests:** +- Scope: Single class/method in isolation +- Examples: `AuthManagerTest`, `ImportModelTest` +- Uses mocking for dependencies +- Fast execution (milliseconds) +- Tests business logic and edge cases + +**Integration Tests:** +- Scope: Multiple components working together +- Examples: `FormTest`, `FilterWidgetTest` (form rendering with widgets) +- Uses real database (in-memory SQLite) +- Tests workflows and component interaction +- Slower execution (seconds per suite) + +**E2E Tests:** +- Framework: Not observed in WinterCMS core (JavaScript e2e exists but minimal coverage) +- Would require: Full browser automation (Selenium/Cypress) +- Not standard in this codebase + +**Fixture-Based Tests:** +- Use model fixtures for state setup +- Examples: `testRestrictedFieldWithUserWithRightPermissions()` - user fixture with permission +- Reduce mock overhead + +## Common Patterns + +**Async Testing (PHP):** + +**Event-Based Async:** +```php +// No explicit async patterns in WinterCMS PHP tests +// Uses synchronous execution with database transactions +``` + +**Database Isolation:** +- Transactions rolled back after each test (Laravel default) +- Fresh database state per test via setUp() + +**Async Testing (JavaScript):** + +**Promise-Based:** +```javascript +FakeDom.new() + .addScript([...]) + .render() + .then((dom) => { + // Assertions here + done(); + }); +``` + +**Callback-Based:** +```javascript +it('test name', function (done) { + asyncOperation(() => { + expect(...).toEqual(...); + done(); + }); +}); +``` + +**Error Testing:** + +**PHP Exception Testing:** +```php +public function testThrowsException() +{ + $this->expectException(SystemException::class); + $this->expectExceptionMessage('Expected message'); + + // Code that throws +} +``` + +**Validation Error Testing:** +```php +public function testValidationRules() +{ + $user = new User; + $user->email = 'invalid'; + + if (!$user->validate()) { + $this->assertCount(1, $user->errors()); + } +} +``` + +**JavaScript Error Testing:** +```javascript +it('throws on invalid input', () => { + expect(() => { + parser.parse(null); + }).toThrow(); +}); +``` + +## Plugin Testing + +**Plugin Test Case** (`modules/system/tests/bootstrap/PluginTestCase.php`): +- Extends base `TestCase` +- Auto-detects plugin under test via namespace +- Loads all plugin dependencies +- Runs migrations in test-specific configuration +- Resets singleton instances between tests + +**Plugin Test Setup:** +```php +abstract class PluginTestCase extends TestCase +{ + use InteractsWithAuthentication; + + public function setUp(): void + { + PluginManager::forgetInstance(); + UpdateManager::forgetInstance(); + parent::setUp(); + + Artisan::call('winter:up'); // Run all migrations + + $pluginCode = $this->guessPluginCode(); + if (!is_null($pluginCode)) { + $this->instantiatePlugin($pluginCode, false); + } + } +} +``` + +**Plugin Test Isolation:** +- Each plugin can run its own test suite +- Test database in-memory and transaction-isolated +- Dependencies automatically resolved and loaded + +--- + +*Testing analysis: 2026-02-04* +*Reference: WinterCMS test patterns (PHP PHPUnit + JavaScript Jest) for Scala rewrite guidance* diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..003fc34 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,52 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**SummerCMS** is a planned rewrite of WinterCMS/OctoberCMS in Scala. The goal is to create a similar Content Management Framework (CMF) with improved performance and stability. + +### Project Status + +This is a **greenfield project**. The repository currently contains: +- `golem15-wintercms-starter/` - Git submodule of the existing WinterCMS implementation (reference) +- `IDEA.md` - High-level requirements and vision + +### Core Requirements (from IDEA.md) + +1. **Model/Controller/Components system** - Similar to WinterCMS patterns +2. **Modern frontend-backend communication** - Replace AJAX with efficient, scalable framework +3. **Admin backend** - YAML/JSON-driven form generation +4. **CLI scaffolding** - Console commands for initial setup +5. **Plugin/Theme system** - Extensible architecture with plugin interdependencies + +## Reference Implementation + +The WinterCMS reference is in `golem15-wintercms-starter/`. See its `CLAUDE.md` for details on: +- Golem15 plugin architecture +- Apparatus framework (DI, scenarios, form widgets) +- PaymentGateway FSM patterns +- User/Auth with JWT +- Translation system + +### Key WinterCMS Patterns to Replicate + +**Plugin Structure:** +- Controllers, Models, Components in plugin root +- YAML-based form fields and list columns (`fields.yaml`, `columns.yaml`) +- Plugin dependencies via `$require` arrays +- Event-driven model extensions + +**Apparatus Framework Concepts:** +- Scenario-based workflow engine +- Automatic dependency injection for components +- Backend CSS/JS injection system + +## Development + +```bash +# Initialize submodules (to access reference implementation) +git submodule update --init --recursive +``` + +Future Scala project setup will be documented here once the build system is established. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c43eab --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# sc-summercms-app diff --git a/golem15-wintercms-starter b/golem15-wintercms-starter new file mode 160000 index 0000000..50217c8 --- /dev/null +++ b/golem15-wintercms-starter @@ -0,0 +1 @@ +Subproject commit 50217c8c207eba48f55a1a06f9db2a93e7761b8c