|
@@ -33,6 +33,7 @@ Google Play en één doorloop die alleen Bob kan doen.
|
|
|
|
|
|
|
|
|
|
|
|
|
| **114** | Opruimen na de filterbalk-ombouw | **Eigenaar: Bob.** Restjes van taak 113 (afgerond 2026-09-20): (a) component-parameter **`parameter1`** op `FilterBalkComponent` ontstond automatisch bij *Convert to Component* en wordt nergens gelezen — mag weg, en daarmee ook de meegegeven waarde op Home en `PUitgaanPage`; (b) **page state `zoekOpen` op Home** is overbodig (de App State-versie stuurt alles); (c) App State **`zoekActief`** (Boolean) bestond al vóór deze taak en heeft **0 gebruikers**; (d) wegwerp: componenten **`FilterBalkKanweg`** en **`FilterBalkComponentCopy`** + pagina's **`homeCopy`** en **`PUitgaanPageCopy`**. Geen van alle blokkeert iets — `dart analyze` geeft 0 errors |
|
|
| **114** | Opruimen na de filterbalk-ombouw | **Eigenaar: Bob.** Restjes van taak 113 (afgerond 2026-09-20): (a) component-parameter **`parameter1`** op `FilterBalkComponent` ontstond automatisch bij *Convert to Component* en wordt nergens gelezen — mag weg, en daarmee ook de meegegeven waarde op Home en `PUitgaanPage`; (b) **page state `zoekOpen` op Home** is overbodig (de App State-versie stuurt alles); (c) App State **`zoekActief`** (Boolean) bestond al vóór deze taak en heeft **0 gebruikers**; (d) wegwerp: componenten **`FilterBalkKanweg`** en **`FilterBalkComponentCopy`** + pagina's **`homeCopy`** en **`PUitgaanPageCopy`**. Geen van alle blokkeert iets — `dart analyze` geeft 0 errors |
|
|
|
|
|
+| **128-B/C/D** | Nieuwsbrieven-blok op `mijnProfiel` | **Wacht op 128-A (Drupal).** Plan is af en doorgemeten — blok, waarschuwing bij 0 gevolgde gemeenten, vertalingen. Eigen blok hieronder |
|
|
|
|
|
|
|
|
## 👤 BOB — OPEN, in volgorde van wat de livegang blokkeert
|
|
## 👤 BOB — OPEN, in volgorde van wat de livegang blokkeert
|
|
|
|
|
|
|
@@ -54,6 +55,7 @@ Google Play en één doorloop die alleen Bob kan doen.
|
|
|
| **123** | `mijn_stadsrechten` uitbreiden met de keten | **Code staat klaar en is op productie getest.** Vervang regels 316-372 van `custom.evenementen_aanmaken.inc` + `cc all`. Blokkeert 124 |
|
|
| **123** | `mijn_stadsrechten` uitbreiden met de keten | **Code staat klaar en is op productie getest.** Vervang regels 316-372 van `custom.evenementen_aanmaken.inc` + `cc all`. Blokkeert 124 |
|
|
|
| **124-126** | Stadsrechten-cascade op `stadsactiviteitAanmaken` | Voorvullen van provincie/gemeente/plaats (124), een **crash** bij een lege plaatsenlijst (125) en een stille verkeerde plaats bij het wisselen van gemeente (126). Eigen blok hieronder |
|
|
| **124-126** | Stadsrechten-cascade op `stadsactiviteitAanmaken` | Voorvullen van provincie/gemeente/plaats (124), een **crash** bij een lege plaatsenlijst (125) en een stille verkeerde plaats bij het wisselen van gemeente (126). Eigen blok hieronder |
|
|
|
| **127** | **Kopie-knop op `MijnAanmeldingen`** (clonen) | Wens Bob 2026-09-20, **vóór de livegang**. Backend bestaat al; Drupal-patch voor 3 gaten staat klaar en is getest (127-A). App-deel in stappen B t/m F; het activiteitendeel (127-E) deelt de cascade met 124. Eigen blok hieronder |
|
|
| **127** | **Kopie-knop op `MijnAanmeldingen`** (clonen) | Wens Bob 2026-09-20, **vóór de livegang**. Backend bestaat al; Drupal-patch voor 3 gaten staat klaar en is getest (127-A). App-deel in stappen B t/m F; het activiteitendeel (127-E) deelt de cascade met 124. Eigen blok hieronder |
|
|
|
|
|
+| **128-A** | **Nieuwsbrieven: Drupal-resource** | Wens Bob 2026-09-21. Plan + code-opzet staan klaar en zijn doorgemeten; alleen bouwen, deployen en de resource **plus de 3 operaties** aanvinken in het endpoint. Blokkeert 128-B/C/D. Eigen blok hieronder |
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
#
|
|
#
|
|
@@ -81,6 +83,182 @@ verse export — zie de commit van die datum.)*
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+## 📰 128 · Nieuwsbrieven aan/uit in de app · Eigenaar: Bob (A) + Claude (B-D)
|
|
|
|
|
+
|
|
|
|
|
+**Wens Bob 2026-09-21.** Op de website kan een ingelogde gebruiker op
|
|
|
|
|
+`/user/<uid>/simplenews` drie nieuwsbrieven aan- en uitvinken. Dat moet ook in
|
|
|
|
|
+de app kunnen. **Plan is af en volledig doorgemeten (2026-09-21); alleen A moet
|
|
|
|
|
+gebouwd/gedeployd worden vóór B t/m D kunnen.**
|
|
|
|
|
+
|
|
|
|
|
+### Wat er al staat — gemeten op productie
|
|
|
|
|
+
|
|
|
|
|
+Simplenews **7.x-1.1**, nieuwsbrieven zijn taxonomy-termen in vocabulary
|
|
|
|
|
+`newsletter` (vid 11). ⚠️ `variable_get('simplenews_vid')` geeft **0** — leid de
|
|
|
|
|
+vocabulary dus nooit daaruit af, gebruik `simplenews_category_get_visible()`.
|
|
|
|
|
+
|
|
|
|
|
+| tid | Nieuwsbrief | actief | onbevestigd | uitgeschreven |
|
|
|
|
|
+|---|---|---|---|---|
|
|
|
|
|
+| 18017 | Ondernemers nieuwsbrief | 11 | **360** | 0 |
|
|
|
|
|
+| 36667 | Uitgaanskrant voor bezoekers van de horeca | 16 | 47 | 1 |
|
|
|
|
|
+| 36676 | Uitgaanskrant.com wekelijkse uitgaansagenda | 18 | 43 | 2 |
|
|
|
|
|
+
|
|
|
|
|
+Opslag: `simplenews_subscriber` (snid/mail/uid/activated) +
|
|
|
|
|
+`simplenews_subscription` (snid/tid/**status**/source). Status: **1 = actief,
|
|
|
|
|
+0 = uitgeschreven, 2 = onbevestigd**.
|
|
|
|
|
+
|
|
|
|
|
+**De wekelijkse agenda (36676) hangt al aan de app.** De edities heten
|
|
|
|
|
+"Favoriete Gemeenten" en selecteren content via de flag **`favorite_town`
|
|
|
|
|
+(fid 2)** — precies de flag die "Gemeente volgen" in
|
|
|
|
|
+`SelectStateDropDownComponent` al zet via `favorieten/flag`. Er hoeft dus niets
|
|
|
|
|
+gekoppeld te worden; alleen het abonnement zelf ontbreekt.
|
|
|
|
|
+
|
|
|
|
|
+### ⚠️ De bevestigingsmail — gemeten, want hier zit een valkuil
|
|
|
|
|
+
|
|
|
|
|
+`simplenews_subscribe_user($mail, $tid, $confirm, $source)` heeft twee takken
|
|
|
|
|
+(`simplenews.module:1281`):
|
|
|
|
|
+
|
|
|
|
|
+- **`$confirm = FALSE`** → status meteen **1 (actief)**, **géén mail**, alleen
|
|
|
|
|
+ `module_invoke_all('simplenews_subscribe_user', ...)`. Dit is wat de
|
|
|
|
|
+ user-settings-pagina uit het screenshot doet
|
|
|
|
|
+ (`simplenews.subscription.inc:97`, source `'website'`).
|
|
|
|
|
+- **`$confirm = TRUE`** → status **2 (onbevestigd)** + bevestigingsmail met
|
|
|
|
|
+ link; pas ná het klikken wordt het actief. **Hier komen die 360 onbevestigde
|
|
|
|
|
+ inschrijvingen vandaan.**
|
|
|
|
|
+
|
|
|
|
|
+Een **welkomstmail bestaat niet**: `simplenews_rules` levert alleen het *event*,
|
|
|
|
|
+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.
|
|
|
|
|
+
|
|
|
|
|
+**Daarom geeft de resource de STATUS terug, niet een kale bool** — dan werkt de
|
|
|
|
|
+app bij beide keuzes en is wisselen later één regel in Drupal, zonder
|
|
|
|
|
+FlutterFlow-wijziging. Zonder status krijg je bij `confirm = TRUE` een echte
|
|
|
|
|
+bug: de switch springt bij de volgende paginaload terug naar uit, want het
|
|
|
|
|
+abonnement is nog onbevestigd. Advies blijft `FALSE` (gebruiker is ingelogd,
|
|
|
|
|
+mailadres is bewezen, en het is exact wat de site zelf doet).
|
|
|
|
|
+
|
|
|
|
|
+### 128-A · Drupal: resource `nieuwsbrieven` · Eigenaar: Bob
|
|
|
|
|
+
|
|
|
|
|
+Zelfde vorm als het bestaande `favorieten` (flag/unflag/is_flagged):
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+GET /nl/flutterdrup/nieuwsbrieven.json -> lijst + status
|
|
|
|
|
+POST /nl/flutterdrup/nieuwsbrieven/subscribe.json -> {"tid": 36676}
|
|
|
|
|
+POST /nl/flutterdrup/nieuwsbrieven/unsubscribe.json -> {"tid": 36676}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Index-respons (alle waarden als string/int, `status` als int):
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+[{"tid":"36676","naam":"...","omschrijving":"...","status":1}]
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Kern van de index-callback:
|
|
|
|
|
+
|
|
|
|
|
+```php
|
|
|
|
|
+function _custom_nieuwsbrieven_index() {
|
|
|
|
|
+ global $user;
|
|
|
|
|
+ if (empty($user->uid)) return services_error('Niet ingelogd', 401);
|
|
|
|
|
+ $out = array();
|
|
|
|
|
+ foreach (_custom_nieuwsbrieven_zichtbaar() as $tid => $c) {
|
|
|
|
|
+ $sub = simplenews_subscriber_load_by_mail($user->mail);
|
|
|
|
|
+ $st = ($sub && isset($sub->newsletter_subscription[$tid]))
|
|
|
|
|
+ ? (int) $sub->newsletter_subscription[$tid]->status : 0;
|
|
|
|
|
+ $out[] = array(
|
|
|
|
|
+ 'tid' => (string) $tid,
|
|
|
|
|
+ 'naam' => $c->name,
|
|
|
|
|
+ 'omschrijving' => trim(strip_tags($c->description)),
|
|
|
|
|
+ 'status' => $st,
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ return $out;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**`_custom_nieuwsbrieven_zichtbaar()` is de spil** en wordt door alle drie de
|
|
|
|
|
+callbacks gebruikt: hij geeft `simplenews_category_get_visible()` terug (dat
|
|
|
|
|
+laat `hidden`-nieuwsbrieven automatisch weg), **minus tid 18017 wanneer de
|
|
|
|
|
+gebruiker geen horecagelegenheid heeft** (Bob's keuze 2026-09-21: de
|
|
|
|
|
+ondernemersnieuwsbrief alleen voor horeca-eigenaren). Eigenaarschap = dezelfde
|
|
|
|
|
+query als `_custom_mijn_horecagelegenheden_service()`
|
|
|
|
|
+(`custom.evenementen_aanmaken.inc:261`): `node` waar
|
|
|
|
|
+`type = CUSTOM_ESTABLISHMENT_NODE_TYPE` en `uid = $user->uid`.
|
|
|
|
|
+
|
|
|
|
|
+Door die filtering **server-side** te doen heeft de app géén conditie nodig, en
|
|
|
|
|
+kan een gemanipuleerd verzoek zich er ook niet op abonneren — mits
|
|
|
|
|
+subscribe/unsubscribe de binnenkomende tid tegen diezelfde lijst controleren
|
|
|
|
|
+(`if (!isset($lijst[$tid])) return services_error('Onbekende nieuwsbrief', 400);`).
|
|
|
|
|
+Zonder die guard kan een client zich op een verborgen nieuwsbrief abonneren.
|
|
|
|
|
+
|
|
|
|
|
+Subscribe/unsubscribe zijn daarna drie regels elk, met
|
|
|
|
|
+`simplenews_subscribe_user($user->mail, $tid, FALSE, 'app')`. **Gebruik source
|
|
|
|
|
+`'app'`** (niet `'website'`), dan is later meetbaar hoeveel abonnees uit de app
|
|
|
|
|
+komen. Geef als respons dezelfde rij terug als in de index, zodat de app de
|
|
|
|
|
+nieuwe status meteen kan tonen.
|
|
|
|
|
+
|
|
|
|
|
+⚠️ **Resource aanzetten is een APARTE stap na het deployen:** in
|
|
|
|
|
+`/admin/structure/services/list/flutterdrup/resources` de regel `nieuwsbrieven`
|
|
|
|
|
+aanvinken **én de drie losse operaties eronder** — precies waar
|
|
|
|
|
+`mijn_aanmeldingen` maandenlang op 404 stond. Daarna `cc all`.
|
|
|
|
|
+Verificatie: anoniem `curl` moet **403** geven (route bestaat, sessie
|
|
|
|
|
+ontbreekt), niet 404.
|
|
|
|
|
+
|
|
|
|
|
+⚠️ **Nooit edge-cachen** — user-specifiek. Het nieuwe pad valt buiten de
|
|
|
|
|
+bestaande Cloudflare Cache Rules (die matchen op `/flutterdrup/views/…`,
|
|
|
|
|
+`plaatsen`), dus dat gaat vanzelf goed; het mag er alleen nooit bij.
|
|
|
|
|
+
|
|
|
|
|
+### 128-B · App: blok "Nieuwsbrieven" op `mijnProfiel` · Eigenaar: Claude
|
|
|
|
|
+
|
|
|
|
|
+Derde blok onder "Mijn horecagelegenheden" en "Mijn redactierechten", in
|
|
|
|
|
+dezelfde vorm. Backend Query op de nieuwe call + **Generate Dynamic Children**,
|
|
|
|
|
+zodat een vierde nieuwsbrief vanzelf verschijnt. Per rij: naam, omschrijving
|
|
|
|
|
+eronder (`bodySmall`/`secondaryText`, het hulpregel-patroon van taak 91), rechts
|
|
|
|
|
+een **Switch**. On Toggle -> `drupalRequest` naar subscribe/unsubscribe met
|
|
|
|
|
+`{"tid": …}` — exact het patroon van het hartje op
|
|
|
|
|
+`HorecagelegenheidCurrent`.
|
|
|
|
|
+
|
|
|
|
|
+⚠️ **Predefined Path `items` (`$[:]`, Is List) gebruiken, geen JSON Path `$`** —
|
|
|
|
|
+anders crasht het blok bij een mislukte call (`jsonBody == null`).
|
|
|
|
|
+⚠️ **Testrespons in FlutterFlow moet gevuld zijn** vóór je `$.status` bindt,
|
|
|
|
|
+anders krijgt het pad type "Anything" en plakt de builder er `.toString()`
|
|
|
|
|
+achter.
|
|
|
|
|
+
|
|
|
|
|
+Bij status 2 een regeltje "Check je mail om je inschrijving te bevestigen"
|
|
|
|
|
+tonen (Visibility-conditie op `$.status == 2`). Bij `confirm = FALSE` komt die
|
|
|
|
|
+stand nooit voor, maar dan is de app al klaar als Bob later wisselt.
|
|
|
|
|
+
|
|
|
|
|
+### 128-C · Waarschuwing bij 0 gevolgde gemeenten · Eigenaar: Claude
|
|
|
|
|
+
|
|
|
|
|
+Bob's keuze 2026-09-21. **Gemeten: 17 van de 18 actieve abonnees op de
|
|
|
|
|
+wekelijkse agenda hebben nul favoriete gemeenten**, en de laatste editie
|
|
|
|
|
+(nid 222817, 2026-09-17) ging naar `sent_subscriber_count: 3`. Wie die switch
|
|
|
|
|
+aanzet zonder gemeenten te volgen, krijgt dus niets — zonder enige uitleg.
|
|
|
|
|
+
|
|
|
|
|
+Onder de rij van tid 36676 een waarschuwing tonen wanneer
|
|
|
|
|
+`FFAppState().favorieteGemeenteIds` leeg is, met een knop naar het
|
|
|
|
|
+gemeente-keuzescherm. Visibility via de bekende route: App State-lijst ->
|
|
|
|
|
+Available Options **Is Set and Not Empty** + **Apply Opposite Statement**.
|
|
|
|
|
+De conditie op de tid zelf hoort een **bool custom function** te zijn (geen
|
|
|
|
|
+JSON-Path-binding) — anders komt er `if (getJsonField(...))` uit, een `dynamic`
|
|
|
|
|
+als `bool` zonder null-check.
|
|
|
|
|
+
|
|
|
|
|
+*(Niet nagelezen: de scs-contentselectie zelf. De tabel heet niet `scs_node`;
|
|
|
|
|
+ik heb dus niet hard bewezen dát die 17 een lege mail krijgen — het beeld past
|
|
|
|
|
+wel. Wie dit ooit exact wil weten, begint bij node 189237, de scheduler-parent.)*
|
|
|
|
|
+
|
|
|
|
|
+### 128-D · Vertalingen · Eigenaar: Claude
|
|
|
|
|
+
|
|
|
|
|
+Kop + waarschuwingstekst + "check je mail"-regel via het globe-icoontje.
|
|
|
|
|
+⚠️ De **omschrijvingen komen uit Drupal** en krijgen dus géén globe en geen
|
|
|
|
|
+EN-vertaling. Die van 36676 bevat bovendien een tikfout die Bob in Drupal kan
|
|
|
|
|
+meenemen: *"Deze wordt donderdagavond **verstuurt**"* -> *verstuurd*.
|
|
|
|
|
+
|
|
|
|
|
+### Bevinding terzijde — geen taak
|
|
|
|
|
+
|
|
|
|
|
+**450 onbevestigde inschrijvingen** (360 + 47 + 43). Die mensen hebben zich ooit
|
|
|
|
|
+aangemeld maar nooit op de bevestigingslink geklikt en krijgen dus niets. Geen
|
|
|
|
|
+app-probleem, wel het sterkste argument voor `confirm = FALSE` in de app.
|
|
|
|
|
+
|
|
|
## 🔭 VERSIE 2.0 — na de livegang · niet agenderen vóór release
|
|
## 🔭 VERSIE 2.0 — na de livegang · niet agenderen vóór release
|
|
|
|
|
|
|
|
*Ideeën die af zijn onderzocht maar bewust wachten. Bob 2026-09-20: eerst live,
|
|
*Ideeën die af zijn onderzocht maar bewust wachten. Bob 2026-09-20: eerst live,
|
|
@@ -486,6 +664,10 @@ met taak 124 — samen oppakken.
|
|
|
|
|
|
|
|
### 127 · "Kopie"-knop op `MijnAanmeldingen` — evenement én activiteit klonen
|
|
### 127 · "Kopie"-knop op `MijnAanmeldingen` — evenement én activiteit klonen
|
|
|
|
|
|
|
|
|
|
+**Eigenaar: Claude — bezig** (sessie 2026-09-21). 127-A is AF: Bob heeft de
|
|
|
|
|
+Drupal-patch op productie gezet en runtime geverifieerd (`entree_tid 36197`,
|
|
|
|
|
+`logo_url` gevuld, `keten 28666|28737|28739`).
|
|
|
|
|
+
|
|
|
**Wens Bob (2026-09-20):** op elke kaart in *Mijn aanmeldingen* een knop **Kopie**.
|
|
**Wens Bob (2026-09-20):** op elke kaart in *Mijn aanmeldingen* een knop **Kopie**.
|
|
|
Eén tik opent het aanmaakformulier volledig ingevuld; de gebruiker vult de datum
|
|
Eén tik opent het aanmaakformulier volledig ingevuld; de gebruiker vult de datum
|
|
|
aan, past eventueel iets aan en dient in. Vóór de livegang. Beide types.
|
|
aan, past eventueel iets aan en dient in. Vóór de livegang. Beide types.
|