Browse Source

Taak 128-A: bevestigingsmail verklaard, confirm-default naar FALSE

De simplenews-instellingenpagina wees het uit: opt-in/out staat op
Double, en die stand betekent 'anonieme bezoekers krijgen een
bevestigingsmail, ingelogde gebruikers worden direct (un)subscribed'.
Een app-gebruiker is altijd ingelogd, dus confirm=TRUE liet de app
juist afwijken van zowel de website als van deze instelling. Default
in de code omgedraaid naar FALSE; de variabele blijft als override.

Daarmee vervalt het eerder genoteerde risico: de 450 onbevestigde
anonieme inschrijvingen tegenover 45 direct-actieve account-
inschrijvingen zijn precies de tweedeling die Double voorschrijft,
geen kapotte mailroute.

De resource blijft de status teruggeven in plaats van een bool, zodat
de app niet hoeft te wijzigen als confirm ooit aangezet wordt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 10 hours ago
parent
commit
1eefc310ef
2 changed files with 84 additions and 122 deletions
  1. 38 35
      TASKS.md
  2. 46 87
      snippets/nieuwsbrieven-drupal.inc.txt

+ 38 - 35
TASKS.md

@@ -130,51 +130,54 @@ en geen van de 6 Rules op de site luistert ernaar (alle zes hangen aan
 `node_insert`/`user_insert`/`node_presave`). "Drupal doet dat standaard al"
 klopt dus niet voor de ingelogde route.
 
-**Bob 2026-09-21: bevestiging moet er komen.** De resource doet daarom
-`subscribe` met `$confirm = TRUE` (status 2 + bevestigingsmail) en `unsubscribe`
-altijd met `FALSE` — met bevestiging zou de gebruiker in de app op "uit" drukken
-en toch abonnee blijven, en afmelden hoort makkelijker te zijn dan aanmelden.
-Om te wisselen zonder codewijziging: `drush vset custom_nieuwsbrieven_confirm 0`.
+**Opgehelderd 2026-09-21 (Bob, met de simplenews-instellingenpagina erbij):
+de app stuurt GEEN bevestigingsmail, en dat is correct.** De drie nieuwsbrieven
+staan op opt-in/out-methode **Double**, en simplenews omschrijft die stand zelf
+als: *"anonymous users receive an (un)subscription confirmation email.
+Authenticated users are (un)subscribed immediately."* Een app-gebruiker is per
+definitie ingelogd, dus die hoort direct verwerkt te worden — net als op
+`/user/<uid>/simplenews`. `custom_nieuwsbrieven_confirm` staat daarom op
+**FALSE** (default in de code); op `1` zetten geeft alsnog bevestiging.
 
-**Daarom geeft de resource de STATUS terug, niet een kale bool** (1 aan / 2 wacht
-op bevestiging / 0 uit). Zonder dat onderscheid springt de switch bij de volgende
-paginaload terug naar uit en lijkt de app kapot.
+Uitschrijven gaat altijd direct, ook als bevestiging aan zou staan: anders
+drukt de gebruiker in de app op "uit" en blijft hij abonnee tot hij een mail
+opent.
 
-### ⚠️ Open risico: komt die bevestigingsmail wel aan?
+**De resource geeft toch de STATUS terug, niet een kale bool** (1 aan / 2 wacht
+op bevestiging / 0 uit). Dat kost niets en houdt de deur open: zet iemand ooit
+`confirm` aan, dan hoeft de app niet aangepast te worden.
 
-Gemeten over de hele database (tid 1 niet meegeteld):
+### Eerder als risico genoteerd, nu verklaard
+
+Van 450 anonieme inschrijvingen is er in tien jaar geen enkele bevestigd,
+terwijl alle 45 account-inschrijvingen meteen op actief staan:
 
 | | anoniem (uid 0) | met account |
 |---|---|---|
