Files
fitme3070.pt/config/functions_site.php
T
andreandClaude Opus 5.5 0587be7d14 feat: borda verde a rodar nos planos, textura metálica, contactos numa só fonte
- Mensalidades: ao passar o rato, a borda do cartão ganha um brilho verde num
  conic-gradient que roda (@property --pc-a animável), centrado no cursor; o
  cartão inclina-se e desliza para o rato como os da equipa.
- «O que dizem de nós» e rodapé: textura discreta de metal preto mate (grão de
  pintura eletrostática, brilho difuso, riscas quase invisíveis), em SVG/CSS.
- Contactos: o site lê só fitme3070_info (Gestão → Contactos). Os campos
  contact_*/social_* de Sistema → Definições não eram usados e foram editados
  por engano — saem do formulário, com um aviso a apontar para Contactos.
  site_info() deixa o WhatsApp só com dígitos (wa.me não aceita + nem espaços).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 21:57:15 +01:00

129 lines
5.8 KiB
PHP

<?php
// ─── Public-site data (plain-PDO replacements for the old CI4 models) ─────────
// The ported website/* partials read the same shapes the CI4 models returned.
// Every query is defensive — the gym-content tables can be empty on a fresh DB.
// fitme3070_info row (id=1). Always returns every key the views read, so empty
// installs render without warnings.
function site_info(PDO $pdo): array {
$cols = ['id','name','adress','phone','telephone','whatsapp','instagram',
'facebook','messenger','email','website','google_maps','google_maps_link'];
$row = [];
try {
$row = $pdo->query("SELECT * FROM fitme3070_info WHERE id = 1 LIMIT 1")
->fetch(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $e) { $row = []; }
foreach ($cols as $c) {
if (!array_key_exists($c, $row) || $row[$c] === null) $row[$c] = '';
}
// wa.me only accepts digits — a stray "+" or space would break the link.
$row['whatsapp'] = preg_replace('/\D+/', '', (string) $row['whatsapp']);
return $row;
}
// Top navigation map (label => CI-style path). Kept identical to the original
// so navbar/footer markup is unchanged; .htaccess rewrites these to site/*.php.
function site_navbar(): array {
// One-page layout: Home, Equipa, Modalidades, Horário, Contactos are all
// anchor links on the single homepage. CrossFit is a separate page styled
// differently (see navbar.php for the 'crossfit_btn' flag).
return [
'Home' => '#home',
'Equipa' => '#equipa',
'Modalidades' => '#modalidades',
'Horário' => '#horario',
'Mensalidades' => '#mensalidades',
'Contactos' => '#contactos',
'CrossFit' => 'home/crossfit', // separate page, navbar styles it specially
];
}
function site_offer(PDO $pdo): array {
try {
return $pdo->query(
"SELECT * FROM website_offer
WHERE o_deleted_at IS NULL OR o_deleted_at = '0000-00-00 00:00:00' OR o_deleted_at = ''"
)->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) { return []; }
}
function site_work_hours(PDO $pdo): array {
try { return $pdo->query("SELECT * FROM work_hours")->fetchAll(PDO::FETCH_ASSOC); }
catch (Throwable $e) { return []; }
}
function site_team(PDO $pdo): array {
try { return $pdo->query("SELECT * FROM team WHERE deleted_at IS NULL")->fetchAll(PDO::FETCH_ASSOC); }
catch (Throwable $e) { return []; }
}
function site_modalidades(PDO $pdo): array {
try {
return $pdo->query(
"SELECT * FROM modalidades WHERE deleted_at IS NULL ORDER BY item_order DESC"
)->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) { return []; }
}
// Currently-visible time-limited events for the homepage banner. Thin wrapper
// over db_events_active() (functions_events.php) so the site code reads in the
// same site_*() style as everything else here.
function site_active_events(PDO $pdo): array {
return function_exists('db_events_active') ? db_events_active($pdo) : [];
}
// ─── Image-path resolver ──────────────────────────────────────────────────────
// DB stores either a bare filename ("1.jpg") or a full relative path
// ("uploads/team/foto/1.jpg"). This normalises both to a working URL.
// $base_dir is the upload subdirectory (e.g. "uploads/team/foto").
function site_img_url($value, $base_dir): string {
// Coerce null/empty to empty string
$value = (string) $value;
if ($value === '') return '';
// Already a full URL
if (substr($value, 0, 4) === 'http') return $value;
// Already has the uploads/ prefix — use as-is
if (substr($value, 0, 8) === 'uploads/') return base_url($value);
// Bare filename — prepend the base directory
return base_url(rtrim((string) $base_dir, '/') . '/' . ltrim($value, '/'));
}
// Schedule joined to modalidades (name/colour/icon), optionally filtered by
// local (e.g. 'crossfit'). Mirrors Horario_model::getAll().
function site_horario(PDO $pdo, string $local = ''): array {
$sql = "SELECT h.*,
TIME_FORMAT(h.hora_inicio, '%H:%i') AS hora_inicio,
TIME_FORMAT(h.hora_fim, '%H:%i') AS hora_fim,
m.nome, m.cor, m.icon
FROM horario h
JOIN modalidades m ON m.m_id = h.id_modalidade";
$args = [];
if ($local !== '') { $sql .= " WHERE h.local = ?"; $args[] = $local; }
$sql .= " ORDER BY h.hora_inicio ASC, m.nome ASC";
try { $st = $pdo->prepare($sql); $st->execute($args); return $st->fetchAll(PDO::FETCH_ASSOC); }
catch (Throwable $e) { return []; }
}
// ─── Indexing ─────────────────────────────────────────────────────────────────
// SEO_NOINDEX=true in .env keeps an install out of search results (meta robots
// + robots.txt "Disallow: /"). For staging copies such as fitme.andmore.pt,
// which serve the same content as the real domain and would otherwise compete
// with it as duplicate content.
function seo_noindex(): bool {
return filter_var(env('SEO_NOINDEX', 'false'), FILTER_VALIDATE_BOOLEAN);
}
// Public, indexable URLs for sitemap.xml: the fixed pages plus any live event
// landing page.
function site_sitemap_urls(PDO $pdo): array {
$urls = [
['loc' => base_url(), 'priority' => '1.0'],
['loc' => base_url('home/crossfit'), 'priority' => '0.8'],
['loc' => base_url('home/privacidade'), 'priority' => '0.2'],
];
foreach (site_active_events($pdo) as $ev) {
$urls[] = ['loc' => base_url('evento/' . rawurlencode($ev['slug'])), 'priority' => '0.6'];
}
return $urls;
}