Live · status OK
Documentation · OW Agenda v1.3.1

OW Agenda
Documentation

La prise de rendez-vous WordPress qui ne laisse personne occuper tes créneaux pour rien.

v1.3.1GPL-2.0-or-laterDocumentation

OW Agenda — Documentation

La prise de rendez-vous en ligne et l'agenda du quotidien, dans WordPress, sans abonnement. Auteur : OptionWeb — Julien Daniel Page du plugin : https://optionweb.dev/fr/addons/ow-agenda/ Licence : GPL-2.0-or-later Version couverte par ce document : 1.3.1


Table des matières

  1. Vue d'ensemble
  2. Installation
  3. Démarrage rapide
  4. Le parcours de réservation
  5. La double vérification par e-mail
  6. La validation des demandes
  7. La page Agenda
  8. Le répertoire clients
  9. Les invitations de séance
  10. E-mails et fichier calendrier (ICS)
  11. Rappels et tâche planifiée
  12. Anti-spam et limites de débit
  13. RGPD
  14. Shortcodes
  15. API REST
  16. Référence des réglages
  17. Données stockées
  18. Internationalisation
  19. Désinstallation
  20. Dépannage
  21. FAQ
  22. Changelog

Vue d'ensemble

OW Agenda ajoute à WordPress un système complet de rendez-vous : une page publique où le visiteur choisit son jour puis son heure, et une page « Agenda » protégée par connexion, pensée pour être utilisée depuis un téléphone, où vous gérez la journée.

Tout tient dans le site. Aucun service externe, aucune API tierce, aucun abonnement : les créneaux sont calculés par votre serveur, les rendez-vous vivent dans vos tables MySQL, les e-mails partent par wp_mail().

Ce qui est livré (tout est gratuit, GPL-2) :

  • Réservation en ligne : choix du jour, choix du créneau, formulaire court, case de consentement RGPD
  • Double opt-in par e-mail : une demande ne devient réelle qu'après clic sur un lien de validation — les bots n'atteignent jamais l'agenda
  • Validation manuelle (recommandée) ou confirmation automatique, avec des boutons Confirmer / Refuser directement dans l'e-mail de notification (liens signés HMAC, sans connexion)
  • Page « Agenda » mobile-first : vue du jour, demandes en attente, rendez-vous téléphoniques ajoutés à la main, blocage de créneaux, replanification, statuts fait / absent
  • Répertoire clients : recherche, création, édition, suppression ; alimenté automatiquement par les rendez-vous validés
  • Invitations de séance : un lien privé permet à un client existant de choisir la date de sa séance, sur un service et une durée que vous fixez
  • E-mails HTML soignés avec pièce jointe ICS (ajout au calendrier du téléphone en un geste), rappel automatique la veille, annulation en ligne par le client
  • Anonymisation automatique des rendez-vous passés, purge des demandes jamais validées
  • API REST complète sous le namespace owag/v1