-| status 1 (actief) | 0 | 45 |
-| status 0 (uit) | 0 | 3 |
-| status 2 (onbevestigd) | **450** | 0 |
-
-Van de 450 anonieme inschrijvingen via de publieke formulieren is er in **tien
-jaar** (2015-12 t/m 2025-12, gestaag verspreid — geen eenmalige import) **geen
-enkele** ooit bevestigd. Bij een werkende mail verwacht je 30-70 %, niet 0 %.
-De inschrijvingen mét account staan allemaal meteen goed, en dat is logisch:
-die liepen via `/user/<uid>/simplenews`, en dat pad gebruikt `confirm = FALSE`
-en stuurt dus nooit een mail. Het enige pad dat wél een bevestigingsmail stuurt,
-is precies het pad waar nooit iemand doorheen komt.
-
-Mailconfiguratie oogt in orde (`smtp_on = 1`, mailsystem → mimemail, geen
-reroute, afzender `contact@uitgaanskrant.com`); de watchdog bevat geen
-mailfouten maar is grotendeels leeg, dus dat bewijst niets. **Testrecept staat
-in `snippets/nieuwsbrieven-drupal.inc.txt`** (schrijft en mailt echt, dus voor
-Bob zelf). Komt de mail niet aan, dan is `custom_nieuwsbrieven_confirm = 0` de
-enige variant die voor de gebruiker werkt totdat dat opgelost is.
+| status 1 | 0 | 45 |
+| status 0 | 0 | 3 |
+| status 2 | **450** | 0 |
+
+Dat leek op een kapotte mailroute, maar het is exact de tweedeling die de
+Double-stand voorschrijft. **Raakt de app niet.** Wat blijft staan is een lage
+conversie op de publieke website-formulieren (0 % bevestigd) — ooit misschien
+een eigen kijkje waard, geen taak.
 
 ### 128-A · Drupal: resource `nieuwsbrieven` · Eigenaar: Bob
 
-**✅ GEDEPLOYD OP DEVBOB 2026-09-21 door Bob; index geverifieerd.** Alle drie de
-operaties staan aan in het endpoint en `nieuwsbrieven.json` levert de juiste lijst:
-een gebruiker zonder de rol `Horeca-owner` ziet 2 nieuwsbrieven, en de status
+**✅ GEDEPLOYD OP DEVBOB ÉN PRODUCTIE 2026-09-21 door Bob.** Alle drie de
+operaties staan op beide omgevingen aan en `nieuwsbrieven.json` levert de juiste
+lijst: een gebruiker zonder de rol `Horeca-owner` ziet 2 van de 3, en de status
 beweegt mee met een abonnement. **Nog niet getest: de twee POST-endpoints** —
-Bobs test liep via de website-pagina (`source = website`, dus het
-`confirm = FALSE`-pad van simplenews zelf), niet via `subscribe.json`. Die POST
-is meteen de test voor het mailrisico hieronder: hij hoort **status 2 plus een
-mail** op te leveren, en zet `source = app`.
+Bobs tests liepen via de website-pagina (`source = website`). Op productie staat
+`source = app` nog op 0 treffers; dat is meteen de manier om te zien of de
+eerste app-aanmelding door de nieuwe route is gekomen.
+
+⚠️ **Eén nazorgpunt op beide omgevingen:** het gedeployde bestand heeft nog
+`variable_get('custom_nieuwsbrieven_confirm', TRUE)`. Dat moet FALSE zijn (zie
+hierboven). Kies één van twee: de regel in `custom.nieuwsbrieven.inc` aanpassen
+(staat al goed in het snippet) óf tot die tijd
+`drush @<alias> vset custom_nieuwsbrieven_confirm 0` op beide omgevingen.
 
 **✅ Code is af en staat klaar in `snippets/nieuwsbrieven-drupal.inc.txt`** (compleet bestand `custom.nieuwsbrieven.inc` + het blok voor `custom_services_resources()` + de deploy- en controlestappen). PHP-syntax gecontroleerd op de server en de index-logica read-only drooggedraaid op productie voor een Horeca-owner, een gewone gebruiker met abonnement en een zonder: rolfilter en statussen kloppen.
 

+ 46 - 87
snippets/nieuwsbrieven-drupal.inc.txt

