Merge pull request 'Promote beta' (#1) from beta into main
This commit was merged in pull request #1.
This commit is contained in:
commit
7dc47f79cc
7 files changed
+58
-7
No files matched your search
@@ -16,6 +16,10 @@ Options -Indexes
|
|||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
RewriteRule "(^|/)\.(?!well-known)" - [F,L]
|
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:
|
# 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;
|
# - parciais (_form.php e afins) só se incluem, nunca se pedem;
|
||||||
# - PHP soltos do platform-tools: só api/ e backups/ (auto-chamada com token)
|
# - PHP soltos do platform-tools: só api/ e backups/ (auto-chamada com token)
|
||||||
|
|||||||
@@ -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,
|
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
|
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.
|
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. `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.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
// Stub: o endpoint vive no platform-tools (Brand feed público, sem auth).
|
||||||
|
require __DIR__ . '/../shared/platform-tools/api/brand.php';
|
||||||
@@ -117,6 +117,15 @@ platform_connect($pdo, [
|
|||||||
],
|
],
|
||||||
'remember' => true, // «Manter sessão iniciada» — agora auth_remember_*
|
'remember' => true, // «Manter sessão iniciada» — agora auth_remember_*
|
||||||
// do platform-tools; ver views/auth/login.php.
|
// 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',
|
'guard' => 'require_sysadmin',
|
||||||
'router' => true,
|
'router' => true,
|
||||||
'conformance' => 'report', // nunca recusa — só reporta no health endpoint
|
'conformance' => 'report', // nunca recusa — só reporta no health endpoint
|
||||||
|
|||||||
+21
-3
@@ -122,6 +122,24 @@ function csv_export(string $type, string $filename, string $summary, array $head
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Logo path (platform convention + static fallback) ───────────────────────
|
||||||
|
// Setting <key> holds a file name. Convention: uploads/public/logo/<file>; 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 ────────────────────────────────────────────────────
|
// ─── Email template loader ────────────────────────────────────────────────────
|
||||||
// {{placeholder}} HTML bodies under templates/emails/. Built locally, delivered
|
// {{placeholder}} HTML bodies under templates/emails/. Built locally, delivered
|
||||||
// by id.andmore.pt via the enqueue_email()/flush_message() façade.
|
// 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);
|
$html = file_get_contents($path);
|
||||||
$brand = htmlspecialchars(APP_NAME, ENT_QUOTES, 'UTF-8');
|
$brand = htmlspecialchars(APP_NAME, ENT_QUOTES, 'UTF-8');
|
||||||
$logo = (string) setting('logo_light_filename', '');
|
$rel = site_logo_rel('logo_light_filename', 'site/assets/img/logos/logo_crossfit_white.svg');
|
||||||
$logoTag = $logo !== ''
|
$logoTag = $rel !== ''
|
||||||
? '<img src="' . htmlspecialchars(base_url('assets/img/logo/' . rawurlencode($logo)), ENT_QUOTES, 'UTF-8')
|
? '<img src="' . htmlspecialchars(base_url($rel), ENT_QUOTES, 'UTF-8')
|
||||||
. '" alt="' . $brand . '" style="max-height:60px;border:0;">'
|
. '" alt="' . $brand . '" style="max-height:60px;border:0;">'
|
||||||
: '<p style="margin:0;color:#fff;font-size:22px;font-weight:bold;letter-spacing:1px;">' . $brand . '</p>';
|
: '<p style="margin:0;color:#fff;font-size:22px;font-weight:bold;letter-spacing:1px;">' . $brand . '</p>';
|
||||||
$html = str_replace('{{logo_html}}', $logoTag, $html);
|
$html = str_replace('{{logo_html}}', $logoTag, $html);
|
||||||
|
|||||||
+1
-1
Submodule shared/platform-tools updated: 12f26fd1cb...1c51c84dae.
@@ -3,7 +3,7 @@ require __DIR__ . '/../../config/db.php';
|
|||||||
require __DIR__ . '/../../config/functions.php';
|
require __DIR__ . '/../../config/functions.php';
|
||||||
$user = require_sysadmin(); // zona de sysadmin: o link só aparece a sysadmin e o servidor tem de concordar
|
$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'];
|
$allowed_ext = ['png', 'jpg', 'jpeg', 'svg', 'webp'];
|
||||||
$max_upload = 2 * 1024 * 1024; // 2 MB
|
$max_upload = 2 * 1024 * 1024; // 2 MB
|
||||||
$errors = [];
|
$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}.";
|
$errors[] = "Tipo de ficheiro não permitido (ou SVG com conteúdo ativo) para o logo {$kind}.";
|
||||||
return null;
|
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;
|
$name = bin2hex(random_bytes(16)) . '.' . $ext;
|
||||||
if (!move_uploaded_file($file['tmp_name'], $dir . $name)) {
|
if (!move_uploaded_file($file['tmp_name'], $dir . $name)) {
|
||||||
$errors[] = "Não foi possível guardar o logo {$kind}.";
|
$errors[] = "Não foi possível guardar o logo {$kind}.";
|
||||||
@@ -166,7 +170,7 @@ include __DIR__ . '/../../includes/navbar.php';
|
|||||||
<label class="form-label small">Logo claro (para fundos escuros — navbar)</label>
|
<label class="form-label small">Logo claro (para fundos escuros — navbar)</label>
|
||||||
<?php if ($logo_light): ?>
|
<?php if ($logo_light): ?>
|
||||||
<div class="mb-2 p-2 rounded" style="background:#161c18;">
|
<div class="mb-2 p-2 rounded" style="background:#161c18;">
|
||||||
<img src="<?= base_url('assets/img/logo/' . rawurlencode($logo_light)) ?>" alt="Logo claro" style="max-height:48px;">
|
<img src="<?= base_url(is_file($logo_dir . basename($logo_light)) ? 'uploads/public/logo/' . rawurlencode($logo_light) : 'assets/img/logo/' . rawurlencode($logo_light)) ?>" alt="Logo claro" style="max-height:48px;">
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<input type="file" name="logo_light" class="form-control" accept=".png,.jpg,.jpeg,.svg,.webp">
|
<input type="file" name="logo_light" class="form-control" accept=".png,.jpg,.jpeg,.svg,.webp">
|
||||||
@@ -176,7 +180,7 @@ include __DIR__ . '/../../includes/navbar.php';
|
|||||||
<label class="form-label small">Logo escuro (para fundos claros — emails)</label>
|
<label class="form-label small">Logo escuro (para fundos claros — emails)</label>
|
||||||
<?php if ($logo_dark): ?>
|
<?php if ($logo_dark): ?>
|
||||||
<div class="mb-2 p-2 rounded border" style="background:#ffffff;">
|
<div class="mb-2 p-2 rounded border" style="background:#ffffff;">
|
||||||
<img src="<?= base_url('assets/img/logo/' . rawurlencode($logo_dark)) ?>" alt="Logo escuro" style="max-height:48px;">
|
<img src="<?= base_url(is_file($logo_dir . basename($logo_dark)) ? 'uploads/public/logo/' . rawurlencode($logo_dark) : 'assets/img/logo/' . rawurlencode($logo_dark)) ?>" alt="Logo escuro" style="max-height:48px;">
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<input type="file" name="logo_dark" class="form-control" accept=".png,.jpg,.jpeg,.svg,.webp">
|
<input type="file" name="logo_dark" class="form-control" accept=".png,.jpg,.jpeg,.svg,.webp">
|
||||||
|
|||||||
Reference in new issue
Block a user