Ajouter les réseaux sociaux au menu public
This commit is contained in:
parent
86642095cd
commit
9e05196440
8 changed files with 67 additions and 13 deletions
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
Toutes les modifications importantes du projet sont consignées ici. Le format s’inspire de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le projet suit le [versionnage sémantique](https://semver.org/lang/fr/).
|
||||
|
||||
## [1.0.0-rc.33] — 2026-10-11
|
||||
|
||||
### Réseaux sociaux dans le menu public
|
||||
|
||||
- ajout des icônes des réseaux configurés immédiatement après le lien Contact ;
|
||||
- masquage automatique des réseaux sans adresse renseignée ;
|
||||
- ouverture sécurisée dans un nouvel onglet et libellés accessibles aux lecteurs d’écran ;
|
||||
- réutilisation d’un catalogue unique entre le menu et la page Contact.
|
||||
|
||||
## [1.0.0-rc.32] — 2026-10-02
|
||||
|
||||
### Statistiques par mois et par période
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ L’import depuis Animal Shelter Manager a été conçu comme un parcours pruden
|
|||
|
||||
Avant la version 1.0, le projet a fait l’objet d’un nettoyage structurel et d’un découpage de ses fichiers les plus volumineux. La recherche universelle, les salles configurables, les traductions française et anglaise et une suite de contrôles isolés ont complété cette phase de stabilisation.
|
||||
|
||||
## `1.0.0-rc.33` — Réseaux visibles dès la navigation
|
||||
|
||||
Le site public reprend désormais dans son menu les réseaux sociaux renseignés par l’association. Les icônes restent discrètes, accessibles et cohérentes avec les liens déjà présentés sur la page Contact.
|
||||
|
||||
## `1.0.0-rc.32` — Statistiques à la période souhaitée
|
||||
|
||||
Les bilans ne sont plus limités à une année complète. Une association peut désormais analyser un mois précis ou choisir librement deux dates ; les volumes d’activité, profils, coûts vétérinaires et comparaisons utilisent tous le même intervalle.
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ L’option `--apply` ne doit être utilisée qu’après lecture des rapports. L
|
|||
|
||||
## État du projet
|
||||
|
||||
La candidate actuelle est `1.0.0-rc.32`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`.
|
||||
La candidate actuelle est `1.0.0-rc.33`. La branche 1.0 est gelée fonctionnellement : seuls les défauts bloquants découverts pendant les validations terrain sont corrigés avant `1.0.0`.
|
||||
|
||||
Le contrôle de stabilisation est disponible dans [docs/AUDIT-1.0.md](docs/AUDIT-1.0.md).
|
||||
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.0.0-rc.32
|
||||
1.0.0-rc.33
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@
|
|||
<div class="col-lg-6">
|
||||
<section class="card h-100">
|
||||
<div class="card-body">
|
||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.32</span>
|
||||
<h3>Statistiques sur mesure</h3>
|
||||
<p class="text-muted">Bilans par année, par mois ou entre deux dates, avec coûts vétérinaires et
|
||||
comparaison de la période précédente.</p>
|
||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.33</span>
|
||||
<h3>Un site public mieux connecté</h3>
|
||||
<p class="text-muted">Les réseaux sociaux renseignés par l’association rejoignent la navigation,
|
||||
avec des liens discrets, sécurisés et accessibles.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,12 +14,7 @@ $obfuscate = static fn (string$value): string => implode('', array_reverse(mb_st
|
|||
class="contact-obfuscated"><?= h($obfuscate($settings['association_email'])) ?></span></div>
|
||||
<?php endif; ?></div>
|
||||
<div class="social-links">
|
||||
<?php $socialNetworks = [
|
||||
'facebook' => ['Facebook','f'], 'instagram' => ['Instagram','◎'], 'youtube' => ['YouTube','▶'],
|
||||
'tiktok' => ['TikTok','♪'], 'whatsapp' => ['WhatsApp','☎'], 'linkedin' => ['LinkedIn','in'],
|
||||
'x' => ['X','𝕏'], 'snapchat' => ['Snapchat','👻'],
|
||||
];
|
||||
foreach ($socialNetworks as $network => [$label,$icon]):
|
||||
<?php foreach ($socialNetworks as $network => [$label,$icon]):
|
||||
$url = trim((string)($settings['public_site_' . $network . '_url'] ?? ''));
|
||||
if ($url === '') continue; ?>
|
||||
<a class="social-link social-<?= h($network) ?>" href="<?= h($url) ?>" target="_blank" rel="noopener noreferrer"><span class="social-icon" aria-hidden="true"><?= h($icon) ?></span><span><?= h($label) ?></span></a>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ $activeSpecies = (string)($selectedSpecies ?? ($animal['species'] ?? ''));
|
|||
$navClass = static fn (bool$active): string => $active ? ' class="active" aria-current="page"' : '';
|
||||
$fullAddress = implode(', ', array_filter([$settings['association_address'] ?? '',trim(($settings['association_postal_code'] ?? '') . ' ' . ($settings['association_city'] ?? ''))]));
|
||||
$hasContactMap = $activePage === 'contact' && is_numeric($settings['association_latitude'] ?? null) && is_numeric($settings['association_longitude'] ?? null);
|
||||
$socialNetworks = [
|
||||
'facebook' => ['Facebook','f'], 'instagram' => ['Instagram','◎'], 'youtube' => ['YouTube','▶'],
|
||||
'tiktok' => ['TikTok','♪'], 'whatsapp' => ['WhatsApp','☎'], 'linkedin' => ['LinkedIn','in'],
|
||||
'x' => ['X','𝕏'], 'snapchat' => ['Snapchat','👻'],
|
||||
];
|
||||
if (!function_exists('public_age')) {
|
||||
function public_age(array$a): string
|
||||
{
|
||||
|
|
@ -170,6 +175,32 @@ if (!function_exists('public_age')) {
|
|||
color: var(--accent-dark)
|
||||
}
|
||||
|
||||
.nav .nav-social-link {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 36px;
|
||||
color: #fff;
|
||||
font-size: .9rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
box-shadow: 0 3px 9px rgba(0, 0, 0, .12)
|
||||
}
|
||||
|
||||
.nav .nav-social-link:hover,
|
||||
.nav .nav-social-link:focus-visible {
|
||||
color: #fff;
|
||||
filter: brightness(.9);
|
||||
transform: translateY(-1px)
|
||||
}
|
||||
|
||||
.nav .nav-social-snapchat,
|
||||
.nav .nav-social-snapchat:hover,
|
||||
.nav .nav-social-snapchat:focus-visible { color: #111 }
|
||||
|
||||
.nav a.active {
|
||||
font-weight: 800
|
||||
}
|
||||
|
|
@ -493,7 +524,12 @@ if (!function_exists('public_age')) {
|
|||
<a<?= $navClass($activePage === 'donation') ?> href="<?= h($siteUrl('donation')) ?>">
|
||||
<?= h(t('public_site.donation')) ?></a>
|
||||
<a<?= $navClass($activePage === 'contact') ?> href="<?= h($siteUrl('contact')) ?>">
|
||||
<?= h(t('public_site.contact')) ?></a>
|
||||
<?= h(t('public_site.contact')) ?></a><?php foreach ($socialNetworks as $network => [$label,$icon]):
|
||||
$socialUrl = trim((string)($settings['public_site_' . $network . '_url'] ?? ''));
|
||||
if ($socialUrl === '') continue; ?>
|
||||
<a class="nav-social-link nav-social-<?= h($network) ?> social-<?= h($network) ?>"
|
||||
href="<?= h($socialUrl) ?>" target="_blank" rel="noopener noreferrer"
|
||||
aria-label="<?= h($label) ?>" title="<?= h($label) ?>"><span aria-hidden="true"><?= h($icon) ?></span></a><?php endforeach; ?>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -296,6 +296,16 @@ try {
|
|||
$externalImage = PublicContentFormatter::render('');
|
||||
$assert(!str_contains($externalImage, '<img'), 'Une image externe a été acceptée');
|
||||
});
|
||||
$test('Le menu public affiche uniquement les réseaux sociaux configurés', function () use ($assert): void {
|
||||
$layout = file_get_contents(__DIR__ . '/../app/Views/public_site_layout.php');
|
||||
$contact = file_get_contents(__DIR__ . '/../app/Views/public_site_contact.php');
|
||||
$assert(str_contains($layout, '$socialNetworks = ['), 'Le catalogue des réseaux sociaux est absent');
|
||||
$assert(str_contains($layout, "public_site_' . \$network . '_url"), 'Les liens sociaux ne viennent pas des paramètres');
|
||||
$assert(str_contains($layout, 'class="nav-social-link'), 'Les icônes sociales sont absentes du menu');
|
||||
$assert(str_contains($layout, 'aria-label="<?= h($label) ?>"'), 'Les icônes sociales ne sont pas accessibles');
|
||||
$assert(str_contains($layout, 'rel="noopener noreferrer"'), 'Les liens externes ne sont pas protégés');
|
||||
$assert(!str_contains($contact, '$socialNetworks = ['), 'Le catalogue social est encore dupliqué');
|
||||
});
|
||||
$test('Authentification et mot de passe haché', function () use ($db, $assert): void {
|
||||
$assert(ini_get('session.use_strict_mode') === '1', 'Le mode strict des sessions est désactivé');
|
||||
$hash = password_hash('MotDePasseSolide!', PASSWORD_DEFAULT);
|
||||
|
|
|
|||
Loading…
Reference in a new issue