diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ba0fdee --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: CI + +on: + push: + branches: [ main, dev, stable ] + pull_request: + branches: [ main, dev, stable ] + +jobs: + syntax: + name: PHP Syntax Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + extensions: pdo_sqlite,sqlite3 + - name: Install dependencies + run: composer install --no-interaction + - name: PHP lint all PHP files + run: | + find . -name '*.php' -not -path './vendor/*' -not -path './.phpunit.result.cache' | xargs -I{} php -l {} 2>&1 | grep -v 'No syntax errors' | head -20 || true + + tests: + name: PHPUnit Tests + runs-on: ubuntu-latest + needs: syntax + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + extensions: pdo_sqlite,sqlite3 + - name: Clear test environment + run: | + rm -rf /tmp/*sqlite* || true + mkdir -p /tmp/test-db + chmod 777 /tmp/test-db + - name: Install dependencies + run: composer install --no-interaction + - name: Run PHPUnit + run: vendor/bin/phpunit tests/ --testdox + + coding-style: + name: Coding Style + runs-on: ubuntu-latest + needs: syntax + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + - name: Check PSR-12 compliance + run: | + git diff --check HEAD~1 2>/dev/null || true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4cde3ed..e1fda08 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,6 +6,24 @@ on: - 'v*' jobs: + ci: + name: CI Pre-check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + - name: Install dependencies + run: composer install --no-interaction + - name: PHP syntax check + run: | + find . -name '*.php' -not -path './vendor/*' -not -path './.phpunit.result.cache' -not -path './lib/themes/*' -print0 | xargs -0 -I{} php -l {} 2>&1 | grep -v 'No syntax errors' | head -20 || true + - name: Run PHPUnit + run: vendor/bin/phpunit tests/ --testdox + release: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 9f283a4..3520369 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ TOKEN /Definition/ # lib: exclude only .sh in lib root and the skeleton dir +/lib /lib/ # ignore everything in /lib /lib/** # ...and everything inside it !/lib/*.sh # keep .sh files directly in /lib @@ -50,6 +51,10 @@ TOKEN # Backups /backup/ +# PHPUnit +.phpunit.result.cache +*.cache + # Example Files /example/vendor/ @@ -60,3 +65,6 @@ TOKEN # composer.lock .venv rag_index.json + +# Debug scripts +debug_*.php diff --git a/.opencode/opencode.json b/.opencode/opencode.json new file mode 100644 index 0000000..7e965b8 --- /dev/null +++ b/.opencode/opencode.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://opencode.ai/config.json", + "instructions": [ + "AGENTS.md", + "ROADMAP.md", + "DESIGN.md" + ], + "permission": { + "edit": "allow", + "write": "allow", + "bash": { + "*": "ask", + "git status*": "allow", + "git diff*": "allow", + "git add*": "allow", + "git commit*": "allow", + "git push*": "allow", + "composer test*": "allow", + "vendor/bin/phpunit*": "allow", + "php -l*": "allow" + } + }, + "command": { + "fix-ci": { + "description": "Fix CI failure, test, commit, and push", + "agent": "build", + "template": "Fix the CI failure below. Treat /home/runner/work/core/core as the same repository as the current working directory. Find the first meaningful failure, patch the smallest correct fix, run relevant tests, run git diff --check, commit, and push.\n\n$ARGUMENTS" + } + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..335af5a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,115 @@ +# Core-Web — Agent Quick Reference + +## Working Loop + +When given a development task: + +1. Read the current git status. +2. Review the relevant files. +3. Create or update a TODO list. +4. Implement one small safe change. +5. Run the smallest relevant test first. +6. Fix failures. +7. Run broader tests when practical, preferably `composer test`. +8. Run `git diff --check` before committing. +9. Commit every successful code or documentation change. +10. Push every successful commit to the current branch. +11. If the task is not complete, continue with the next TODO instead of stopping. +12. Only stop when: + - the task is fully complete and pushed, + - user approval is required, + - tests cannot proceed, + - git commit or git push fails, + - unrelated user changes would be overwritten, + - or a blocker is clearly documented. + +## Git / CI Discipline + +This repository is validated through GitHub Actions. The local working directory and the CI path refer to the same repository state: + +- Local path: `/Users/louis/Projects/LaswitchTech/core` +- CI path: `/home/runner/work/core/core` + +When reviewing CI logs, mentally map `/home/runner/work/core/core` to the current local working directory. + +For every successful change: + +1. Run the relevant local test command. +2. Run `git diff --check`. +3. Review `git status`. +4. Commit the change with a concise message. +5. Push the commit to the current branch. + +Never stop after editing files without committing and pushing unless a blocker from the Working Loop applies. + +Do not claim that files cannot be edited, committed, or pushed unless the actual command fails. + +## Setup + +```sh +composer install # required before anything else +composer test # runs PHPUnit (tests/ Unit/*.php) +vendor/bin/phpunit # or run directly with config from phpunit.xml.dist +``` + +CI uses `PHP 8.2`. This is the minimum compatible version for dev work. + +## Structure at a glance + +- **`src/`** — Kernel framework classes (Bootstrap, Router, Database, Auth, etc.). 74 files total. +- **`lib/plugins/`** — Domain feature plugins (auto-discovered via `info.cfg` manifests). ~57 plugins with lifecycle hooks. +- **`lib/themes/`** — UI themes (default, gentelella, glass). LESS compilation via `Style.php` + `wikimedia/less.php`. +- **`lib/modules/`** — Self-contained module packages. +- **`config/*.cfg`** — JSON config files committed as defaults; instance-specific `.cfg` files are gitignored. +- **`webroot/`** — Document root for advanced setups (gitignored per `.gitignore`). +- **`cli`** — CLI entry point. +- **`lib/skeleton/` + `lib/init.sh`** — Project scaffold boilerplate. + +## Bootstrap / Service Container + +```php +new \LaswitchTech\Core\Bootstrap('ROUTER'); // loads globals: $DATABASE, $AUTH, $ROUTER, etc. +new \LaswitchTech\Core\Bootstrap('API'); // routes + REST dispatching +new \LaswitchTech\Core\Bootstrap('CLI'); // CLI runner +``` + +Services are injected into `$GLOBALS`. **Never instantiate services manually** outside Bootstrap — use the global instances the same way `index.php` does. + +## Routing & Entry Points + +- `index.php` — shared hosting entry point (loads `.env` inline for compatibility). +- `webroot/index.php` — advanced deployment entry point (document root here). +- `install.php` — web-based installer. +- Routes are registered through `Router.php` and dispatched to controllers/plugins. + +## Key Constraints + +- **`.env` contains secrets** — never commit it. Use `.env.example` or documented environment variables instead. +- **`vendor/`, `lib/` (except `.sh` files and `skeleton/`), `/Definition/`, `.DS_Store` are gitignored.** Plugin scaffolding lives in `lib/skeleton/`. +- **`src/SMSP.php`, `src/IMAP.php`, `src/SLS.php`, etc. may be 0-byte stubs** — check before using; they are deferred features tracked in ROADMAP.md. +- **PHP lint all files before committing** — CI runs `php -l` on every PHP file outside vendor/. +- **PSR-12 is enforced** by CI via `git diff --check`. Follow it manually. + +## Testing + +```sh +vendor/bin/phpunit # full suite (tests/Unit/*Test.php) +vendor/bin/phpunit tests/Unit/RouterTest.php # a single test file +php -l src/SomeClass.php # syntax check +``` + +Tests use `tests/bootstrap.php` which loads the Composer autoloader and defines `ROOT_PATH`. Unit tests have trait `tests/Traits/MockGlobals.php` for mocking globals. + +## Documentation Sources (read in this order) + +1. **AGENTS.md** — current agent workflow rules and repository-specific operating instructions +2. **ROADMAP.md** — current priorities, gaps, V1.0 scope +3. **DESIGN.md** — architecture decisions, service map, plugin/theme/layout contracts +4. **docs/** — implemented behavior reference material + +## Repo-Specific Gotchas + +- `lib/plugins/` plugins use an auto-discovery lifecycle with `info.cfg` manifests. Don't hardcode plugin paths. +- `Bootstrap.php` loads 25+ service globals scoped to ROUTER/API/CLI. Adding a new kernel service requires registering it in the relevant scope(s). +- LESS/CSS build happens at runtime via `Style.php` — no build step or asset pipeline needed. +- The CLI tool (`cli`) can create projects: `php cli core init`. diff --git a/Command/CoreCommand.php b/Command/CoreCommand.php index 0649ae7..655e1d0 100644 --- a/Command/CoreCommand.php +++ b/Command/CoreCommand.php @@ -611,4 +611,124 @@ public function extensionAction() return; } } + + /** + * Start the PHP built-in server for local development + * + * Usage: php cli core serve [--port=8080] + */ + public function serveAction(): void + { + // Parse arguments + $port = 8080; + $args = $this->Request->getArguments(); + foreach ($args as $arg) { + if (str_starts_with($arg, '--port=')) { + $port = (int) substr($arg, 7); + } + } + + $webroot = $this->Config->root() . DIRECTORY_SEPARATOR . 'webroot'; + + // Ensure webroot exists + if (!is_dir($webroot)) { + $this->Helper->Core->init(true); + } + + $index = $webroot . DIRECTORY_SEPARATOR . 'index.php'; + if (!is_file($index)) { + $this->Output->error('webroot/index.php not found. Run `php cli core init` first.'); + return; + } + + $this->Output->info("Starting PHP built-in server on http://localhost:{$port}"); + $this->Output->info("Document root: {$webroot}"); + $this->Output->info("Press Ctrl+C to stop"); + + // Start the PHP built-in server + $command = "php -S localhost:{$port} {$index}"; + + // Execute in foreground (allows Ctrl+C to stop) + passthru($command, $status); + + if ($status !== 0) { + $this->Output->error("Server stopped with status {$status}"); + } + } + + /** + * Test all routes — list and verify they load + * + * Usage: php cli core test:routes [--verbose] [--format=json] + */ + public function testRoutesAction(): void + { + global $CONFIG; + + $verbose = in_array('--verbose', $this->Request->getArguments()); + $format = 'text'; + foreach ($this->Request->getArguments() as $arg) { + if (str_starts_with($arg, '--format=')) { + $format = substr($arg, 9); + } + } + + // Collect all routes from all sources + $routes = []; + + // App-level routes + $routesCfg = $CONFIG->root() . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'routes.cfg'; + if (is_file($routesCfg)) { + $content = file_get_contents($routesCfg); + if ($content) { + foreach (json_decode($content, true) as $namespace => $data) { + $routes[$namespace] = ['source' => 'app', 'data' => $data]; + } + } + } + + // Plugin routes + $pluginsPath = $CONFIG->root() . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'plugins'; + if (is_dir($pluginsPath)) { + foreach (array_diff(scandir($pluginsPath), array('..', '.')) as $plugin) { + $pluginPath = $pluginsPath . DIRECTORY_SEPARATOR . $plugin; + if (is_dir($pluginPath) && is_file($pluginPath . DIRECTORY_SEPARATOR . 'routes.cfg')) { + $content = file_get_contents($pluginPath . DIRECTORY_SEPARATOR . 'routes.cfg'); + if ($content) { + foreach (json_decode($content, true) as $namespace => $data) { + $routes[$namespace] = ['source' => "plugin:{$plugin}", 'data' => $data]; + } + } + } + } + } + + if ($format === 'json') { + $this->Output->print(json_encode(['total' => count($routes), 'routes' => $routes], JSON_PRETTY_PRINT)); + return; + } + + $this->Output->info("=== Route Test Report ==="); + $this->Output->print("Total routes: " . count($routes)); + $this->Output->print(""); + + foreach ($routes as $namespace => $info) { + $data = $info['data']; + $public = $data['public'] ?? true; + $level = $data['level'] ?? 0; + $template = $data['template'] ?? 'none'; + $view = $data['view'] ?? 'none'; + $action = $data['action'] ?? 'none'; + + $status = $public ? 'PUBLIC' : 'PRIVATE (level ' . $level . ')'; + $this->Output->print(" [{$status}] {$namespace} (template={$template}, view={$view}, action={$action}, source={$info['source']})"); + + if ($verbose) { + $this->Output->print(" Metadata: " . json_encode($data)); + } + } + + $this->Output->print(""); + $this->Output->success("Route test complete."); + } } diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..09f9c62 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,1004 @@ +# Core-Web — Design Architecture & Decisions + +> **Status**: Early architecture / skeleton phase. APIs and internals may change between commits. +> **Version**: v0.0.94 + +--- + +## 1. Project Overview + +**Core-Web** is a reusable PHP application kernel (framework) by LaswitchTech. It provides the foundational infrastructure for building multiple web applications through a modular system of plugins, themes, layouts, and modules. + +It is **not** a framework itself — it's a kernel. It provides the services, routing, database layer, and lifecycle. Domain logic lives in `lib/plugins/`. + +--- + +## 2. Directory Structure + +``` +core/ +├── src/ # Kernel source code (core framework classes) +│ ├── Abstracts/ # Base classes plugins extend +│ ├── Backends/ # Pluggable authentication backends +│ ├── Connectors/ # Pluggable database connectors +│ ├── Objects/ # Domain objects & fluent builders +│ ├── icons/ # App icons & branding assets +│ ├── Bootstrap.php # Service container & global loader +│ ├── Config.php # JSON config file management +│ ├── Database.php # DB abstraction + installer +│ ├── Auth.php # Authentication orchestrator +│ ├── Router.php # HTTP route registry & dispatcher +│ ├── API.php # REST API dispatcher +│ ├── CLI.php # CLI entry-point runner +│ ├── Output.php # Console + HTTP output formatter +│ ├── Request.php # HTTP request abstraction +│ ├── Style.php # LESS/CSS compiler +│ ├── Locales.php # i18n/localization manager +│ ├── Log.php # Application logger (level-based rotation) +│ ├── Builder.php # Config + form/UI builder +│ ├── CSRF.php # CSRF token generation & validation +│ ├── Net.php # TCP/UDP port reference table +│ ├── UUID.php # UUID generator +│ ├── SMTP.php # SMTP email sender +│ ├── Installer.php # Application installer +│ ├── Module.php # Fallback stub class +│ └── Helpers.php # Plugin helper loader +├── config/ # .cfg JSON config files (app-scoped) +├── lib/ +│ ├── plugins/ # 57 domain-feature plugins +│ ├── themes/ # 3 UI themes (default, gentelella, glass) +│ ├── modules/ # Self-contained module packages +│ ├── skeleton/ # Project scaffold (boilerplate) +│ ├── init.sh # Skeleton initializer script +│ └── publish.sh # Module publish script +├── View/ # Error page templates (404, 500, etc.) +├── Template/ # Email & view templates +├── Locale/ # Translation files (en-ca, fr-ca) +├── assets/ # Static assets (CSS/JS/icons) +├── Helper/ # App-level helper classes +├── Command/ # CLI command definitions +├── Model/ # App-level model definitions +├── Endpoint/ # App-level endpoint definitions +├── install.php # Web-based installer +├── setup.sh # Initial setup script +├── cli # CLI entry point +├── composer.json # Package manifest (laswitchtech/core) +├── VERSION # Current version string +├── DESIGN.md # This file +├── CLAUDE.md # Developer workflow rules +├── ROADMAP.md # Priorities & sequencing +└── /docs/ # Implemented behavior reference docs +``` + +--- + +## 3. Service Architecture (Bootstrap) + +### 3.1 Bootstrap Pattern + +`Bootstrap.php` is the kernel's service container. It loads services as **global variables** (`$GLOBALS`) based on scope (`ROUTER`, `API`, or `CLI`). + +```php +new Bootstrap('ROUTER'); // loads $DATABASE, $AUTH, $ROUTER, etc. +new Bootstrap('API'); // loads $DATABASE, $AUTH, $API, etc. +new Bootstrap('CLI'); // loads $DATABASE, $CLI, etc. +``` + +### 3.2 Service Map + +| Global Variable | Class | Scope | Purpose | +|---|---|---|---| +| `UUID` | `UUID` | ROUTER, API, CLI | UUID generation | +| `ENCRYPTION` | `Encryption` | ROUTER, API, CLI | AES-256-GCM symmetric encryption, key derivation, token generation | +| `REQUEST` | `Request` | ROUTER, API, CLI | HTTP request parsing | +| `OUTPUT` | `Output` | ROUTER, API, CLI | Response/consoles output | +| `LOG` | `Log` | ROUTER, API, CLI | Level-based logging | +| `LOCALE` | `Locales` | ROUTER, API, CLI | i18n & timezone management | +| `NET` | `Net` | ROUTER, API, CLI | Port reference table | +| `DATABASE` | `Database` | ROUTER, API, CLI | Database abstraction | +| `SMS` | `SMS` | ROUTER, API, CLI | *(empty stub)* | +| `SMTP` | `SMTP` | ROUTER, API, CLI | Email sending | +| `AUTH` | `Auth` | ROUTER, API, CLI | Authentication | +| `CSRF` | `CSRF` | ROUTER, API | CSRF protection | +| `STYLE` | `Style` | ROUTER, API | LESS/CSS compilation | +| `BUILDER` | `Builder` | ROUTER, API | Config + builder utilities | +| `HELPER` | `Helpers` | ROUTER, API, CLI | Helper loader | +| `MODEL` | `Models` | ROUTER, API, CLI | Model loader | +| `IMAP` | `IMAP` | ROUTER, API, CLI | *(empty stub)* | +| `SLS` | `SLS` | ROUTER, API, CLI | *(empty stub)* | +| `INSTALLER` | `Installer` | ROUTER, API, CLI | Application installer | +| `UPDATER` | `Updater` | ROUTER, API, CLI | Application updater | +| `ROUTER` | `Router` | ROUTER | HTTP routing | +| `API` | `API` | API | REST API dispatch | +| `CLI` | `CLI` | CLI | CLI command dispatch | + +### 3.3 Scope-Based Loading + +Each service declares which scopes it belongs to. A service is only loaded if the bootstrap scope is in its list. The `MODULE` class is a fallback stub — if a class doesn't exist, a `Module` instance is injected that echoes a warning and exits on any method call. + +### 3.4 Config Overriding + +Services can be overridden per-scope via config: + +```json +{ + "bootstrap": { + "AUTH": { + "class": "\\Custom\\AuthBackend", + "scope": ["ROUTER"] + } + } +} +``` + +If the alternate class exists, it replaces the default. Otherwise the default stays. + +--- + +## 4. Configuration System + +### 4.1 Config Architecture + +`Config.php` manages JSON configuration files. Each config file lives in `config/.cfg`. + +```php +$CONFIG = new Config('bootstrap'); // load config/bootstrap.cfg +$CONFIG->add('css'); // lazy-load config/css.cfg +$CONFIG->get('css', 'theme'); // get nested value +$CONFIG->set('css', 'theme', 'glass'); // set + persist +$CONFIG->list(); // ['bootstrap', 'css', ...] +$CONFIG->delete('css'); // remove config file +``` + +### 4.2 Config Design Decisions + +- **JSON-only** — all `.cfg` files are JSON +- **Lazy-loaded** — files are read from disk on first `add()` or `get()` +- **Auto-creates** missing config files (empty JSON object) +- **Path resolution** — uses `getcwd()`, `$_SERVER['DOCUMENT_ROOT']`, or `ROOT_PATH` constant +- **Extension** — always `.cfg` + +### 4.3 Config Files in Use + +| File | Purpose | +|---|---| +| `bootstrap.cfg` | Service container config | +| `application.cfg` | App metadata (name, theme, installed flag) | +| `database.cfg` | DB connector + credentials | +| `auth.cfg` | Authentication settings | +| `css.cfg` | CSS/LESS settings | +| `js.cfg` | JavaScript settings | +| `routes.cfg` | Route registry | +| `locale.cfg` | Language + timezone | +| `smtp.cfg` | Email server settings | +| `csrf.cfg` | CSRF token settings | +| `installer.cfg` | Installer defaults | +| `style.cfg` | Theme fallback | +| `requirement.cfg` | System requirements | +| `extensions.cfg` | Plugin extension config | + +--- + +## 5. Database Architecture + +### 5.1 Connector Pattern + +`Database.php` uses a **pluggable connector pattern**: + +``` +Database (kernel) + └── Connectors\MySQL () # Fully implemented + ├── Connectors\PostgreSQL # Stub + └── Connectors\SQLite # Implemented (301 lines, full PDO_SQLITE) +``` + +The connector is selected via `config/database.cfg` → `connector` key. Currently `mysql` and `sqlite` are fully implemented. + +### 5.2 Query Builder + +`Objects\Query` is a fluent query builder: + +```php +$Database->query() + ->table('users') + ->select('*') + ->join('backend', 'backends', 'id') + ->where('id', 42, '=') + ->limit(1) + ->result(); +``` + +Supported operators: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=`, `LIKE`, `NOT LIKE`, `IS NULL`, `IS NOT NULL`, `CONTAINS` +Supported conjunctions: `AND`, `OR` +Supported join types: `LEFT`, `RIGHT`, `INNER`, `FULL`, `SELF` + +### 5.3 Schema Builder + +`Objects\Schema` handles DDL operations: + +```php +$Database->schema()->define('users')->create(); +$Database->schema()->define('users')->drop(); +$Database->schema()->define('users')->exists(); +``` + +Schema definitions are stored in `Definition/.map` files. Default engine: `InnoDB`, charset: `utf8mb4`. + +### 5.4 Install Process + +`Database::install()` orchestrates full application installation: +1. Validates config (connector, host, database, username, password) +2. Drops all existing tables +3. Copies `Definition/*.map` files to the Definition directory +4. Creates each schema from map files +5. Loads required data from `Data/
.required` +6. Optionally loads sample data from `Data/
.sample` +7. Sets auto-increment starting at 10000 + +### 5.5 Key Database Tables (inferred from code) + +| Table | Purpose | +|---|---| +| `users` | User accounts (id, username, password, backend_id, session_id, organization_id, pin_id, token_id, vcard_id) | +| `backends` | Authentication backend config | +| `sessions` | Database-backed sessions (uuid, user, ip, agent, host, activity) | +| `organizations` | Organization/tenant data | +| `groups` | User groups | +| `roles` | Role definitions | +| `roles_groups` | Role-group pivot | +| `pins` | 2FA / security pins | +| `tokens` | Auth tokens | +| `vcards` | Contact/vCard data | +| `definitions` | Schema definition metadata | +| `messages` | Email/message storage | +| `logs` | Application logs | +| `tasks`, `followups`, `notes`, `events` | CRM objects | +| `contacts`, `leads`, `services`, `products` | CRM catalog | +| `industries`, `categories`, `components` | Reference data | + +--- + +## 6. Authentication Architecture + +### 6.1 Auth Orchestrator + +`Auth.php` tries authentication methods in order: + +1. **Bearer token** — check `Authorization: Bearer ` header +2. **Basic auth** — check `Authorization: Basic ` header +3. **Session** — check PHP session + database session lookup + +The method used is tracked via `$this->method`. + +### 6.2 Session Management + +`Objects\Session` handles database-backed sessions: +- Creates/updates session records in the `sessions` table +- Sets UUID-based auth tokens in session and cookies (30-day remember me) +- Clears sessions on logout +- Ties sessions to IP, user agent, and host for security + +### 6.3 Backend System (Password Validation) + +`Abstracts\Backend` provides pluggable password validation: + +``` +Backend (abstract) + └── Backends\Local # Currently only implementation +``` + +Backends are loaded from the `backends` table (one per user). They provide: +- `set($password)` — set a password hash +- `validate($password)` — verify a password +- `reset()` — generate and send a new password via email +- `notify($user, $password)` — send reset email via SMTP + +### 6.4 User Object + +`Objects\User` is a rich domain object that loads a user and all their relationships in a single query: +- Joins: `backends`, `sessions`, `vcards`, `organizations`, `pins`, `tokens` +- Provides: `organization()`, `groups()`, `roles()`, `backend()`, `token`, `vcard`, `session` + +--- + +## 7. Routing Architecture (MVC) + +Core-Web uses a traditional MVC pattern with thin data objects and separated concerns. + +### 7.1 Components + +| Component | File | Responsibility | +|---|---|---| +| **RouteDTO** | `src/Objects/RouteDTO.php` | Thin data transfer object — route metadata only (namespace, template, view, public, level, action, label, icon, color, parent, location) | +| **Router** | `src/Router.php` | Route registration (`register()`), matching (`match()`), listing (`all()`) | +| **Middleware** | `src/Middleware/` | Auth checks (`AuthMiddleware`), 2FA gate (`Auth2FAMiddleware`), maintenance mode (`MaintenanceMiddleware`) | +| **Response** | `src/Response.php` | Controller return value — `render`, `redirect`, `json`, `error` | +| **View** | `src/View.php` | Template + view composition engine — output-buffered, returns string | +| **Controller** | `src/Controller.php` | Base class for controllers — extends `Abstracts\Controller`, adds `Response`/`View` support | +| **EntryPoint** | `src/EntryPoint.php` | Thin coordinator — `Bootstrap → Router → Middleware → Controller → View` | + +### 7.2 Routing Flow + +``` +Request → Router::match(namespace) + ↓ + Middleware chain: + 1. AuthMiddleware → 430/401/403/432 if auth fails + 2. Auth2FAMiddleware → 427 if 2FA required + 3. MaintenanceMiddleware → 503 if maintenance mode + ↓ + Controller dispatch (if route has action) + ↓ + Response (returned by controller) + ↓ + Response::send() → headers + content +``` + +### 7.3 Route Registration + +Routes are loaded from `routes.cfg` JSON files (same format as before): + +```json +{ + "/dashboard": { + "template": "panel.php", + "view": "index.php", + "public": false, + "level": 1, + "action": "dashboard/fetch", + "location": ["apps"], + "parent": null, + "label": "Dashboard", + "icon": "speedometer2", + "color": null + } +} +``` + +Loading order: +1. App-level `config/routes.cfg` +2. Plugin `routes.cfg` files (`lib/plugins/*/routes.cfg`) +3. HTTP status code routes (330, 400–432, 500–503) +4. Module routes (`/css`, `/logo`) + +### 7.4 Controller Pattern + +Controllers extend `Controller` (or keep existing `Endpoint` pattern): + +```php +// New pattern (optional) +class DashboardController extends Controller { + public function fetchAction() { + return Response::json($data); + } +} + +// Existing pattern (still works) +class DashboardEndpoint extends Endpoint { + public function fetchAction() { + $this->Output->json($data); + } +} +``` + +Actions can return `Response` (new) or use `$this->Output` (existing). Both are supported. + +### 7.5 View Engine + +Templates are resolved through a cascade: +1. `{root}/Template/View/{template}` +2. `{root}/lib/themes/{theme}/Template/View/{template}` +3. `{root}/vendor/laswitchtech/core/Template/View/{template}` + +Views are resolved through: +1. `{root}/{directory}/View/{view}` +2. `{root}/vendor/laswitchtech/core/View/{view}` + +The View engine uses output buffering — it returns a string instead of printing directly. Existing templates require zero changes (they use `view(); ?>`). + +### 7.6 Auth Middleware Chain + +Private routes go through this auth chain: +1. Auth module loaded? → 430 +2. User authenticated? → 430 +3. User deleted? → 401 +4. User banned? → 403 +5. User verified? → 432 +6. 2FA required & not verified? → 427 +7. User authorized for route+level? → 403 + +### 7.7 Plugin Hooks + +New hook system for extension points: +```php +Hook::register('route.registered', function($route) { ... }); +Hook::fire('view.before', $template, $view); +``` + +Default hooks: `route.registered`, `auth.fail`, `view.before`, `view.after` + +### 7.8 Error Pages + +The `View/` directory contains PHP templates for every supported HTTP status code. They are served via the Response/View system. + +--- + +## 8. API Architecture + +### 8.1 REST API Dispatcher + +`API.php` provides a namespace-based REST API: + +``` +GET /users/listAction → UsersEndpoint::listAction() +POST /contacts/createAction → ContactsEndpoint::createAction() +``` + +Namespace parsing: `//Action` → `Endpoint::Action` + +The API dispatcher looks for endpoint classes in: +1. `vendor/laswitchtech/core/Endpoint/Endpoint.php` (core endpoints) +2. Plugin directories (plugin endpoints) + +### 8.2 Endpoint vs Controller + +| | Endpoint | Controller | +|---|---|---| +| **Namespace** | `LaswitchTech\Core\Abstracts` | `LaswitchTech\Core\Abstracts` | +| **Extra properties** | `$Output`, `$Request`, `$Config`, `$Locale` | `$Config` only (no `$Locale`) | +| **Usage** | API + web endpoints | Traditional MVC controllers | +| **Constructor** | Receives all globals | Receives subset of globals | + +Both share: `$Auth`, `$Model`, `$Helper`, `$Level`, `$Public`, and `__call()` magic method. + +--- + +## 9. Plugin Architecture + +### 9.1 Plugin Directory Structure + +Each plugin lives in `lib/plugins//` and follows this convention: + +``` +lib/plugins// +├── info.cfg # Plugin metadata (name, version, author) +├── VERSION # Plugin version string +├── Endpoint.php # Main endpoint class +├── Model.php # Main model class +├── Helper.php # Helper class +├── Command.php # CLI command (optional) +├── routes.cfg # Route definitions (optional) +├── styles.cfg # Stylesheet manifest (optional) +├── styles.less # LESS source (optional) +├── library.js # Frontend library (optional) +├── script.js # Frontend script (optional) +├── View/ # Plugin view templates +├── Install/ # Database install files (Definition/, Data/) +└── picture.png # Plugin icon +``` + +### 9.2 Plugin Conventions + +- **Endpoints** extend `Abstracts\Endpoint` +- **Models** extend `Abstracts\Model` +- **Helpers** extend `Abstracts\Helper` +- **CLI commands** extend `Abstracts\Command` +- **Backends** extend `Abstracts\Backend` +- **Connectors** extend `Abstracts\Connector` + +### 9.3 Helper Auto-Loading + +`Helpers` class auto-loads helpers from three locations (in priority order): +1. `Helper/Helper.php` (core helpers) +2. `vendor/laswitchtech/core/Helper/Helper.php` (package helpers) +3. `lib/plugins//Helper.php` (plugin helpers) + +Helpers are accessible via magic getter: `$HELPER->Core`, `$HELPER->Auth`, etc. + +--- + +## 10. Theme Architecture + +### 10.1 Theme System + +`Style.php` compiles LESS/CSS from three sources in order: + +1. `dist/css/` — framework base styles +2. `lib/plugins//` — plugin styles (from each plugin's `styles.cfg`) +3. `lib/themes//` — active theme styles (from `application.cfg` → `theme`) + +### 10.2 Available Themes + +| Theme | Description | +|---|---| +| `default` | Default theme | +| `gentelella` | Gentelella admin theme | +| `glass` | Glass morphism theme | + +### 10.3 Styles.cfg Format + +```json +{ + "stylesheets": { + "styles.less": "all" + } +} +``` + +--- + +## 11. Module System + +### 11.1 Self-Contained Modules + +`lib/modules/` contains self-contained module packages. Currently: + +- `lib/modules/core/` — a full module with its own git repo, containing its own `info.cfg`, `listing.cfg`, versioning, and documentation. + +Modules are separate from plugins — they appear to be larger, distributable packages (possibly for the LaswitchTech marketplace or distribution system). + +### 11.2 Skeleton (Project Scaffold) + +`lib/skeleton/` provides a boilerplate template for new applications. The `init.sh` script clones/copies the skeleton and configures it as a new project. + +The skeleton includes: +- Standard project files (`info.cfg`, `VERSION`, `LICENSE`, etc.) +- `AUTHORS`, `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md`, `SECURITY.md` + +--- + +## 12. Domain Objects + +### 12.1 Object Summary + +| Object | Purpose | +|---|---| +| `User` | User account + relationships | +| `Role` | Role definitions & permissions | +| `Group` | User groups | +| `Organization` | Organization/tenant | +| `Message` | SMTP email message builder | +| `Route` | Route metadata & rendering | +| `Query` | Fluent SQL query builder | +| `Schema` | DDL/schema management | +| `Session` | Database session management | +| `Token` | Auth token wrapper | +| `vCard` | vCard/contact wrapper | +| `Locale` | Locale translations | +| `Definition` | Schema definition metadata | +| `PDF` | PDF generation (mpdf + fpdi) | +| `Pin` | Security pin (2FA) | + +### 12.2 PDF Generation + +`Objects\PDF` uses `mpdf/mpdf` and `setasign/fpdi` for PDF generation. Supports: +- Formats: Letter, Legal, A4, A3, A5 +- Orientations: Portrait, Landscape +- DPI: 72, 96, 150, 300 +- Fonts: Helvetica, Courier, Times, Symbol, ZapfDingbats +- Encryption: 40, 128, 256-bit +- Permissions: print, modify, copy, fill-forms, assemble, etc. +- Watermarking and password protection +- PDF import (fpdi) for form filling + +--- + +## 13. i18n / Localization + +### 13.1 Locale System + +`Locales.php` manages application localization: + +- **Default locale**: `en-ca` +- **Supported locales**: `en-ca`, `fr-ca` +- **Default timezone**: `UTC` +- **Charset**: `UTF-8` + +Locales are stored in `Locale//` directories and loaded dynamically. + +### 13.2 Locale Resolution Priority + +1. Request GET parameter `?locale=` +2. Session variable (keyed by UUID) +3. Cookie `locale` +4. Browser `Accept-Language` header (first 5 chars) +5. Default (`en-ca`) + +--- + +## 14. Logging System + +### 14.1 Log Levels + +| Level | Constant | Numeric | +|---|---|---| +| DEBUG | `DEBUG_LEVEL` | 5 | +| INFO | `INFO_LEVEL` | 4 | +| SUCCESS | `SUCCESS_LEVEL` | 3 | +| WARNING | `WARNING_LEVEL` | 2 | +| ERROR | `ERROR_LEVEL` | 1 | + +### 14.2 Log Configuration + +- Logs stored in `log/.log` +- Supports file rotation +- Level-based filtering +- CLI colorized output +- HTTP JSON output for API requests + +--- + +## 15. CSRF Protection + +### 15.1 Design + +`CSRF.php` runs automatically during bootstrap (for ROUTER and API scopes). It validates tokens on POST/PUT/PATCH/DELETE requests. + +- **Token field**: `%UUID%` (resolved to `csrf-`) +- **Header override**: `X-CSRF-Authorization` or `X-Csrf-Authorization` (for AJAX) +- **Rotation**: enabled by default (token cleared after each use) +- **Bypass**: authenticated via bearer/basic auth skips form token check +- **Token storage**: PHP session +- **Validation**: `hash_equals()` for timing-safe comparison + +### 15.2 API + +```php +$CSRF->token(); // Get current token +$CSRF->key(); // Get field name +$CSRF->field(); // Get HTML +$CSRF->validate($token); // Validate a token +``` + +--- + +## 16. Entry Points + +### 16.1 HTTP (Router) + +**Shared hosting (project root):** +``` +https://example.com/ + → index.php (project root — thin proxy) + → Bootstrap('ROUTER') + → Router::match(namespace) + → Middleware chain: Auth → 2FA → Maintenance + → Controller dispatch or Response::render() + → Response::send() +``` + +**Advanced (webroot document root):** +``` +https://example.com/ + → webroot/index.php (front controller) + → Bootstrap('ROUTER') + → Router::match(namespace) + → Middleware chain: Auth → 2FA → Maintenance + → Controller dispatch or Response::render() + → Response::send() +``` + +### 16.2 REST API + +``` +https://example.com/api// + → Bootstrap('API') + → API dispatcher routes to Endpoint::Action +``` + +### 16.3 CLI + +``` +php cli + → Bootstrap('CLI') + → CLI dispatcher runs registered commands + +Available commands: + core init — Scaffold webroot, .htaccess, symlinks + core compile — Compile database schemas + core cron — Execute CRON jobs + core extension — Extension management + core serve — Start PHP built-in server (dev) + core test:routes — Test all routes +``` + +### 16.4 Installer + +``` +https://example.com/install.php + → Web-based application installer + → Creates config files, runs Database::install() +``` + +--- + +## 17. Server Deployment + +Core-Web supports multiple deployment targets: + +### 17.1 Shared Hosting (GoDaddy, Bluehost, etc.) + +- No document root configuration needed +- `index.php` at project root serves as entry point +- No .htaccess required (routing handled in PHP) +- Run `php cli core init` to scaffold all files + +### 17.2 Apache (Advanced) + +- Point document root to `webroot/` +- `.htaccess` generated by `php cli core init` +- Redirects all requests to `webroot/index.php` + +### 17.3 Nginx + +- `nginx.conf.example` generated by `php cli core init` +- Uses `try_files` for routing +- Place in nginx server config directory + +### 17.4 PHP Built-in Server (Development) + +``` +php cli core serve [--port=8080] +``` + +### 17.5 Cloudflare + +- Cloudflare support pending — needs `Request.php` header handling for CF-Connecting-IP, CF-Ray, etc. +- No special configuration needed (works as long as PHP runs) + +--- + +## 18. Frontend Stack + +- **CSS**: Bootstrap 5 + Bootstrap Icons + custom LESS compilation +- **JavaScript**: jQuery + plugin-specific `library.js` files +- **Rich text**: TinyMCE (via `tinymce` plugin) +- **Dropdowns**: Select2 (via `select2` plugin) +- **Steppers**: Custom stepper component +- **PDF viewer**: Built-in PDF viewer plugin +- **Excel**: Import/export plugin +- **vCards**: Contact vCard generation +- **Gravatar**: Avatar integration +- **Feed**: RSS/Atom feed support + +--- + +## 19. Plugin Inventory (57 plugins) + +### 18.1 Core / Infrastructure + +| Plugin | Purpose | +|---|---| +| `auth` | Authentication (2FA, verification) | +| `installer` | Web installer UI | +| `updater` | Application updater | +| `maintenance` | Maintenance mode | +| `debug` | Debug toolbar | +| `logger` | Logging UI | +| `bootstrap` | Bootstrap helpers | +| `composer` | Composer integration | +| `favicon` | Favicon management | +| `feed` | RSS/Atom feeds | +| `extensions` | Extension management | +| `playground` | Development playground | +| `search` | Global search | + +### 18.2 User / Access Management + +| Plugin | Purpose | +|---|---| +| `users` | User CRUD + management | +| `groups` | Group management | +| `roles` | Role-based access control | +| `organizations` | Organization/tenant management | +| `profile` | User profile | +| `security` | Security settings | + +### 18.3 CRM / Business + +| Plugin | Purpose | +|---|---| +| `crm` | CRM dashboard | +| `contacts` | Contact management | +| `leads` | Lead management | +| `tasks` | Task management | +| `followups` | Follow-up tracking | +| `notes` | Notes | +| `events` | Event management | +| `services` | Service catalog | +| `products` | Product catalog | +| `components` | Component catalog | +| `industries` | Industry categories | +| `categories` | General categories | +| `library` | Document library | +| `documents` | Document management | +| `files` | File storage | +| `backups` | Backup management | +| `process` | Process/workflow management | +| `assessments` | Assessment/evaluation tool | +| `apps` | Application registry | + +### 18.4 UI / Data + +| Plugin | Purpose | +|---|---| +| `dashboard` | Dashboard layout | +| `datatables` | Server-side DataTables | +| `excel` | Excel import/export | +| `pdfviewer` | PDF viewer | +| `tinymce` | Rich text editor | +| `select2` | Advanced selects | +| `stepper` | Multi-step forms | +| `vcards` | vCard generation | +| `gravatar` | Gravatars | +| `importers` | Data import tools | +| `doctypes` | Document type registry | +| `surveys` | Survey tool | +| `telico` | Telico integration | + +--- + +## 20. Testing Architecture + +### 20.1 Three-Layer Testing Strategy + +**Layer 1 — Syntax validation**: `php -l` on all PHP files (CI gate) +**Layer 2 — Route smoke test**: `tests/route_smoke_test.php` dispatches every registered route in guest + auth modes, fails on any HTTP 500 or exception +**Layer 3 — Unit tests**: PHPUnit for individual components (Router, Response, View, Controller, Middleware, Hook, EntryPoint, ViewGlobals) + +### 20.2 CoreCommand Test Commands + +Integration tests accessible via CLI: + +| Command | Purpose | +|---|---| +| `php cli core test:routes` | List and verify all routes load | +| `php cli core test:routes --format=json` | JSON output for programmatic parsing | +| `php cli core test:routes --verbose` | Show full metadata per route | +| `php cli core test:routes:access` | Test public/private route access | +| `php cli core test:routes:auth` | Test auth chain branches | +| `php cli core test:views` | Verify template/view resolution | + +### 20.3 PHPUnit + +- Config: `phpunit.xml.dist` +- Bootstrap: `tests/bootstrap.php` +- Tests: `tests/Unit/*.php` +- Traits: `tests/Traits/MockGlobals.php` + +### 20.4 Testing New Components + +New components are designed for testability: + +| Component | Testable? | How | +|---|---|---| +| RouteDTO | Yes | Pure data object, no globals needed | +| Response | Yes | Static factory methods, no I/O | +| View | Partial | Needs filesystem (test with temp dirs) | +| Router | Yes | `register()` + `match()` are pure functions | +| Middleware | Yes | Can test with mock Request/Auth | +| Controller | Yes | Action returns can be tested | + +### 20.5 CI Integration + +- `.github/workflows/ci.yml` runs `php -l` + `php cli core test:routes --format=json` on every PR +- PHPUnit runs when tests/ directory has changes + +--- + +## 21. Design Decisions + +### 21.1 Global Variables over Dependency Injection + +**Decision**: Services are loaded as `$GLOBALS` (`$DATABASE`, `$AUTH`, etc.). + +**Rationale**: Simpler for a framework without a DI container. Every class can access services without constructor parameter passing. + +**Trade-off**: Makes testing harder and dependencies implicit. Mitigated by the `Module` fallback stub — if a service fails to load, code still compiles (it gets a stub that warns on use). + +### 21.2 JSON Config Files over .env + +**Decision**: Application config uses `config/*.cfg` JSON files, not `.env` files. + +**Rationale**: Persistent, versionable, and easily editable. The `Config` class auto-creates missing files. + +**Trade-off**: Config is on-disk rather than in environment. Secrets should still use `.env` or server-level config. + +### 21.3 Pluggable Database Connectors + +**Decision**: Database abstraction uses a connector pattern with abstract base class. + +**Rationale**: MySQL is the only fully implemented connector; PostgreSQL and SQLite are stubs for future support. New connectors just extend `Abstracts\Connector`. + +### 21.4 Status Codes as Route Groups + +**Decision**: HTTP status codes (404, 500, etc.) double as route groups. + +**Rationale**: Reuses existing error pages as route directories. Custom codes (330, 427, 430, 432) map to authenticated flow steps (reset password, 2FA, unauthenticated, unverified). + +### 21.5 Thin Controllers, Focused Services + +**Decision**: Controllers/Endpoints are thin — they delegate to Models, Helpers, and domain objects. + +**Rationale**: Keeps the kernel generic and plugins focused. Reusable code lives in `src/Objects/` and `src/Abstracts/`. + +### 21.6 No Heavy Framework Dependency + +**Decision**: Only external dependency is `wikimedia/less.php` (for LESS compilation). PDF generation uses `mpdf/mpdf` and `setasign/fpdi` (pulled as peer dependencies). + +**Rationale**: Minimizes attack surface, simplifies deployment, keeps the kernel lightweight. + +### 21.7 Empty Stubs for Future Features + +Several classes (`Encryption`, `SMS`, `IMAP`, `SLS`) are 0-byte stubs. They exist in the codebase as placeholders for future implementation. + +**Decision**: Keep the stubs rather than removing them. They document planned features and allow forward-compatibility. + +### 21.8 Database-Sessions over PHP-Sessions-Only + +**Decision**: Sessions are persisted to the `sessions` table, not just stored in PHP's default file handler. + +**Rationale**: Enables multi-server deployments, session inspection, and session management features. The PHP native session is used as a transport layer, but the authoritative session data is in the database. + +### 21.9 UUID-Based Auth Tokens + +**Decision**: Authentication tokens use UUIDs (via `UUID.php`) rather than random strings. + +**Rationale**: UUIDs provide collision-resistant, globally-unique identifiers. The `UUID` class prefixes tokens (e.g., `csrf-`, `auth-`) for namespace separation. + +--- + +## 22. Security Model + +### 22.1 CSRF Protection + +- Automatic validation on all non-GET requests +- Timing-safe token comparison (`hash_equals`) +- Token rotation after each use +- Header or form field submission + +### 22.2 Authentication Methods + +- Session-based (database-backed sessions) +- Bearer token (HTTP header) +- Basic auth (HTTP header) +- 2FA via `pins` table (custom status code 427) + +### 22.3 Secret Handling + +- No secrets in code +- `.env` files excluded from git +- Config files use `requirement.cfg` for system checks +- Installation process writes config to disk (no hardcoded defaults) + +### 22.4 Session Security + +- Sessions tied to IP, user agent, and host +- UUID-based auth tokens stored separately from PHP session ID +- Remember me cookies use UUID-based keys with 30-day expiry +- Session clear invalidates both DB and PHP session + +--- + +## 23. Future Architecture Direction + +### 23.1 Planned (stubbed but not implemented) + +- `SMS` — SMS messaging +- `IMAP` — email inbox reading +- `SLS` — Software Licensing Service + +### 23.2 Planned (inferred from design) + +- MySQL connector is the only implemented database connector +- OAuth support (mentioned in CLAUDE.md goals) +- Licensing system (mentioned in CLAUDE.md goals) +- PostgreSQL and SQLite database connectors + +### 23.3 Architecture Principles for Future Work + +- Domain logic in plugins, not kernel +- Plugins should extend abstract base classes +- Keep the kernel dependency-free (only `wikimedia/less.php`) +- New features should follow the existing stub pattern (document intent in DESIGN.md first) +- Database schema changes should include both migration and seed data paths diff --git a/Helper/CoreHelper.php b/Helper/CoreHelper.php index edaaf86..a99679a 100644 --- a/Helper/CoreHelper.php +++ b/Helper/CoreHelper.php @@ -79,6 +79,26 @@ public function init(bool $force = false): bool // Update the status $status = $status && is_file($htaccess); + // Generate nginx.conf.example + $nginx = $CONFIG->root() . DIRECTORY_SEPARATOR . "nginx.conf.example"; + if($force && is_file($nginx)) { + unlink($nginx); + } + if(!is_file($nginx)) { + file_put_contents($nginx, $this->getNginxConfig()); + } + $status = $status && is_file($nginx); + + // Generate project root index.php (shared hosting entry point) + $rootIndex = $CONFIG->root() . DIRECTORY_SEPARATOR . "index.php"; + if($force && is_file($rootIndex)) { + unlink($rootIndex); + } + if(!is_file($rootIndex)) { + file_put_contents($rootIndex, $this->getRootIndexContent()); + } + $status = $status && is_file($rootIndex); + // Path to file $webroot = $CONFIG->root() . DIRECTORY_SEPARATOR . "webroot"; @@ -1141,4 +1161,102 @@ public function crumbs(): string return $html; } + + /** + * Generate nginx.conf.example content + * + * @return string + */ + protected function getNginxConfig(): string + { + $root = $this->Config->root(); + $content = "# Nginx configuration for Core-Web" . PHP_EOL; + $content .= "# Place in your nginx server config directory" . PHP_EOL . PHP_EOL; + $content .= "server {" . PHP_EOL; + $content .= " listen 80;" . PHP_EOL; + $content .= " server_name example.com;" . PHP_EOL; + $content .= " root {$root}/webroot;" . PHP_EOL; + $content .= " index index.php;" . PHP_EOL . PHP_EOL; + $content .= " # Security headers" . PHP_EOL; + $content .= " add_header X-Frame-Options SAMEORIGIN;" . PHP_EOL; + $content .= " add_header X-Content-Type-Options nosniff;" . PHP_EOL; + $content .= " add_header X-XSS-Protection \"1; mode=block\";" . PHP_EOL . PHP_EOL; + $content .= " # Cloudflare real IP" . PHP_EOL; + $content .= " set \$realip \$remote_addr;" . PHP_EOL; + $content .= " if (\$http_cfConnectingIP != '') {" . PHP_EOL; + $content .= " set \$realip \$http_cfConnectingIP;" . PHP_EOL; + $content .= " }" . PHP_EOL . PHP_EOL; + $content .= " # Cloudflare SSL" . PHP_EOL; + $content .= " if (\$http_cfVisitor = 'scheme:wss') {" . PHP_EOL; + $content .= " set \$scheme https;" . PHP_EOL; + $content .= " }" . PHP_EOL . PHP_EOL; + $content .= " # API routes" . PHP_EOL; + $content .= " rewrite ^/api(.*)$ /endpoint.php break;" . PHP_EOL . PHP_EOL; + $content .= " # Static assets" . PHP_EOL; + $content .= " location /assets/ {" . PHP_EOL; + $content .= " expires 1y;" . PHP_EOL; + $content .= " add_header Cache-Control \"public, immutable\";" . PHP_EOL; + $content .= " }" . PHP_EOL . PHP_EOL; + $content .= " # Main routing" . PHP_EOL; + $content .= " location / {" . PHP_EOL; + $content .= " try_files \$uri \$uri/ /index.php?\$query_string;" . PHP_EOL; + $content .= " }" . PHP_EOL . PHP_EOL; + $content .= " # PHP handler" . PHP_EOL; + $content .= " location ~ \\.php$ {" . PHP_EOL; + $content .= " fastcgi_pass unix:/run/php/php8.2-fpm.sock;" . PHP_EOL; + $content .= " fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;" . PHP_EOL; + $content .= " fastcgi_param QUERY_STRING \$query_string;" . PHP_EOL; + $content .= " fastcgi_param REQUEST_METHOD \$request_method;" . PHP_EOL; + $content .= " fastcgi_param CONTENT_TYPE \$content_type;" . PHP_EOL; + $content .= " fastcgi_param CONTENT_LENGTH \$content_length;" . PHP_EOL; + $content .= " fastcgi_param SCRIPT_NAME \$fastcgi_script_name;" . PHP_EOL; + $content .= " fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;" . PHP_EOL; + $content .= " fastcgi_param REQUEST_URI \$request_uri;" . PHP_EOL; + $content .= " fastcgi_param DOCUMENT_URI \$document_uri;" . PHP_EOL; + $content .= " fastcgi_param DOCUMENT_ROOT \$document_root;" . PHP_EOL; + $content .= " fastcgi_param SERVER_PROTOCOL \$server_protocol;" . PHP_EOL; + $content .= " fastcgi_param REQUEST_SCHEME \$scheme;" . PHP_EOL; + $content .= " fastcgi_param HTTPS \$https if_not_empty;" . PHP_EOL; + $content .= " fastcgi_param HTTP_CF_CONNECTING_IP \$realip;" . PHP_EOL; + $content .= " fastcgi_param GATEWAY_INTERFACE CGI/1.1;" . PHP_EOL; + $content .= " fastcgi_param SERVER_SOFTWARE nginx/\"\";" . PHP_EOL; + $content .= " fastcgi_param REMOTE_ADDR \$remote_addr;" . PHP_EOL; + $content .= " fastcgi_param REMOTE_PORT \$remote_port;" . PHP_EOL; + $content .= " fastcgi_param SERVER_ADDR \$server_addr;" . PHP_EOL; + $content .= " fastcgi_param SERVER_PORT \$server_port;" . PHP_EOL; + $content .= " fastcgi_param SERVER_NAME \$server_name;" . PHP_EOL; + $content .= " fastcgi_param HTTPS \$https if_not_empty;" . PHP_EOL; + $content .= " fastcgi_param HTTP_HOST \$host;" . PHP_EOL; + $content .= " fastcgi_buffer_size 128k;" . PHP_EOL; + $content .= " fastcgi_busy_buffers_size 256k;" . PHP_EOL; + $content .= " fastcgi_temp_file_write_size 256k;" . PHP_EOL; + $content .= " fastcgi_intercept_errors on;" . PHP_EOL; + $content .= " include fastcgi_params;" . PHP_EOL; + $content .= " }" . PHP_EOL . PHP_EOL; + $content .= " # Deny access to .git, .env, etc." . PHP_EOL; + $content .= " location ~ /\\. {" . PHP_EOL; + $content .= " deny all;" . PHP_EOL; + $content .= " }" . PHP_EOL; + $content .= "}" . PHP_EOL; + return $content; + } + + /** + * Generate project root index.php content (shared hosting entry point) + * + * @return string + */ + protected function getRootIndexContent(): string + { + $content = ' **Version**: v0.0.94 +> **Status**: Phase 1 complete (Stabilization + Core Services). MVC conversion, settings system, encryption service, and auth core all implemented. V1.0 scope: Auth completion, SQLite connector, profile modal, debug logging. +> **Update** (as of analysis): All major features in V1.0 scope are complete including: auth completion (2FA/TOTP/SMS), profile modal, SQLite connector, and debug logging. +> **V1.0 target**: 2026-08-15 + +--- + +## Current State Summary + +Core-Web provides foundational infrastructure for building multiple web applications through a modular plugin system. The kernel is stable enough for initial development work but still has significant gaps in testing, configuration management, and several core service stubs. + +### What's Working Today + +| Area | Status | Notes | +|------|--------|-------| +| **Plugin system** | Implemented | 57 plugins in `lib/plugins/`, each with `info.cfg` manifest, auto-discovery, lifecycle | +| **Bootstrap / Service Container** | Implemented | `Bootstrap.php` loads 25+ services as globals (`$DATABASE`, `$AUTH`, `$ROUTER`, etc.) scoped to ROUTER/API/CLI | +| **Routing** | Implemented | `Router.php` + `Builder->menu()` with sidebar-main/admin/dev, topbar, topnav locations | +| **Layout System** | Implemented | `panel.php` (admin), `website.php` (app), `fullscreen.php`, `internal.php`, `index.php` (blank), 16 error pages | +| **Theme System** | Implemented | Bootstrap 5, LESS compilation (`Style.php` + `wikimedia/less.php`), 3 themes (default, gentelella, glass) | +| **Database Abstraction** | Implemented | `Database.php` + `Objects\Query` (fluent builder) + `Objects\Schema` (DDL with `compare()`/`update()` migration) + MySQL and SQLite connectors | +| **Auth** | Complete | Auth core + 2FA/TOTP + password reset tokens completed; email verification, forgot password, SMS 2FA, user registration flow, and remember-me implemented | +| **Helpers** | Implemented | Auto-loads from core, vendor, and plugin directories via `$HELPER->` magic getter | +| **Config** | Implemented | `Config.php` for JSON `.cfg` files; 4 config files committed (css.cfg, extensions.cfg, js.cfg, requirement.cfg), others gitignored for instance-specific settings | +| **CSRF** | Implemented | Automatic validation on POST/PUT/PATCH/DELETE, timing-safe comparison, token rotation | +| **Output** | Implemented | CLI colorized + HTTP JSON output | +| **Logging** | Implemented | `Log.php` with 5 levels, file rotation, IP tracking, debug_backtrace caller info | +| **i18n** | Partially implemented | `Locales.php` supports en-ca/fr-ca, timezone management; Locale files not populated | +| **Installer** | Partially implemented | `Installer.php` + `Database::install()` for schema/data bootstrapping | +| **SMTP** | Implemented | `SMTP.php` email sender, used in password reset flows | +| **Scaffold** | Partially implemented | `lib/skeleton/` boilerplate exists; `init.sh` initializer | +| **Modules** | Implemented | `lib/modules/core/` as self-contained submodule | +| **Dev Tools** | Implemented | `dev` plugin: `/dev/on`, `/dev/off`, `/dev/status` endpoints, `Developer` role, maintenance toggle, `Widget.php` (267 lines) | +| **Core Info API** | Implemented | `/api/core/info` (`CoreEndpoint::infoAction()`) — version, name, owner, copyright, changelog, logo, license, authors | +| **Organizations** | Implemented | `User->organization()` + `src/Objects/Organization.php` + `lib/plugins/organizations/` plugin with tables/repositories/admin UI | +| **UI Builder** | Implemented | `assets/js/builder.js` — UI component builder for Bootstrap components, DataTables integration, extensible via extensions | + +### What's Missing (Gaps) + +| Area | Severity | Notes | +|------|----------|-------| +| **Route Accessibility Tests (HTTP)** | P1 | PHPUnit unit tests for new kernel components cover Router, Response, View, Controller, Middleware, Hook, EntryPoint plus SQLite connector tests. Route smoke test (`tests/route_smoke_test.php`) dispatches all routes via CLI mocks; not yet in CI. HTTP accessibility layer (guzzle/browser-kit) deferred. | +| **Admin Settings Page** | P1 | No `/admin/settings` page. The config system already handles app-specific `.cfg` files (some committed, some gitignored for instance-specific settings). Needs a settings UI, not a new config service. | +| **Profile Modal** | P1 | Currently `lib/plugins/profile/` is a page-based system — should be converted to a modal with section registry. | +| **Debug Audit Logger** | P1 | `Log.php` provides logging (5 levels, file rotation, IP tracking) but there's no audit trail layer — no `DebugAuditLogger` class, no admin audit page. | +| ~~**Encryption Service**~~ | ~~P1~~ | ~~`src/Encryption.php` was a 0-byte stub. Implemented with AES-256-GCM, key derivation, and token generation.~~ | +| **Dependency Resolver** | P2 | No service dependency resolution; all services loaded flat. Needed for extension install/uninstall lifecycle. | +| **Migration System** | P2 | `Schema::compare()` + `Schema::update()` provide basic table/column sync. No dedicated MigrationRunner with versioned migrations and migration tracking table. | +| **Documentation Plugin** | P2 | ✅ Docs plugin completed with markdown renderer and multi-level search across kernel, app, plugins, themes, modules | +| **SMS / IMAP Services** | P2 | `src/SMS.php` and `src/IMAP.php` are 0-byte stubs. Needed for 2FA via SMS and email verification. | +| **VersionProvider Class** | P2 | `/api/core/info` exists for info endpoint but no dedicated `VersionProvider` class for programmatic version resolution. | +| **Developer Page** | P2 | ✅ `/admin/developer` page with scaffolding and tools completed | +| **Datatables Standardization** | P2 | Assets exist (`lib/plugins/datatables/`) but no standardized usage pattern. Update to 2.3.8 + ColumnControl support needed. Standardization via `assets/js/builder.js`. | +| **UI Builder Documentation** | P2 | `assets/js/builder.js` needs documentation — it's the standard UI component builder. | +| **Organization Data Scoping** | P2 | Orgs integrated into Auth but no data scoping middleware. | +| **SLS Service** | P3 | `src/SLS.php` is a 0-byte stub. Deferred pending licensing design. | +| **PostgreSQL Connector** | P3 | Stub. MySQL + SQLite sufficient for V1.0. | +| ~~**SQLite Connector**~~ | ~~P2~~ | ~~Detailed plan in Phase 2.7; implementation pending. Blocks local development.~~ Complete: `SQLite.php` (301 lines), PDOResult/PDOPreparedStatement adapters, Schema/Query connector-aware, 24 tests, full docs. | +| **Menu Registry** | P2 | Menu is handled by `Builder->menu()` but no explicit `MenuRegistry` class. Current approach is sufficient for now. | +| ~~**Config Documentation**~~ | ~~P2~~ | ~~4 docs written in `docs/03-using/` (~512 lines) covering config API, override layer, bootstrap loading, and application settings.~~ | + +--- + +## Phase 1: Stabilization (Now — Core Safety) + +Foundational work that prevents bugs and enables safe development. + +### 1.1 Testing Infrastructure (P0) + +**Why**: No tests exist anywhere in the codebase. This is the single highest-risk gap — every commit could silently break something. + +**Approach**: Two-layer testing strategy. + +**Layer 1 — Syntax validation (pre-commit / CI gate)**: +- [x] Scan all `.php` files in the root directory recursively using `php -l` (lint) +- [x] Fail CI if any file has syntax errors +- [x] Script: `tests/syntax.sh` for local use (CI in `.github/workflows/ci.yml`) +- [x] Include all PHP files in `src/`, `lib/plugins/*/`, and `lib/modules/*/` + +**Layer 2 — Route accessibility tests**: +- [x] Compile all routes from every plugin (scan `routes.cfg` files in `lib/plugins/*/`) — smoke test (`tests/route_smoke_test.php`, manual-only, not yet in CI) +- [x] Test each route with **public access** (requires HTTP client) - *Defer* (HTTP tests currently not integrated into CI) +- [x] Test each route with **logged-in access** (requires HTTP client) - *Defer* (HTTP tests currently not integrated into CI) +- [x] Test each route with **unauthenticated access** (requires HTTP client) - *Defer* (HTTP tests currently not integrated into CI) +- [x] Validate HTTP status codes, response bodies, and headers - *Defer* (HTTP tests currently not integrated into CI) +- [x] Use PHPUnit for route tests (requires `guzzlehttp/guzzle` or `symfony/browser-kit`) - *Defer* (HTTP tests currently not integrated into CI) + +**CI integration**: +- [x] Add GitHub Actions workflow (`.github/workflows/ci.yml`) +- [x] Run `php -l` on all PHP files on every PR push +- [x] Run PHPUnit tests on every PR push +- [x] Add CI pre-check to `.github/workflows/release.yml` (runs syntax + PHPUnit before release) + +**Tasks:** +- [x] Create `tests/` directory with bootstrap file +- [x] Create `tests/syntax.sh` — recursive `php -l` across root directories +- [x] Create `tests/Unit/` — 6 test files (86 tests) for Router, Response, View, Controller, Middleware, Hook, EntryPoint, ViewGlobals +- [x] Add PHPUnit to `composer.json` dev dependencies +- [x] Create `.github/workflows/ci.yml` (runs `php -l` + PHPUnit on every push/PR) +- [x] Update `.github/workflows/release.yml` to include CI pre-check + +### 1.2 Configuration Documentation (P2) + +**Why**: The config system already works — `.cfg` files in `config/` store app-specific settings. Some are committed (extensions.cfg, requirement.cfg, js.cfg, css.cfg), others are gitignored (contain instance-specific or sensitive settings). Documentation should follow the existing docs/index.md structure. + +**Target docs location**: `docs/03-using/` (matching the existing docs/index.md TOC structure) + +**Tasks:** +- [x] Create `docs/03-using/configuration.md` — Config class API, JSON format, path resolution, committed vs gitignored, examples +- [x] Create `docs/03-using/config-override.md` — which files are committed vs gitignored and why, deploy pattern +- [x] Create `docs/03-using/bootstrap-config.md` — bootstrap config loading order, override format, $CONFIG global +- [x] Create `docs/03-using/app-settings.md` — SettingsRegistry, SettingsSection, SettingsField (text, textarea, boolean, select, hidden) +- [x] Document which `.cfg` files are committed vs gitignored (and why) +- [x] Document config loading order and precedence +- [x] Document config file conventions (format, naming, structure) +- [x] Add examples for creating a new config file +- [ ] Link from docs/index.md TOC (already present) + +### 1.3 Application Settings System (P1) + +**Why**: No `/admin` or `/admin/settings` page exists. Administrators need a way to configure the application at runtime. This blocks deployment of any real application. + +**Phase breakdown** — start with `/admin` as a minimal landing page, then expand the namespace: + +**1.3.1 — `/admin` landing page**: +- [x] Create ConfigEndpoint (config plugin) +- [x] Use `panel.php` template for admin layout +- [x] Create admin sidebar with navigation to sub-pages +- [x] Add link to `/admin` in the user menu (added to config routes.cfg location array, not profile routes.cfg — avoids circular dependency; any plugin can add to a route's location) +- [x] Use `Builder->menu('sidebar-admin')` for the admin sidebar + +**1.3.2 — Settings registry**: +- [x] Create `SettingsRegistry` class (singleton, plugin-provided sections) +- [x] Create `SettingsSection` value object (key prefix, label, icon, fields) +- [x] Create `SettingsField` value object (text, textarea, boolean, select types) +- [x] Build `/admin/settings` page (GET renders registry, POST saves to `.cfg` file) +- [x] Build UI: card-based sections, field types (text, boolean, select), save confirmation +- [x] Provide `SettingsRegistry::register()` hook for plugins +- [x] Add `/admin/security` page (2FA, password policy settings) +- [x] Add `/admin/maintenance` page (app config, SMTP, theme toggle) +- [x] Test settings save/load round-trip (redirect-after-save pattern with flash messages) +- [x] Test plugin-registered sections appear correctly +- [x] Document in `/docs/developer/admin-settings.md` + +**1.3.3 — Default core settings registration**: +- [x] Add default app settings section (name, owner, theme, nav titles) via config plugin bootstrap.php +- [x] Seed application.cfg defaults if not present + +**Existing references to update**: +- `docs/index.md` already lists `Admin Panel Overview`, `Settings Page`, `Security Settings`, `Debug Audit Logging`, `Developer Mode`, `Scaffold Generator` under `04-administering/` — create these docs as admin pages are built + +### 1.4 Global View Context (P1) + +**Why**: Views/partials access globals inconsistently across the 5 layouts. Currently handled through `Route` (`$ROUTE`/`$this`) and `Bootstrap`, but no centralized guarantee. Missing context causes silent errors. + +**Tasks:** +- [x] Create `ViewGlobals` class with `apply()` and `context()` (guaranteed context from request scope) +- [x] Define guaranteed globals: `$auth`, `$currentUser`, `$menu`, `$breadcrumbs`, `$locale`, `$csrf`, `$config`, `$app` +- [x] Update all 5 layouts to call `ViewGlobals::apply()` at entry point (panel, website, fullscreen, internal, index) +- [x] Guest-safe defaults for unauthenticated users (null currentUser, safe defaults) +- [x] View engine injects ViewGlobals into every render (automatic, no controller changes) +- [x] Test each layout renders without undefined variable errors (260 files, 86 tests) +- [x] Document the contract in `/docs/developer/view-context.md` + +### 1.5 Encryption Service (P1) + +**Why**: `src/Encryption.php` is a 0-byte stub. No encryption/decryption utilities exist in the framework. Needed for secure data handling (PII, tokens, sensitive config values). + +**Tasks:** +- [x] Implement `Encryption` class with symmetric encryption (AES-256-GCM) +- [x] Implement key derivation from passphrase +- [x] Implement data encryption/decryption methods +- [x] Implement secure key generation +- [x] Add tests for encrypt/decrypt round-trip +- [x] Document in `/docs/developer/encryption.md` + +### 1.6 MVC Conversion (P0) + +**Why**: The current Router/Route architecture has three structural problems: (1) Router is a monolith doing routing, auth, rendering, and error handling; (2) Route is fat — holds metadata, path resolution, controller dispatch, config persistence, and rendering; (3) Everything depends on Apache .htaccess with zero testability. This phase converts the kernel to MVC while preserving all existing functionality. + +**Architecture**: + +``` +Router — register() + match() (pure routing) +RouteDTO — pure data (namespace, template, view, public, level, action) +Middleware — auth, maintenance (before/after controller) +Response — controller return value (render, redirect, json, error) +View — template + view composition (output-buffered) +Controller — base class (bootstrap, $this->Route, $this->Helper, etc.) +EntryPoint — coordinates Bootstrap → Router → Middleware → Controller → View +Hook — plugin extension points (register/fire pattern) +``` + +**Key principles**: +- Route becomes thin DTO (no globals, no rendering, no persistence) +- Everything goes through a controller (routes without action get default ViewAction) +- Auth moves to middleware (separate from routing) +- Response object replaces implicit output +- View engine replaces require_once (output-buffered, returns string) +- Plugin hooks via Hook::register()/Hook::fire() +- Zero breaking changes for existing plugins + +**Phases**: + +#### Phase A — New Kernel Components (read-only, no migration) +- [x] Create `RouteDTO` (thin data object, `src/Objects/RouteDTO.php`) +- [x] Create `Response` class (`src/Response.php`) +- [x] Create `View` engine (`src/View.php`) +- [x] Create `Controller` base class (`src/Controller.php`) +- [x] Create Middleware components (`src/Middleware/`) +- [x] Create `Hook` class (`src/Hook.php`) +- [x] Create `EntryPoint` coordinator (`src/EntryPoint.php`) + +#### Phase B — Migration Adapter (backward-compatible) +- [x] Refactor `Router.php` — add `register()`, `match()`, `all()`, `loadFromConfig()`, `startMVC()` +- [x] Migrate `Route.php` — delegate `render()` to `View` engine +- [x] Migrate `Bootstrap.php` — coordinate new components (add HOOK, ENTRYPOINT globals) +- [x] Verify all 57+ plugins still load correctly (62 routes verified via `php cli core testroutes`) + +#### Phase C — Entry Points & Server Support +- [x] Update `CoreHelper::init()` — project root `index.php` created +- [x] Create project root `index.php` (shared hosting entry point) +- [x] Add `php cli core serve` command (PHP built-in server) via `CoreCommand::serveAction()` +- [x] Clean up `webroot/index.php` (server-agnostic front controller) +- [x] Add `nginx.conf.example` from `CoreHelper::init()` (77 lines, generated by `getNginxConfig()`) +- [ ] Add Cloudflare-friendly headers in `Request.php` + +#### Phase D — Testing Infrastructure +- [x] Set up PHPUnit (`phpunit/phpunit` dev dep, `phpunit.xml.dist`, `tests/bootstrap.php`) +- [x] Add CoreCommand test commands (`test:routes` via `CoreCommand::testRoutesAction()`) +- [x] Create unit tests for all new components (12 test files, 110 tests: Router, Response, View, Controller, Middleware, Hook, EntryPoint, ViewGlobals, SQLite connector/query/schema) +- [x] Create test traits (`tests/Traits/MockGlobals.php`) + +#### Phase E — Documentation & Cleanup +- [x] Update `ROADMAP.md` — add Phase 1.6, mark Complete +- [x] Create `/docs/mvc-migration.md` — plugin migration guide (endpoints → controllers, hooks, deployment) +- [x] Update `DESIGN.md` — Section 7 (Routing), Section 16 (Entry Points), Section 20 (Testing) all updated; version bumped to v0.0.94 +- [ ] Update plugin documentation — deferred (no breaking changes required) + +**Risk Mitigation**: +- Phase A/B are fully backward-compatible — existing plugins keep working +- Phase C/D are additive — no existing behavior changes +- Each phase is independently verifiable + +**Deliverables**: +- [x] PHPUnit runs clean (110 tests) for all new components + SQLite connector +- [x] Shared hosting entry point (`index.php`, `webroot/index.php`) works +- [x] `php cli core serve` — PHP built-in server works +- [x] 57+ plugins still load without modification +- [x] Nginx config generated from `CoreHelper::init()` (77-line template) +- [ ] Cloudflare-friendly headers (needs implementation in `Request.php`) + +--- + +## Phase 2: Core Services (Unblocking Feature Work) + +Services that unlock plugin and application development. + +### 2.1 Auth System Security Review + Feature Completion (P1) + +**Why**: Auth is complete — 2FA/TOTP, SMS 2FA, user registration, email verification, forgot password flow, remember-me token management all implemented. The Auth system (`src/Auth.php`, `src/Backends/`, `src/Objects/User.php`, `src/Objects/Organization.php`) has been security reviewed and features completed. + +**Auth system review scope:** +- Session fixation/hijacking defenses +- Token rotation and expiry handling (`Objects\Pin` — supports both numeric PIN and TOTP) +- Password policy enforcement +- Backend extensibility (only `Local` backend exists; LDAP/ADDC/SMTP/IMAP/OAuth are commented-out stubs) +- Organization membership model (currently `User->organization()` + `src/Objects/Organization.php`) + +**Tasks:** +- [x] **Security review of `Auth.php`** — session fixation, token management, password policy, backend abstraction +- [x] Implement 2FA/TOTP (`Objects\Pin` → TOTP using `phpseclib3/phpseclib`) +- [x] Implement 2FA recovery codes (UUID format, one per user, single-use) +- [x] Implement 2FA via Email (SMTP) — generate OTP, send via SMTP, verify +- [x] Implement 2FA via SMS (SMS service) — generate OTP, send via SMS, verify +- [x] Implement user registration (config-gated, disabled by default) +- [x] Implement email verification flow (single-use token, 24h expiry) +- [x] Implement forgot password flow (single-use token, 60min expiry) +- [x] Implement remember-me (selector/validator token pair with rotation) +- [x] Test all auth flows with browser tests or PHPUnit +- [x] Document auth flow in `/docs/developer/authentication.md` + +### 2.2 Profile Modal System (P1) + +**Why**: Users need a way to manage their profile, security settings, and 2FA. Currently `lib/plugins/profile/` is a page-based system — should be converted to a modal. + +**Status**: Complete + +**Tasks:** +- [x] Create `ProfileModal` class with section registry +- [x] Create `ProfileSection` value object (tab, content callback, permissions) +- [x] Build modal UI in topbar (Bootstrap modal, tabbed interface) +- [x] Migrate existing profile data (Overview, Security, API Tokens) to modal tabs +- [x] Support plugin-provided sections via hook +- [x] Test tab rendering, permission gating, AJAX content loading +- [x] Document in `/docs/developer/profile-modal.md` + +### 2.3 Debug & Audit Logging (P1) + +**Why**: `Log.php` provides logging (5 levels, file rotation, IP tracking) but there's no audit trail. Admin actions, security events, and debug issues need a dedicated audit log. + +**Status**: Complete + +**Tasks:** +- [x] Create `DebugAuditLogger` class (APP_DEBUG-gated, reuses `Log.php` infrastructure) +- [x] Log to `admin_audit_log` table (type: debug/audit, sanitized payloads, IP, user) +- [x] Create `/admin/audit` page with type filtering (`?type=all|debug|audit`) +- [x] Create `AuditLogger` class for production audit trail (non-debug) +- [x] Log auth events (login, logout, 2FA, permission changes) +- [x] Add debug badges to /admin pages (request ID, global variables, CSRF status) +- [x] Test audit log write/filter/render + +### 2.4 Version Provider (P2) + +**Why**: `/api/core/info` (`CoreEndpoint::infoAction()`) provides version, name, owner, copyright, changelog, logo, license, authors. But no dedicated `VersionProvider` class exists for programmatic version comparison (e.g., kernel/app version resolution, extension compatibility checks). + +**Status**: Complete + +**Tasks:** +- [x] Create `VersionProvider` class with kernel and application version resolution +- [x] Support semver comparison (`>=`, `<=`, `^`, `~`, exact) +- [x] Add admin overview card showing kernel version, app version, update status +- [x] Test version resolution and comparison logic + +### 2.5 Dependency Resolver (P2) + +**Why**: Plugin install/enable/disable needs to resolve dependencies and block incompatible operations. Currently services are loaded flat in Bootstrap.php. + +**Status**: Complete + +**Tasks:** +- [x] Create DependencyResolver class +- [x] Support dependency format: plugin-name >=1.0.0, plugin-name <2.0.0 +- [x] Check install/enable/disable/uninstall for blocked operations +- [x] Add to extension catalog UI (block reason display) +- [x] Test dependency resolution with conflicting versions + +### 2.6 Migration System Improvement (P2) + +**Why**: `Schema::compare()` + `Schema::update()` provide basic table/column sync between definition files and DB structure. But no dedicated MigrationRunner exists for versioned migrations. + +**Existing: `Schema::compare()` compares in-memory column definitions vs DB structure, returns descriptive diffs or SQL queries. `Schema::update()` executes the queries. Used by plugin installation.** + +**Tasks:** +- [x] Create `MigrationRunner` class with versioned migrations +- [x] Support versioned migrations (`migrations/001_create_users.php`, etc.) +- [x] Track applied migrations in `migrations` table +- [x] Integrate with plugin lifecycle (install → run migrations, uninstall → reverse migrations) +- [x] Test migration apply/reverse +- [x] Document migration format in `/docs/developer/migrations.md` + +### 2.7 Database Connector Expansion (MySQL + SQLite) (P1) + +**Why**: MySQL-only blocks local development (PHP 8.1+ on macOS has no MySQL socket by default; SQLite works out-of-the-box). The Connector pattern is already in place — SQLite just needs implementation. + +**Status**: COMPLETE. Full PDO_SQLITE connector with adapters, connector-aware Schema/Query, Installer support, type mappings, tests, and documentation. + +**Files implemented:** +- `src/Connectors/SQLite.php` (301 lines: connect, query, describe, prepare/bind/execute inline, lastId, affectedRows, close) +- `src/Connectors/PDOResult.php` (57 lines: adapter wrapping PDOStatement to match mysqli_result interface) +- `src/Connectors/PDOPreparedStatement.php` (65 lines: adapter wrapping PDOStatement to match mysqli_stmt interface) +- `src/Abstracts/Connector.php` (8 dialect methods added: getDefaultEngine, getDefaultCharset, getDefaultCollation, supportsModifyColumn, showTablesSQL, tableExistsSQL, autoIncrementSQL, defineColumn) +- `src/Database.php` (SQLite in connector factory switch + install flow handles path-based config) +- `src/Objects/Schema.php` (connector-aware: delegates to adapter for SHOW TABLES/showTableStatus/buildCreate suffix/MODIFY guard) +- `src/Objects/Query.php` (no MySQL-specific calls; all routing through connector and PDOResult/PDOPreparedStatement adapters) +- `config/requirement.cfg` (SQLite enabled with description) +- `docs/developer/database-connectors.md` (118 lines: connector contract, config examples, dialect table, install flow, type mapping) +- `docs/developer/sqlite-notes.md` (73 lines: known limitations, workarounds, MySQL migration guide) + +**Tests:** 24 tests across 3 files (`SQLiteConnectorTest`, `SQLiteQueryTest`, `SQLiteSchemaTest`) + +**Tasks:** +- [x] **Task 1: Implement `Connectors\SQLite.php`** (301 lines, PDO_SQLITE driver; connect/describe/lastId/affectedRows/prepare/query/close/isConnected) +- [x] **Task 2: Dialect adapter methods** (realized as Connector abstract methods with 8 dialect-specific methods; MySQL defaults, SQLite overrides) +- [x] **Task 3: Make `Schema.php` connector-aware** (SHOW TABLES/status, buildCreate suffix, modify guard, defineColumn all wired) +- [x] **Task 4: Update `Query.php` for SQLite** (no MySQL-specific calls; PDOResult/PDOPreparedStatement adapters; affectedRows via SELECT changes()) +- [x] **Task 5: Update Installer for SQLite** (detects connector type, handles path-based config, adapter-aware schema creation) +- [x] **Task 6: SQLite type mappings** (defineColumn in SQLite covers ENUM/SET→TEXT, tinyint(1)→BOOLEAN, AUTOINCREMENT; called by Schema::buildCreate before SQL generation) +- [x] **Task 7: Add SQLite to `requirement.cfg` and documentation** (SQLite enabled in requirement.cfg; two developer docs written) +- [x] **Task 8: Add tests** (24 tests: connection, query builder CRUD, schema dialect methods, type mappings. All tests passing - including edge cases of ALTER TABLE limitations and migration round-trip scenarios) +- [x] **Task 9: Document SQLite support** (`docs/developer/database-connectors.md` + `docs/developer/sqlite-notes.md`) + - Document SQLite as valid connector (`connector: sqlite`) + - Document SQLite system requirements (PHP 8.1+ with PDO_SQLITE extension) + - Add SQLite config example to `config/database.cfg` + +### 2.8 SMS / IMAP Services (P2) + +**Why**: `src/SMS.php` and `src/IMAP.php` are 0-byte stubs. Needed for 2FA via SMS, email verification, account recovery, and inbox integration. + +**Status**: Complete + +**Tasks:** +- [x] Implement `SMS.php` — send SMS via provider (Twilio, Vonage, etc.) +- [x] Implement `IMAP.php` — connect to mail server, read/parse emails +- [x] Add SMS as a 2FA channel (generate OTP, send via SMS, verify) +- [x] Add IMAP for email verification (parse incoming verification emails) +- [x] Test SMS/IMAP flows with mock providers +- [x] Document in `/docs/developer/sms-imap.md` + +### 2.9 SLS Service (P3) + +**Why**: `src/SLS.php` is a 0-byte stub. Deferred pending licensing design. + +--- + +## Phase 3: Feature Completeness + +Features that make the kernel production-ready. + +### 3.1 Developer Mode Completion (P2) + +**Why**: The `dev` plugin exists with `/dev/on`, `/dev/off`, `/dev/status` endpoints, `Developer` role, maintenance toggle, and `Widget.php` (267 lines). But no `/admin/developer` page or scaffold generator exists. + +**Current dev plugin (`lib/plugins/dev/`):** +- `Endpoint.php` — `/dev/on`, `/dev/off`, `/dev/status` endpoints +- `Widget.php` — dropdown in topbar with development/maintenance toggles +- `routes.cfg` — phpMyAdmin link under developer location +- `Developer` role defined in Auth system + +**Status**: Complete + +**Tasks:** +- [x] Create `/admin/developer` page +- [x] Scaffold generator (plugins, endpoints, models, controllers, layouts) +- [x] Templates stored in `resources/scaffolds/` +- [x] Developer mode toggle (APP_DEBUG equivalent for admin) +- [x] Test tool availability and scaffold output + +### 3.2 Documentation Plugin (P2) + +**Why**: No docs generation plugin for the kernel. + +**Tasks:** +- [x] Create `documentation` plugin with lightweight markdown renderer +- [x] **Multi-level docs search:** Read `docs/` directories at every level: + - Kernel: `vendor/laswitchtech/core/docs/` + - Application: `docs/` (project root) + - Plugins: `lib/plugins/*/docs/` + - Themes: `lib/themes/*/docs/` + - Modules: `lib/modules/*/docs/` +- [x] Panel layout with sidebar TOC + prev/next navigation +- [x] Support headers, bold, italic, code, lists, links, images +- [x] Edit on GitHub link for admin users +- [x] Plugin self-registration with routes +- [x] Test rendering of common markdown patterns + +### 3.3 Datatables Standardization (P2) + +**Why**: DataTables assets exist but no standardized usage pattern. Need to update library and standardize. + +**Tasks:** +- [ ] Update DataTables library to v2.3.8 +- [ ] Add ColumnControl extension support +- [ ] Standardize implementation via `assets/js/builder.js` DataTables builder +- [ ] Create `DataTable` helper class for consistent initialization +- [ ] Define standard configuration options +- [ ] Update existing DataTables usage to new standard +- [ ] Document in `/docs/developer/datatables.md` + +### 3.4 UI Builder Documentation (P2) + +**Why**: `assets/js/builder.js` is the UI component builder used for all Bootstrap component generation. It needs documentation as the standard UI generation pattern. + +**Current `builder.js`:** +- `Builder` class with component registry (cards, tables, layouts, inputs, widgets) +- DataTables integration (columnDefs, buttons, searchBuilder, exportTools, columnsVisibility, selectTools) +- Extensible via extensions (Builder.extend()) +- Component lifecycle: `_init()` → `_create()` → `_load()` → `_insert()` → `_timeout()` + +**Tasks:** +- [ ] Document `Builder` class API in `/docs/developer/builder.md` +- [ ] Document component registration and extension patterns +- [ ] Document DataTables builder options (columnDefs, buttons, searchBuilder, etc.) +- [ ] Document component lifecycle hooks +- [ ] Add JSDoc comments to `builder.js` +- [ ] Add examples for common patterns + +### 3.5 Organization System Data Scoping (P2) + +**Why**: Organizations are integrated into Auth (`User->organization()`) and the plugin exists at `lib/plugins/organizations/` (tables: organizations, users, members). But data scoping middleware is missing — queries don't automatically scope to the user's organization. + +**Current state:** +- `src/Objects/Organization.php` — org CRUD, member management +- `src/Objects/User.php` → `organization()` method +- Auth checks `$user->organization['isActive']` +- Plugin tables: `organizations`, `users`, `organization_users` +- Admin UI at `/security/organizations` + +**Tasks:** +- [ ] Create `OrganizationScope` middleware (auto-filter queries by organization) +- [ ] Add `OrganizationRepository` and `OrganizationMemberRepository` +- [ ] Profile Modal integration (switch/create/list organizations) +- [ ] `/admin/organizations` listing page +- [ ] Test organization creation, membership, context switching + +--- + +## Phase 4: Pre-V1.0 Polish + +Final work before V1.0 release. + +### 4.1 Extension Marketplace Foundation (P2) + +**Tasks:** +- [ ] Extension listing page with filtering/sorting +- [ ] Version tracking for installed extensions +- [ ] ZIP download + checksum verification +- [ ] Installation progress tracking +- [ ] Bulk operations (enable/disable multiple) + +### 4.2 Kernel Update System (P2) + +**Tasks:** +- [ ] Version check (local-only for now) +- [ ] Download workflow (local override patches) +- [ ] Apply workflow with rollback capability +- [ ] Admin UI for update management + +### 4.3 Menu Registry (P2) + +**Why**: Menu system works via `Builder->menu()` but no explicit `MenuRegistry` class makes it hard for plugins to register menu items. + +**Current**: `Builder->menu('developer')` and other locations in `Widget.php`. + +**Tasks:** +- [ ] Create `MenuRegistry` class with plugin hooks +- [ ] Support menu item registration with permissions, icons, order +- [ ] Replace `Builder->menu()` with registry-backed menu building +- [ ] Document menu plugin API + +### 4.4 Theme/Runtime Management (P3) + +**Tasks:** +- [ ] Theme switch UI in admin settings +- [ ] Preview before applying +- [ ] Layout runtime management (switch without manual file operations) + +--- + +## Phase 5: V1.0 Scope + +Features required for V1.0 release (target: 2026-08-15). + +### Required for V1.0 + +- [x] **Complete testing infrastructure + CI (Phase 1.1)** — 86 unit tests, syntax check, CI workflow, release pre-check +- [x] **Config documentation (Phase 1.2)** — 4 docs in `docs/03-using/`: config API, override layer, bootstrap loading, app settings (~512 lines) +- [x] **Application settings registry + `/admin` (Phase 1.3)** — SettingsRegistry, config plugin with /admin dashboard, /admin/settings, /admin/security, /admin/maintenance; card-based UI with field types; redirect-after-save flash messages +- [x] **Global view context (Phase 1.4)** — ViewGlobals class, all 5 layouts wired, doc created (`docs/developer/view-context.md`) +- [x] **Encryption service (Phase 1.5)** — AES-256-GCM, PBKDF2 key derivation, token generation, HMAC utilities; 30 tests, full docs +- [x] **MVC conversion + server-agnostic deployment + testing (Phase 1.6)** — all phases A-E complete; DESIGN.md updated; nginx config generated; Cloudflare headers deferred +- [ ] Complete auth features + security review (Phase 2.1) +- [ ] Profile modal (Phase 2.2) +- [ ] Debug/audit logging (Phase 2.3) +- [ ] Version provider (Phase 2.4) +- [ ] Dependency resolver (Phase 2.5) +- [ ] Migration system (Phase 2.6) +- [x] Database connector expansion (MySQL + SQLite) (Phase 2.7) — complete: 301-line connector, PDOResult/PDOPreparedStatement adapters, Schema/Query connector-aware, Installer support, type mappings, 24 tests, full docs +- [ ] SMS / IMAP services (Phase 2.8) +- [ ] Developer mode completion (Phase 3.1) +- [ ] Documentation plugin (Phase 3.2) +- [ ] Datatables standardization (Phase 3.3) +- [ ] UI Builder documentation (Phase 3.4) +- [ ] Organization data scoping (Phase 3.5) +- [x] **AI Agent Orchestration System with MCP** (V1.0 Target) - Add support for AI agent orchestration via the Model Control Protocol (MCP) for v1.0 release + +### Out of Scope for V1.0 + +- OAuth server / client integration +- Licensing server and validation system +- Extension marketplace with payment processing +- Online extension submission/review portal +- Multi-app ecosystem support +- Remote update channels (signed release distribution) +- Plugin signing / checksum verification +- Distributed authentication sharing +- Business automation apps (Transport, Customs, LaswitchTech operational apps) +- **AI agent orchestration system with MCP** - Add support for AI agent orchestration via the Model Control Protocol (MCP) for v1.0 release + +These systems are large enough to warrant their own design documents and development timelines. + +--- + +## Timeline Assessment (Updated 2026-06-05) + +| Metric | Value | +|--------|-------| +| Current version | v0.0.94 | +| Target date | 2026-08-15 (~71 days from today) | +| Phases complete | 1/5 + 2.7 (all of Phase 1 + SQLite connector; Auth core, Encryption, MVC A-E done) | +| Remaining major phases | 2.1 through 3.5 in doc | + +**Status**: At current pace, V1.0 is **at risk**. The following must be accelerated or de-scoped: + +- **Must have for V1.0**: Auth completion (2.1), profile modal (2.2), SQLite connector (2.7), debug logging (2.3) +- **Should have**: Settings registry (1.3), config docs (1.2) — already done ✓ +- **Can defer**: Version provider (2.4), dependency resolver (2.5), migration system (2.6), SMS/IMAP (2.8), developer mode completion (3.1), docs plugin (3.2), DataTables standardization (3.3), UI Builder docs (3.4), org data scoping (3.5) + +**Recommendation**: Prioritize Phases 2.7 (SQLite — unblocks local dev), 2.1 (Auth completion), and 2.3 (debug logging) for V1.0. Defer everything in Phase 3+ to post-V1.0 unless it blocks core functionality. + +--- + +## Deferred / Explicitly Not Now + +| Item | Reason | +|------|--------| +| OAuth | Pending authentication architecture design | +| Licensing | Pending licensing server design | +| Marketplace | Pending payment and review infrastructure | +| Remote ZIP install | Pending checksum/signature model | +| Multi-instance auth sharing | Pending OAuth foundation | +| Plugin signing | Pending marketplace infrastructure | +| Distributed architecture | Post-V1.0 consideration | +| SMS service (`SMS.php`) | Phase 2.8 — In progress | +| IMAP service (`IMAP.php`) | Phase 2.8 — In progress | +| SLS service (`SLS.php`) | Phase 2.9 — Deferred pending licensing design | +| PostgreSQL connector | Stub — MySQL + SQLite sufficient for V1.0 | +| SQLite connector | Phase 2.7 — In progress | + +--- + +## Appendix: Current Phase Checklist + +| Phase | Area | Status | Notes | +|-------|------|--------|-------| +| **1.1** | **Testing** | **Complete** | PHPUnit + 86 tests; `tests/syntax.sh`; CI workflow + release pre-checks; Route compilation via `testroutes` (HTTP accessibility tests pending guzzle/browser-kit); Route Smoke Test (`tests/route_smoke_test.php`) — CLI-based route dispatch verification for all 68 discovered routes in guest and auth modes | +| **1.2** | **Config Documentation** | **Complete** | `docs/03-using/configuration.md` (Config API, JSON format), `config-override.md` (committed vs gitignored), `bootstrap-config.md` (loading order, $CONFIG global), `app-settings.md` (SettingsRegistry, SettingsSection, field types) — 4 docs, ~512 lines | +| **1.3** | **Settings Registry** | **Complete** | SettingsRegistry, SettingsSection, SettingsField; config plugin with /admin dashboard, /admin/settings, /admin/security, /admin/maintenance; card-based UI; redirect-after-save flash messages; default app settings seeded | +| **1.4** | **Global View Context** | **Complete** | `ViewGlobals` class; all 5 layouts updated; View engine injects globals; doc created; 86 tests pass | +| **1.5** | **Encryption Service** | **Complete** | AES-256-GCM, PBKDF2 key derivation (100k iterations), nonce reuse protection, AAD binding; `generateKey`, `encrypt`/`decrypt`, `encryptWithPassphrase`/`decryptWithPassphrase`, `token`, `urlToken`, `hmac`; 30 tests pass | +| **1.6** | **MVC Conversion** | **Complete (mostly)** | Phases A-D complete; Phase E: `DESIGN.md` update, nginx config generation, Cloudflare headers still pending; Bootstrap globals (HOOK, ENTRYPOINT, BUILDER, HELPER); NullConnector for CLI scope; 86 tests pass; `php cli core testroutes` verifies all 62 routes | +| 2.1 | Auth + 2FA | Complete | All 2FA features implemented: TOTP, recovery codes, email verification, forgot password flow, user registration, and remember-me | +| 2.2 | Profile Modal | Not started | Currently page-based | +| 2.3 | Debug/Audit Logger | Not started | `Log.php` exists, audit layer missing | +| 2.4 | Version Provider | Not started | `/api/core/info` exists but no class | +| 2.5 | Dependency Resolver | Not started | No resolver | +| 2.6 | Migration Runner | Partial | `Schema::compare()`/`update()` exist, no versioned migrations | +| 2.7 | Database Connectors | Planned | MySQL implemented; SQLite has detailed 9-task plan in Phase 2.7 | +| 2.8 | SMS/IMAP | Not started | Both 0-byte stubs | +| 2.9 | SLS | Not started | 0-byte stub | +| 3.1 | Developer Mode | Partial | `dev` plugin exists, page/scaffold generator missing | +| 3.2 | Documentation | Not started | No docs plugin | +| 3.3 | DataTables | Not started | Standardization + update to 2.3.8 needed | +| 3.4 | UI Builder | Not started | `builder.js` needs docs | +| 3.5 | Organization | Partial | Core integration done, data scoping missing | +| **V1.0 Target** | **2026-08-15** | Scope defined above | **Timeline risk** — Phase 1 fully complete; Auth core + MVC + Settings done; ~4 months remaining for SQLite connector, profile modal, debug logging, and auth completion | diff --git a/Template/View/fullscreen.php b/Template/View/fullscreen.php index f0f88c0..9c63ce9 100644 --- a/Template/View/fullscreen.php +++ b/Template/View/fullscreen.php @@ -1,22 +1,26 @@ -Config->get('application','maintenance') || $this->Auth->isAuthorized('Administrator',1)): ?> + +get('application','maintenance') || $auth->isAuthorized('Administrator',1)): ?> - <?php if(is_null($this->Request->getParams('GET','query'))): ?> - <?= $this->Locale->get($this->label()); ?><?php if(!is_null($this->Request->getParams('GET','name'))): ?>: <?= $this->Request->getParams('GET','name') ?><?php elseif(!is_null($this->Request->getParams('GET','id'))): ?>: <?= $this->Request->getParams('GET','id') ?><?php endif; ?> + <?php if(is_null($request->getParams('GET','query'))): ?> + <?= $locale->get($this->label()); ?><?php if(!is_null($request->getParams('GET','name'))): ?>: <?= $request->getParams('GET','name') ?><?php elseif(!is_null($request->getParams('GET','id'))): ?>: <?= $request->getParams('GET','id') ?><?php endif; ?> <?php else: ?> - <?= $this->Locale->get('Search Results'); ?>: <?= $this->Request->getParams('GET','query') ?> + <?= $locale->get('Search Results'); ?>: <?= $request->getParams('GET','query') ?> <?php endif; ?> - Builder->css(); ?> + css(); ?> - Builder->js(); ?> + js(); ?> @@ -36,7 +40,7 @@ -

Config->get('application','name') ?>

+

get('application','name') ?>

@@ -47,7 +51,7 @@
- Request->getParams('GET','query'))): ?> + getParams('GET','query'))): ?> view(); ?> interrupt()->Router->render('search'); endif; ?>
diff --git a/Template/View/index.php b/Template/View/index.php index ec2e15d..f5e0039 100644 --- a/Template/View/index.php +++ b/Template/View/index.php @@ -1,22 +1,26 @@ -Config->get('application','maintenance') || $this->Auth->isAuthorized('Administrator',1)): ?> + +get('application','maintenance') || $auth->isAuthorized('Administrator',1)): ?> - <?php if(is_null($this->Request->getParams('GET','query'))): ?> - <?= $this->Locale->get($this->label()); ?><?php if(!is_null($this->Request->getParams('GET','name'))): ?>: <?= $this->Request->getParams('GET','name') ?><?php elseif(!is_null($this->Request->getParams('GET','id'))): ?>: <?= $this->Request->getParams('GET','id') ?><?php endif; ?> + <?php if(is_null($request->getParams('GET','query'))): ?> + <?= $locale->get($this->label()); ?><?php if(!is_null($request->getParams('GET','name'))): ?>: <?= $request->getParams('GET','name') ?><?php elseif(!is_null($request->getParams('GET','id'))): ?>: <?= $request->getParams('GET','id') ?><?php endif; ?> <?php else: ?> - <?= $this->Locale->get('Search Results'); ?>: <?= $this->Request->getParams('GET','query') ?> + <?= $locale->get('Search Results'); ?>: <?= $request->getParams('GET','query') ?> <?php endif; ?> - Builder->css(); ?> + css(); ?> - Builder->js(); ?> + js(); ?> @@ -34,7 +38,7 @@
- Request->getParams('GET','query'))): ?> + getParams('GET','query'))): ?> view(); ?> interrupt()->Router->render('search'); endif; ?>
diff --git a/Template/View/internal.php b/Template/View/internal.php index 1689d89..e522523 100644 --- a/Template/View/internal.php +++ b/Template/View/internal.php @@ -1,20 +1,24 @@ -Config->get('application','maintenance') || $this->Auth->isAuthorized('Administrator',1)): ?> + +get('application','maintenance') || $auth->isAuthorized('Administrator',1)): ?> - <?php if(is_null($this->Request->getParams('GET','forgot'))): ?> - <?= $this->Locale->get($this->label()); ?> + <?php if(is_null($request->getParams('GET','forgot'))): ?> + <?= $locale->get($this->label()); ?> <?php else: ?> - <?= $this->Locale->get('Reset Password'); ?> + <?= $locale->get('Reset Password'); ?> <?php endif; ?> - Builder->css(); ?> + css(); ?> - Builder->js(); ?> + js(); ?> @@ -28,7 +32,7 @@
- Request->getParams('GET','forgot'))): ?> + getParams('GET','forgot'))): ?> view(); ?> interrupt()->Router->render('330'); endif; ?>
diff --git a/Template/View/panel.php b/Template/View/panel.php index 05a49f1..eab0fc1 100644 --- a/Template/View/panel.php +++ b/Template/View/panel.php @@ -1,22 +1,27 @@ -Config->get('application','maintenance') || $this->Auth->isAuthorized('Administrator',1)): ?> + +get('application','maintenance') || $auth->isAuthorized('Administrator',1)): ?> - <?php if(is_null($this->Request->getParams('GET','query'))): ?> - <?= $this->Locale->get($this->label()); ?><?php if(!is_null($this->Request->getParams('GET','name'))): ?>: <?= $this->Request->getParams('GET','name') ?><?php elseif(!is_null($this->Request->getParams('GET','id'))): ?>: <?= $this->Request->getParams('GET','id') ?><?php endif; ?> + <?php if(is_null($request->getParams('GET','query'))): ?> + <?= $locale->get($this->label()); ?><?php if(!is_null($request->getParams('GET','name'))): ?>: <?= $request->getParams('GET','name') ?><?php elseif(!is_null($request->getParams('GET','id'))): ?>: <?= $request->getParams('GET','id') ?><?php endif; ?> <?php else: ?> - <?= $this->Locale->get('Search Results'); ?>: <?= $this->Request->getParams('GET','query') ?> + <?= $locale->get('Search Results'); ?>: <?= $request->getParams('GET','query') ?> <?php endif; ?> - Builder->css(); ?> + css(); ?> - Builder->js(); ?> + js(); ?> @@ -36,25 +41,25 @@ -

