$root ? '/' : '/site', 'animals' => $root ? '/animaux' : '/site/animaux', 'animal' => $root ? '/adoption' : '/site/animal', 'about' => $root ? '/presentation' : '/site/presentation', 'donation' => $root ? '/dons' : '/site/dons', 'contact' => $root ? '/contact' : '/site/contact', 'legal' => $root ? '/mentions-legales' : '/site/mentions-legales', 'privacy' => $root ? '/confidentialite' : '/site/confidentialite', 'logo' => $root ? '/logo-association' : '/site/logo', default => $root ? '/' : '/site', }; return $path . ($query ? '?' . http_build_query($query) : ''); } public static function dispatch(string $path): void { $path = self::normalize($path); if (AppSettings::get('public_site_enabled') !== '1') { http_response_code(404); self::render('public_site_unavailable.php', ['title' => t('public_site.unavailable')]); return; } if ($path === '/site/logo') { self::logo(); return; } match ($path) { '/site', '/site/' => self::home(), '/site/animaux' => self::animals(), '/site/animal' => self::animal(), '/site/dons' => self::donation(), '/site/contact' => self::contact(), '/site/presentation' => self::about(), '/site/mentions-legales' => self::legal(), '/site/confidentialite' => self::privacy(), default => self::notFound(), }; } private static function normalize(string $path): string { return match ($path) { '/' => '/site', '/animaux' => '/site/animaux', '/adoption' => '/site/animal', '/presentation' => '/site/presentation', '/dons' => '/site/dons', '/contact' => '/site/contact', '/mentions-legales' => '/site/mentions-legales', '/confidentialite' => '/site/confidentialite', '/logo-association' => '/site/logo', default => $path, }; } private static function home(): void { $db = DB::pdo(); $animals = self::animalQuery('', 8); $counts = $db ->query( "SELECT species,COUNT(*) count FROM animals WHERE website_published=1 AND adoption_availability='available' AND archived_at IS NULL AND deleted_at IS NULL AND status NOT IN ('adopte','decede') GROUP BY species ORDER BY species", ) ->fetchAll(PDO::FETCH_ASSOC); self::render('public_site_home.php', [ 'title' => AppSettings::get('public_site_home_title'), 'animals' => $animals, 'speciesCounts' => $counts, ]); } private static function animals(): void { $species = SpeciesService::normalize((string) ($_GET['espece'] ?? '')); if ($species !== '' && !SpeciesService::exists($species, false)) { $species = ''; } self::render('public_site_animals.php', [ 'title' => t('public_site.animals'), 'animals' => self::animalQuery($species, 200), 'selectedSpecies' => $species, 'publicSpecies' => self::species(), ]); } private static function animal(): void { $id = (int) ($_GET['id'] ?? 0); $s = DB::pdo()->prepare( "SELECT a.*,s.name species_name,s.icon species_icon,(SELECT filename FROM animal_photos p WHERE p.animal_id=a.id AND p.is_public=1 ORDER BY p.is_primary DESC,p.id DESC LIMIT 1) primary_photo FROM animals a LEFT JOIN ref_species s ON s.code=a.species WHERE a.id=? AND a.website_published=1 AND a.adoption_availability='available' AND a.archived_at IS NULL AND a.deleted_at IS NULL AND a.status NOT IN ('adopte','decede')", ); $s->execute([$id]); $animal = $s->fetch(PDO::FETCH_ASSOC); if (!$animal) { self::notFound(); return; } $p = DB::pdo()->prepare( 'SELECT filename,caption FROM animal_photos WHERE animal_id=? AND is_public=1 ORDER BY is_primary DESC,id DESC', ); $p->execute([$id]); self::render('public_site_animal.php', [ 'title' => $animal['name'], 'animal' => $animal, 'photos' => $p->fetchAll(PDO::FETCH_ASSOC), ]); } private static function donation(): void { self::render('public_site_text.php', [ 'title' => AppSettings::get('public_site_donation_title'), 'content' => AppSettings::get('public_site_donation_text'), 'kind' => 'donation', ]); } private static function contact(): void { self::render('public_site_contact.php', ['title' => t('public_site.contact')]); } private static function about(): void { if (AppSettings::get('public_site_about_enabled') !== '1') { self::notFound(); return; } self::render('public_site_text.php', [ 'title' => AppSettings::get('public_site_about_title'), 'content' => AppSettings::get('public_site_about_text'), 'kind' => 'about', ]); } private static function legal(): void { self::render('public_site_text.php', [ 'title' => t('public_site.legal'), 'content' => self::legalText(), 'kind' => 'legal', ]); } private static function privacy(): void { self::render('public_site_text.php', [ 'title' => t('public_site.privacy'), 'content' => self::privacyText(), 'kind' => 'privacy', ]); } public static function legalText(?array $settings = null): string { $settings ??= AppSettings::all(); $custom = trim((string) ($settings['public_site_legal_text'] ?? '')); if ($custom !== '') { return $custom; } $address = implode( ', ', array_filter([ $settings['association_address'] ?? '', trim(($settings['association_postal_code'] ?? '') . ' ' . ($settings['association_city'] ?? '')), ]), ) ?: t('public_site.to_complete'); return t('public_site.default_legal', [ 'association' => $settings['association_name'] ?: t('public_site.to_complete'), 'address' => $address, 'email' => $settings['association_email'] ?: t('public_site.to_complete'), 'rna' => $settings['association_rna'] ?: t('public_site.to_complete'), 'siret' => $settings['association_siret'] ?: t('public_site.to_complete'), ]); } public static function privacyText(?array $settings = null): string { $settings ??= AppSettings::all(); $custom = trim((string) ($settings['public_site_privacy_text'] ?? '')); if ($custom !== '') { return $custom; } $address = implode( ', ', array_filter([ $settings['association_address'] ?? '', trim(($settings['association_postal_code'] ?? '') . ' ' . ($settings['association_city'] ?? '')), ]), ) ?: t('public_site.to_complete'); return t('public_site.default_privacy', [ 'association' => $settings['association_name'] ?: t('public_site.to_complete'), 'address' => $address, 'email' => $settings['association_email'] ?: t('public_site.to_complete'), ]); } private static function logo(): void { $path = AssociationBrand::path(); if (!$path) { http_response_code(404); return; } $mime = (new finfo(FILEINFO_MIME_TYPE))->file($path) ?: 'application/octet-stream'; header('Content-Type: ' . $mime); header('Content-Length: ' . filesize($path)); header('Cache-Control: public,max-age=3600'); readfile($path); } private static function animalQuery(string $species, int $limit): array { $sql = "SELECT a.id,a.name,a.species,a.sex,a.birth_date,a.birth_is_estimated,a.birth_estimated_months,a.breed,a.color,a.sterilization_status,a.compatibility_dogs,a.compatibility_cats,a.compatibility_children,a.house_trained,a.website_description,s.name species_name,s.icon species_icon,(SELECT filename FROM animal_photos p WHERE p.animal_id=a.id AND p.is_public=1 ORDER BY p.is_primary DESC,p.id DESC LIMIT 1) primary_photo FROM animals a LEFT JOIN ref_species s ON s.code=a.species WHERE a.website_published=1 AND a.adoption_availability='available' AND a.archived_at IS NULL AND a.deleted_at IS NULL AND a.status NOT IN ('adopte','decede')"; $params = []; if ($species !== '') { $sql .= ' AND a.species=:species'; $params[':species'] = $species; } $sql .= ' ORDER BY a.updated_at DESC,a.name COLLATE NOCASE LIMIT ' . max(1, min(500, $limit)); $s = DB::pdo()->prepare($sql); $s->execute($params); return $s->fetchAll(PDO::FETCH_ASSOC); } private static function species(): array { return DB::pdo() ->query( "SELECT s.code,s.name,s.icon,COUNT(a.id) count FROM ref_species s JOIN animals a ON a.species=s.code AND a.website_published=1 AND a.adoption_availability='available' AND a.archived_at IS NULL AND a.deleted_at IS NULL AND a.status NOT IN ('adopte','decede') GROUP BY s.id ORDER BY s.sort_order,s.name COLLATE NOCASE", ) ->fetchAll(PDO::FETCH_ASSOC); } private static function render(string $view, array $vars = []): void { extract($vars, EXTR_SKIP); $settings = AppSettings::all(); $publicSpecies = self::species(); require dirname(__DIR__) . '/Views/public_site_layout.php'; } private static function notFound(): void { http_response_code(404); self::render('public_site_unavailable.php', ['title' => t('public_site.not_found')]); } }