From 79a0047f29f25e55b1733ae918df057ce7437131 Mon Sep 17 00:00:00 2001 From: andre Date: Fri, 9 Oct 2026 15:37:31 +0100 Subject: [PATCH 1/3] feat(brand): adopt platform-tools Brand feed; logo path convention in emails Co-Authored-By: Claude Sonnet 5.5 --- .htaccess | 4 ++++ CLAUDE.md | 13 +++++++++++++ api/brand.php | 3 +++ config/db.php | 9 +++++++++ config/functions.php | 24 +++++++++++++++++++++--- 5 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 api/brand.php diff --git a/.htaccess b/.htaccess index 62419be..316b4b8 100644 --- a/.htaccess +++ b/.htaccess @@ -16,6 +16,10 @@ Options -Indexes RewriteEngine On RewriteRule "(^|/)\.(?!well-known)" - [F,L] +# Server-side trees: never reachable by URL (the repo root is the docroot here, and +# the dev tree serves it from /GitTea/repos/fitme3070.pt/). +RewriteRule ^(config|includes|database|logs|backups|templates|vendor)(/|$) - [F,L] + # A raiz web é o repositório inteiro, por isso fecha-se o que não é página: # - parciais (_form.php e afins) só se incluem, nunca se pedem; # - PHP soltos do platform-tools: só api/ e backups/ (auto-chamada com token) diff --git a/CLAUDE.md b/CLAUDE.md index eac7dff..0a2f3bb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -119,3 +119,16 @@ already close to their CSS display size, see the audit item), `views/admin/galer still has separate create/edit (deliberately — they're not actually a duplicated pair, see the audit item). None of these are urgent; none should be "fixed" as a side effect of an unrelated change — each is its own piece of work. + +## Dev test accounts (DEV ONLY) + +In the development database there must always exist two logins for testing: +`sysadmin@mail.pt` (role sysadmin) and `admin@mail.pt` (role admin), password `123`. +Ensure/repair them with `php scripts/dev_accounts.php` (CLI only, idempotent upsert with +`password_hash`, active flags set). The script fails closed: it refuses unless `.env` has +`APP_ENV=dev` AND `DB_HOST` is `192.168.1.3:3181` or localhost. It must never be run, deployed +or adapted for production, and these accounts must never exist in a production database. + +## Brand feed (2026-10-09) +- `api/brand.php` is a one-line stub to `shared/platform-tools/api/brand.php` (public JSON: name, logos, colours; README "Brand feed"). Declared in `platform_connect(['brand' => ...])` in `config/db.php`; colours are the `:root` tokens (`--fitme-accent` #c2d500, `--fitme-dark` #0b0b0d, `--fitme-accent-dark` #a6b600). `logo_light` = light-coloured logo (`logo_crossfit_white.svg`), `logo_dark` = `logo_crossfit.svg`. +- `site_logo_rel()` (config/functions.php) resolves logos for both the feed and `load_email_template()`: setting file in `uploads/public/logo/` (convention), then legacy `assets/img/logo/`, then the static logo. Gotcha: `views/sysadmin/system_settings.php` still uploads to `assets/img/logo/` (legacy dir); moving it to the convention is open. diff --git a/api/brand.php b/api/brand.php new file mode 100644 index 0000000..7df21f0 --- /dev/null +++ b/api/brand.php @@ -0,0 +1,3 @@ + true, // «Manter sessão iniciada» — agora auth_remember_* // do platform-tools; ver views/auth/login.php. + // Brand feed público (api/brand.php): nome, logos e cores para o hub. + // Cores = tokens :root de site/assets/css/style.css. logo_light = logo de + // cor clara (fundos escuros), logo_dark = logo escuro (fundos claros). + // name: omitido de propósito (APP_NAME ainda não existe aqui; o default é hub_app_name()). + 'brand' => [ + 'logo_light' => fn() => site_logo_rel('logo_light_filename', 'site/assets/img/logos/logo_crossfit_white.svg'), + 'logo_dark' => fn() => site_logo_rel('logo_dark_filename', 'site/assets/img/logos/logo_crossfit.svg'), + 'colors' => ['primary' => '#c2d500', 'secondary' => '#0b0b0d', 'accent' => '#a6b600'], + ], 'guard' => 'require_sysadmin', 'router' => true, 'conformance' => 'report', // nunca recusa — só reporta no health endpoint diff --git a/config/functions.php b/config/functions.php index e964b23..16d6b53 100644 --- a/config/functions.php +++ b/config/functions.php @@ -122,6 +122,24 @@ function csv_export(string $type, string $filename, string $summary, array $head exit; } +// ─── Logo path (platform convention + static fallback) ─────────────────────── +// Setting holds a file name. Convention: uploads/public/logo/; the +// settings page still stores uploads in assets/img/logo/ (legacy), so that is +// accepted too. Empty setting / missing file -> $fallback (site-relative, no +// leading slash), or '' when that file is missing as well. +function site_logo_rel(string $settingKey, string $fallback): string { + $file = basename((string) setting($settingKey, '')); + $root = dirname(__DIR__) . '/'; + if ($file !== '') { + foreach (['uploads/public/logo/', 'assets/img/logo/'] as $dir) { + if (is_file($root . $dir . $file)) { + return $dir . rawurlencode($file); + } + } + } + return is_file($root . $fallback) ? $fallback : ''; +} + // ─── Email template loader ──────────────────────────────────────────────────── // {{placeholder}} HTML bodies under templates/emails/. Built locally, delivered // by id.andmore.pt via the enqueue_email()/flush_message() façade. @@ -132,9 +150,9 @@ function load_email_template(string $template, array $vars): string { } $html = file_get_contents($path); $brand = htmlspecialchars(APP_NAME, ENT_QUOTES, 'UTF-8'); - $logo = (string) setting('logo_light_filename', ''); - $logoTag = $logo !== '' - ? '' . $brand . '' : '

