diff --git a/.gitignore b/.gitignore index 1d63556..dd845fb 100644 --- a/.gitignore +++ b/.gitignore @@ -27,11 +27,22 @@ public/themes/ # Temporary files *.tmp *.temp +.bak/ # Local configuration & credentials config.json +config.*.json +!config.json.example admin/config/admin.json -# No content +# No content (per-domain content dirs included) content/ +content-*/ !content/.gitkeep + +# Test results +pentest_results.* +accessibility-test-results.* +enhanced-test-results.* +cli/test/functional/test-report*.md +cli/test/functional/function-test.md diff --git a/AGENTS.md b/AGENTS.md index 8a1ed74..4ec3c92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,15 +18,14 @@ codepress/ │ │ ├── class/ │ │ │ ├── CodePressCMS.php # Hoofd CMS class │ │ │ ├── ThemeManager.php # Thema-resolver + Twig render + SCSS compile -│ │ │ ├── Logger.php # Logging systeem -│ │ │ └── SimpleTemplate.php # Legacy Mustache-style engine (niet meer gebruikt) +│ │ │ └── Logger.php # Logging systeem │ │ ├── plugin/ │ │ │ ├── PluginManager.php # Plugin loader │ │ │ └── CMSAPI.php # API voor plugins │ │ ├── config.php # Config loader (leest config.json) │ │ └── index.php # Bootstrap (autoloader, requires) -│ ├── lang/ # Taalbestanden (nl.php, en.php) │ └── router.php # PHP dev server router (serveert ook /themes/) +├── language/ # Taalbestanden (nl/, en/, de/ — elk met site.php + admin.php) ├── themes/ # Dynamische thema's (volledig zelfstandig) │ ├── default/ # Standaard thema │ │ ├── theme.json # { title, default_layout, layout→.twig mapping, kleuren } @@ -34,33 +33,24 @@ codepress/ │ │ ├── full_content.twig # Layout: volledige breedte │ │ ├── left_sidebar.twig # Layout: sidebar links │ │ ├── right_sidebar.twig # Layout: sidebar rechts -│ │ ├── custom1.twig # Layout: custom +│ │ ├── custom1.twig # Layout: custom +│ │ ├── guide.twig # Layout: handleiding │ │ ├── partials/ # header.twig, navigation.twig, footer.twig -│ │ ├── css/theme.scss # SCSS bron (runtime gecompileerd) -│ │ └── js/theme.js # Thema JavaScript +│ │ ├── assets/scss/theme.scss # SCSS bron (runtime gecompileerd) +│ │ └── assets/js/ # app.js, bootstrap.bundle.min.js │ ├── demo/ # Demo thema (zelfde structuur, andere look) -│ └── test/ # Test thema ├── admin/ # Admin paneel │ ├── config/ │ │ ├── app.php # Admin app configuratie -│ │ └── admin.json # Gebruikers & security (file-based) +│ │ ├── admin.json # Gebruikers & security (file-based, gitignored) +│ │ └── admin.json.example # Voorbeeld met placeholder-wachtwoord │ ├── src/ │ │ └── AdminAuth.php # Authenticatie (sessies, bcrypt, CSRF, lockout) -│ ├── templates/ -│ │ ├── login.php # Login pagina -│ │ ├── layout.php # Admin layout met sidebar -│ │ └── pages/ -│ │ ├── dashboard.php -│ │ ├── content.php -│ │ ├── content-edit.php -│ │ ├── content-new.php -│ │ ├── content-dir-form.php -│ │ ├── config.php -│ │ ├── plugins.php -│ │ ├── plugin-config.php -│ │ ├── theme.php -│ │ └── users.php -│ └── storage/logs/ # Admin logs +│ ├── theme/default/views/ # Twig templates +│ │ ├── login.twig # Login pagina +│ │ ├── layouts/admin.twig # Admin layout met sidebar +│ │ └── pages/ # dashboard, content, content-edit, config, plugins, theme, users, statistics, logs, security, update, guide, media, etc. +│ └── storage/logs/ # Admin logs (gedeeld met front-end) ├── cli/ # CLI scripts & tests │ └── test/ │ ├── accessibility.sh # WCAG 2.1 AA test suite @@ -69,7 +59,7 @@ codepress/ │ └── pentest/ # Penetratietesten ├── plugins/ # CMS plugins │ ├── HTMLBlock/ -│ └── MQTTTracker/ +│ └── Navigation/ ├── public/ # Web root │ ├── assets/css/js/ │ ├── index.php # Website entry point @@ -92,7 +82,7 @@ codepress/ - Admin entry point + routing: `public/admin.php` - Admin authenticatie: `admin/src/AdminAuth.php` - **Content**: Stored in `content/`. Supports `.md` (Markdown), `.php` (Dynamic), `.html` (Static). -- **Templating**: Twig templates in `themes//`. `ThemeManager` rendert via Twig en compileert `css/theme.scss` runtime naar `public/themes//theme.css`. Layout gekozen via frontmatter `layout:` key; onbekende layouts vallen terug op `default_layout` in `theme.json`. +- **Templating**: Twig templates in `themes//`. `ThemeManager` rendert via Twig en compileert `assets/scss/theme.scss` runtime naar `public/themes//theme.css`. Layout gekozen via frontmatter `layout:` key; onbekende layouts vallen terug op `default_layout` in `theme.json`. - **Navigation**: Auto-generated from directory structure. Folders require an index file to be clickable in breadcrumbs. - **Security**: - Always use `htmlspecialchars()` for outputting user/content data @@ -104,9 +94,9 @@ codepress/ ## Admin Console - **File-based**: Geen database. Gebruikers opgeslagen in `admin/config/admin.json` - **Routing**: Via `?route=` parameter in `public/admin.php` -- **Routes**: `login`, `logout`, `dashboard`, `content`, `content-edit`, `content-new`, `content-delete`, `config`, `plugins`, `plugins-new`, `plugins-edit`, `plugins-config`, `plugins-toggle`, `plugins-delete`, `users` +- **Routes**: `login`, `logout`, `dashboard`, `content`, `content-edit`, `content-new`, `content-delete`, `content-dir-create`, `content-dir-rename`, `content-dir-delete`, `content-move`, `config`, `plugins`, `plugins-new`, `plugins-edit`, `plugins-config`, `plugins-toggle`, `plugins-delete`, `theme`, `theme-new`, `users`, `security`, `statistics`, `logs`, `guide`, `media`, `update` - **Auth**: Session-based. `AdminAuth` class handelt login, logout, CSRF, brute-force lockout af -- **Templates**: Pure PHP templates in `admin/templates/pages/`. Layout in `layout.php` +- **Templates**: Twig templates in `admin/theme/default/views/`. Layout in `layouts/admin.twig` ## Important: Title vs File/Directory Name Logic - **CRITICAL**: When user asks for "title" corrections, they usually mean **FILE/DIRECTORY NAME WITHOUT LANGUAGE PREFIX AND EXTENSIONS**, not the HTML title from content! @@ -121,5 +111,5 @@ codepress/ ## Bekende aandachtspunten - LSP errors over "Undefined function" in PHP files zijn vals-positief (standaard PHP functies worden niet herkend door de LSP). Negeer deze. - Zie `TODO.md` voor alle openstaande verbeteringen en nieuwe features. -- `vendor/` map bevat Composer dependencies (CommonMark, Twig, scssphp, Mustache). Niet handmatig wijzigen. +- `vendor/` map bevat Composer dependencies (CommonMark, Twig, scssphp, GeoIP2). Niet handmatig wijzigen. - `admin/config/admin.json` bevat wachtwoord-hashes. Niet committen met echte productie-wachtwoorden. diff --git a/README.en.md b/README.en.md index 639716f..97eb303 100644 --- a/README.en.md +++ b/README.en.md @@ -63,8 +63,8 @@ codepress/ ├── cms/ # Core CMS engine │ ├── core/class/ # CMS classes (CodePressCMS, ThemeManager, etc.) │ ├── core/plugin/ # Plugin system (PluginManager, CMSAPI) -│ ├── lang/ # Translation files (nl.php, en.php) │ └── router.php # PHP dev server router (clean URLs) +├── language/ # Translation files (nl/, en/, de/ — each with site.php + admin.php) ├── admin/ # Admin console │ ├── config/ # Admin configuration (admin.json) │ ├── src/AdminAuth.php # Authentication, roles, permissions diff --git a/README.md b/README.md index 3bc7353..4ecb308 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ codepress/ ├── cms/ # Core CMS engine │ ├── core/class/ # CMS classes (CodePressCMS, ThemeManager, etc.) │ ├── core/plugin/ # Plugin systeem (PluginManager, CMSAPI) -│ ├── lang/ # Taalbestanden (nl.php, en.php) │ └── router.php # PHP dev server router (schone URLs) +├── language/ # Taalbestanden (nl/, en/, de/ — elk met site.php + admin.php) ├── admin/ # Admin console │ ├── config/ # Admin configuratie (admin.json) │ ├── src/AdminAuth.php # Authenticatie, rollen, permissies diff --git a/TODO.md b/TODO.md index 5be141d..adb2c50 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,56 @@ # TODO +## Voor elke versie verhoging. Deze nooit weghalen. +- [ ] Pentest controles uitgevoerd +- [ ] WCAG 2.1 AA accessibility tests +- [ ] Volledige git commit maken. +- [ ] Verslag maken voor opdrcht gever +- [ ] Na convormatie versie verhogen +- [ ] Bij Voltooid versie ophoging aanmaken en deze allemaal unvinken voor volgende ronde. + +## Te doen ⏳ + +- [ ] Multidomein implementeren (elk domein = eigen thema + content, één admin + site name) + - [ ] Fase 1: Config-resolutie + - [ ] For apache instants domein settings /public/noorlander.info/ or /public/mycode.name/ + - [ ] config.domains.json (registry: host, aliases, redirect, config) + .example + - [ ] cms/core/domain.php: normalizeHost/getDomainRegistry/resolveDomain/loadSiteConfigForHost + - [ ] cms/core/config.php refactor → herbruikbare functie, compatibel blijven + - [ ] Per-domein config-bestanden (content_dir, active_theme) in the main content dir. like: /content/domain1 /content/domain2 os /content/noorlander.info/ /content/mycode.name/ + - [ ] Fase 2: Front-end + - [ ] cms/router.php: taal-prefix dynamisch uit actieve config + - [ ] public/.htaccess: generieke `([a-z]{2})` taalregel + - [ ] CodePressCMS::getCurrentLanguage(): validatie via config['language']['available'] + - [ ] getInternalHosts(): registry-hosts toevoegen (cross-domein links = intern) + - [ ] public/index.php: /-media/ en /-assets/ via actieve content_dir + - [ ] Fase 3: Admin (domeinbeheer + switch) + - [ ] admin/config/app.php: domains_json pad + - [ ] public/admin.php: $_SESSION['admin_domain'] + routes domains/domain-switch + - [ ] domains.twig + sidebar-item/badge in admin.twig + - [ ] Bestaande handlers laten werken op actief domein (config_json/content_dir patchen) + - [ ] Fase 4: Housekeeping + - [ ] .gitignore: config.*.json, content-*/ + - [ ] AGENTS.md + config.json.example bijwerken + - [ ] Verificatie: php -l, curl met Host-header, domein-switch in admin testen + ## Voltooid ✅ - [x] Admin code en niet gebruikte mappen/bestanden opschonen - [x] version.php changelog verwijderen (staat in git) - [x] Guide mappenstructuur reorganiseren (NL/EN → rollen) - [x] README.md compacter maken met verwijzingen naar guide +- [x] Admin dashboard template check — Twig-commentaren `{# ... #}` verwijderd uit Dashboard.php +- [x] Plugins — zichtbaar verschil tussen system en content plugins (badge + border + icoon in plugins.twig) +- [x] Plugins — alleen actieve plugins zichtbaar in sidebar (PluginManager laadt alleen enabled plugins) +- [x] Plugins — dubbele enabled_plugins config opgelost (plugins.enabled verwijderd, alleen enabled_plugins op top-level) +- [x] Admin config — Analytics & Logging toggles verwijderd van config-pagina +- [x] Handleidingen gecontroleerd en bijgewerkt (20 bestanden NL+EN: configuratie, plugins, plugin-development, core-classes, theme-json, layouts, scss-styling, admin-beheerder, nieuw-thema, architectuur) +- [x] Content backup/restore optie + content-git repository integratie (ContentBackup class, content-backup.twig, ZIP backup/restore, git init/commit/log/restore) +- [x] Pentest controles uitgevoerd (30/30 tests geslaagd — 0 vulnerabilities) +- [x] WCAG 2.1 AA accessibility tests (25/25 tests geslaagd — 100% compliance, test-script verbeterd met min/max checks en grep -E) -## Te doen ⏳ - -- [ ] Pentest controles uitvoeren -- [ ] WCAG 2.1 AA accessibility tests +## v2.6.0 (2026-08-15) ✅ +- [x] Pentest controles uitgevoerd (30/30) +- [x] WCAG 2.1 AA accessibility tests (25/25) +- [x] Verslag gemaakt (docs/release-notes/v2.6.0.md) +- [x] Versie verhoogd naar 2.6.0 diff --git a/admin/config/admin.json.example b/admin/config/admin.json.example new file mode 100644 index 0000000..dcfc488 --- /dev/null +++ b/admin/config/admin.json.example @@ -0,0 +1,21 @@ +{ + "users": [ + { + "username": "admin", + "password_hash": "$2y$12$CV2uORTIiie6s3jf9ANaneL7VLNqA5smXyrIzkcJFjlS2Vxfw1/HK", + "role": "admin", + "email": "", + "author_name": "", + "author_email": "", + "created": "2025-01-01" + } + ], + "security": { + "session_timeout": 1800, + "max_login_attempts": 5, + "lockout_duration": 900 + }, + "config": { + "default_page": "auto" + } +} \ No newline at end of file diff --git a/admin/src/AdminAuth.php b/admin/src/AdminAuth.php index a5fdf22..a1e862d 100644 --- a/admin/src/AdminAuth.php +++ b/admin/src/AdminAuth.php @@ -16,7 +16,7 @@ class AdminAuth */ public const ROLE_PERMISSIONS = [ 'admin' => ['*'], - 'content-manager' => ['dashboard', 'content', 'content-edit', 'content-new', 'content-delete', 'content-dir-create', 'content-dir-rename', 'content-dir-delete', 'content-move', 'guide', 'logout'], + 'content-manager' => ['dashboard', 'content', 'content-edit', 'content-new', 'content-delete', 'content-dir-create', 'content-dir-rename', 'content-dir-delete', 'content-move', 'content-backup', 'content-restore', 'content-git-init', 'content-git-commit', 'content-git-restore', 'guide', 'logout'], 'bi-manager' => ['dashboard', 'statistics', 'logs', 'guide', 'logout'], 'site-admin' => ['dashboard', 'theme', 'theme-new', 'plugins', 'plugins-new', 'plugins-edit', 'plugins-config', 'plugins-toggle', 'plugins-delete', 'statistics', 'logs', 'update', 'guide', 'logout'], ]; @@ -28,7 +28,7 @@ class AdminAuth 'admin' => 'Admin', 'content-manager' => 'Content Beheerder', 'bi-manager' => 'BI Beheerder', - 'site-admin' => ' Site Admin', + 'site-admin' => 'Site Admin', ]; public function __construct(array $appConfig) @@ -170,10 +170,19 @@ class AdminAuth if (!$this->isAuthenticated()) { return null; } - return [ - 'username' => $_SESSION['admin_user'], + $username = $_SESSION['admin_user']; + $userData = [ + 'username' => $username, 'role' => $_SESSION['admin_role'] ?? 'admin' ]; + // Enrich with profile fields from admin.json + $userEntry = $this->findUser($username); + if ($userEntry) { + $userData['email'] = $userEntry['email'] ?? ''; + $userData['author_name'] = $userEntry['author_name'] ?? ''; + $userData['author_email'] = $userEntry['author_email'] ?? ''; + } + return $userData; } /** @@ -244,13 +253,16 @@ class AdminAuth 'username' => $u['username'], 'role' => $role, 'role_label' => self::getRoleLabel($role), - 'created' => $u['created'] ?? '' + 'created' => $u['created'] ?? '', + 'email' => $u['email'] ?? '', + 'author_name' => $u['author_name'] ?? '', + 'author_email' => $u['author_email'] ?? '', ]; } return $users; } - public function addUser(string $username, string $password, string $role = 'admin'): array + public function addUser(string $username, string $password, string $role = 'admin', string $email = '', string $authorName = '', string $authorEmail = ''): array { if ($this->findUser($username)) { return ['success' => false, 'message' => 'Gebruiker bestaat al.']; @@ -266,6 +278,9 @@ class AdminAuth 'username' => $username, 'password_hash' => password_hash($password, PASSWORD_DEFAULT), 'role' => $role, + 'email' => $email, + 'author_name' => $authorName, + 'author_email' => $authorEmail, 'created' => date('Y-m-d') ]; $this->saveAdminConfig(); @@ -273,6 +288,24 @@ class AdminAuth return ['success' => true, 'message' => 'Gebruiker aangemaakt.']; } + /** + * Update the profile (email, author_name, author_email) of a user. + */ + public function updateUserProfile(string $username, string $email = '', string $authorName = '', string $authorEmail = ''): array + { + foreach ($this->adminConfig['users'] as &$userEntry) { + if ($userEntry['username'] === $username) { + $userEntry['email'] = $email; + $userEntry['author_name'] = $authorName; + $userEntry['author_email'] = $authorEmail; + $this->saveAdminConfig(); + $this->log('info', "Profiel bijgewerkt: {$username}"); + return ['success' => true, 'message' => 'Profiel opgeslagen.']; + } + } + return ['success' => false, 'message' => 'Gebruiker niet gevonden.']; + } + /** * Change the role of an existing user. */ diff --git a/admin/theme/default/views/layouts/admin.twig b/admin/theme/default/views/layouts/admin.twig index e42b2a9..1417104 100644 --- a/admin/theme/default/views/layouts/admin.twig +++ b/admin/theme/default/views/layouts/admin.twig @@ -1,9 +1,9 @@ - + - {% block title %}CodePress Admin{% endblock %} + {% block title %}{{ ta.admin_title|default('CodePress Admin') }}{% endblock %} @@ -37,98 +37,95 @@