OW Agenda — Documentatie
Online afspraken maken en de agenda van elke dag, in WordPress, zonder abonnement. Auteur: OptionWeb — Julien Daniel Plugin-pagina: https://optionweb.dev/nl/addons/ow-agenda/ Licentie: GPL-2.0-or-later Versie die dit document beslaat: 1.3.1
Inhoudsopgave
- Overzicht
- Installatie
- Snelstart
- Het reservatietraject
- De dubbele verificatie per e-mail
- De bevestiging van aanvragen
- De Agendapagina
- Het klantenbestand
- De sessie-uitnodigingen
- E-mails en agendabestand (ICS)
- Herinneringen en geplande taak
- Anti-spam en rate limits
- AVG
- Shortcodes
- REST API
- Instellingenreferentie
- Opgeslagen gegevens
- Internationalisatie
- Deïnstallatie
- Probleemoplossing
- FAQ
- Changelog
Overzicht
OW Agenda voegt aan WordPress een volledig afsprakensysteem toe: een publieke pagina waar de bezoeker eerst zijn dag en dan zijn uur kiest, en een met een login beveiligde pagina “Agenda”, gemaakt om vanaf een telefoon te gebruiken, waar u de dag beheert.
Alles blijft binnen de site. Geen externe dienst, geen API van derden, geen abonnement:
de tijdsloten worden door uw server berekend, de afspraken leven in uw MySQL-tabellen, de
e-mails vertrekken via wp_mail().
Wat er meegeleverd wordt (alles gratis, GPL-2):
- Online reserveren: dag kiezen, tijdslot kiezen, kort formulier, AVG-toestemmingsvakje
- Dubbele opt-in per e-mail: een aanvraag wordt pas echt na een klik op een bevestigingslink — bots raken nooit tot in de agenda
- Handmatige bevestiging (aanbevolen) of automatische bevestiging, met de knoppen Bevestigen / Weigeren rechtstreeks in de meldingsmail (HMAC-ondertekende links, zonder in te loggen)
- Mobile-first pagina “Agenda”: dagoverzicht, openstaande aanvragen, telefonische afspraken die u zelf toevoegt, tijdsloten blokkeren, verplaatsen, statussen gedaan / afwezig
- Klantenbestand: zoeken, aanmaken, bewerken, verwijderen; automatisch aangevuld door de bevestigde afspraken
- Sessie-uitnodigingen: met een privélink kiest een bestaande klant zelf de datum van zijn sessie, voor een dienst en een duur die u vastlegt
- Verzorgde HTML-e-mails met ICS-bijlage (met één beweging in de agenda van de telefoon), automatische herinnering de dag voordien, online annulering door de klant
- Automatische anonimisering van afgelopen afspraken, opruiming van nooit bevestigde aanvragen
- Volledige REST API onder de namespace
owag/v1
Wat hij bewust niet doet:
- Geen online betaling: OW Agenda maakt de afspraak, hij int geen voorschot (gebruik WooCommerce als dat uw behoefte is)
- Eén enkele agenda: geen beheer van meerdere praktijkhouders of meerdere ruimtes; de plugin beschrijft één planning, die van de site
- Geen tweerichtingssynchronisatie met Google Agenda of Outlook: de agenda van de klant wordt bijgewerkt via een ICS-bestand (enkel heen), uw persoonlijke onbeschikbaarheden voert u in als blokkeringen
- Geen dienstencatalogus aan bezoekerszijde: de publieke reservatie gaat over één enkele prestatie (label en duur instelbaar); de andere prestaties verlopen via de sessie-uitnodigingen
- Geen terugkerende afspraken, geen videogesprek, geen wachtkamer
Die lijst is een architectuurkeuze, geen verborgen roadmap: een eenvoudige agenda die zich nooit van tijdslot vergist, is meer waard dan een opgeblazen systeem waar niemand aan durft te raken.
Installatie
Vanuit de .zip
- Download
ow-agenda-1.3.1.zipvan https://optionweb.dev/nl/addons/ow-agenda/ - Plugins → Nieuwe plugin → Plugin uploaden
- Kies het bestand, klik op Nu installeren en daarna op Activeren
Via FTP
Pak het archief uit en plaats de map ow-agenda in /wp-content/plugins/, activeer de
plugin daarna via Plugins.
Vereisten
- WordPress 6.3 of nieuwer
- PHP 7.4 of nieuwer (8.1+ aanbevolen)
- MySQL 5.7+ / MariaDB 10.2+ — de functies
GET_LOCK()/RELEASE_LOCK()worden gebruikt om gelijktijdige reservaties te serialiseren - Werkende e-mailverzending. Dat is de enige echte afhankelijkheid van de plugin: het hele traject (verificatie, bevestiging, herinnering, annulering) verloopt via e-mail. Installeer op gedeelde hosting een SMTP-plugin voor u de agenda online zet.
Wat er geïnstalleerd wordt
Bij de activatie maakt OW Agenda drie tabellen aan:
| Tabel | Inhoud |
|---|---|
{prefix}_owag_appointments | Alle items in de agenda: online consultaties, sessies uit een uitnodiging, handmatig ingevoerde afspraken, geblokkeerde tijdsloten |
{prefix}_owag_invites | De privélinks “kies de datum van uw sessie” |
{prefix}_owag_customers | Het klantenbestand |
…en, enkel de allereerste keer, twee gepubliceerde pagina’s:
- “Een afspraak maken” — bevat de shortcode
[ow_agenda] - “Agenda” — bevat de shortcode
[ow_agenda_admin]
Hun ID’s worden bewaard in de opties owag_page_booking en owag_page_admin; de vlag
owag_pages_created zorgt ervoor dat deactiveren en opnieuw activeren geen duplicaten
aanmaakt. U kan die pagina’s hernoemen, in een menu zetten of hun permalink wijzigen: de
plugin volgt ze op hun ID.
De activatie voegt ook toe:
- de capability
owag_manageaan de rol beheerder — die, en nietmanage_options, geeft toegang tot de agenda en tot de REST-routes voor beheer; - de standaardinstellingen in de optie
owag_settings; - de uurlijkse geplande taak
owag_hourly_event(eerste uitvoering 5 minuten na de activatie).
Een HMAC-geheim (owag_admin_secret, 64 tekens, niet automatisch geladen) wordt aangemaakt
bij de eerste beheerdersmelding: het ondertekent de knoppen Bevestigen / Weigeren in de
e-mails.
Iemand anders toegang geven
De Agendapagina is zichtbaar voor elke ingelogde gebruiker die over owag_manage beschikt.
Om de agenda open te stellen voor een assistente zonder haar de sleutels van de site te
geven:
// À exécuter une fois (mu-plugin, ou plugin de gestion des rôles).
$role = get_role( 'editor' );
if ( $role ) { $role->add_cap( 'owag_manage' ); }
Snelstart
Ga na de activatie naar OW Agenda (hoofdmenu van het beheer). Het instellingenscherm past op één pagina; vijf minuten volstaan.
1. De openingsuren
Vul voor elke dag van de week een tijdsblok in (09:00 → 18:00) en, als u ’s middags
sluit, een tweede blok (14:00 → 18:30). Een dag die u leeg laat, is een gesloten dag.
Standaard: maandag tot vrijdag, 09:00–18:00; zaterdag en zondag gesloten.
De uren worden geïnterpreteerd in de tijdzone van de site (Instellingen → Algemeen → Tijdzone). Controleer die voor u de reservatie openzet: alle datums worden in lokale tijd opgeslagen.
2. De online reserveerbare prestatie
- Label van de consultatie — wat de bezoeker als titel van de widget ziet (“Consultatie”, “Eerste afspraak”, “Doorlichting”…)
- Duur van de consultatie — standaard 60 minuten. Het is die duur die in de agenda geblokkeerd wordt.
- Interval van de tijdsloten — het verschil tussen twee aangeboden uren (standaard 30 minuten). Met een duur van 60 en een interval van 30 biedt u 9:00, 9:30, 10:00… aan, en de eerste afspraak om 9:30 haalt 9:00 en 10:00 uit de lijst.
- Buffertijd tussen twee afspraken — ademruimte in minuten die aan weerszijden van elk kandidaat-tijdslot toegevoegd wordt (standaard 0).
3. De termijnen
- Minimale reservatietermijn — standaard 24 u: een bezoeker kan niet boeken voor over een uur.
- Reservatiehorizon — standaard 60 dagen: daarbuiten wordt niets meer aangeboden.
- Online annulering door de klant — tot 24 u voor de afspraak; daarna vraagt de annuleringspagina om te bellen.
4. De bevestiging en de e-mails
- Bevestiging van de aanvragen: handmatige bevestiging (aanbevolen) of automatische bevestiging.
- Dubbele e-mailverificatie: laat dit aangevinkt. Het is de beste antibotbescherming van de plugin.
- Beheerdersmeldingen sturen naar: uw adres. Standaard het beheerdersadres van de site.
Sla op en open dan de pagina “Een afspraak maken”: de widget toont de eerstvolgende 14 dagen en uw vrije tijdsloten. U staat online.
5. De agenda op de telefoon
Open de pagina “Agenda” op uw telefoon, log in en gebruik daarna “Toevoegen aan startscherm”. De plugin publiceert een app-manifest: de pagina opent dan schermvullend, zonder browserbalk, als een app.
Het reservatietraject
Aan bezoekerszijde is de reservatiepagina een op zichzelf staand component (JavaScript
zonder framework, minder dan 10 KB) dat praat met de publieke endpoints owag/v1/public/*.
Stap 1 — de dag
Bij het laden vraagt de widget de beschikbaarheden op voor 14 dagen vanaf vandaag en toont hij een balk met aanklikbare dagen. Een dag zonder vrij tijdslot is grijs en niet aanklikbaar. De knop “Volgende data bekijken” laadt de 14 volgende dagen, zo vaak als nodig tot aan de ingestelde horizon.
De datums van de widget worden opgebouwd in lokale tijd van de browser, nooit in UTC. Dat is een correctie uit 1.3.0: voordien zag een bezoeker ten oosten van UTC die de pagina kort na middernacht opende, de lijst de dag voordien beginnen en verloor hij een dag beschikbaarheid.
Stap 2 — het uur
De klik op een dag roept /public/slots aan en toont de vrije beginuren. Een tijdslot wordt
aangeboden wanneer al deze voorwaarden vervuld zijn:
- het valt binnen een openingsblok van de betrokken dag;
- de volledige afspraak (begin + duur) past voor het sluitingsuur;
- het begint na nu + de minimale termijn;
- de dag ligt binnen de reservatiehorizon;
- het overlapt met geen enkel actief item, buffertijd inbegrepen — en “actief item” omvat de bevestigde afspraken, de openstaande aanvragen, de blokkeringen, de handmatig ingevoerde telefonische afspraken, en de nog niet geverifieerde aanvragen die hun tijdslot nog vasthouden.
Dat is het belangrijke punt: alles wat de agenda bezet, sluit het online tijdslot af. Een afspraak die telefonisch gemaakt en in de agenda genoteerd is, verdwijnt onmiddellijk uit de tijdsloten die op de site aangeboden worden.
Stap 3 — het formulier
Vier velden, waarvan drie verplicht: naam (minstens 2 tekens), telefoon (minstens 8 cijfers), e-mail (gevalideerd), vrij bericht. Een AVG-toestemmingsvakje, standaard niet aangevinkt, is verplicht.
Het formulier bevat daarnaast twee velden die voor de bezoeker onzichtbaar zijn — een botval en een tijdstempel van het openen van de pagina — die beschreven worden in de sectie Anti-spam.
Stap 4 — het antwoord
Afhankelijk van uw configuratie zijn er drie uitkomsten:
| Configuratie | Aangemaakte status | Getoond bericht |
|---|---|---|
| Dubbele e-mailverificatie actief (standaard) | unverified | “Nog één stap: er is net een e-mail naar u verstuurd…” |
| Verificatie uitgeschakeld + handmatige bevestiging | pending | “Uw aanvraag is goed ontvangen!” |
| Verificatie uitgeschakeld + automatische bevestiging | confirmed | “Uw afspraak is bevestigd!” |
Werd het tijdslot ingenomen tussen het tonen van de lijst en het versturen van het formulier,
dan krijgt de bezoeker een duidelijk bericht (owag_slot_taken, HTTP 409) en wordt de
dagenlijst automatisch herladen. Twee bezoekers kunnen niet hetzelfde tijdslot krijgen: de
kritieke sectie wordt geserialiseerd door een named MySQL-lock, eigen aan uw database en
uw tabelprefix, wat ook op gedeelde hosting standhoudt.
De dubbele verificatie per e-mail
Dit is de functie die OW Agenda onderscheidt van een vermomd contactformulier.
Wanneer Dubbele e-mailverificatie actief is (instelling email_verify, standaard aan):
- De aanvraag wordt opgeslagen met de status
unverified. De beheerder wordt niet verwittigd. De afspraak verschijnt niet als een aanvraag om te behandelen. - De bezoeker krijgt een e-mail “Nog één stap…” met een bevestigingsknop.
- Het tijdslot wordt 45 minuten vastgehouden. Blijft de bevestiging uit, dan komt het daarna weer vrij voor iedereen — een bot kan uw dag dus niet bevriezen.
- De klik op de knop opent een op zichzelf staande pagina (
?owag_verify=<uid>) met de samenvatting van de afspraak en een knop “Ik bevestig mijn aanvraag”. De bevestiging vertrekt pas bij die klik: antivirussoftware en linkscanners die de URL’s uit e-mails bezoeken, bevestigen niets. - De aanvraag wordt dan
pending(handmatige bevestiging) ofconfirmed(automatische bevestiging), de klant krijgt de bijbehorende e-mail, en pas dan vertrekt de melding naar de beheerder. - Nooit bevestigde aanvragen worden na 48 u verwijderd door de uurlijkse taak. Ze laten geen spoor en geen persoonsgegevens na.
Is het tijdslot tussen het versturen en de klik door iemand anders ingenomen (het venster van 45 minuten is voorbij), dan geeft de bevestiging een duidelijke fout en wordt de aanvraag geannuleerd — nooit een spookafspraak op een al bezet tijdslot.
Praktisch gevolg: een ongeldig e-mailadres levert geen enkele afspraak op. U verliest geen tijd meer met het terugbellen van verzonnen nummers. De prijs is één extra klik voor de klant; de ervaring leert dat het aantal afhakers veel lager ligt dan het aantal valse afspraken dat het voorkomt.
Reservaties uit een sessie-uitnodiging slaan deze stap over: de klant heeft al bewezen dat hij zijn mailbox leest door op de privélink te klikken die hij ontvangen heeft.
De bevestiging van aanvragen
Handmatige bevestiging (aanbevolen)
Standaardinstelling: auto_confirm = 0. Een geverifieerde aanvraag komt binnen met de status
pending. Er is de klant niets beloofd zolang u niets gedaan hebt — maar het tijdslot is al
geblokkeerd, niemand kan het u afsnoepen.
U krijgt een e-mail “Te bevestigen:
- De afspraak bevestigen — cyaan
- Weigeren — rode omlijning
De knoppen in de e-mail: HMAC-ondertekende links
Die knoppen vragen geen login. Ze verwijzen naar uw site met drie parameters:
owag_admin_action (confirm of refuse), uid (de willekeurige identificatie van de
afspraak) en sig, een HMAC-SHA256-handtekening van uid|action berekend met het
privégeheim van de site (owag_admin_secret).
- De handtekening wordt gecontroleerd voor er ook maar één detail getoond wordt: een geknoeide link toont niets anders dan “Ongeldige link”.
- De vergelijking gebruikt
hash_equals()— geen informatielek via de responstijd. - De pagina toont een samenvatting en een knop; de actie wordt pas bij de klik uitgevoerd. Een linkscanner bevestigt dus niets in uw plaats.
- Een al behandelde aanvraag geeft “Al behandeld (status: … )” in plaats van een fout.
Bevestigen stuurt automatisch de bevestigingsmail naar de klant, met het agendabestand. Weigeren stuurt de annuleringsmail. In beide gevallen is de agenda bij voor u uw telefoon hebt opgeborgen.
Die links blijven geldig zolang de afspraak in afwachting staat. Verwijdert u de optie
owag_admin_secret(gedeeltelijke herstelling van de database, handmatige opkuis), dan werken alle al verstuurde links niet meer: er wordt een nieuw geheim aangemaakt en de oude handtekeningen komen niet meer overeen. Gebruik dan de Agendapagina.
Automatische bevestiging
Instelling auto_confirm = 1. Een vrij tijdslot wordt onmiddellijk bevestigd: de klant krijgt
zijn bevestiging met het agendabestand, u krijgt een melding “Nieuwe afspraak”. Voorbehouden
voor activiteiten waar elk open tijdslot een aanvaardbaar tijdslot is.
Beide instellingen combineren met de dubbele verificatie: zelfs bij automatische bevestiging
gaat de aanvraag eerst door unverified als email_verify actief is.
De Agendapagina
De pagina “Agenda” ([ow_agenda_admin]) is het werkinstrument van elke dag. Ze is eerst
voor de telefoon ontworpen: brede knoppen, één kolom, geen tabel die horizontaal moet
schuiven, en ze werkt uiteraard even goed op een computerscherm.
Ze is beveiligd: een niet-ingelogde bezoeker ziet een inloglink, een ingelogde gebruiker
zonder de capability owag_manage ziet “De toegang is voorbehouden aan het team”. De pagina
staat op noindex,nofollow: ze zal nooit in Google terechtkomen.
Tabblad Agenda — het dagoverzicht
De lopende dag, item per item, met het uur, de duur, het label, de status en de herkomst (site, uitnodiging, handmatige invoer). Navigatie van dag naar dag, knop Vandaag. Bij elke afspraak:
- Details — de gegevens van de klant, het bericht dat bij de reservatie achtergelaten werd, en een blok interne notities (nooit zichtbaar voor de klant) die opgeslagen worden zonder ook maar één e-mail te versturen
- Bevestigen ✓ — voor een aanvraag die nog in afwachting staat; de bevestigingsmail vertrekt
- Gedaan / Afwezig (no-show) — eindstatussen van de afspraak
- Verplaatsen — nieuwe datum, nieuw uur, eventueel gewijzigde duur. De plugin vraagt of de klant per e-mail verwittigd moet worden; zo ja, dan krijgt hij een bijgewerkte bevestiging en een agendabestand dat het item corrigeert dat al op zijn telefoon staat
- De afspraak annuleren — ook hier met de keuze om de klant al dan niet te verwittigen
- De blokkering verwijderen — voor een geblokkeerd tijdslot
Wanneer een actie een overlapping veroorzaakt (verplaatsing naar een bezet tijdslot), waarschuwt de plugin en vraagt ze een bevestiging: u kan forceren. Het is uw planning, u weet wat u doet — het vangnet is enkel blokkerend voor bezoekers.
Een afspraak of een blokkering toevoegen
De knop “Een afspraak toevoegen” opent één formulier dat twee doelen dient:
- Afspraak — de klant heeft u gebeld. Naam, telefoon, e-mail (optioneel), dienst, datum, uur, duur, interne notities. Vult u een e-mailadres in en vinkt u de optie aan, dan krijgt de klant de bevestiging met zijn agendabestand, alsof hij online geboekt had. De afspraak vult ook het klantenbestand aan.
- Blokkering (verlof, pauze…) — een reden, een datum, een uur, een duur. Geen e-mail, geen klantfiche, maar het tijdslot verdwijnt onmiddellijk uit de online reservatie. Zo legt u een week verlof vast, een opleiding, of het middaguur dat u niet geboekt wil zien.
Tabblad In afwachting
De lijst van geverifieerde aanvragen die op uw beslissing wachten, over alle dagen heen (afspraken die al voorbij zijn, vallen erbuiten). Twee knoppen per regel: Bevestigen en Weigeren. Dit is het eerste scherm om ’s ochtends te openen.
Tip: de agenda als app
Op de Agendapagina declareert de plugin een webapp-manifest (?owag_manifest=1) met de naam
van de site, het pictogram van de site en de themakleur. Op Android zoals op iOS biedt het
browsermenu dan “Toevoegen aan startscherm” aan: u krijgt een pictogram, een schermvullende
start, en u hoeft de URL nooit meer in te typen.
Het klantenbestand
Het derde tabblad van de Agendapagina is een licht bestand, bewust minimaal: naam, telefoon, e-mail, vrije notities.
- Het vult zichzelf. Zodra een afspraak bevestigd is (geverifieerde online reservatie, sessie op uitnodiging, handmatige invoer), wordt de klant aan een bestaande fiche gekoppeld op zijn e-mail en daarna op zijn telefoon. Bestaat hij niet, dan wordt de fiche aangemaakt; bestaat hij wel, dan worden enkel de lege velden aangevuld — de plugin overschrijft nooit wat u zelf hebt ingevuld.
- Zoeken op naam, telefoon of e-mail (maximaal 200 fiches getoond).
- Handmatig aanmaken en bewerken, met een notitieveld voor de voorgeschiedenis, de voorkeuren, de aandachtspunten.
- Twee snelkoppelingen per fiche: Nieuwe afspraak (vult het handmatige invoerformulier vooraf in) en Uitnodigen voor een sessie (vult de uitnodiging vooraf in).
- Een fiche verwijderen: de afgelopen afspraken blijven in de agenda staan, alleen de fiche uit het bestand verdwijnt.
Dit bestand is geen CRM en doet ook niet alsof. Het beantwoordt één vraag: “Wie was dat weer, die dame van 14 u?”
De sessie-uitnodigingen
De use case: een bestaande klant heeft recht op een sessie — een opvolging, een bijwerking, een prestatie die elders verkocht is — en u wil dat hij zijn datum kiest zonder langs de publieke reservatie te gaan, voor een dienst en een duur die u vastlegt.
Vanuit het tabblad Uitnodigingen:
- Vul de naam van de klant en de titel van de sessie in (verplicht), de duur (15 tot 720 minuten) en, als u dat wenst, zijn e-mail en telefoon.
- Kies de geldigheidsduur van de link: standaard 60 dagen, van 1 tot 365.
- Vink eventueel “De link per e-mail naar de klant sturen” aan — de klant krijgt een e-mail “Kies de datum van uw sessie” met de titel, de duur, de vervaldatum en een knop.
- Kopieer anders de link en stuur hem via sms, WhatsApp of met de ingebouwde deelknop van de telefoon.
De link verwijst naar uw reservatiepagina met een privétoken (?owag_invite=<token>, 32
hexadecimale tekens uit een cryptografische generator). De widget toont dan de titel en de
duur van de sessie, en niet die van de standaardconsultatie. De klant vult hetzelfde korte
formulier in.
Regels die een uitnodigingslink beheersen:
- Eenmalig gebruik. Zodra de sessie geboekt is, antwoordt de link “Deze link is al gebruikt” (HTTP 410). Twee gelijktijdige openingen met hetzelfde token kunnen geen twee sessies aanmaken: het token wordt verbruikt onder dezelfde lock als de reservatie van het tijdslot.
- Gedateerd. Na de vervaldatum vraagt de link om opnieuw contact op te nemen (HTTP 410).
- Zonder dubbele opt-in. De klant heeft die link al gekregen via een kanaal dat u beheerst:
de sessie wordt rechtstreeks als
confirmedaangemaakt, met bevestiging en agendabestand. - Hergebruikt bij annulering. Wordt de sessie geannuleerd (door u of door de klant), dan wordt de uitnodiging vrijgegeven: de link wordt weer actief en de uitnodiging duikt weer op in de lijst met open uitnodigingen. Dat is een correctie uit 1.3.0 — voordien maakte een annulering de link definitief kapot en moest u een nieuwe maken.
- Intrekbaar. Een uitnodiging uit de lijst verwijderen, schakelt de link onmiddellijk uit.
E-mails en agendabestand (ICS)
De transactionele e-mails
Alle berichten zijn in HTML, op een tabelopmaak die compatibel is met Gmail en Outlook: witte
kaart op lichte achtergrond, naam van de site in de hoofding, samenvattingskader met cyaan
bies, pilvormige knoppen. Ze worden verstuurd via wp_mail() en respecteren dus uw
SMTP-configuratie en de gebruikelijke WordPress-filters.
| Ontvanger | Trigger | |
|---|---|---|
| Bevestiging van de aanvraag | Klant | Online reservatie wanneer de dubbele verificatie actief is |
| Aanvraag ontvangen | Klant | Aanvraag geverifieerd, in afwachting van uw beslissing |
| Afspraak bevestigd | Klant | Bevestiging (handmatig, automatisch, invoer door beheerder, verplaatsing) — met ICS-bijlage |
| Afspraak geannuleerd | Klant | Annulering door de klant, weigering of annulering door de beheerder — met ICS-annuleringsbijlage |
| Herinnering | Klant | Automatisch, tussen 12 en 36 u voor de afspraak |
| Kies de datum van uw sessie | Klant | Aanmaak van een uitnodiging met verzending per e-mail |
| Te bevestigen: … | Beheerder | Nieuwe geverifieerde aanvraag in afwachting — met de knoppen Bevestigen / Weigeren |
| Nieuwe afspraak: … | Beheerder | Nieuwe bevestigde afspraak (automatische bevestiging, uitnodiging) |
| Annulering door de klant: … | Beheerder | De klant heeft zelf geannuleerd vanuit zijn e-mail |
Het adres voor beheerdersmeldingen is de instelling notify_email (standaard het
beheerdersadres van de site). E-mails naar de klant vertrekken enkel als het adres geldig is,
en nooit voor een geblokkeerd tijdslot.
De bevestigings- en annuleringsmails herinneren de klant eraan dat hij online kan annuleren, met het aantal uren opzeg dat effectief ingesteld is.
De ICS-bijlage
Het .ics-bestand bij de bevestigingen is wat een e-mail omzet in een afspraak die echt in de
telefoon van de klant staat: één beweging op de bijlage en het item staat in zijn agenda, met
zijn eigen herinnering.
Het bestand voldoet aan RFC 5545 en bevat wat nodig is opdat agendatoepassingen (Google, Outlook, Apple) de updates correct verwerken:
METHOD:REQUESTbij het aanmaken en bij elke wijziging van het uur,METHOD:CANCELbij de annulering;- een stabiele
UIDafgeleid van de identificatie van de afspraak en van het domein van de site — die zorgt ervoor dat een wijziging het bestaande item vervangt in plaats van een tweede aan te maken; - een oplopende
SEQUENCEafgeleid van de datum van laatste wijziging, herleid tot een absoluut tijdstip: de overgang naar de wintertijd kan het revisienummer niet doen teruglopen (en de annulering telt er 1 bij, om hoger te blijven dan een wijziging in dezelfde seconde); ORGANIZER(uw meldingsadres) enATTENDEE(de klant);DTSTART/DTENDin UTC,STATUS:CONFIRMEDofSTATUS:CANCELLED;- regels gevouwen op 75 bytes zonder ooit een UTF-8-teken in twee te knippen.
Een verplaatsing van een bevestigde afspraak stuurt dus een nieuw bestand dat het item corrigeert dat al bij de klant staat; een annulering stuurt een bestand dat het wist. Dat is een correctie uit 1.3.0: voordien bleef het oude uur in de agenda van de klant staan.
Het bestand wordt in de tijdelijke map van de server geschreven — nooit in
wp-content/uploads/, dat publiek geserveerd wordt — via de bestands-API van WordPress, en
het wordt gewist zodra de verzending klaar is, ook als die mislukt. Het bevat de naam en het
e-mailadres van de klant: het heeft geen seconde langer dan nodig op de schijf te staan.
Herinneringen en geplande taak
Eén enkele geplande taak, owag_hourly_event, elk uur uitgevoerd. Ze doet drie dingen.
1. De herinnering de dag voordien
De herinnering vertrekt wanneer de afspraak binnen 12 tot 36 uur begint. Dat is geen vast tijdvenster, en dat is bewust: WP-Cron wordt pas geactiveerd bij een paginabezoek. Een site zonder verkeer ’s avonds verloor de herinneringen voor de volgende dag definitief. Met een schuivend venster vertrekt de herinnering bij de eerste beschikbare uitvoering en haalt ze de achterstand in.
Voorwaarden: bevestigde afspraak, ingevuld e-mailadres, het is geen blokkering, en de herinnering is nog niet verstuurd.
Elke herinnering wordt atomair gereserveerd voor de verzending (een voorwaardelijke
UPDATE op de vlag reminder_sent): twee overlappende cron-uitvoeringen kunnen niet tweemaal
hetzelfde bericht versturen. Mislukt de verzending, dan wordt de reservatie teruggegeven en
wordt de herinnering bij de volgende uitvoering opnieuw geprobeerd. Een korte lock (10 minuten)
voorkomt bovendien dat twee gelijktijdige doorlopen dezelfde lijst afgaan.
Een afspraak verplaatsen zet de vlag terug op nul: de klant krijgt wel degelijk een herinnering voor het nieuwe uur.
2. Het opruimen van niet-bevestigde aanvragen
Aanvragen die langer dan 48 uur op unverified blijven staan, worden verwijderd. (Hun
tijdslot was al na 45 minuten weer vrijgegeven.)
3. De AVG-anonimisering
Zie de sectie AVG.
Het vangnet
Een WP-Cron-event kan verdwijnen: databaseoptimalisatie, gedeeltelijke herstelling,
opkuisplugin, reset van de optie cron. OW Agenda controleert bij elke paginalading of de taak
wel degelijk ingepland is en plant ze anders opnieuw in. Herinneringen, opruiming en
bewaartermijn kunnen niet in stilte stilvallen.
Een echte systeemcron gebruiken
WP-Cron hangt af van het verkeer. Verkies op een weinig bezochte site een systeemcron:
// 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 en rate limits
Reserveren is publiek van aard: geen account, geen login, geen nonce mogelijk. Vijf onafhankelijke beschermingen werken daarom stroomopwaarts.
1. De botval (honeypot)
Het formulier bevat een tekstveld buiten het scherm, onzichtbaar voor een mens en aanlokkelijk voor een bot. Het veld moet aanwezig én leeg zijn. Een ingevuld veld — of een ontbrekend veld, wat een rechtstreekse verzending naar de API verraadt — levert een droge weigering op (HTTP 403), zonder bruikbaar detail.
2. De invultijd
De pagina geeft de tijdstempel van haar weergave mee. Een inzending die minder dan 4 seconden na het laden binnenkomt, wordt geweigerd. Ook hier is een ontbrekende of misvormde tijdstempel een weigering: in 1.3.0 zijn die twee velden verplicht geworden — ze weglaten schakelde de controle simpelweg uit.
3. Het uurquotum per adres
5 reservaties per uur en per bereik. In IPv4 is het bereik het adres; in IPv6 is het het /64-prefix — een aanvaller beschikt daar over 2⁶⁴ adressen, het volledige adres tellen zou niets beperken.
De teller wordt pas na de validatie van het formulier verhoogd: een bezoeker die zich drie keer na elkaar van e-mailadres vergist, zit niet meer voor niets een uur geblokkeerd.
4. Het dagplafond op niet-geverifieerde aanvragen
Maximaal 3 aanvragen die op e-mailbevestiging wachten mogen op eenzelfde dag een tijdslot vasthouden. Zonder dat plafond volstonden een paar verzendingen om alle tijdsloten van één datum te bevriezen. Daarboven krijgt de bezoeker de uitnodiging om later opnieuw te proberen of een andere dag te kiezen (HTTP 429).
5. De reservatielock
Het reserveren van een tijdslot en het verbruiken van een uitnodiging gebeuren in een kritieke sectie die geserialiseerd wordt door een named MySQL-lock, waarvan de naam afgeleid is van uw database en uw tabelprefix (twee sites op dezelfde gedeelde hosting blokkeren elkaar dus niet). Wordt de lock niet binnen 5 seconden verkregen, dan geeft de aanvraag een duidelijke HTTP 503 in plaats van een dubbele afspraak.
Die lock vervangt sinds 1.3.0 een transactie die op een kale SELECT steunde: onder
REPEATABLE READ legt die geen enkele rijvergrendeling, en konden twee gelijktijdige bezoekers
hetzelfde uur boeken.
En de dubbele verificatie
Dat is de doeltreffendste laag: zelfs wie alles hierboven passeert, raakt als bot nooit tot in uw agenda of uw mailbox, want de melding naar de beheerder vertrekt pas na een klik op een link die per e-mail ontvangen is. Zie De dubbele verificatie per e-mail.
AVG
Een afsprakenagenda verwerkt mogelijke gezondheidsgegevens, contactgegevens en historieken. De standaardinstellingen zijn bewust behoudend.
Toestemming
Het toestemmingsvakje is verplicht en standaard niet aangevinkt op het publieke formulier: “Ik ga ermee akkoord dat mijn gegevens gebruikt worden voor het beheer van mijn afspraak.” Een reservatie zonder toestemming wordt aan serverzijde geweigerd, niet enkel in de browser.
Minimalisering
- Er wordt geen enkel IP-adres in de database opgeslagen. Het uurquotum gebruikt een tijdelijke teller (transient) waarvan de sleutel een hash van het netwerkbereik is, na een uur opgeruimd.
- Geen enkele user agent, geen tracker, geen cookie die door de plugin geplaatst wordt.
- Er worden geen gegevens naar een externe dienst gestuurd: geen CDN, geen externe oproep, geen telemetrie.
- Nooit bevestigde aanvragen worden na 48 uur verwijderd — ze laten geen gegevens achter.
- Geannuleerde afspraken worden niet meer door de agenda-API teruggegeven: onnodige persoonsgegevens circuleren niet.
Automatische anonimisering
Instelling retention_months, standaard 24 maanden. Elk uur worden de afspraken die langer
dan die termijn afgelopen zijn, ter plaatse geanonimiseerd: de naam wordt vervangen door
“Geanonimiseerd”, telefoon en e-mail worden leeggemaakt, het bericht van de klant en de interne
notities worden gewist. De rij blijft bestaan, wat de statistische geschiedenis van de
activiteit bewaart (hoeveel afspraken, wanneer, voor welke prestatie) zonder ook maar één
persoonsgegeven bij te houden.
Zet 0 om de automatische anonimisering uit te schakelen — met kennis van zaken.
Let op: de anonimisering richt zich op afspraken met een e-mailadres. Een handmatig ingevoerde afspraak zonder e-mail (enkel naam en telefoon) wordt niet bereikt door de automatische opruiming; verwijder ze handmatig als uw bewaarbeleid dat vereist.
Recht op inzage en recht op wissing
De plugin koppelt zich niet aan de ingebouwde exporteurs/wissers van WordPress (die werken op gebruikersaccounts, en klanten hebben er geen). Om een verzoek te behandelen:
- Inzage — zoek de persoon in het klantenbestand (naam, telefoon of e-mail) en loop zijn
afspraken in de agenda door. Databasebeheerders kunnen ook rechtstreeks
{prefix}_owag_appointmentsen{prefix}_owag_customersbevragen op de kolomcustomer_email/email. - Wissing — verwijder de fiche uit het bestand en annuleer daarna de betrokken afspraken of
laat ze anonimiseren. Onmiddellijk wissen gebeurt in de database op
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';
Verwerkingsregister
Voor uw register: doel beheer van de afspraken; rechtsgrond toestemming (online reservatie) of uitvoering van een overeenkomst / precontractuele maatregelen (telefonisch gemaakte afspraak); categorieën identiteit, contactgegevens, inhoud van het bericht, interne notities; bewaartermijn standaard 24 maanden, daarna anonimisering; ontvangers geen enkele verwerker buiten uw hostingprovider en uw dienst voor e-mailverzending.
Shortcodes
[ow_agenda] — de reservatie voor bezoekers
Op een publieke pagina te plaatsen. Geen attributen: de prestatie, de duur en de uren komen uit de instellingen. De shortcode laadt zijn stylesheet en zijn script (samen minder dan 12 KB, zonder afhankelijkheden) en rendert een container die zich vult na de eerste API-oproep.
[ow_agenda]
Het is deze shortcode die de URL-parameter ?owag_invite=<token> interpreteert: dezelfde pagina
bedient de standaardconsultatie en de sessies op uitnodiging.
[ow_agenda_admin] — de beheerpagina
Op een aparte pagina te plaatsen. Geen attributen. De shortcode regelt de toegang zelf:
- niet-ingelogde bezoeker → inloglink die naar de pagina terugleidt;
- ingelogd zonder de capability
owag_manage→ bericht over beperkte toegang; - ingelogd met de capability → de volledige interface (agenda, in afwachting, klanten, uitnodigingen).
[ow_agenda_admin]
Beide pagina’s worden bij de activatie automatisch met deze shortcodes aangemaakt. Hebt u ze
verwijderd, dan volstaat het een pagina te maken en de shortcode erin te plakken — denk er dan
aan de optie owag_page_admin (of owag_page_booking) bij te werken zodat de links in de
e-mails en het manifest naar de juiste plek wijzen.
De op zichzelf staande pagina’s
Vier URL-parameters, die op om het even welke pagina van de site onderschept worden, leveren op
zichzelf staande pagina’s op die buiten het thema geserveerd worden (minimale weergave,
noindex,nofollow):
| URL | Rol |
|---|---|
?owag_verify=<uid> | Bevestiging van de aanvraag door de klant (dubbele opt-in) |
?owag_cancel=<uid> | Online annulering door de klant |
?owag_admin_action=confirm|refuse&uid=…&sig=… | Bevestiging / weigering vanuit de beheerdersmail |
?owag_manifest=1 | App-manifest van de Agendapagina |
Die pagina’s voeren nooit een actie uit bij het laden: ze tonen een samenvatting en een knop. Geen enkele bot, geen enkele e-mailantivirus, geen enkele prefetch van een browser kan een afspraak bevestigen of annuleren in de plaats van een mens.
REST API
Alle endpoints leven onder de namespace owag/v1, op de gebruikelijke REST-root
(https://example.com/wp-json/owag/v1/…).
Publieke endpoints
Geen authenticatie: dit zijn de routes van de reservatiewidget en van de links die per e-mail ontvangen worden. Hun beveiliging steunt op willekeurige tokens (32 hexadecimale tekens uit een cryptografische generator), op HMAC-handtekeningen en op de beschermingen die in Anti-spam beschreven zijn.
| Methode | Pad | Parameters | Rol |
|---|---|---|---|
GET | /public/days | from (Y-m-d, standaard: vandaag), token (uitnodiging, optioneel) | Geeft label, duration en days terug: 14 dagen vanaf from, elk met zijn aantal vrije tijdsloten |
GET | /public/slots | date (Y-m-d, verplicht), token | Geeft slots terug: de vrije beginuren (HH:MM) |
POST | /public/book | date, time, name, phone, email, message, consent, website (botval), ts (tijdstempel), token | Maakt de aanvraag aan; geeft status terug en het bericht dat getoond moet worden |
POST | /public/verify | uid | Bevestigt een aanvraag met status unverified |
POST | /public/cancel | uid | Annuleert een pending- of confirmed-afspraak, binnen de opzegtermijn |
POST | /public/admin-action | uid, action (confirm|refuse), sig | Bevestigt of weigert vanuit de beheerdersmail (HMAC-handtekening) |
Voorbeeld van een reservatie:
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é…"
}
Beheer-endpoints
Ze vereisen allemaal de capability owag_manage en een geldige WordPress REST-nonce
(header X-WP-Nonce).
| Methode | Pad | Parameters | Rol |
|---|---|---|---|
GET | /admin/agenda | from, to (Y-m-d) | events (het gevraagde bereik, geannuleerde afspraken uitgesloten) en pending (alle toekomstige aanvragen in afwachting) |
POST | /admin/appointment | kind (manual|block|consult|seance), date, time, duration (5–720 min), label, name, phone, email, notes, force, send_email | Maakt een handmatige afspraak of een blokkering aan |
POST | /admin/appointment/{id} | status (pending|confirmed|cancelled|done|noshow), name, phone, email, label, notes, date, time, duration, force, silent | Werkt bij, verplaatst, wijzigt de status. silent=1 onderdrukt elke e-mailverzending |
GET | /admin/customers | q (zoekopdracht) | Het bestand (maximaal 200 fiches) |
POST | /admin/customers | name (verplicht), phone, email, notes | Maakt een fiche aan |
POST | /admin/customers/{id} | idem | Werkt een fiche bij |
DELETE | /admin/customers/{id} | — | Verwijdert een fiche |
GET | /admin/invites | — | De open uitnodigingen (nooit gebruikt of vrijgegeven, niet vervallen) |
POST | /admin/invites | name (verplicht), label (verplicht), duration (15–720), days_valid (1–365, standaard 60), email, phone, send_email | Maakt een uitnodiging aan; geeft url en email_sent terug |
DELETE | /admin/invites/{id} | — | Verwijdert een uitnodiging |
GET | /admin/settings | — | De effectieve instellingen |
POST | /admin/settings | JSON-body (zie Instellingenreferentie) | Slaat de instellingen op |
Elke route declareert haar toegangscontrole expliciet; de capability wordt gecontroleerd voor er ook maar iets verwerkt wordt.
Foutcodes
De fouten zijn standaard WP_Error-objecten, met een leesbare code en een HTTP-status.
| Code | HTTP | Betekenis |
|---|---|---|
owag_spam | 403 | Botval ingevuld, of tijdstempel ontbreekt / te recent |
owag_rate | 429 | Uurquotum bereikt voor dit adres |
owag_day_full | 429 | Te veel aanvragen in afwachting van bevestiging op deze datum |
owag_busy | 503 | Reservatielock niet verkregen — zo dadelijk opnieuw proberen |
owag_slot_taken | 409 | Tijdslot intussen ingenomen |
owag_overlap | 409 | Overlapping vastgesteld (beheer) — opnieuw versturen met force |
owag_invite_invalid | 404 | Onbekend uitnodigingstoken |
owag_invite_used | 410 | Uitnodiging al verbruikt |
owag_invite_expired | 410 | Uitnodiging vervallen |
owag_too_late | 403 | Online annulering buiten de opzegtermijn |
owag_bad_sig | 403 | Ongeldige HMAC-handtekening |
owag_not_found | 404 | Afspraak of fiche niet gevonden |
owag_name, owag_phone, owag_email, owag_consent | 400 | Ongeldig veld of ontbrekende toestemming |
owag_bad_input, owag_bad_date | 400 | Ongeldige datum, ongeldig uur of ongeldige invoer |
owag_noop | 400 | Lege update-aanvraag |
owag_insert | 500 | Schrijffout in de database |
Developer hooks
In 1.3.1 stelt OW Agenda geen enkele publieke filter of action beschikbaar: er is geen
uitbreidings-API om te documenteren, en niets van wat volgt moet geraden worden. De enige
bruikbare hook is het geplande event owag_hourly_event, waarop u uw eigen onderhoud kan
aanhaken:
add_action( 'owag_hourly_event', function () {
// Votre traitement horaire, exécuté juste après celui du plugin.
} );
Voor al de rest is de REST API het integratiepunt: ze stelt precies bloot wat de interface doet, niet meer en niet minder.
Instellingenreferentie
Alle instellingen zitten in één enkele optie, owag_settings. Lezen en schrijven in 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,
] ) );
Standaardwaarden
[
// 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',
]
Detail
| Instelling | Type | Grenzen in de interface | Effect |
|---|---|---|---|
hours | array | 1 of 2 blokken per dag | Twee blokken maken een middagpauze mogelijk. Een blok wordt enkel bewaard als begin en einde geldig zijn en begin < einde. Een lege dag = gesloten |
slot_step | integer | 5 → 120 min | Afstand tussen de aangeboden uren. Hoe fijner, hoe langer de lijst |
consult_duration | integer | 15 → 480 min | Duur die in de agenda gereserveerd wordt voor een online consultatie |
buffer | integer | 0 → 120 min | Buffertijd die aan weerszijden van het kandidaat-tijdslot toegepast wordt |
min_notice_hours | integer | 0 → 168 u | Er wordt niets aangeboden voor nu + deze termijn |
max_advance_days | integer | 7 → 365 d | Horizon waarboven geen enkel tijdslot meer aangeboden wordt |
auto_confirm | 0 / 1 | — | Handmatige bevestiging of onmiddellijke bevestiging |
email_verify | 0 / 1 | — | Dubbele opt-in per e-mail (actief laten) |
cancel_notice_hours | integer | 0 → 168 u | Daarbuiten wordt de online annulering geweigerd met een bericht dat vraagt om te bellen |
notify_email | — | Ontvanger van de beheerdersmeldingen; dient ook als ORGANIZER in de ICS-bestanden | |
retention_months | integer | 0 → 120 maanden | Anonimisering van de afgelopen afspraken; 0 schakelt uit |
consult_label | tekst | — | Titel die op de reservatiewidget getoond wordt |
Twee niet-instelbare constanten verdienen het gekend te zijn, omdat ze het gedrag van de dubbele opt-in bepalen: het tijdslot van een niet-geverifieerde aanvraag wordt 45 minuten vastgehouden, en de aanvraag wordt na 48 uur verwijderd.
Opslaan via de REST API aanvaardt dezelfde sleutels in JSON; gehele getallen worden op minimaal nul gebracht, het meldingsadres wordt enkel aanvaard als het geldig is, en elk uurblok wordt aan serverzijde opnieuw gevalideerd.
Opgeslagen gegevens
Drie tabellen, allemaal met het prefix van uw installatie. De datums zijn DATETIME-waarden in
lokale tijd van de site, nooit in UTC — dat maakt de queries leesbaar en de vergelijking van
uren triviaal.
{prefix}_owag_appointments
| Kolom | Type | Opmerkingen |
|---|---|---|
id | BIGINT | Primaire sleutel |
uid | CHAR(32) | Uniek publiek token (hexadecimaal, cryptografische generator). Dient als sleutel voor de bevestigings-, annulerings- en beheerderslinks, en als ICS-UID |
kind | VARCHAR(16) | consult, seance, manual, block |
service_label | VARCHAR(120) | Getoonde titel |
customer_name, customer_phone, customer_email | VARCHAR | Contactgegevens |
customer_msg | TEXT | Bericht dat de klant achterliet |
admin_notes | TEXT | Interne notities, nooit naar de klant verstuurd |
start_dt, end_dt | DATETIME | Grenzen van het tijdslot (geïndexeerd op start_dt) |
status | VARCHAR(16) | unverified, pending, confirmed, cancelled, done, noshow |
source | VARCHAR(16) | web, invite, admin |
invite_id | BIGINT | Oorspronkelijke uitnodiging, indien van toepassing |
reminder_sent | TINYINT | Herinneringsvlag (teruggezet op 0 bij een verplaatsing) |
created_at, updated_at | DATETIME | updated_at voedt het ICS-revisienummer |
{prefix}_owag_invites
id, token (CHAR(32), uniek), contactgegevens van de klant, service_label,
duration_min, expires_at, appointment_id (ingevuld wanneer de link verbruikt wordt,
teruggezet op NULL als de afspraak geannuleerd wordt), created_at.
{prefix}_owag_customers
id, name, phone, email, notes, created_at, updated_at. Index op name en email.
Eén enkele optie draagt de configuratie (owag_settings); de andere zijn technisch:
owag_admin_secret (HMAC-geheim), owag_db_version, owag_pages_created,
owag_page_booking, owag_page_admin.
Internationalisatie
De volledige interface is vertaalbaar: beheerinstellingen, reservatiewidget, Agendapagina, op
zichzelf staande pagina’s, API-berichten en e-mailsjablonen. De brontaal is het Engels; de
plugin declareert het vertaaldomein ow-agenda en het pad /languages.
Het pakket levert het sjabloon languages/ow-agenda.pot. Een volledige Franse vertaling
wordt meegeleverd, gemaakt op basis van de oorspronkelijke formulering van de interface.
Een vertaling handmatig installeren
Compileer de .po en plaats de .mo in de vertaalmap van WordPress:
msgfmt ow-agenda-fr_FR.po -o ow-agenda-fr_FR.mo
# puis : wp-content/languages/plugins/ow-agenda-fr_FR.mo
WordPress laadt de catalogus bij de eerste vertaalde tekst; een handmatige oproep is niet nodig.
Twee valkuilen voor vertalers
- De teksten die voor JavaScript bestemd zijn gebruiken genummerde placeholders
(
%1$s,%2$s) en nooit een kale%s: de kleine formatter in de scripts begrijpt alleen die. Een niet-genummerde placeholder zou letterlijk getoond worden. - Twee teksten zijn PHP-datumformaten, geen zinnen:
l, F j, Y \a\t g:i aenl j F Y \a\t H:i. Vertaal ze als formaten (de tokens herschikken, letterlijke letters met een backslash escapen). Het Frans gebruiktl j F Y \à H\hi.
Deïnstallatie
De plugin deactiveren doet één ding: de uurlijkse geplande taak verwijderen. Uw gegevens, uw instellingen en uw pagina’s blijven intact, en alles vertrekt opnieuw bij de heractivatie.
De plugin verwijderen vanuit WordPress voert de volledige deïnstallatie uit:
- verwijdering van de drie tabellen (
owag_appointments,owag_invites,owag_customers); - verwijdering van de opties
owag_settings,owag_admin_secret,owag_db_version,owag_pages_created,owag_page_booking,owag_page_admin; - verwijdering van de geplande taak;
- intrekking van de capability
owag_managebij de rol beheerder.
De twee pagina’s die bij de activatie aangemaakt zijn, worden niet verwijderd — ze kunnen herwerkt zijn, in een menu staan, geïndexeerd zijn. Het is aan u om ze in de prullenbak te gooien als u ze niet meer wil.
Het verwijderen van de tabellen is definitief. Exporteer vooraf uw database als de geschiedenis van de afspraken waarde voor u heeft.
Probleemoplossing
Er verschijnt geen enkel tijdslot op de reservatiepagina
Loop de lijst in volgorde af:
- Zijn de uren van die dag ingevuld? Een lege dag is een gesloten dag.
- De minimale termijn (standaard 24 u) verbergt onvermijdelijk vandaag en een deel van morgen.
- Past de duur in het tijdsblok? Met een blok 09:00–12:00 en een duur van 240 minuten kan er geen enkel tijdslot bestaan.
- De horizon: dagen voorbij
max_advance_daysworden nooit aangeboden. - De buffertijd: een ruime buffer op een volle planning kan alles dichtzetten.
- Een blokkering dekt misschien de hele dag. Open de Agendapagina op de betrokken dag.
- De tijdzone van de site (Instellingen → Algemeen): een verkeerd ingestelde tijdzone verschuift alle berekeningen.
De e-mails komen niet aan
Dat is bijna altijd de afleverbaarheid, niet de plugin.
- Controleer of de afspraak wel degelijk in de Agendapagina staat: staat ze er, dan heeft de plugin zijn werk gedaan.
- Installeer een SMTP-plugin (Fluent SMTP, WP Mail SMTP…). De functie
mail()van PHP wordt door de overgrote meerderheid van de ontvangende servers geweigerd. - Controleer SPF, DKIM en DMARC van uw domein.
- Test het adres in OW Agenda → Beheerdersmeldingen sturen naar: een ongeldig adres blokkeert de meldingen in stilte.
De herinnering de dag voordien is niet vertrokken
De herinnering hangt af van WP-Cron, dat van het verkeer afhangt. Schakel op een weinig bezochte site over op een echte systeemcron (zie Herinneringen en geplande taak). Controleer ook of de afspraak wel degelijk bevestigd is, of ze een e-mailadres draagt en of het geen blokkering is.
De Agendapagina toont “De toegang is voorbehouden aan het team”
De ingelogde gebruiker heeft de capability owag_manage niet. Die wordt bij de activatie aan de
beheerders gegeven; voor de andere rollen, zie Installatie.
De knop “Bevestigen” in de e-mail toont “Ongeldige link”
Drie mogelijke oorzaken: de link werd door het e-mailprogramma afgekapt (probeer de volledige URL te kopiëren), de afspraak is al behandeld (het bericht vermeldt dat), of het HMAC-geheim van de site is opnieuw aangemaakt. In dat laatste geval gebruikt u de Agendapagina: de oude links kunnen per definitie niet meer gevalideerd worden.
Twee afspraken staan op hetzelfde tijdslot
Sinds 1.3.0 is dat onmogelijk via de online reservatie. Wel kan de beheerder een overlapping
forceren vanaf de Agendapagina (de plugin meldt dat en vraagt een bevestiging): ga eerst na of
het niet om een bewuste forcering gaat. Controleer ook of uw database GET_LOCK() wel degelijk
ondersteunt — een exotische hosting zou dan 503-fouten geven bij het reserveren in plaats van
duplicaten.
De pagina’s “Reserveren” en “Agenda” zijn niet aangemaakt
Ze worden maar één keer aangemaakt, bij de allereerste activatie. Hebt u ze verwijderd, maak dan
twee pagina’s met de shortcodes [ow_agenda] en [ow_agenda_admin] en laat de opties
owag_page_booking en owag_page_admin ernaar wijzen, zodat de links in de e-mails correct
blijven:
update_option( 'owag_page_booking', 42 ); // ID de votre page de réservation
update_option( 'owag_page_admin', 43 ); // ID de votre page Agenda
Het agendabestand zit niet in bijlage
Het bestand wordt via de bestands-API van WordPress in de tijdelijke map van de server geschreven. Is die map niet beschrijfbaar (quota, rechten, dichtgetimmerde hosting), dan vertrekt de e-mail zonder bijlage in plaats van te mislukken. Controleer de schrijfrechten en de vrije schijfruimte.
De uren staan een uur verkeerd
Instellingen → Algemeen → Tijdzone. Kies een stad (Europe/Brussels) in plaats van een vaste
UTC-verschuiving: alleen een benoemde tijdzone beheert de overgang naar de zomertijd.
FAQ
Is OW Agenda echt gratis?
Ja. GPL-2.0-or-later, als .zip te downloaden op
https://optionweb.dev/nl/addons/ow-agenda/. Geen Pro-versie, geen licentiesleutel, geen enkele
functie achter slot, geen account om aan te maken, geen quotum op het aantal afspraken.
Houden de online tijdsloten rekening met telefonisch gemaakte afspraken? Ja. Elk item in de agenda — online reservatie, handmatig ingevoerde afspraak, sessie op uitnodiging, blokkering — maakt zijn tijdsspanne onbeschikbaar voor de online reservatie.
Moet de klant een account aanmaken? Nee. Geen account, geen wachtwoord. Alle acties van de klant (bevestiging, annulering) verlopen via privélinks die per e-mail ontvangen worden.
Kan men meerdere praktijkhouders of meerdere ruimtes beheren? Nee. OW Agenda beschrijft één enkele planning. Voor twee onafhankelijke agenda’s hebt u twee sites nodig (of twee sites van een multisite-netwerk).
Synchroniseert het met Google Agenda? In de richting die voor de klant telt: ja. Elke bevestiging bevat een standaard agendabestand dat het item toevoegt — en bijwerkt, en verwijdert — in Google Agenda, Outlook of de agenda van een iPhone. De plugin leest echter uw externe agenda’s niet: uw persoonlijke onbeschikbaarheden voert u in als blokkeringen.
Kan men een voorschot laten betalen bij de reservatie? Nee, dat valt bewust buiten het bereik van de plugin.
Hoeveel prestaties kan men online aanbieden? Eén enkele, waarvan u het label en de duur kiest. De andere prestaties verlopen via de sessie-uitnodigingen, waarbij u de titel en de duur geval per geval vastlegt. Die keuze is bewust: een menu met prestaties op een publieke pagina vermenigvuldigt de slecht gerichte aanvragen.
Werkt het op een multisite? Ja. Elke site van het netwerk heeft zijn eigen tabellen, zijn eigen instellingen en zijn eigen agenda.
Vertraagt de plugin de site? Nee. Er wordt geen enkel script geladen buiten de twee betrokken pagina’s; de reservatiewidget weegt minder dan 12 KB, zonder jQuery of framework. Het periodieke werk past in één uurlijkse taak.
Wat gebeurt er als een klant zijn e-mail nooit bevestigt? Zijn tijdslot komt na 45 minuten weer vrij en zijn aanvraag wordt na 48 uur verwijderd. U hoort er nooit iets over.
Waar vind ik support?
- Supportportaal: https://optionweb.dev/nl/addons/support/
- E-mail: support@optionweb.dev
- Documentatie: https://optionweb.dev/nl/addons/ow-agenda/
Changelog
1.3.1
Compliance
- De op zichzelf staande actie-, verificatie- en annuleringspagina’s drukken hun CSS en hun
JavaScript niet meer inline af: beide zijn voortaan bestanden die geregistreerd en geserveerd
worden door de enqueue-API van WordPress. De twee variabele knopkleuren lopen via aangepaste
CSS-eigenschappen, en het script wordt geconfigureerd via
wp_localize_script(). - De vertaalcatalogi zitten niet meer in het pakket: enkel
languages/ow-agenda.potwordt meegeleverd, het Frans wordt verdeeld via de WordPress.org-taalpakketten die door translate.wordpress.org gevoed worden. - De minimale WordPress-versie gaat terug naar 6.3: niets in het pakket hangt nog af van de detectie van meegeleverde vertalingen die in 6.7 werd ingevoerd.
REST API
- Elke beheerroute declareert haar
permission_callbackvoortaan expliciet in plaats van hem te delen viaarray_merge(). De capabilitycontrole werd al toegepast; ze is nu zichtbaar voor statische analyse.
Geen enkele gedragswijziging.
1.3.0
Internationalisatie
- Plugin volledig vertaalbaar (domein
ow-agenda, Engels als brontaal): instellingen, publieke pagina’s, API-berichten, e-mails en JavaScript-interface. - Sjabloon
languages/ow-agenda.potmeegeleverd en volledige Franse vertaling.
Beveiliging
- De publieke identificaties van de afspraken en de uitnodigingstokens worden gegenereerd door een
cryptografische generator (
random_bytes()). Die tokens zijn de enige authenticatie van de annulerings-, bevestigings- en uitnodigingslinks. - De botval en de tijdstempel zijn verplicht geworden: ze weglaten schakelde de anti-spamcontroles uit.
- Het uurquotum wordt samengeteld per /64-prefix in IPv6, en een dagplafond beperkt de niet-geverifieerde aanvragen — een bezoeker kan niet langer alle tijdsloten van één dag vasthouden.
- Het quotum wordt pas na de validatie van het formulier afgeteld: een typfout blokkeert een legitieme bezoeker niet meer een uur lang.
Betrouwbaarheid
- Gelijktijdige reservaties van hetzelfde tijdslot worden geserialiseerd door een named MySQL-lock. De vorige transactie legde geen enkele rijvergrendeling: twee bezoekers konden hetzelfde uur boeken.
- De herinnering de dag voordien gebruikt een schuivend venster van 12 tot 36 u en haalt haar achterstand in; elke verzending wordt atomair gereserveerd, twee cron-uitvoeringen kunnen dezelfde herinnering niet meer tweemaal versturen.
- De uurlijkse taak plant zichzelf automatisch opnieuw in als ze verdwijnt: herinneringen, opruiming en anonimisering kunnen niet meer in stilte stilvallen.
- Het verplaatsen van een afspraak werkt de agenda van de klant bij (
SEQUENCE,METHOD:REQUEST, organisator en deelnemer); de annulering stuurt eenMETHOD:CANCELdie het item verwijdert. - De datums van de widget worden berekend in lokale tijd en niet in UTC: geen verloren dag meer voor bezoekers ten oosten van UTC.
- De annulering van een afspraak die uit een uitnodiging voortkomt, geeft de uitnodiging vrij, die opnieuw bruikbaar wordt.
- Geannuleerde afspraken en vervallen niet-bevestigde aanvragen worden niet meer aan de agenda teruggegeven: minder persoonsgegevens in de antwoorden, geen spookafspraken meer.
1.2.1
- Conformiteit met Plugin Check: escaping van de output, sanitizing van de input, nonce op de instellingen, readme.
- De deïnstallatie verwijdert ook de tabel van het klantenbestand en het HMAC-geheim.
1.2.0
- HTML-e-mails (kaartopmaak, accentkleur van de site) voor alle meldingen.
- Knoppen Bevestigen / Weigeren in de beheerdersmail (HMAC-ondertekende links).
- Klantenbestand: zoeken, aanmaken, bewerken, verwijderen, snelkoppelingen naar een nieuwe afspraak en een uitnodiging.
1.1.0
- Dubbele verificatie per e-mail tegen bots (tijdslot 45 minuten vastgehouden, opruiming na 48 uur).
1.0.0
- Eerste versie: online reserveren, mobile-first beheeragenda, sessie-uitnodigingen, e-mails met agendabestand en herinnering de dag voordien.
Ontworpen door OptionWeb — Julien Daniel, Châtelet, België.