@@ -28,97 +28,54 @@ valt buiten de bestaande Cloudflare Cache Rules (die matchen op
 er alleen nooit aan toe.
 
 
-⚠️⚠️ TEST EERST OF DIE BEVESTIGINGSMAIL AANKOMT — er is reden tot twijfel
-------------------------------------------------------------------------
-Gemeten 2026-09-21 over de hele database (tid 1, een oude restterm, niet
-meegeteld):
+BEVESTIGINGSMAIL — waarom die er voor de app NIET is
+-----------------------------------------------------
+De drie nieuwsbrieven staan op opt-in/out-methode **Double**. Simplenews
+omschrijft die stand zelf zo:
+
+  "Double: When (un)subscribing at a subscription form, anonymous users
+   receive an (un)subscription confirmation email. Authenticated users
+   are (un)subscribed immediately."
+
+Bevestiging geldt dus alleen voor ANONIEME bezoekers. Een app-gebruiker is
+per definitie ingelogd en wordt direct (un)subscribed — net als op
+/user/<uid>/simplenews. Daarom staat `custom_nieuwsbrieven_confirm` op
+FALSE: zo doet de app exact hetzelfde als de website.
+
+Dit verklaart ook het cijfer dat eerder verdacht leek. Gemeten over de hele
+database (tid 1, een oude restterm, niet meegeteld):
 
               anoniem (uid 0)   met account (uid > 0)
   status 1              0                 45
   status 0              0                  3
   status 2            450                  0
 
-Dus: van de 450 anonieme inschrijvingen via de publieke formulieren is er
-in TIEN JAAR (2015-12 t/m 2025-12, gestaag verspreid — geen eenmalige
-import) GEEN ENKELE ooit bevestigd. Bij een werkende bevestigingsmail zou
-je 30-70% verwachten, niet 0%.
-
-De inschrijvingen mét account staan allemaal meteen goed, en dat is ook
-logisch: die liepen via /user/<uid>/simplenews, en dat pad gebruikt
-confirm=FALSE en stuurt dus nooit een mail. Met andere woorden: het enige
-pad dat wél een bevestigingsmail stuurt, is ook precies het pad waar nooit
-iemand doorheen komt.
-
-Dat kan twee dingen betekenen: de mail wordt niet bezorgd, of de
-bevestigingslink werkt niet. De mailconfiguratie zelf oogt in orde
-(smtp_on = 1, mailsystem delegeert naar mimemail, geen reroute_email,
-afzender contact@uitgaanskrant.com) en de watchdog bevat geen mailfouten —
-maar die is grotendeels leeg, dus dat bewijst niets.
-
-WAAROM DIT ERTOE DOET: zet je bevestiging aan voor de app zonder dit te
-testen, dan zet de gebruiker de switch aan, gebeurt er verder niets, en
-blijft het abonnement voor eeuwig op status 2 staan. Precies wat die 450
-nu doen.
-
-TEST (schrijft wel, en verstuurt een echte mail — daarom niet door Claude
-gedraaid):
-
-  # 1. abonneer jezelf MET bevestiging op de bezoekersnieuwsbrief
-  drush @uitgaanskrant.com php-eval \
-    "simplenews_subscribe_user('JOUW@MAIL.NL', 36667, TRUE, 'test');"
-
-  # 2. check de status — hoort 2 te zijn
-  drush @uitgaanskrant.com sql-query \
-    "SELECT s.tid, s.status FROM simplenews_subscription s
-     INNER JOIN simplenews_subscriber sub ON sub.snid=s.snid
-     WHERE sub.mail='JOUW@MAIL.NL';"
-
-  # 3. komt de mail aan? klik de link. Daarna moet stap 2 status 1 geven.
-
-  # 4. opruimen
-  drush @uitgaanskrant.com php-eval \
-    "simplenews_unsubscribe_user('JOUW@MAIL.NL', 36667, FALSE, 'test');"
-
-Komt de mail NIET aan, dan is dat eerst een mailprobleem om op te lossen —
-en zolang dat niet opgelost is, is `custom_nieuwsbrieven_confirm` op 0
-(direct abonneren, geen mail) de enige variant die voor de gebruiker
-werkt. De code hieronder ondersteunt beide zonder wijziging.
-
-
-BEVESTIGINGSMAIL — hoe je dit aanpast
---------------------------------------
-Staat AAN (Bob 2026-09-21). Concreet gedrag:
-
-  ABONNEREN   -> simplenews zet het abonnement op status 2 (onbevestigd)
-                 en mailt een link. Pas na het klikken wordt het status 1
-                 en ontvangt de gebruiker de nieuwsbrief.
-  UITSCHRIJVEN-> gaat ALTIJD direct, zonder mail. Bewust: met bevestiging
-                 zou de gebruiker in de app op 'uit' drukken en tóch
-                 abonnee blijven tot hij een mail opent. Dat is slechte UX
-                 en juridisch precies de verkeerde kant op — afmelden moet
-                 makkelijker zijn dan aanmelden.
-
-Omzetten naar 'direct abonneren, geen mail' kan zonder codewijziging:
-  drush @uitgaanskrant.com vset custom_nieuwsbrieven_confirm 0
-Terug naar bevestiging:
+Precies de tweedeling die de Double-stand voorschrijft: anoniem gaat naar
+status 2 (wacht op de bevestigingslink), ingelogd gaat meteen naar 1. Geen
+kapotte mailroute. Wel blijft staan dat van die 450 anonieme inschrijvingen
+er in tien jaar nul zijn bevestigd — dat is een lage conversie op de
+publieke formulieren en misschien ooit een eigen kijkje waard, maar het
+raakt de app niet.
+
+WIL JE HET TOCH MET BEVESTIGING (dan blijft het abonnement op status 2 tot
+de gebruiker op de link klikt — de app toont die stand al):
+  drush @uitgaanskrant.com vset custom_nieuwsbrieven_confirm 1
+Terug naar direct abonneren:
   drush @uitgaanskrant.com vdel custom_nieuwsbrieven_confirm
 
-De TEKST van die bevestigingsmail is al Nederlands en staat in variabelen,
-te bewerken op  /admin/config/services/simplenews  (tab Subscription):
-  simplenews_confirm_subscribe_subject
-      "Bevestiging voor [simplenews-category:name] van [site:name]"
-  simplenews_confirm_subscribe_unsubscribed
-      "Wij hebben een verzoek ontvangen om [simplenews-subscriber:mail] in
-       te schrijven op de [simplenews-category:name] nieuwsbrief …
-       [simplenews-subscriber:subscribe-url]"
-De tekst noemt nu "de website op [site:url]" — dat klopt straks niet meer
-voor een aanmelding vanuit de app. Overweeg een neutralere formulering.
 
-⚠️ Deze instelling raakt ALLEEN de app. De website-pagina uit je screenshot
-(/user/<uid>/simplenews) heeft confirm=FALSE HARDCODED in simplenews zelf
-(includes/simplenews.subscription.inc regel 97) en stuurt dus nog steeds
-geen mail. Wil je dat gelijktrekken, dan is dat een aparte ingreep
-(hook_form_alter die dat submit-handler vervangt) — niet in deze patch.
+GEDRAG IN HET KORT
+-------------------
+  ABONNEREN    -> direct actief (status 1), geen mail. Zie hierboven.
+  UITSCHRIJVEN -> altijd direct, zonder mail. Ook met bevestiging aan:
+                  anders drukt de gebruiker in de app op "uit" en blijft
+                  hij abonnee tot hij een mail opent. Afmelden hoort
+                  makkelijker te zijn dan aanmelden, en "Double" schrijft
+                  voor ingelogde gebruikers sowieso direct uitschrijven voor.
+
+⚠️ Deze instelling raakt ALLEEN de app. De website-pagina
+(/user/<uid>/simplenews) heeft confirm=FALSE hardcoded in simplenews zelf
+(includes/simplenews.subscription.inc regel 97); daar verandert niets aan.
 
 
 =====================================================================
@@ -295,11 +252,13 @@ function _custom_nieuwsbrieven_wijzig($tid, $aan) {
     // Al actief? Niets doen. Anders stuurt simplenews bij confirm=TRUE tóch
     // een mail ("je bent al ingeschreven") bij elke dubbele tik.
     if ($huidig !== 1) {
-      // TRUE  = bevestigingsmail, abonnement blijft op status 2 tot de
-      //         gebruiker op de link klikt.
-      // FALSE = meteen actief, geen mail (wat de website zelf doet voor
-      //         ingelogde gebruikers).
-      $confirm = (bool) variable_get('custom_nieuwsbrieven_confirm', TRUE);
+      // FALSE (default) = meteen actief, geen mail. Dit is wat de
+      //   opt-in/out-methode "Double" voorschrijft: anonieme bezoekers
+      //   krijgen een bevestigingsmail, ingelogde gebruikers worden
+      //   direct (un)subscribed. Een app-gebruiker is altijd ingelogd.
+      // TRUE = bevestigingsmail; het abonnement blijft dan op status 2
+      //   tot de gebruiker op de link klikt.
+      $confirm = (bool) variable_get('custom_nieuwsbrieven_confirm', FALSE);
       simplenews_subscribe_user($account->mail, $tid, $confirm, 'app');
     }
   }