Abonnement agenda (.ics) aux distributions
Un flux iCalendar généré depuis distributions.php, servi sur /calendrier.ics, permet d'ajouter toutes les distributions à venir à son agenda (iPhone, Android, Google Agenda, Outlook). En abonnement webcal://, les agendas suivent les changements de dates sans rien réimporter. Les heures sont converties depuis le champ `horaires` en tenant compte du changement d'heure ; les distributions annulées sont exclues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b705f25eff
commit
b8f55e5609
@@ -4,3 +4,5 @@ ErrorDocument 404 /404.html
|
||||
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
|
||||
|
||||
RewriteRule ^calendrier\.ics$ calendrier.php [L]
|
||||
|
||||
@@ -69,3 +69,14 @@ Le script PHP :
|
||||
- Les horaires propres à chaque date (la Guinguette finit plus tard)
|
||||
|
||||
Les dates sont triées chronologiquement et la première date future devient automatiquement "Prochaine distribution".
|
||||
|
||||
## Abonnement agenda (.ics)
|
||||
|
||||
`calendrier.php` (servi à l'adresse `/calendrier.ics` grâce au `.htaccess`)
|
||||
génère un flux iCalendar à partir de `distributions.php`. Les personnes
|
||||
abonnées depuis le bouton « Ajouter les distributions à mon agenda » voient
|
||||
les modifications arriver automatiquement : il suffit de mettre à jour
|
||||
`distributions.php`. Les distributions annulées ne figurent pas dans le flux.
|
||||
|
||||
Les horaires doivent rester au format `17h-19h` ou `17h-21h30` pour pouvoir
|
||||
être convertis en heures de début et de fin.
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Flux iCalendar (.ics) des distributions, généré depuis distributions.php.
|
||||
*
|
||||
* Utilisable en abonnement (webcal://) : les agendas se mettent à jour
|
||||
* automatiquement quand le fichier de configuration change.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/distributions-utils.php';
|
||||
|
||||
/**
|
||||
* Convertit une date et une heure au format '17h' ou '21h30' en horodatage UTC iCalendar
|
||||
*/
|
||||
function heureVersIcs(string $date, string $heure): string
|
||||
{
|
||||
if (!preg_match('/^(\d{1,2})h(\d{2})?$/', trim($heure), $m)) {
|
||||
throw new InvalidArgumentException("Horaire invalide : $heure");
|
||||
}
|
||||
$dateHeure = new DateTime($date, new DateTimeZone('Europe/Paris'));
|
||||
$dateHeure->setTime((int) $m[1], (int) ($m[2] ?? 0));
|
||||
$dateHeure->setTimezone(new DateTimeZone('UTC'));
|
||||
return $dateHeure->format('Ymd\THis\Z');
|
||||
}
|
||||
|
||||
/**
|
||||
* Échappe un texte pour une valeur iCalendar
|
||||
*/
|
||||
function echapperIcs(string $texte): string
|
||||
{
|
||||
return str_replace(['\\', ';', ',', "\n"], ['\\\\', '\;', '\,', '\n'], $texte);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replie une ligne à 75 octets maximum, sans couper un caractère multi-octets
|
||||
*/
|
||||
function plierLigneIcs(string $ligne): string
|
||||
{
|
||||
$resultat = '';
|
||||
$courante = '';
|
||||
foreach (mb_str_split($ligne) as $caractere) {
|
||||
if (strlen($courante) + strlen($caractere) > 75) {
|
||||
$resultat .= $courante . "\r\n";
|
||||
$courante = ' ';
|
||||
}
|
||||
$courante .= $caractere;
|
||||
}
|
||||
return $resultat . $courante . "\r\n";
|
||||
}
|
||||
|
||||
$lignes = [
|
||||
'BEGIN:VCALENDAR',
|
||||
'VERSION:2.0',
|
||||
'PRODID:-//Les Frouzivores//Distributions//FR',
|
||||
'CALSCALE:GREGORIAN',
|
||||
'METHOD:PUBLISH',
|
||||
'X-WR-CALNAME:Distributions Les Frouzivores',
|
||||
'X-WR-TIMEZONE:Europe/Paris',
|
||||
'REFRESH-INTERVAL;VALUE=DURATION:P1D',
|
||||
'X-PUBLISHED-TTL:P1D',
|
||||
];
|
||||
|
||||
$maintenant = gmdate('Ymd\THis\Z');
|
||||
|
||||
foreach (chargerDistributions() as $distribution) {
|
||||
if ($distribution['type'] === 'annulee') {
|
||||
continue;
|
||||
}
|
||||
|
||||
[$debut, $fin] = explode('-', $distribution['horaires']);
|
||||
$lieu = getLieuInfo($distribution['lieu']);
|
||||
$titre = 'Distribution Les Frouzivores';
|
||||
if (!empty($distribution['label'])) {
|
||||
$titre .= ' – ' . $distribution['label'];
|
||||
}
|
||||
|
||||
$lignes[] = 'BEGIN:VEVENT';
|
||||
$lignes[] = 'UID:distribution-' . $distribution['date'] . '@lesfrouzivores.fr';
|
||||
$lignes[] = 'DTSTAMP:' . $maintenant;
|
||||
$lignes[] = 'DTSTART:' . heureVersIcs($distribution['date'], $debut);
|
||||
$lignes[] = 'DTEND:' . heureVersIcs($distribution['date'], $fin);
|
||||
$lignes[] = 'SUMMARY:' . echapperIcs($titre);
|
||||
if ($lieu) {
|
||||
$lignes[] = 'LOCATION:' . echapperIcs($lieu['nom'] . ', ' . $lieu['adresse'] . ', 31270 Frouzins');
|
||||
}
|
||||
$lignes[] = 'DESCRIPTION:' . echapperIcs(
|
||||
"Distribution mensuelle des commandes passées sur local.direct.\n"
|
||||
. 'Commander : https://www.local.direct/les-frouzivores'
|
||||
);
|
||||
$lignes[] = 'URL:https://lesfrouzivores.fr/';
|
||||
$lignes[] = 'END:VEVENT';
|
||||
}
|
||||
|
||||
$lignes[] = 'END:VCALENDAR';
|
||||
|
||||
header('Content-Type: text/calendar; charset=utf-8');
|
||||
header('Content-Disposition: inline; filename="frouzivores-distributions.ics"');
|
||||
|
||||
foreach ($lignes as $ligne) {
|
||||
echo plierLigneIcs($ligne);
|
||||
}
|
||||
@@ -324,6 +324,32 @@ $prochaineDistribution = trouverProchaineDistribution($distributions);
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="mt-8 text-center">
|
||||
<a
|
||||
href="webcal://lesfrouzivores.fr/calendrier.ics"
|
||||
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-green-600 hover:bg-green-700"
|
||||
>
|
||||
<i class="fas fa-calendar-plus mr-2"></i>
|
||||
Ajouter les distributions à mon agenda
|
||||
</a>
|
||||
<p class="mt-3 text-sm text-gray-500">
|
||||
Ou via
|
||||
<a
|
||||
href="https://calendar.google.com/calendar/render?cid=webcal://lesfrouzivores.fr/calendrier.ics"
|
||||
class="text-green-700 underline hover:text-green-800"
|
||||
>Google Agenda</a
|
||||
>
|
||||
· <a
|
||||
href="/calendrier.ics"
|
||||
download="frouzivores-distributions.ics"
|
||||
class="text-green-700 underline hover:text-green-800"
|
||||
>télécharger le fichier .ics</a
|
||||
>
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
L’agenda se met à jour tout seul si une date change.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user