IDEA Extended
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "golem15-wintercms-starter"]
|
||||
path = golem15-wintercms-starter
|
||||
url = git@github.com:golem15com/wn-starter-app.git
|
||||
231
.planning/codebase/ARCHITECTURE.md
Normal file
231
.planning/codebase/ARCHITECTURE.md
Normal 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*
|
||||
592
.planning/codebase/CONCERNS.md
Normal file
592
.planning/codebase/CONCERNS.md
Normal 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*
|
||||
228
.planning/codebase/CONVENTIONS.md
Normal file
228
.planning/codebase/CONVENTIONS.md
Normal 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*
|
||||
240
.planning/codebase/INTEGRATIONS.md
Normal file
240
.planning/codebase/INTEGRATIONS.md
Normal 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
126
.planning/codebase/STACK.md
Normal 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*
|
||||
248
.planning/codebase/STRUCTURE.md
Normal file
248
.planning/codebase/STRUCTURE.md
Normal 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*
|
||||
467
.planning/codebase/TESTING.md
Normal file
467
.planning/codebase/TESTING.md
Normal 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
52
CLAUDE.md
Normal 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
golem15-wintercms-starter
Submodule
1
golem15-wintercms-starter
Submodule
Submodule golem15-wintercms-starter added at 50217c8c20
Reference in New Issue
Block a user