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
|
RewriteCond %{HTTPS} off
|
||||||
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
|
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 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".
|
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>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user