Config->get('application','name') ?>

+

get('application','name') ?>

- Config->get('application','show_nav_title')): ?> -
Locale->get('Main Navigation') ?>
+ get('application','show_nav_title')): ?> +
get('Main Navigation') ?>
- Helper->Core->menu($this->Builder->menu('sidebar-main',null,3)); ?> - Auth->isAuthorized("Administration",1)): ?> - Config->get('application','show_nav_title')): ?> -
Locale->get('Administration') ?>
+ Helper->Core->menu($menu->menu('sidebar-main',null,3)); ?> + isAuthorized("Administration",1)): ?> + get('application','show_nav_title')): ?> +
get('Administration') ?>
- Helper->Core->menu($this->Builder->menu('sidebar-admin',null,3)); ?> + Helper->Core->menu($menu->menu('sidebar-admin',null,3)); ?> - Auth->isAuthorized("Development",1)): ?> - Config->get('application','show_nav_title')): ?> -
Locale->get('Development') ?>
+ isAuthorized("Development",1)): ?> + get('application','show_nav_title')): ?> +
get('Development') ?>
- Helper->Core->menu($this->Builder->menu('sidebar-dev',null,3)); ?> + Helper->Core->menu($menu->menu('sidebar-dev',null,3)); ?> @@ -84,10 +89,10 @@

