Files
andre 725f5b9c26 feat(site): per-gym SEO texts, event gym selector, hero/navbar polish
- Generated SEO titles/descriptions per gym; gym seo fields override each on its own
- Keywords, author, og:site_name, favicon and JSON-LD logo follow the gym
- CrossFit page 404s before output for gyms without a box
- Events: «Mostrar em» gym selector; event page uses its gym's brand
- Hero event button text colour from the event colour (--ev-on)
- Navbar bottom line stops at the other-gym head (--switch-w)
- Diagonal hero blur mask; remove the trial link
2026-10-26 12:15:37 +00:00

240 lines
13 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// ── Event landing page ──────────────────────────────────────────────────────
// Branded, time-limited landing page reached at /evento/<slug> (see .htaccess).
// Visible to the public only while the event is live (db_events_active rule);
// logged-in staff may preview drafts/scheduled/expired events. Carries an
// optional lead-capture form that stores submissions in event_leads.
require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../config/functions.php';
// Resolve any SSO session up front (mirrors _open.php) so the is_staff() preview
// gate below sees a logged-in admin. _open.php's later call then no-ops.
$slug = preg_replace('/[^a-z0-9\-]/', '', strtolower((string) ($_GET['slug'] ?? '')));
$event = $slug !== '' ? db_events_get_by_slug($pdo, $slug) : null;
// Gate: live for everyone, otherwise only staff (preview).
$can_view = $event && (events_is_live($event) || is_staff());
// ── Lead form submission (PRG) ──────────────────────────────────────────────
$lead_errors = [];
if ($can_view && $_SERVER['REQUEST_METHOD'] === 'POST') {
csrf_check();
$back = base_url('evento/' . rawurlencode($event['slug']));
// Anti-spam partilhado (config/functions_forms.php). Recusado = mesma
// resposta de sucesso; fica em blocked_submissions.
if (public_form_screen($pdo, 'evento', 'event_leads',
(string) ($_POST['lead_name'] ?? ''), (string) ($_POST['lead_message'] ?? '')) !== null) {
header('Location: ' . $back . '?ok=1#inscricao');
exit;
}
// The exact wording the visitor consents to — stored for GDPR evidence.
$consent_text = 'Autorizo o contacto e o tratamento dos meus dados para efeitos desta inscrição, nos termos da Política de Privacidade.';
$lead = [
'name' => trim((string) ($_POST['lead_name'] ?? '')),
'email' => trim((string) ($_POST['lead_email'] ?? '')),
'phone' => trim((string) ($_POST['lead_phone'] ?? '')),
'message' => trim((string) ($_POST['lead_message'] ?? '')),
'consent' => !empty($_POST['lead_consent']) ? 1 : 0,
'consent_text' => $consent_text,
// IP real atrás do proxy (config/env.php) — o primeiro valor do
// X-Forwarded-For é escrito pelo cliente.
'ip' => client_ip(),
'user_agent' => (string) ($_SERVER['HTTP_USER_AGENT'] ?? ''),
];
if ($lead['name'] === '') $lead_errors[] = 'Indica o teu nome.';
if ($lead['email'] === '' && $lead['phone'] === '') $lead_errors[] = 'Indica um email ou um telefone para te podermos contactar.';
if ($lead['email'] !== '' && !filter_var($lead['email'], FILTER_VALIDATE_EMAIL)) $lead_errors[] = 'O email indicado não é válido.';
if (!$lead['consent']) $lead_errors[] = 'É necessário aceitar o tratamento dos dados para enviar a inscrição.';
if (!$lead_errors) {
db_event_lead_create($pdo, (int) $event['e_id'], $lead);
header('Location: ' . $back . '?ok=1#inscricao');
exit;
}
// fall through and re-render with $lead_errors + the submitted $lead values
$lead_old = $lead;
}
// ── Not found / not visible ─────────────────────────────────────────────────
if (!$can_view) {
http_response_code(404);
$current_uri_2 = '';
require __DIR__ . '/_open.php';
?>
<section class="event-notfound" style="padding:160px 0 90px;text-align:center;">
<div class="container">
<h1 style="font-size:2.4rem;">Evento não encontrado</h1>
<p class="text-muted">Este evento pode já ter terminado ou o link estar incorreto.</p>
<a href="<?= base_url('/') ?>" class="site-btn mt-3">Voltar ao início</a>
</div>
</section>
<?php
echo view('footer');
return;
}
// ── Visible: render the landing page ────────────────────────────────────────
$is_preview = !events_is_live($event);
$hero_url = $event['hero_image'] !== '' ? base_url('uploads/events/' . rawurlencode($event['hero_image'])) : '';
$accent = $event['cor'] !== '' ? $event['cor'] : '#C2D500';
// Human date line for the event window.
$date_line = '';
if (!empty($event['event_starts_at'])) {
$date_line = format_date_pt((string) $event['event_starts_at']);
if (!empty($event['event_ends_at'])
&& substr((string) $event['event_ends_at'], 0, 10) !== substr((string) $event['event_starts_at'], 0, 10)) {
$date_line .= ' – ' . format_date_pt((string) $event['event_ends_at']);
}
}
// CTA target: explicit URL, else the on-page form (when shown), else contacts.
$cta_label = trim((string) $event['cta_label']);
$cta_href = trim((string) $event['cta_url']);
if ($cta_href === '') {
$cta_href = ((int) $event['show_lead_form'] === 1) ? '#inscricao' : base_url('home/contactos');
}
// ── SEO ─────────────────────────────────────────────────────────────────────
// include.php / seo.php honour these globals when set before _open.php runs.
$event_url = base_url('evento/' . rawurlencode($event['slug']));
$seo_desc = $event['summary'] !== ''
? $event['summary']
: trim(mb_substr(strip_tags((string) ($event['subtitle'] ?: $event['description'])), 0, 200));
$current_uri_2 = 'evento';
// An event tied to one gym (Eventos → «Mostrar em») renders in that gym's brand.
$ev_gym = !empty($event['gym_id']) ? db_gyms_get($pdo, (int) $event['gym_id']) : null;
if ($ev_gym && empty($_GET['gym'])) $_GET['gym'] = $ev_gym['slug'];
$ev_gym = $ev_gym ?? site_gym($pdo) ?? gym_fallback();
$GLOBALS['_page_meta_override'] = [
($event['title'] !== '' ? $event['title'] : 'Evento') . ' | ' . $ev_gym['name'],
$seo_desc,
];
$GLOBALS['_canonical_override'] = $event_url;
$GLOBALS['_og_type_override'] = 'article';
if ($hero_url !== '') $GLOBALS['_og_image_override'] = $hero_url;
// Event structured data (schema.org/Event) for rich link previews / search.
$info = site_info($pdo, (int) $ev_gym['id']);
$ld_event = [
'@context' => 'https://schema.org',
'@type' => 'Event',
'name' => $event['title'],
'description' => $seo_desc !== '' ? $seo_desc : $event['title'],
'url' => $event_url,
'eventStatus' => 'https://schema.org/EventScheduled',
'eventAttendanceMode' => 'https://schema.org/OfflineEventAttendanceMode',
];
if ($hero_url !== '') $ld_event['image'] = [$hero_url];
if (!empty($event['event_starts_at'])) $ld_event['startDate'] = str_replace(' ', 'T', (string) $event['event_starts_at']);
if (!empty($event['event_ends_at'])) $ld_event['endDate'] = str_replace(' ', 'T', (string) $event['event_ends_at']);
$ld_event['location'] = $event['location'] !== ''
? ['@type' => 'Place', 'name' => $event['location']]
: ['@type' => 'Place',
'name' => ($info['name'] ?? '') !== '' ? $info['name'] : $ev_gym['name'],
'address' => $info['adress'] ?? ''];
$ld_event['organizer'] = [
'@type' => 'Organization',
'name' => ($info['name'] ?? '') !== '' ? $info['name'] : $ev_gym['name'],
'url' => base_url(),
];
$GLOBALS['_jsonld_extra'] = $ld_event;
require __DIR__ . '/_open.php';
?>
<?php if ($is_preview): ?>
<div class="event-preview-bar">Pré-visualização — este evento não está visível ao público (<?= htmlspecialchars(events_status($event)) ?>).</div>
<?php endif; ?>
<section class="event-hero set-bg<?= $hero_url ? '' : ' event-hero--plain' ?>"
<?= $hero_url ? 'style="background-image:url(\'' . htmlspecialchars($hero_url, ENT_QUOTES) . '\')"' : '' ?>>
<div class="event-hero-overlay"></div>
<div class="container">
<div class="event-hero-inner">
<span class="event-hero-eyebrow" style="color:<?= htmlspecialchars($accent, ENT_QUOTES) ?>">Evento</span>
<h1><?= htmlspecialchars($event['title']) ?></h1>
<?php if ($event['subtitle'] !== ''): ?><p class="event-hero-sub"><?= htmlspecialchars($event['subtitle']) ?></p><?php endif; ?>
<ul class="event-hero-meta">
<?php if ($date_line !== ''): ?><li><i class="fas fa-calendar-alt"></i> <?= htmlspecialchars($date_line) ?></li><?php endif; ?>
<?php if ($event['location'] !== ''): ?><li><i class="fas fa-map-marker-alt"></i> <?= htmlspecialchars($event['location']) ?></li><?php endif; ?>
</ul>
<?php if ($cta_label !== ''): ?>
<a href="<?= htmlspecialchars($cta_href, ENT_QUOTES) ?>" class="site-btn event-hero-cta"
style="background:<?= htmlspecialchars($accent, ENT_QUOTES) ?>"
<?= ($cta_href[0] ?? '') === '#' ? 'data-scroll' : '' ?>><?= htmlspecialchars($cta_label) ?></a>
<?php endif; ?>
</div>
</div>
</section>
<?php if (trim((string) $event['description']) !== ''): ?>
<section class="event-body">
<div class="container">
<div class="row justify-content-md-center">
<div class="col-lg-8">
<?php /* description is admin-authored HTML — sanitised allowlist HTML */ ?>
<?= clean_richtext($event['description']) ?>
</div>
</div>
</div>
</section>
<?php endif; ?>
<?php if ((int) $event['show_lead_form'] === 1): ?>
<section class="event-leadform" id="inscricao">
<div class="container">
<div class="row justify-content-md-center">
<div class="col-lg-7">
<div class="section-title text-center deco">
<span class="section-eyebrow">Participa</span>
<h2>Inscreve-te</h2>
<p class="section-subtitle">Deixa os teus dados e entramos em contacto.</p>
</div>
<?php if (isset($_GET['ok'])): ?>
<div class="event-alert event-alert--ok">Inscrição enviada com sucesso. Obrigado!</div>
<?php endif; ?>
<?php foreach ($lead_errors as $e): ?>
<div class="event-alert event-alert--err"><?= htmlspecialchars($e) ?></div>
<?php endforeach; ?>
<form method="POST" action="<?= base_url('evento/' . rawurlencode($event['slug'])) ?>#inscricao" class="event-form">
<?= csrf_field() ?>
<?= public_form_trap('evento') /* honeypot + carimbo + prova de JS */ ?>
<div class="event-form-grid">
<div class="event-field">
<label>Nome *</label>
<input type="text" name="lead_name" required maxlength="150" value="<?= htmlspecialchars($lead_old['name'] ?? '') ?>">
</div>
<div class="event-field">
<label>Telefone</label>
<input type="tel" name="lead_phone" maxlength="40" value="<?= htmlspecialchars($lead_old['phone'] ?? '') ?>">
</div>
<div class="event-field event-field--full">
<label>Email</label>
<input type="email" name="lead_email" maxlength="190" value="<?= htmlspecialchars($lead_old['email'] ?? '') ?>">
</div>
<div class="event-field event-field--full">
<label>Mensagem</label>
<textarea name="lead_message" rows="3" maxlength="1000"><?= htmlspecialchars($lead_old['message'] ?? '') ?></textarea>
</div>
</div>
<label class="event-consent">
<input type="checkbox" name="lead_consent" value="1" required <?= !empty($lead_old['consent']) ? 'checked' : '' ?>>
<span>Autorizo o contacto e o tratamento dos meus dados para esta inscrição, nos termos da <a href="<?= base_url('home/privacidade') ?>" target="_blank" rel="noopener">Política de Privacidade</a>.</span>
</label>
<div class="text-center mt-3">
<button type="submit" class="site-btn" style="background:<?= htmlspecialchars($accent, ENT_QUOTES) ?>">Enviar inscrição</button>
</div>
</form>
</div>
</div>
</div>
</section>
<?php endif; ?>
<?php
echo view('footer');