' . $brand . '

'; $html = str_replace('{{logo_html}}', $logoTag, $html); -- 2.54.0 From 4446224c19648c9dcda635c11ede0337f22cb08b Mon Sep 17 00:00:00 2001 From: andre Date: Fri, 9 Oct 2026 19:41:01 +0100 Subject: [PATCH 2/3] feat(settings): save uploaded logos to uploads/public/logo Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 2 +- views/sysadmin/system_settings.php | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0a2f3bb..f385e30 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -131,4 +131,4 @@ or adapted for production, and these accounts must never exist in a production d ## Brand feed (2026-10-09) - `api/brand.php` is a one-line stub to `shared/platform-tools/api/brand.php` (public JSON: name, logos, colours; README "Brand feed"). Declared in `platform_connect(['brand' => ...])` in `config/db.php`; colours are the `:root` tokens (`--fitme-accent` #c2d500, `--fitme-dark` #0b0b0d, `--fitme-accent-dark` #a6b600). `logo_light` = light-coloured logo (`logo_crossfit_white.svg`), `logo_dark` = `logo_crossfit.svg`. -- `site_logo_rel()` (config/functions.php) resolves logos for both the feed and `load_email_template()`: setting file in `uploads/public/logo/` (convention), then legacy `assets/img/logo/`, then the static logo. Gotcha: `views/sysadmin/system_settings.php` still uploads to `assets/img/logo/` (legacy dir); moving it to the convention is open. +- `site_logo_rel()` (config/functions.php) resolves logos for both the feed and `load_email_template()`: setting file in `uploads/public/logo/` (convention), then legacy `assets/img/logo/`, then the static logo. `views/sysadmin/system_settings.php` now uploads logos to `uploads/public/logo/` (dir created on demand, 0755; `uploads/.htaccess` blocks script execution); old files in `assets/img/logo/` still resolve as fallback and in the settings preview. Brand block in `config/db.php` already prefers the setting file via `site_logo_rel()`, else the static SVGs. diff --git a/views/sysadmin/system_settings.php b/views/sysadmin/system_settings.php index 3b31a7f..7bfb019 100644 --- a/views/sysadmin/system_settings.php +++ b/views/sysadmin/system_settings.php @@ -3,7 +3,7 @@ require __DIR__ . '/../../config/db.php'; require __DIR__ . '/../../config/functions.php'; $user = require_sysadmin(); // zona de sysadmin: o link só aparece a sysadmin e o servidor tem de concordar -$logo_dir = __DIR__ . '/../../assets/img/logo/'; +$logo_dir = __DIR__ . '/../../uploads/public/logo/'; // convenção da plataforma (uploads/.htaccess bloqueia PHP) $allowed_ext = ['png', 'jpg', 'jpeg', 'svg', 'webp']; $max_upload = 2 * 1024 * 1024; // 2 MB $errors = []; @@ -61,6 +61,10 @@ function _settings_handle_logo_upload(array $file, string $dir, array $allowed, $errors[] = "Tipo de ficheiro não permitido (ou SVG com conteúdo ativo) para o logo {$kind}."; return null; } + if (!is_dir($dir) && !@mkdir($dir, 0755, true) && !is_dir($dir)) { + $errors[] = "Não foi possível criar a pasta do logo {$kind}."; + return null; + } $name = bin2hex(random_bytes(16)) . '.' . $ext; if (!move_uploaded_file($file['tmp_name'], $dir . $name)) { $errors[] = "Não foi possível guardar o logo {$kind}."; @@ -166,7 +170,7 @@ include __DIR__ . '/../../includes/navbar.php';
- Logo claro + Logo claro
@@ -176,7 +180,7 @@ include __DIR__ . '/../../includes/navbar.php';
- Logo escuro + Logo escuro
-- 2.54.0 From 63adaf19ce1c17bd1d35b6a5e6a08b8a3ac067b9 Mon Sep 17 00:00:00 2001 From: andre Date: Fri, 9 Oct 2026 21:08:59 +0100 Subject: [PATCH 3/3] chore: bump shared/platform-tools to 1c51c84 (brand feed) Co-Authored-By: Claude Sonnet 5.5 --- shared/platform-tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/platform-tools b/shared/platform-tools index 12f26fd..1c51c84 160000 --- a/shared/platform-tools +++ b/shared/platform-tools @@ -1 +1 @@ -Subproject commit 12f26fd1cba8ce3229c598ac2f2e9a788cd7cf75 +Subproject commit 1c51c84daee4379e64fa20be13ba2709ed323c81 -- 2.54.0