Ce qu'il ne fait pas, délibérément :

  • Pas de paiement en ligne : OW Agenda prend le rendez-vous, il n'encaisse pas d'acompte (utilisez WooCommerce si c'est votre besoin)
  • Un seul agenda : pas de gestion multi-praticiens ni multi-ressources ; le plugin décrit un planning, celui du site
  • Pas de synchronisation bidirectionnelle avec Google Agenda ou Outlook : le calendrier du client est mis à jour par fichier ICS (aller simple), vos indisponibilités personnelles se saisissent comme des blocages
  • Pas de catalogue de services côté visiteur : la réservation publique porte sur une prestation unique (libellé et durée configurables) ; les autres prestations passent par les invitations de séance
  • Pas de rendez-vous récurrents, pas de visioconférence, pas de salle d'attente

Cette liste est un choix d'architecture, pas une roadmap masquée : un agenda simple qui ne se trompe jamais de créneau vaut mieux qu'une usine à gaz que personne n'ose toucher.


Installation

Depuis le .zip

  1. Téléchargez ow-agenda-1.3.1.zip depuis https://optionweb.dev/fr/addons/ow-agenda/
  2. Extensions → Ajouter → Téléverser une extension
  3. Choisissez le fichier, cliquez sur Installer, puis sur Activer

Par FTP

Décompressez l'archive et déposez le dossier ow-agenda dans /wp-content/plugins/, puis activez l'extension depuis Extensions.

Prérequis

  • WordPress 6.3 ou plus récent
  • PHP 7.4 ou plus récent (8.1+ recommandé)
  • MySQL 5.7+ / MariaDB 10.2+ — les fonctions GET_LOCK() / RELEASE_LOCK() sont utilisées pour sérialiser les réservations simultanées
  • Un envoi d'e-mails qui fonctionne. C'est la seule dépendance réelle du plugin : tout le parcours (validation, confirmation, rappel, annulation) passe par l'e-mail. Sur un hébergement mutualisé, installez un plugin SMTP avant de mettre l'agenda en ligne.

Ce qui est installé

À l'activation, OW Agenda crée trois tables :

TableContenu
{prefix}_owag_appointmentsTous les événements de l'agenda : consultations web, séances issues d'une invitation, rendez-vous saisis à la main, blocages de créneaux
{prefix}_owag_invitesLes liens privés « choisissez la date de votre séance »
{prefix}_owag_customersLe répertoire clients

…et, la première fois seulement, deux pages publiées :

  • « Réserver un rendez-vous » — contient le shortcode [ow_agenda]
  • « Agenda » — contient le shortcode [ow_agenda_admin]

Leurs identifiants sont mémorisés dans les options owag_page_booking et owag_page_admin ; le drapeau owag_pages_created garantit qu'une désactivation suivie d'une réactivation ne recrée pas de doublons. Vous pouvez renommer ces pages, les déplacer dans un menu, changer leur permalien : le plugin les suit par leur ID.

L'activation ajoute aussi :

  • la capacité owag_manage au rôle administrateur — c'est elle, et non manage_options, qui ouvre l'agenda et les routes REST d'administration ;
  • les réglages par défaut dans l'option owag_settings ;
  • la tâche planifiée horaire owag_hourly_event (première exécution 5 minutes après l'activation).

Un secret HMAC (owag_admin_secret, 64 caractères, non chargé automatiquement) est généré à la première notification admin : c'est lui qui signe les boutons Confirmer / Refuser des e-mails.

Donner l'accès à quelqu'un d'autre

La page Agenda est visible par tout utilisateur connecté disposant de owag_manage. Pour ouvrir l'agenda à une assistante sans lui donner les clés du site :

// À exécuter une fois (mu-plugin, ou plugin de gestion des rôles).
$role = get_role( 'editor' );
if ( $role ) { $role->add_cap( 'owag_manage' ); }

Démarrage rapide

Après activation, rendez-vous dans OW Agenda (menu principal de l'administration). L'écran de réglages tient en une page ; cinq minutes suffisent.

1. Les horaires d'ouverture

Pour chaque jour de la semaine, saisissez une plage (09:00 → 18:00) et, si vous coupez le midi, une seconde plage (14:00 → 18:30). Un jour laissé vide est un jour fermé. Par défaut : du lundi au vendredi, 09:00–18:00 ; samedi et dimanche fermés.

Les horaires sont interprétés dans le fuseau du site (Réglages → Général → Fuseau horaire). Vérifiez-le avant d'ouvrir la réservation : toutes les dates sont stockées en heure locale.

2. La prestation réservable en ligne

  • Libellé de la consultation — ce que le visiteur voit en titre du widget (« Consultation », « Premier rendez-vous », « Bilan »…)
  • Durée de la consultation — 60 minutes par défaut. C'est cette durée qui est bloquée dans l'agenda.
  • Granularité des créneaux — l'écart entre deux heures proposées (30 minutes par défaut). Avec une durée de 60 et une granularité de 30, vous proposez 9:00, 9:30, 10:00… et le premier rendez-vous pris à 9:30 supprime 9:00 et 10:00 de la liste.
  • Battement entre deux rendez-vous — minutes de respiration ajoutées de part et d'autre de chaque créneau candidat (0 par défaut).

3. Les délais

  • Délai minimum de réservation — 24 h par défaut : un visiteur ne peut pas réserver pour dans une heure.
  • Horizon de réservation — 60 jours par défaut : au-delà, plus rien n'est proposé.
  • Annulation en ligne par le client — jusqu'à 24 h avant le rendez-vous ; passé ce délai, la page d'annulation invite à téléphoner.

4. La validation et les e-mails

  • Confirmation des demandes : validation manuelle (recommandé) ou confirmation automatique.
  • Double vérification e-mail : laissez-la cochée. C'est la meilleure protection anti-bot du plugin.
  • Notifications admin envoyées à : votre adresse. Par défaut, l'e-mail d'administration du site.

Enregistrez, puis ouvrez la page « Réserver un rendez-vous » : le widget affiche les 14 prochains jours et vos créneaux libres. Vous êtes en ligne.

5. L'agenda sur le téléphone

Ouvrez la page « Agenda » sur votre téléphone, connectez-vous, puis utilisez « Ajouter à l'écran d'accueil ». Le plugin publie un manifeste d'application : la page s'ouvre alors en plein écran, sans barre de navigateur, comme une application.


Le parcours de réservation

Côté visiteur, la page de réservation est un composant autonome (JavaScript sans framework, moins de 10 Ko) qui parle aux endpoints publics owag/v1/public/*.

Étape 1 — le jour

Au chargement, le widget demande les disponibilités sur 14 jours à partir d'aujourd'hui et affiche une bande de jours cliquables. Un jour sans créneau libre est grisé et non cliquable. Le bouton « Voir les dates suivantes » charge les 14 jours suivants, autant de fois que nécessaire jusqu'à l'horizon configuré.

Les dates du widget sont construites en heure locale du navigateur, jamais en UTC. C'est un correctif de la 1.3.0 : auparavant, un visiteur situé à l'est de UTC ouvrant la page peu après minuit voyait la liste commencer la veille et perdait une journée de disponibilité.

Étape 2 — l'heure

Le clic sur un jour appelle /public/slots et affiche les heures de début libres. Un créneau est proposé quand toutes ces conditions sont remplies :

  1. il tombe dans une plage d'ouverture du jour concerné ;
  2. le rendez-vous entier (début + durée) tient avant la fermeture ;
  3. il commence après maintenant + délai minimum ;
  4. le jour est dans l'horizon de réservation ;
  5. il ne chevauche aucun événement actif, battement compris — et « événement actif » inclut les rendez-vous confirmés, les demandes en attente, les blocages, les rendez-vous téléphoniques saisis à la main, et les demandes non encore vérifiées qui retiennent encore leur créneau.

C'est le point important : tout ce qui occupe l'agenda ferme le créneau en ligne. Un rendez-vous pris au téléphone et noté dans l'agenda disparaît immédiatement des créneaux proposés sur le site.

Étape 3 — le formulaire

Quatre champs, dont trois obligatoires : nom (2 caractères minimum), téléphone (8 chiffres minimum), e-mail (validé), message libre. Une case de consentement RGPD, non cochée par défaut, est obligatoire.

Le formulaire embarque en plus deux champs invisibles pour le visiteur — un piège à robots et un horodatage d'ouverture de page — décrits dans la section Anti-spam.

Étape 4 — la réponse

Selon votre configuration, trois issues :

ConfigurationStatut crééMessage affiché
Double vérification e-mail active (défaut)unverified« Un dernier geste : un e-mail vient de vous être envoyé… »
Vérification désactivée + validation manuellepending« Votre demande est bien reçue ! »
Vérification désactivée + confirmation automatiqueconfirmed« Votre rendez-vous est confirmé ! »

Si le créneau a été pris entre l'affichage de la liste et l'envoi du formulaire, le visiteur reçoit un message explicite (owag_slot_taken, HTTP 409) et la liste des jours se recharge automatiquement. Deux visiteurs ne peuvent pas obtenir le même créneau : la section critique est sérialisée par un verrou nommé MySQL, propre à votre base et à votre préfixe de tables, ce qui tient même sur un hébergement mutualisé.


La double vérification par e-mail

C'est la fonctionnalité qui distingue OW Agenda d'un formulaire de contact déguisé.

Quand Double vérification e-mail est active (réglage email_verify, activé par défaut) :

  1. La demande est enregistrée avec le statut unverified. L'administrateur n'est pas notifié. Le rendez-vous n'apparaît pas comme une demande à traiter.
  2. Le visiteur reçoit un e-mail « Un dernier geste… » contenant un bouton de validation.
  3. Le créneau est retenu 45 minutes. Passé ce délai sans validation, il redevient disponible pour tout le monde — un bot ne peut donc pas geler votre journée.
  4. Le clic sur le bouton ouvre une page autonome (?owag_verify=<uid>) qui affiche le récapitulatif du rendez-vous et un bouton « Je confirme ma demande ». La validation ne part qu'au clic : les antivirus et scanners de liens qui visitent les URL des e-mails ne valident rien.
  5. La demande devient alors pending (validation manuelle) ou confirmed (confirmation automatique), le client reçoit l'e-mail correspondant, et c'est seulement là que la notification admin part.
  6. Les demandes jamais validées sont supprimées au bout de 48 h par la tâche horaire. Elles ne laissent aucune trace ni aucune donnée personnelle.

Si, entre l'envoi et le clic, le créneau a été pris par quelqu'un d'autre (la fenêtre de 45 minutes est passée), la validation renvoie une erreur claire et la demande est annulée — jamais de rendez-vous fantôme sur un créneau déjà occupé.

Conséquence pratique : une adresse e-mail invalide ne produit aucun rendez-vous. Vous ne perdez plus de temps à rappeler des numéros bidon. Le prix à payer est un clic supplémentaire pour le client ; l'expérience montre que le taux d'abandon est très inférieur au taux de faux rendez-vous qu'il évite.

Les réservations issues d'une invitation de séance sautent cette étape : le client a déjà prouvé qu'il lisait sa boîte mail en cliquant sur le lien privé qu'il a reçu.


La validation des demandes

Validation manuelle (recommandée)

Réglage par défaut : auto_confirm = 0. Une demande vérifiée arrive avec le statut pending. Rien n'est promis au client tant que vous n'avez rien fait — mais le créneau est déjà bloqué, personne ne peut vous le doubler.

Vous recevez un e-mail « À confirmer : » contenant le récapitulatif complet (service, date, nom, téléphone, e-mail, message du client) et deux boutons :

  • Confirmer le rendez-vous — cyan
  • Refuser — contour rouge

Les boutons de l'e-mail : liens signés HMAC

Ces boutons ne demandent aucune connexion. Ils pointent vers votre site avec trois paramètres : owag_admin_action (confirm ou refuse), uid (l'identifiant aléatoire du rendez-vous) et sig, une signature HMAC-SHA256 de uid|action calculée avec le secret privé du site (owag_admin_secret).

  • La signature est vérifiée avant l'affichage du moindre détail : un lien trafiqué n'affiche rien d'autre que « Lien invalide ».
  • La comparaison utilise hash_equals() — pas de fuite d'information par le temps de réponse.
  • La page affiche un récapitulatif et un bouton ; l'action ne s'exécute qu'au clic. Un scanner de liens ne confirme donc rien à votre place.
  • Une demande déjà traitée renvoie « Déjà traité (statut : … ) » plutôt qu'une erreur.

Confirmer envoie automatiquement l'e-mail de confirmation au client, avec le fichier calendrier. Refuser envoie l'e-mail d'annulation. Dans les deux cas, l'agenda est à jour avant que vous n'ayez rangé votre téléphone.

Ces liens restent valides tant que le rendez-vous est en attente. Si vous supprimez l'option owag_admin_secret (restauration partielle de base, nettoyage manuel), tous les liens déjà envoyés cessent de fonctionner : un nouveau secret est généré et les anciennes signatures ne correspondent plus. Passez alors par la page Agenda.

Confirmation automatique

Réglage auto_confirm = 1. Un créneau libre est confirmé immédiatement : le client reçoit sa confirmation avec le fichier calendrier, vous recevez une notification « Nouveau rendez-vous ». À réserver aux activités où tout créneau ouvert est un créneau acceptable.

Les deux réglages se combinent avec la double vérification : même en confirmation automatique, la demande passe d'abord par unverified si email_verify est actif.


La page Agenda

La page « Agenda » ([ow_agenda_admin]) est l'outil de tous les jours. Elle est conçue pour le téléphone d'abord : boutons larges, une colonne, pas de tableau à faire défiler horizontalement, et elle fonctionne évidemment aussi bien sur un écran d'ordinateur.

Elle est protégée : un visiteur non connecté voit un lien de connexion, un utilisateur connecté sans la capacité owag_manage voit « L'accès est réservé à l'équipe ». La page est en noindex,nofollow : elle ne se retrouvera jamais dans Google.

Onglet Agenda — la vue du jour

La journée en cours, événement par événement, avec l'heure, la durée, le libellé, le statut et l'origine (site, invitation, saisie manuelle). Navigation d'un jour à l'autre, bouton Aujourd'hui. Sur chaque rendez-vous :

  • Détails — coordonnées du client, message laissé lors de la réservation, et un bloc de notes internes (jamais visibles par le client) enregistrées sans envoyer le moindre e-mail
  • Confirmer ✓ — pour une demande encore en attente ; l'e-mail de confirmation part
  • Fait / Absent (no-show) — statuts de fin de rendez-vous
  • Déplacer — nouvelle date, nouvelle heure, durée éventuellement modifiée. Le plugin vous demande si le client doit être prévenu par e-mail ; si oui, il reçoit une confirmation à jour et un fichier calendrier qui corrige l'événement déjà présent sur son téléphone
  • Annuler le rendez-vous — avec, là encore, le choix de prévenir ou non le client
  • Supprimer le blocage — pour un créneau bloqué

Quand une action crée un chevauchement (déplacement sur un créneau occupé), le plugin prévient et demande confirmation : vous pouvez forcer. C'est votre planning, vous savez ce que vous faites — le garde-fou n'est bloquant que pour les visiteurs.

Ajouter un rendez-vous ou un blocage

Le bouton « Ajouter un rendez-vous » ouvre une fiche unique qui sert deux usages :

  • Rendez-vous — le client vous a appelé. Nom, téléphone, e-mail (facultatif), service, date, heure, durée, notes internes. Si vous renseignez une adresse e-mail et cochez l'option, le client reçoit la confirmation avec son fichier calendrier, comme s'il avait réservé en ligne. Le rendez-vous alimente aussi le répertoire clients.
  • Blocage (congé, pause…) — un motif, une date, une heure, une durée. Aucun e-mail, aucune fiche client, mais le créneau disparaît immédiatement de la réservation en ligne. C'est ainsi qu'on pose une semaine de congés, une formation, ou le créneau de midi qu'on ne veut pas voir réservé.

Onglet En attente

La liste des demandes vérifiées qui attendent votre décision, tous jours confondus (les rendez-vous déjà passés en sont exclus). Deux boutons par ligne : Confirmer et Refuser. C'est le premier écran à ouvrir le matin.

Astuce : l'agenda comme application

Sur la page Agenda, le plugin déclare un manifeste d'application web (?owag_manifest=1) avec le nom du site, l'icône du site et la couleur de thème. Sur Android comme sur iOS, le menu du navigateur propose alors « Ajouter à l'écran d'accueil » : vous obtenez une icône, un lancement plein écran, et plus jamais l'URL à retaper.


Le répertoire clients

Le troisième onglet de la page Agenda est un répertoire léger, volontairement minimal : nom, téléphone, e-mail, notes libres.

  • Il se remplit tout seul. Dès qu'un rendez-vous est validé (réservation en ligne vérifiée, séance sur invitation, saisie manuelle), le client est rapproché d'une fiche existante par son e-mail, puis par son téléphone. S'il n'existe pas, la fiche est créée ; s'il existe, seuls les champs vides sont complétés — le plugin n'écrase jamais ce que vous avez saisi à la main.
  • Recherche sur le nom, le téléphone ou l'e-mail (200 fiches affichées au maximum).
  • Création et édition manuelles, avec un champ de notes pour l'historique, les préférences, les précautions à prendre.
  • Deux raccourcis par fiche : Nouveau rendez-vous (pré-remplit la fiche de saisie manuelle) et Inviter pour une séance (pré-remplit l'invitation).
  • Suppression d'une fiche : les rendez-vous passés restent dans l'agenda, seule la fiche du répertoire disparaît.

Ce répertoire n'est pas un CRM et ne prétend pas l'être. Il répond à une question : « C'est qui, déjà, la dame de 14 h ? »


Les invitations de séance

Le cas d'usage : un client existant a droit à une séance — un suivi, une retouche, une prestation vendue par ailleurs — et vous voulez qu'il choisisse sa date sans passer par la réservation publique, sur un service et une durée que vous fixez.

Depuis l'onglet Invitations :

  1. Renseignez le nom du client, l'intitulé de la séance (obligatoires), sa durée (15 à 720 minutes) et, si vous le souhaitez, son e-mail et son téléphone.
  2. Choisissez la validité du lien : 60 jours par défaut, de 1 à 365.
  3. Cochez éventuellement « Envoyer le lien au client par e-mail » — le client reçoit un e-mail « Choisissez la date de votre séance » avec l'intitulé, la durée, la date d'expiration et un bouton.
  4. Sinon, copiez le lien et envoyez-le par SMS, WhatsApp, ou par le bouton de partage natif du téléphone.

Le lien pointe vers votre page de réservation avec un jeton privé (?owag_invite=<token>, 32 caractères hexadécimaux tirés d'un générateur cryptographique). Le widget affiche alors l'intitulé et la durée de la séance, et non la consultation standard. Le client remplit le même formulaire court.

Règles qui gouvernent un lien d'invitation :

  • Usage unique. Une fois la séance réservée, le lien répond « Ce lien a déjà été utilisé » (HTTP 410). Deux ouvertures simultanées avec le même jeton ne peuvent pas créer deux séances : la consommation du jeton se fait sous le même verrou que la réservation du créneau.
  • Daté. Passée l'expiration, le lien invite à vous recontacter (HTTP 410).
  • Sans double opt-in. Le client a déjà reçu ce lien par un canal que vous maîtrisez : la séance est créée directement en confirmed, avec confirmation et fichier calendrier.
  • Recyclé en cas d'annulation. Si la séance est annulée (par vous ou par le client), l'invitation est libérée : le lien redevient actif et l'invitation réapparaît dans la liste des invitations ouvertes. C'est un correctif de la 1.3.0 — auparavant, une annulation tuait définitivement le lien et il fallait en refaire un.
  • Révocable. Supprimer une invitation depuis la liste désactive le lien immédiatement.

E-mails et fichier calendrier (ICS)

Les e-mails transactionnels

Tous les messages sont en HTML, sur une mise en page en tableaux compatible Gmail et Outlook : carte blanche sur fond clair, nom du site en en-tête, encadré récapitulatif à liseré cyan, boutons pilule. Ils sont envoyés par wp_mail(), donc respectent votre configuration SMTP et les filtres WordPress habituels.

E-mailDestinataireDéclencheur
Validation de la demandeClientRéservation web quand la double vérification est active
Demande reçueClientDemande validée, en attente de votre décision
Rendez-vous confirméClientConfirmation (manuelle, automatique, saisie admin, déplacement) — avec pièce jointe ICS
Rendez-vous annuléClientAnnulation par le client, refus ou annulation par l'admin — avec pièce jointe ICS d'annulation
RappelClientAutomatiquement, entre 12 et 36 h avant le rendez-vous
Choisissez la date de votre séanceClientCréation d'une invitation avec envoi par e-mail
À confirmer : …AdminNouvelle demande vérifiée en attente — avec les boutons Confirmer / Refuser
Nouveau rendez-vous : …AdminNouveau rendez-vous confirmé (confirmation automatique, invitation)
Annulation client : …AdminLe client a annulé lui-même depuis son e-mail

L'adresse de notification admin est le réglage notify_email (par défaut l'e-mail d'administration du site). Les e-mails client ne partent que si l'adresse est valide, et jamais pour un blocage de créneau.

Les e-mails de confirmation et d'annulation rappellent au client qu'il peut annuler en ligne, avec le nombre d'heures de préavis effectivement configuré.

La pièce jointe ICS

Le fichier .ics joint aux confirmations est ce qui transforme un e-mail en rendez-vous réellement présent dans le téléphone du client : un geste sur la pièce jointe, et l'événement est dans son calendrier, avec son propre rappel.

Le fichier est conforme à la RFC 5545 et contient ce qu'il faut pour que les clients de calendrier (Google, Outlook, Apple) traitent correctement les mises à jour :

  • METHOD:REQUEST à la création et à chaque révision d'horaire, METHOD:CANCEL à l'annulation ;
  • un UID stable dérivé de l'identifiant du rendez-vous et du domaine du site — c'est lui qui permet à une révision de remplacer l'événement existant au lieu d'en créer un second ;
  • un SEQUENCE croissant dérivé de la date de dernière modification, ramenée à un instant absolu : le passage à l'heure d'hiver ne peut pas faire reculer le numéro de révision (et l'annulation ajoute 1, pour rester supérieure à une révision survenue dans la même seconde) ;
  • ORGANIZER (votre adresse de notification) et ATTENDEE (le client) ;
  • DTSTART / DTEND en UTC, STATUS:CONFIRMED ou STATUS:CANCELLED ;
  • pliage des lignes à 75 octets sans jamais couper un caractère UTF-8.

Un déplacement de rendez-vous confirmé envoie donc un nouveau fichier qui corrige l'événement déjà présent chez le client ; une annulation envoie un fichier qui le supprime. C'est un correctif de la 1.3.0 : auparavant l'ancien horaire restait au calendrier du client.

Le fichier est écrit dans le répertoire temporaire du serveur — jamais dans wp-content/uploads/, qui est servi publiquement — via l'API fichiers de WordPress, et il est effacé aussitôt l'envoi terminé, y compris si l'envoi échoue. Il porte le nom et l'e-mail du client : il n'a rien à faire sur le disque une seconde de plus que nécessaire.


Rappels et tâche planifiée

Une seule tâche planifiée, owag_hourly_event, exécutée toutes les heures. Elle fait trois choses.

1. Le rappel de la veille

Le rappel part quand le rendez-vous commence dans 12 à 36 heures. Ce n'est pas une fenêtre horaire fixe, et c'est délibéré : WP-Cron ne se déclenche qu'à la visite d'une page. Un site sans trafic le soir perdait définitivement les rappels de la journée suivante. Avec une fenêtre glissante, le rappel part à la première exécution disponible et rattrape le retard.

Conditions : rendez-vous confirmé, adresse e-mail renseignée, ce n'est pas un blocage, et le rappel n'a pas déjà été envoyé.

Chaque rappel est réservé de façon atomique avant l'envoi (un UPDATE conditionnel sur le drapeau reminder_sent) : deux exécutions de cron qui se chevauchent ne peuvent pas envoyer deux fois le même message. Si l'envoi échoue, la réservation est rendue et le rappel sera retenté à l'exécution suivante. Un verrou court (10 minutes) évite en plus que deux passages simultanés parcourent la même liste.

Déplacer un rendez-vous remet le drapeau à zéro : le client reçoit bien un rappel pour le nouvel horaire.

2. La purge des demandes non validées

Les demandes restées unverified depuis plus de 48 heures sont supprimées. (Leur créneau, lui, était déjà rendu disponible après 45 minutes.)

3. L'anonymisation RGPD

Voir la section RGPD.

Le filet de sécurité

Un événement WP-Cron peut disparaître : optimisation de base de données, restauration partielle, plugin de nettoyage, réinitialisation de l'option cron. OW Agenda vérifie à chaque chargement que la tâche est bien programmée et la reprogramme sinon. Rappels, purge et rétention ne peuvent pas s'arrêter en silence.

Utiliser un vrai cron système

WP-Cron dépend du trafic. Sur un site peu visité, préférez un cron système :

// wp-config.php
define( 'DISABLE_WP_CRON', true );
# crontab -e — toutes les 15 minutes
*/15 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null

Anti-spam et limites de débit

La réservation est publique par nature : pas de compte, pas de connexion, pas de nonce possible. Cinq protections indépendantes travaillent donc en amont.

1. Le piège à robots (honeypot)

Le formulaire contient un champ texte hors écran, invisible pour un humain et appétissant pour un robot. Le champ doit être présent et vide. Un champ rempli — ou absent, ce qui trahit un envoi direct à l'API — renvoie un refus sec (HTTP 403), sans détail exploitable.

2. Le temps de remplissage

La page transmet l'horodatage de son affichage. Une soumission arrivée moins de 4 secondes après le chargement est refusée. Là encore, un horodatage absent ou malformé est un refus : en 1.3.0, ces deux champs sont devenus obligatoires — les omettre désactivait purement et simplement le contrôle.

3. Le quota horaire par adresse

5 réservations par heure et par portée. En IPv4, la portée est l'adresse ; en IPv6, c'est le préfixe /64 — un attaquant y dispose de 2⁶⁴ adresses, compter l'adresse entière ne limiterait rien.

Le compteur n'est incrémenté qu'après la validation du formulaire : un visiteur qui se trompe d'e-mail trois fois de suite n'est plus bloqué une heure pour rien.

4. Le plafond quotidien de demandes non vérifiées

Au maximum 3 demandes en attente de validation e-mail peuvent retenir un créneau sur une même journée. Sans ce plafond, quelques envois suffisaient à geler tous les créneaux d'une date. Au-delà, le visiteur reçoit une invitation à réessayer plus tard ou à choisir un autre jour (HTTP 429).

5. Le verrou de réservation

La réservation d'un créneau et la consommation d'une invitation se font dans une section critique sérialisée par un verrou nommé MySQL, dont le nom est dérivé de votre base et de votre préfixe de tables (deux sites d'un même hébergement mutualisé ne se bloquent donc pas mutuellement). Si le verrou n'est pas obtenu en 5 secondes, la requête renvoie un HTTP 503 explicite plutôt qu'un double rendez-vous.

Ce verrou remplace, depuis la 1.3.0, une transaction qui reposait sur un SELECT nu : sous REPEATABLE READ, il ne pose aucun verrou de ligne, et deux visiteurs simultanés pouvaient réserver le même horaire.

Et la double vérification

C'est la couche la plus efficace : même en franchissant tout ce qui précède, un robot n'atteint jamais votre agenda ni votre boîte mail, puisque la notification admin n'est envoyée qu'après clic sur un lien reçu par e-mail. Voir La double vérification par e-mail.


RGPD

Un agenda de rendez-vous manipule des données de santé potentielles, des coordonnées et des historiques. Les réglages par défaut sont volontairement conservateurs.

Consentement

La case de consentement est obligatoire et non cochée par défaut sur le formulaire public : « J'accepte que mes coordonnées soient utilisées pour la gestion de mon rendez-vous. » Une réservation sans consentement est refusée côté serveur, pas seulement côté navigateur.

Minimisation

  • Aucune adresse IP n'est stockée en base. Le quota horaire utilise un compteur temporaire (transient) dont la clé est un condensé de la portée réseau, purgé au bout d'une heure.
  • Aucun user agent, aucun traceur, aucun cookie déposé par le plugin.
  • Aucune donnée n'est envoyée à un service tiers : pas de CDN, pas d'appel externe, pas de télémétrie.
  • Les demandes jamais validées sont supprimées après 48 heures — elles ne laissent aucune donnée derrière elles.
  • Les rendez-vous annulés ne sont plus renvoyés par l'API de l'agenda : les données personnelles inutiles ne circulent pas.

Anonymisation automatique

Réglage retention_months, 24 mois par défaut. Chaque heure, les rendez-vous terminés depuis plus longtemps que ce délai sont anonymisés sur place : le nom est remplacé par « Anonymisé », le téléphone et l'e-mail sont vidés, le message du client et les notes internes sont effacés. La ligne reste, ce qui préserve l'historique statistique de l'activité (combien de rendez-vous, quand, pour quelle prestation) sans conserver la moindre donnée personnelle.

Mettez 0 pour désactiver l'anonymisation automatique — en connaissance de cause.

À noter : l'anonymisation cible les rendez-vous porteurs d'une adresse e-mail. Un rendez-vous saisi à la main sans e-mail (nom et téléphone seulement) n'est pas atteint par la purge automatique ; supprimez-le manuellement si votre politique de rétention l'exige.

Droit d'accès et droit à l'effacement

Le plugin ne branche pas les exporteurs/effaceurs natifs de WordPress (ceux-ci travaillent sur des comptes utilisateurs, or les clients n'en ont pas). Pour traiter une demande :

  • Accès — recherchez la personne dans le répertoire clients (nom, téléphone ou e-mail) et parcourez ses rendez-vous dans l'agenda. Les administrateurs de base de données peuvent aussi interroger directement {prefix}_owag_appointments et {prefix}_owag_customers sur la colonne customer_email / email.
  • Effacement — supprimez la fiche du répertoire, puis annulez ou laissez anonymiser les rendez-vous concernés. Une suppression immédiate se fait en base sur customer_email.
-- Effacement manuel des données personnelles d'une personne (adaptez le préfixe).
UPDATE wp_owag_appointments
   SET customer_name = 'Anonymisé', customer_phone = '', customer_email = '',
       customer_msg = NULL, admin_notes = NULL
 WHERE customer_email = 'personne@example.com';

DELETE FROM wp_owag_customers WHERE email = 'personne@example.com';

Registre des traitements

Pour votre registre : finalité gestion des rendez-vous ; base légale consentement (réservation en ligne) ou exécution d'un contrat / mesures précontractuelles (rendez-vous pris par téléphone) ; catégories identité, coordonnées, contenu du message, notes internes ; durée 24 mois par défaut, puis anonymisation ; destinataires aucun sous-traitant hors votre hébergeur et votre service d'envoi d'e-mails.


Shortcodes

[ow_agenda] — la réservation visiteur

À placer sur une page publique. Aucun attribut : la prestation, la durée et les horaires viennent des réglages. Le shortcode charge sa feuille de style et son script (moins de 12 Ko au total, sans dépendance) et rend un conteneur qui se remplit après le premier appel à l'API.

[ow_agenda]

C'est ce shortcode qui interprète le paramètre d'URL ?owag_invite=<token> : la même page sert la consultation standard et les séances sur invitation.

[ow_agenda_admin] — la page de gestion

À placer sur une page dédiée. Aucun attribut. Le shortcode gère lui-même l'accès :

  • visiteur non connecté → lien de connexion renvoyant vers la page ;
  • connecté sans la capacité owag_manage → message d'accès restreint ;
  • connecté avec la capacité → l'interface complète (agenda, en attente, clients, invitations).
[ow_agenda_admin]

Les deux pages sont créées automatiquement à l'activation avec ces shortcodes. Si vous les avez supprimées, il suffit de créer une page et d'y coller le shortcode — pensez alors à mettre à jour l'option owag_page_admin (ou owag_page_booking) pour que les liens des e-mails et le manifeste pointent au bon endroit.

Les pages autonomes

Quatre paramètres d'URL, interceptés sur n'importe quelle page du site, produisent des pages autonomes servies hors du thème (rendu minimal, noindex,nofollow) :

URLRôle
?owag_verify=<uid>Validation de la demande par le client (double opt-in)
?owag_cancel=<uid>Annulation en ligne par le client
?owag_admin_action=confirm|refuse&uid=…&sig=…Confirmation / refus depuis l'e-mail admin
?owag_manifest=1Manifeste d'application de la page Agenda

Ces pages n'exécutent jamais d'action au chargement : elles affichent un récapitulatif et un bouton. Aucun robot, aucun antivirus de messagerie, aucun prefetch de navigateur ne peut confirmer ou annuler un rendez-vous à la place d'un humain.


API REST

Tous les endpoints vivent sous le namespace owag/v1, à la racine REST habituelle (https://example.com/wp-json/owag/v1/…).

Endpoints publics

Aucune authentification : ce sont les routes du widget de réservation et des liens reçus par e-mail. Leur sécurité repose sur des jetons aléatoires (32 caractères hexadécimaux issus d'un générateur cryptographique), sur des signatures HMAC, et sur les protections décrites dans Anti-spam.

MéthodeCheminParamètresRôle
GET/public/daysfrom (Y-m-d, défaut : aujourd'hui), token (invitation, facultatif)Renvoie label, duration et days : 14 jours à partir de from, chacun avec son nombre de créneaux libres
GET/public/slotsdate (Y-m-d, requis), tokenRenvoie slots : les heures de début libres (HH:MM)
POST/public/bookdate, time, name, phone, email, message, consent, website (piège), ts (horodatage), tokenCrée la demande ; renvoie status et le message à afficher
POST/public/verifyuidValide une demande unverified
POST/public/canceluidAnnule un rendez-vous pending ou confirmed, dans la limite du préavis
POST/public/admin-actionuid, action (confirm|refuse), sigConfirme ou refuse depuis l'e-mail admin (signature HMAC)

Exemple de réservation :

curl -X POST https://example.com/wp-json/owag/v1/public/book \
  -H 'Content-Type: application/json' \
  -d '{
    "date": "2026-09-15",
    "time": "14:30",
    "name": "Marie Dupont",
    "phone": "0470 12 34 56",
    "email": "marie@example.com",
    "message": "Première visite",
    "consent": 1,
    "website": "",
    "ts": 1757942400
  }'
{
  "ok": true,
  "status": "unverified",
  "message": "Un dernier geste : un e-mail vient de vous être envoyé…"
}

Endpoints d'administration

Tous exigent la capacité owag_manage et un nonce REST WordPress valide (en-tête X-WP-Nonce).

MéthodeCheminParamètresRôle
GET/admin/agendafrom, to (Y-m-d)events (la plage demandée, annulés exclus) et pending (toutes les demandes en attente à venir)
POST/admin/appointmentkind (manual|block|consult|seance), date, time, duration (5–720 min), label, name, phone, email, notes, force, send_emailCrée un rendez-vous manuel ou un blocage
POST/admin/appointment/{id}status (pending|confirmed|cancelled|done|noshow), name, phone, email, label, notes, date, time, duration, force, silentMet à jour, déplace, change le statut. silent=1 supprime tout envoi d'e-mail
GET/admin/customersq (recherche)Le répertoire (200 fiches maximum)
POST/admin/customersname (requis), phone, email, notesCrée une fiche
POST/admin/customers/{id}idemMet à jour une fiche
DELETE/admin/customers/{id}Supprime une fiche
GET/admin/invitesLes invitations ouvertes (jamais utilisées ou libérées, non expirées)
POST/admin/invitesname (requis), label (requis), duration (15–720), days_valid (1–365, défaut 60), email, phone, send_emailCrée une invitation ; renvoie url et email_sent
DELETE/admin/invites/{id}Supprime une invitation
GET/admin/settingsLes réglages effectifs
POST/admin/settingscorps JSON (voir Référence des réglages)Enregistre les réglages

Chaque route déclare explicitement son contrôle d'accès ; la capacité est vérifiée avant l'exécution du moindre traitement.

Codes d'erreur

Les erreurs sont des WP_Error standard, avec un code lisible et un statut HTTP.

CodeHTTPSignification
owag_spam403Piège à robots rempli, ou horodatage manquant / trop récent
owag_rate429Quota horaire atteint pour cette adresse
owag_day_full429Trop de demandes en attente de validation sur cette date
owag_busy503Verrou de réservation non obtenu — réessayer dans un instant
owag_slot_taken409Créneau pris entre-temps
owag_overlap409Chevauchement détecté (admin) — renvoyer avec force
owag_invite_invalid404Jeton d'invitation inconnu
owag_invite_used410Invitation déjà consommée
owag_invite_expired410Invitation expirée
owag_too_late403Annulation en ligne hors délai de préavis
owag_bad_sig403Signature HMAC invalide
owag_not_found404Rendez-vous ou fiche introuvable
owag_name, owag_phone, owag_email, owag_consent400Champ invalide ou consentement manquant
owag_bad_input, owag_bad_date400Date, heure ou saisie invalide
owag_noop400Requête de mise à jour vide
owag_insert500Échec d'écriture en base

Crochets développeur

En 1.3.1, OW Agenda n'expose ni filtre ni action publique : il n'y a pas d'API d'extension à documenter, et rien de ce qui suit ne doit être deviné. Le seul crochet utilisable est l'événement planifié owag_hourly_event, sur lequel vous pouvez brancher votre propre entretien :

add_action( 'owag_hourly_event', function () {
    // Votre traitement horaire, exécuté juste après celui du plugin.
} );

Pour tout le reste, l'API REST est le point d'intégration : elle expose exactement ce que fait l'interface, ni plus ni moins.


Référence des réglages

Tous les réglages tiennent dans une seule option, owag_settings. Lecture et écriture en PHP :

$s = owag_settings();          // réglages effectifs (défauts + enregistrés)
$duree = (int) $s['consult_duration'];

update_option( 'owag_settings', array_merge( owag_settings(), [
    'consult_duration' => 45,
    'buffer'           => 10,
] ) );

Valeurs par défaut

[
    // Horaires : clé = 1 (lundi) … 7 (dimanche), valeur = liste de plages [début, fin]
    'hours' => [
        '1' => [ [ '09:00', '18:00' ] ],
        '2' => [ [ '09:00', '18:00' ] ],
        '3' => [ [ '09:00', '18:00' ] ],
        '4' => [ [ '09:00', '18:00' ] ],
        '5' => [ [ '09:00', '18:00' ] ],
        '6' => [],   // samedi fermé
        '7' => [],   // dimanche fermé
    ],
    'slot_step'           => 30,   // granularité des créneaux proposés (minutes)
    'consult_duration'    => 60,   // durée d'une consultation (minutes)
    'buffer'              => 0,    // battement autour de chaque rendez-vous (minutes)
    'min_notice_hours'    => 24,   // délai minimum avant réservation
    'max_advance_days'    => 60,   // horizon maximum de réservation
    'auto_confirm'        => 0,    // 0 = validation manuelle, 1 = confirmation automatique
    'email_verify'        => 1,    // double vérification par e-mail
    'cancel_notice_hours' => 24,   // préavis d'annulation en ligne par le client
    'notify_email'        => get_option( 'admin_email' ),
    'retention_months'    => 24,   // anonymisation des RDV passés (0 = jamais)
    'consult_label'       => 'Consultation',
]

Détail

RéglageTypeBornes de l'interfaceEffet
hourstableau1 ou 2 plages par jourDeux plages permettent la coupure de midi. Une plage n'est retenue que si début et fin sont valides et que début < fin. Un jour vide = fermé
slot_stepentier5 → 120 minEspacement des heures proposées. Plus il est fin, plus la liste est longue
consult_durationentier15 → 480 minDurée réservée dans l'agenda pour une consultation web
bufferentier0 → 120 minBattement appliqué de part et d'autre du créneau candidat
min_notice_hoursentier0 → 168 hRien n'est proposé avant maintenant + ce délai
max_advance_daysentier7 → 365 jHorizon au-delà duquel plus aucun créneau n'est proposé
auto_confirm0 / 1Validation manuelle ou confirmation immédiate
email_verify0 / 1Double opt-in par e-mail (à laisser actif)
cancel_notice_hoursentier0 → 168 hAu-delà, l'annulation en ligne est refusée avec un message invitant à téléphoner
notify_emaile-mailDestinataire des notifications admin ; sert aussi d'ORGANIZER dans les fichiers ICS
retention_monthsentier0 → 120 moisAnonymisation des rendez-vous terminés ; 0 désactive
consult_labeltexteTitre affiché sur le widget de réservation

Deux constantes non configurables méritent d'être connues, parce qu'elles gouvernent le comportement du double opt-in : le créneau d'une demande non vérifiée est retenu 45 minutes, et la demande est supprimée après 48 heures.

L'enregistrement par l'API REST accepte les mêmes clés en JSON ; les entiers sont ramenés à zéro minimum, l'adresse de notification n'est acceptée que si elle est valide, et chaque plage horaire est revalidée côté serveur.


Données stockées

Trois tables, toutes préfixées par le préfixe de votre installation. Les dates sont des DATETIME en heure locale du site, jamais en UTC — c'est ce qui rend les requêtes lisibles et les comparaisons d'horaires triviales.

{prefix}_owag_appointments

ColonneTypeNotes
idBIGINTClé primaire
uidCHAR(32)Jeton public unique (hexadécimal, générateur cryptographique). Sert de clé aux liens de validation, d'annulation et d'action admin, et d'UID ICS
kindVARCHAR(16)consult, seance, manual, block
service_labelVARCHAR(120)Intitulé affiché
customer_name, customer_phone, customer_emailVARCHARCoordonnées
customer_msgTEXTMessage laissé par le client
admin_notesTEXTNotes internes, jamais envoyées au client
start_dt, end_dtDATETIMEBornes du créneau (indexées sur start_dt)
statusVARCHAR(16)unverified, pending, confirmed, cancelled, done, noshow
sourceVARCHAR(16)web, invite, admin
invite_idBIGINTInvitation d'origine, le cas échéant
reminder_sentTINYINTDrapeau de rappel (remis à 0 en cas de déplacement)
created_at, updated_atDATETIMEupdated_at alimente le numéro de révision ICS

{prefix}_owag_invites

id, token (CHAR(32), unique), coordonnées du client, service_label, duration_min, expires_at, appointment_id (renseigné quand le lien est consommé, remis à NULL si le rendez-vous est annulé), created_at.

{prefix}_owag_customers

id, name, phone, email, notes, created_at, updated_at. Index sur name et email.

Une seule option porte la configuration (owag_settings) ; les autres sont techniques : owag_admin_secret (secret HMAC), owag_db_version, owag_pages_created, owag_page_booking, owag_page_admin.


Internationalisation

L'ensemble de l'interface est traduisible : réglages d'administration, widget de réservation, page Agenda, pages autonomes, messages de l'API et gabarits d'e-mails. La langue source est l'anglais ; le plugin déclare le domaine de traduction ow-agenda et le chemin /languages.

Le paquet livre le gabarit languages/ow-agenda.pot. Une traduction française complète est fournie, produite à partir de la formulation d'origine de l'interface.

Installer une traduction manuellement

Compilez le .po et déposez le .mo dans le dossier des traductions de WordPress :

msgfmt ow-agenda-fr_FR.po -o ow-agenda-fr_FR.mo
# puis : wp-content/languages/plugins/ow-agenda-fr_FR.mo

WordPress charge le catalogue à la première chaîne traduite ; aucun appel manuel n'est nécessaire.

Deux pièges pour les traducteurs

  • Les chaînes destinées au JavaScript utilisent des marqueurs numérotés (%1$s, %2$s) et jamais un %s nu : le petit formateur embarqué dans les scripts ne comprend que ceux-là. Un marqueur non numéroté serait affiché littéralement.
  • Deux chaînes sont des formats de date PHP, pas des phrases : l, F j, Y \a\t g:i a et l j F Y \a\t H:i. Traduisez-les comme des formats (réordonner les jetons, échapper les lettres littérales par un antislash). Le français utilise l j F Y \à H\hi.

Désinstallation

Désactiver le plugin ne fait qu'une chose : supprimer la tâche planifiée horaire. Vos données, vos réglages et vos pages restent intacts, et tout repart à la réactivation.

Supprimer le plugin depuis WordPress exécute la désinstallation complète :

  • suppression des trois tables (owag_appointments, owag_invites, owag_customers) ;
  • suppression des options owag_settings, owag_admin_secret, owag_db_version, owag_pages_created, owag_page_booking, owag_page_admin ;
  • suppression de la tâche planifiée ;
  • retrait de la capacité owag_manage du rôle administrateur.

Les deux pages créées à l'activation ne sont pas supprimées — elles peuvent avoir été retravaillées, mises au menu, indexées. À vous de les mettre à la corbeille si vous n'en voulez plus.

La suppression des tables est définitive. Exportez votre base avant si l'historique des rendez-vous a une valeur pour vous.


Dépannage

Aucun créneau n'apparaît sur la page de réservation

Passez la liste dans l'ordre :

  1. Les horaires du jour sont-ils renseignés ? Un jour vide est un jour fermé.
  2. Le délai minimum (24 h par défaut) masque forcément aujourd'hui et une partie de demain.
  3. La durée tient-elle dans la plage ? Avec une plage 09:00–12:00 et une durée de 240 minutes, aucun créneau ne peut exister.
  4. L'horizon : les jours au-delà de max_advance_days ne sont jamais proposés.
  5. Le battement : un battement large sur un planning chargé peut tout fermer.
  6. Un blocage couvre peut-être la journée. Ouvrez la page Agenda au jour concerné.
  7. Le fuseau horaire du site (Réglages → Général) : un fuseau mal réglé décale tous les calculs.

Les e-mails n'arrivent pas

C'est presque toujours la délivrabilité, pas le plugin.

  1. Vérifiez que le rendez-vous existe bien dans la page Agenda : s'il y est, le plugin a fait son travail.
  2. Installez un plugin SMTP (Fluent SMTP, WP Mail SMTP…). La fonction mail() de PHP est rejetée par l'immense majorité des serveurs de réception.
  3. Vérifiez SPF, DKIM et DMARC de votre domaine.
  4. Testez l'adresse dans OW Agenda → Notifications admin envoyées à : une adresse invalide bloque silencieusement les notifications.

Le rappel de la veille n'est pas parti

Le rappel dépend de WP-Cron, qui dépend du trafic. Sur un site peu visité, basculez sur un vrai cron système (voir Rappels et tâche planifiée). Vérifiez aussi que le rendez-vous est bien confirmé, qu'il porte une adresse e-mail et que ce n'est pas un blocage.

La page Agenda affiche « L'accès est réservé à l'équipe »

L'utilisateur connecté n'a pas la capacité owag_manage. Elle est donnée aux administrateurs à l'activation ; pour les autres rôles, voir Installation.

Le bouton « Confirmer » de l'e-mail affiche « Lien invalide »

Trois causes possibles : le lien a été tronqué par le client de messagerie (essayez de copier l'URL complète), le rendez-vous a déjà été traité (le message le précise), ou le secret HMAC du site a été régénéré. Dans ce dernier cas, passez par la page Agenda : les anciens liens ne peuvent plus être validés, par construction.

Deux rendez-vous se retrouvent sur le même créneau

Depuis la 1.3.0, c'est impossible par la réservation en ligne. En revanche, l'admin peut forcer un chevauchement depuis la page Agenda (le plugin le signale et demande confirmation) : vérifiez d'abord qu'il ne s'agit pas d'un forçage volontaire. Vérifiez aussi que votre base supporte bien GET_LOCK() — un hébergement exotique renverrait alors des erreurs 503 à la réservation plutôt que des doublons.

Les pages « Réserver » et « Agenda » n'ont pas été créées

Elles ne sont créées qu'une fois, à la toute première activation. Si vous les avez supprimées, créez deux pages avec les shortcodes [ow_agenda] et [ow_agenda_admin], puis pointez-y les options owag_page_booking et owag_page_admin pour que les liens des e-mails restent corrects :

update_option( 'owag_page_booking', 42 );  // ID de votre page de réservation
update_option( 'owag_page_admin', 43 );    // ID de votre page Agenda

Le fichier calendrier n'est pas joint

Le fichier est écrit dans le répertoire temporaire du serveur via l'API fichiers de WordPress. Si ce répertoire n'est pas accessible en écriture (quota, droits, hébergement verrouillé), l'e-mail part sans pièce jointe plutôt que d'échouer. Vérifiez les droits d'écriture et l'espace disque.

Les horaires sont décalés d'une heure

Réglages → Général → Fuseau horaire. Choisissez une ville (Europe/Brussels) plutôt qu'un décalage UTC fixe : seul un fuseau nommé gère le passage à l'heure d'été.


FAQ

OW Agenda est-il vraiment gratuit ? Oui. GPL-2.0-or-later, téléchargeable en .zip depuis https://optionweb.dev/fr/addons/ow-agenda/. Pas de version Pro, pas de clé de licence, pas de fonctionnalité verrouillée, pas de compte à créer, pas de quota de rendez-vous.

Les créneaux en ligne tiennent-ils compte des rendez-vous pris par téléphone ? Oui. Tout événement de l'agenda — réservation web, rendez-vous saisi à la main, séance sur invitation, blocage — rend sa plage horaire indisponible à la réservation en ligne.

Le client doit-il créer un compte ? Non. Aucun compte, aucun mot de passe. Toutes les actions du client (validation, annulation) passent par des liens privés reçus par e-mail.

Peut-on gérer plusieurs praticiens ou plusieurs salles ? Non. OW Agenda décrit un seul planning. Pour deux agendas indépendants, il faut deux sites (ou deux sites d'un réseau multisite).

Est-ce que ça se synchronise avec Google Agenda ? Dans le sens qui compte pour le client : oui. Chaque confirmation embarque un fichier calendrier standard qui ajoute — et met à jour, et supprime — l'événement dans Google Agenda, Outlook ou le calendrier d'iPhone. En revanche, le plugin ne lit pas vos agendas externes : vos indisponibilités personnelles se saisissent comme des blocages.

Peut-on faire payer un acompte à la réservation ? Non, c'est hors du périmètre assumé du plugin.

Combien de prestations peut-on proposer en ligne ? Une seule, dont vous choisissez le libellé et la durée. Les autres prestations passent par les invitations de séance, qui vous laissent fixer l'intitulé et la durée au cas par cas. Ce choix est délibéré : un menu de prestations sur une page publique multiplie les demandes mal ciblées.

Fonctionne-t-il sur un multisite ? Oui. Chaque site du réseau a ses propres tables, ses propres réglages et son propre agenda.

Le plugin ralentit-il le site ? Non. Aucun script n'est chargé en dehors des deux pages concernées ; le widget de réservation pèse moins de 12 Ko, sans jQuery ni framework. Le travail périodique tient en une tâche horaire.

Que se passe-t-il si un client ne valide jamais son e-mail ? Son créneau est rendu disponible au bout de 45 minutes et sa demande est supprimée au bout de 48 heures. Vous n'en entendez jamais parler.

Où trouver le support ?


Changelog

1.3.1

Conformité

  • Les pages autonomes d'action, de vérification et d'annulation n'impriment plus leur CSS ni leur JavaScript en ligne : les deux sont désormais des fichiers enregistrés et servis par l'API d'enqueue de WordPress. Les deux couleurs de bouton variables passent par des propriétés CSS personnalisées, et le script est configuré via wp_localize_script().
  • Les catalogues de traduction ne sont plus embarqués : seul languages/ow-agenda.pot est livré, le français étant distribué par les paquets de langue WordPress.org alimentés par translate.wordpress.org.
  • La version minimale de WordPress revient à 6.3 : plus rien dans le paquet ne dépend de la découverte des traductions embarquées introduite en 6.7.

API REST

  • Chaque route d'administration déclare désormais son permission_callback explicitement au lieu de le partager via array_merge(). Le contrôle de capacité était déjà appliqué ; il est maintenant visible pour l'analyse statique.

Aucun changement de comportement.

1.3.0

Internationalisation

  • Plugin entièrement traduisible (domaine ow-agenda, anglais comme langue source) : réglages, pages publiques, messages de l'API, e-mails et interface JavaScript.
  • Gabarit languages/ow-agenda.pot livré et traduction française complète.

Sécurité

  • Les identifiants publics des rendez-vous et les jetons d'invitation sont générés par un générateur cryptographique (random_bytes()). Ces jetons sont la seule authentification des liens d'annulation, de validation et d'invitation.
  • Le piège à robots et l'horodatage sont devenus obligatoires : les omettre désactivait les contrôles anti-spam.
  • Le quota horaire est agrégé par préfixe /64 en IPv6, et un plafond quotidien limite les demandes non vérifiées — un visiteur ne peut plus retenir tous les créneaux d'une journée.
  • Le quota n'est décompté qu'après validation du formulaire : une faute de frappe ne bloque plus un visiteur légitime pendant une heure.

Fiabilité

  • Les réservations simultanées d'un même créneau sont sérialisées par un verrou nommé MySQL. La transaction précédente ne posait aucun verrou de ligne : deux visiteurs pouvaient réserver le même horaire.
  • Le rappel de la veille utilise une fenêtre glissante de 12 à 36 h et rattrape son retard ; chaque envoi est réservé de façon atomique, deux exécutions de cron ne peuvent plus envoyer deux fois le même rappel.
  • La tâche horaire se reprogramme automatiquement si elle disparaît : rappels, purge et anonymisation ne peuvent plus s'arrêter en silence.
  • Le déplacement d'un rendez-vous met à jour le calendrier du client (SEQUENCE, METHOD:REQUEST, organisateur et participant) ; l'annulation envoie un METHOD:CANCEL qui supprime l'événement.
  • Les dates du widget sont calculées en heure locale et non en UTC : plus de journée perdue pour les visiteurs à l'est de UTC.
  • L'annulation d'un rendez-vous issu d'une invitation libère l'invitation, qui redevient utilisable.
  • Les rendez-vous annulés et les demandes non validées expirées ne sont plus renvoyés à l'agenda : moins de données personnelles dans les réponses, plus de rendez-vous fantômes.

1.2.1

  • Conformité Plugin Check : échappement des sorties, nettoyage des entrées, nonce sur les réglages, readme.
  • La désinstallation supprime aussi la table du répertoire clients et le secret HMAC.

1.2.0

  • E-mails HTML (mise en page en carte, couleur d'accent du site) pour toutes les notifications.
  • Boutons Confirmer / Refuser dans l'e-mail admin (liens signés HMAC).
  • Répertoire clients : recherche, création, édition, suppression, raccourcis vers un nouveau rendez-vous et une invitation.

1.1.0

  • Double vérification par e-mail contre les robots (créneau retenu 45 minutes, purge après 48 heures).

1.0.0

  • Version initiale : réservation en ligne, agenda d'administration mobile-first, invitations de séance, e-mails avec fichier calendrier et rappel de la veille.

Conçu par OptionWeb — Julien Daniel, Châtelet, Belgique.