IDEA Extended

This commit is contained in:
Jakub Zych
2026-02-04 01:06:15 +01:00
parent 7446e886a8
commit d8a4a4555c
11 changed files with 2189 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "golem15-wintercms-starter"]
path = golem15-wintercms-starter
url = git@github.com:golem15com/wn-starter-app.git

View File

@@ -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 <command>`
- 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*

View File

@@ -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*

View File

@@ -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
<?php namespace Backend\Models;
use Backend\Facades\Backend;
use Backend\Facades\BackendAuth;
use Illuminate\Support\Facades\Event;
use Winter\Storm\Auth\Models\User as UserBase;
use Winter\Storm\Support\Facades\Mail;
/**
* Administrator user model
* ...
*/
class User extends UserBase
```
**Path Aliases:**
- No import aliases in WinterCMS codebase - full namespace paths are used
- Traits are explicitly used via `use` keyword within class body (e.g., `use \Winter\Storm\Database\Traits\SoftDelete;`)
## Error Handling
**Patterns:**
- No explicit try-catch in models/controllers; exceptions bubble to framework handlers
- Validation errors handled via model `$rules` property (e.g., `modules/backend/models/User.php` lines 27-32)
- Framework handles model validation failures and redirects with flash messages
- Exception types: `Winter\Storm\Exception\SystemException` used for framework-level errors (seen in tests)
**Validation:**
- Model rules defined in `public $rules` array with pipe-separated validators:
```php
public $rules = [
'email' => '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*

View File

@@ -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*

126
.planning/codebase/STACK.md Normal file
View File

@@ -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*

View File

@@ -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*

View File

@@ -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
<coverage>
<include>
<directory suffix=".php">./modules/</directory>
</include>
<exclude>
<file>./modules/backend/routes.php</file>
<file>./modules/cms/routes.php</file>
<file>./modules/system/routes.php</file>
<directory suffix=".php">./modules/backend/database</directory>
<directory suffix=".php">./modules/cms/database</directory>
<directory suffix=".php">./modules/system/database</directory>
</exclude>
</coverage>
```
## 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*

52
CLAUDE.md Normal file
View File

@@ -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.

1
README.md Normal file
View File

@@ -0,0 +1 @@
# sc-summercms-app