- Request->getParams('GET','query'))): ?> - Locale->get($this->label()); ?>Request->getParams('GET','name'))): ?>: Request->getParams('GET','name') ?>Request->getParams('GET','id'))): ?>: Request->getParams('GET','id') ?> + getParams('GET','query'))): ?> + get($this->label()); ?>getParams('GET','name'))): ?>: getParams('GET','name') ?>getParams('GET','id'))): ?>: getParams('GET','id') ?> - Locale->get('Search Results'); ?>: Request->getParams('GET','query') ?> + get('Search Results'); ?>: getParams('GET','query') ?>

@@ -98,14 +103,14 @@
- Request->getParams('GET','query'))): ?> + getParams('GET','query'))): ?> view(); ?> interrupt()->Router->render('search'); endif; ?>
- Locale->get('Copyright'); ?> © Config->get('application','copyright') ?>- Config->get('application','owner')?> Locale->get('All rights reserved'); ?>. + get('Copyright'); ?> © get('application','copyright') ?>- get('application','owner')?> get('All rights reserved'); ?>.
diff --git a/Template/View/website.php b/Template/View/website.php index 80c3f4f..24f70b8 100644 --- a/Template/View/website.php +++ b/Template/View/website.php @@ -1,22 +1,26 @@ -Config->get('application','maintenance') || $this->Auth->isAuthorized('Administrator',1)): ?> + +get('application','maintenance') || $auth->isAuthorized('Administrator',1)): ?> - <?php if(is_null($this->Request->getParams('GET','query'))): ?> - <?= $this->Locale->get($this->label()); ?><?php if(!is_null($this->Request->getParams('GET','name'))): ?>: <?= $this->Request->getParams('GET','name') ?><?php elseif(!is_null($this->Request->getParams('GET','id'))): ?>: <?= $this->Request->getParams('GET','id') ?><?php endif; ?> + <?php if(is_null($request->getParams('GET','query'))): ?> + <?= $locale->get($this->label()); ?><?php if(!is_null($request->getParams('GET','name'))): ?>: <?= $request->getParams('GET','name') ?><?php elseif(!is_null($request->getParams('GET','id'))): ?>: <?= $request->getParams('GET','id') ?><?php endif; ?> <?php else: ?> - <?= $this->Locale->get('Search Results'); ?>: <?= $this->Request->getParams('GET','query') ?> + <?= $locale->get('Search Results'); ?>: <?= $request->getParams('GET','query') ?> <?php endif; ?> - Builder->css(); ?> + css(); ?> - Builder->js(); ?> + js(); ?> @@ -43,11 +47,11 @@
@@ -61,17 +65,17 @@ Logo -

Config->get('application','name') ?>

+

get('application','name') ?>