Browse Source

API-oppervlak dichtgezet (fase 1+2) en mijn_aanmeldingen-filters bewezen

Fase 2 op devbob en productie doorgevoerd: node/retrieve + relationships/files,
file/create + file/retrieve, taxonomy_term en taxonomy_vocabulary uit. Endpoint
flutterdrup gaat daarmee van 42 naar 22 methodes.

Het bewezen lek is weg: node/<nid>.json gaf anoniem 200 met 46 velden inclusief
'name' (de inlognaam van de auteur, dus user enumeration) en geeft nu 404.
Nagemeten dat ongepubliceerde nodes al 403 gaven en file/create anonieme POSTs
al weigerde, dus die twee waren minder urgent dan eerder ingeschat.

Regressiecheck op beide omgevingen: vijf views-endpoints 200, plaatsen /
plaatsen_bij_gemeente / horecacategorieen 200, POST user/login.json 401 op
onzin, en een afbeelding uit een view 200 image/jpeg — file/retrieve uitzetten
breekt dus geen afbeeldingen.

mijn_aanmeldingen.json: ?type= en ?komend= staan op productie en zijn met een
echte sessie bewezen, mét tegencontrole dat onbekende parameters stil genegeerd
worden. Drupal-kant van taak 110 is af.

G0.7 feature graphic klaar (variant B), in ~/uk-play-assets/feature-graphic/.
Pagina Event vastgelegd als doodlopend spoor: onbereikbaar in de app, maar
verwijderen/hernoemen/verplaatsen falen alle drie.

CLAUDE.md: drush-scripts moeten naar aegir1 (scp -P 959), bouw een stand-kop in
beheerscripts, en meet bij API-dichtzetten ook wat moet blijven werken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 14 hours ago
parent
commit
638837e2af
2 changed files with 89 additions and 64 deletions
  1. 42 0
      CLAUDE.md
  2. 47 64
      TASKS.md

+ 42 - 0
CLAUDE.md

@@ -2301,6 +2301,16 @@ kind zijn eigen hoogte. Zat er geen pagination op, dan is de Replace zonder
 voorbereiding te doen en gaat `shrinkWrap` er vanzelf af (wat klopt zodra de
 voorbereiding te doen en gaat `shrinkWrap` er vanzelf af (wat klopt zodra de
 ouder een begrensde hoogte geeft, bv. een `Expanded`).
 ouder een begrensde hoogte geeft, bv. een `Expanded`).
 
 
+**⚠️ Een sleep in de Widget Tree landt alleen als de DOEL-rij vooraf GESELECTEERD
+is (2026-09-20, taak 110, ~12 mislukte pogingen vóór dit duidelijk was).** Klik
+eerst de doel-`Column` aan (rechterpaneel toont 'm), druk `Escape`, en sleep dán.
+Een sleep naar een niet-geselecteerde doelrij laat de bronrij gedimd achter en
+verandert niets — ook niet bij een tweede poging of na een herlaad. Een verse
+`zoom` van de boom vóór elke sleep blijft nodig, want de rijen schuiven.
+**Tussen twee `TabBar Page`s slepen kan bovendien niet rechtstreeks:** in twee
+hops via de buitenste `Column` (drop op de Column-rij → positie 0, dan die
+Column-rij deselecteren, doel-kolom selecteren en opnieuw slepen) werkt wel.
+
 **Een widget VERPLAATSEN lukt wél — sleep de rij in de Widget Tree, en
 **Een widget VERPLAATSEN lukt wél — sleep de rij in de Widget Tree, en
 een drop op een container-rij voegt hem er altijd als EERSTE kind in.**
 een drop op een container-rij voegt hem er altijd als EERSTE kind in.**
 Bevestigd 2026-09-03 (P1-36, `HorecagelegenheidoverzichtKaart`): een
 Bevestigd 2026-09-03 (P1-36, `HorecagelegenheidoverzichtKaart`): een
@@ -4909,6 +4919,38 @@ minder calls per scherm, niet in Views.
   kijk naar 404 vs 403** — 403 ("Toegang geweigerd voor gebruiker anonymous")
   kijk naar 404 vs 403** — 403 ("Toegang geweigerd voor gebruiker anonymous")
   betekent "route bestaat, sessie ontbreekt", 404 betekent "resource niet
   betekent "route bestaat, sessie ontbreekt", 404 betekent "resource niet
   aangezet". Dat onderscheid scheelt zoeken in de verkeerde hoek.
   aangezet". Dat onderscheid scheelt zoeken in de verkeerde hoek.
+
+**⚠️ Een drush-script dat Claude schrijft moet eerst NAAR AEGIR1 — drush draait
+niet op Bob's desktop.** Kostte 2026-09-20 een hele ronde: het script stond in
+`/home/bob/Projects/`, Bob draaide op de server het gelijknamige oude script uit
+`~/tmp/`, en de uitvoer zág er goed uit (hij zette netjes 12 methodes uit) —
+alleen waren het de verkeerde. **Bouw daarom in elk beheerscript een
+"HUIDIGE STAND"-kop in die per doelwit `<-- FASE N` toont**, dan zie je in de
+droogdraai meteen of je het juiste script voor je hebt. Overzetten:
+```
+scp -P 959 <script> o1@aegir1.digitalforce.nl:~/tmp/
+drush @<alias> php-script ~/tmp/<script>        # droogdraai als default
+drush @<alias> php-script ~/tmp/<script> doen   # uitvoeren
+```
+Aliassen: `@uitgaanskrant.com` (productie) en `@devbob.uitgaanskrant.com`.
+
+**Meet bij het uitzetten van API-oppervlak ook wat er moet BLIJVEN werken — en
+meet dat op devbob vóór productie.** Alleen "geeft het nu 404?" is een halve
+check; de vraag is of de app nog draait. Vaste regressieset voor dit project:
+de vijf views-endpoints · `plaatsen` · `plaatsen_bij_gemeente` ·
+`horecacategorieen` · `POST user/login.json` met onzin-gegevens (moet **401**
+geven, niet 404) · en **een afbeeldings-URL uit een view**. Dat laatste nam
+2026-09-20 de enige reële twijfel weg bij `file/retrieve`: afbeeldingen lopen via
+directe `sites/.../files/`-URL's en staan volledig los van de Services
+`file`-resource, dus die uitzetten breekt niets.
+
+**Een 403 op een schrijf-resource is niet hetzelfde als een lek.** Bij het
+afwegen van wat er dicht moet: test elke resource ook echt anoniem in plaats van
+op zijn naam af te gaan. `file/create` klonk als open schrijftoegang maar weigerde
+anonieme POSTs (403), en `node/retrieve` respecteerde node access netjes
+(ongepubliceerde node → 403). Wat er wél lekte was subtieler: van een
+**gepubliceerde** node gaf `node/<nid>.json` 46 velden terug inclusief `name` —
+de inlognaam van de auteur, dus user enumeration op al je redacteuren.
   hierop wijst:** een endpoint/pad dat op de ene Drupal-omgeving prima
   hierop wijst:** een endpoint/pad dat op de ene Drupal-omgeving prima
   werkt en op de andere een 404 geeft mét een compleet lege body en
   werkt en op de andere een 404 geeft mét een compleet lege body en
   zonder de gebruikelijke custom response-headers (bij dit project
   zonder de gebruikelijke custom response-headers (bij dit project

+ 47 - 64
TASKS.md

@@ -18,24 +18,26 @@ De **app-kant is vrijwel af.** Op 2026-09-19 zijn o.a. afgerond: de
 header-locatie-keten (Home toont *Heel Nederland*, provinciepagina de
 header-locatie-keten (Home toont *Heel Nederland*, provinciepagina de
 provincienaam), het adaptive icon, de loginpagina, het drawer-logo, Crashlytics,
 provincienaam), het adaptive icon, de loginpagina, het drawer-logo, Crashlytics,
 "Mijn aanmeldingen", de categoriedropdown op het horeca-overzicht, de
 "Mijn aanmeldingen", de categoriedropdown op het horeca-overzicht, de
-horeca-agenda-entiteiten en de opruimronde. Wat rest is **livegang-werk**:
-Google Play, het API-oppervlak dichtzetten, en één doorloop die alleen Bob kan
-doen.
+horeca-agenda-entiteiten en de opruimronde. Op 2026-09-20 kwamen daar het **dichtgezette API-oppervlak** (42 → 22 methodes,
+het `name`-lek weg) en de **feature graphic** bij. Wat rest is **livegang-werk**:
+Google Play en één doorloop die alleen Bob kan doen.
 
 
 ## 👤 CLAUDE — OPEN
 ## 👤 CLAUDE — OPEN
 
 
 | # | Taak | Kort |
 | # | Taak | Kort |
 |---|---|---|
 |---|---|---|
 | **G0.6** | Play-screenshots | ✅ Nieuwe set van 10 klaar (2026-09-19 avond). Alleen `tel-04-horeca.png` moet over zodra *Show Test Ads* uit staat |
 | **G0.6** | Play-screenshots | ✅ Nieuwe set van 10 klaar (2026-09-19 avond). Alleen `tel-04-horeca.png` moet over zodra *Show Test Ads* uit staat |
-| **nameten** | Na het services-script op productie | `node/<nid>.json` moet 403/404 geven i.p.v. 200 met 46 velden |
 
 
-| **110** | `MijnProfiel` ombouwen naar 3 tabs | **Eigenaar: Claude — bezig (2026-09-20).** Tab 1 *Mijn evenementen* (komende horeca-evenementen + horecakaarten met knop "+ Evenement aanmaken"), tab 2 *Stadseditor* (komende activiteiten + gemeenten met knop "+ Activiteit aanmaken"), tab 3 *Account*. Opzet zoals `Favorieten`. Drupal-kant (Bob): `mijn_aanmeldingen.json` heeft `?type=` en `?komend=1` — ✅ op devbob geplaatst en getest 2026-09-20, **nog naar productie**. Hint-link onder de horecalijst → `https://uitgaanskrant.com/nl/support/mobieleapp` (Bob). Bijvangst: "Uitloggen" stond op `desktop: false` |
+| **110** | `MijnProfiel` ombouwen naar 3 tabs | **Eigenaar: Claude — bezig (2026-09-20).** Tab 1 *Mijn evenementen* (komende horeca-evenementen + horecakaarten met knop "+ Evenement aanmaken"), tab 2 *Stadseditor* (komende activiteiten + gemeenten met knop "+ Activiteit aanmaken"), tab 3 *Account*. Opzet zoals `Favorieten`. ✅ **Drupal-kant is AF**: `mijn_aanmeldingen.json?type=go_out_event|activity` en `?komend=1` staan op **productie** en zijn met een echte sessie bewezen (2026-09-20, `bobcity`), mét tegencontrole dat onbekende parameters stil genegeerd worden. Hertesten kan met `/home/bob/Projects/test-mijn-aanmeldingen.sh [prod]` (leest het wachtwoord zelf in). Hint-link onder de horecalijst → `https://uitgaanskrant.com/nl/support/mobieleapp` (Bob). Bijvangst: "Uitloggen" stond op `desktop: false` |
+
+| **112** | `Selectprovinciegemeente` / `SelectStateDropDownComponent` opknappen | **Eigenaar: Claude — bezig (2026-09-20).** (a) hartje-tekst → "Maak favoriet" / "Favoriet"; (b) mailtekst `24bdnkhi` weg; (c) favorietenlijst van `Wrap` → `Column` met echte rijen (speldje + naam + chevron, witte rij, scheidingslijn) onder de kop "of kies een favoriet"; (d) hartje-status synchroniseren met de serverlijst (`favorieteGemeenteIds` wordt nu nooit vanaf de server gevuld); (e) kop+lijst achter de ingelogd-conditie; (f) lege `drupalRequest('GET','','','','','')` uit de On-Component-Load-keten |
+
+| **113** | Filterbalk (zoek + datum + chips) ook op `PUitgaanPage` | **Eigenaar: Claude — bezig (2026-09-20).** `PUitgaanPage` bedient **provincie én gemeente** (10 drawer-ingangen: 5 categorieën × `provincieSelectId`/`gemeenteSelectId`) — één keer bouwen dekt beide. Drupal is al klaar: gemeten op productie dat `townid` samen met `datum_van`/`datum_tot`/`zoek` werkt op plaats- (28695), gemeente- (28694) én provincie-tid (28666), mét tegencontrole `onzinparam=123` → ongewijzigd. Nog te doen in de app: (1) `UitgaanstabelCall` 3 variabelen + query-parameters erbij; (2) `PUitgaantabelKaartComponent` 3 parameters (Required **uit**) + binden in de Backend Query; (3) filterbalk op de pagina. Onderzoeksstap loopt: werkt *Convert to Component* op het filterblok in `homeCopy`? |
 
 
 ## 👤 BOB — OPEN, in volgorde van wat de livegang blokkeert
 ## 👤 BOB — OPEN, in volgorde van wat de livegang blokkeert
 
 
 | # | Taak | Waarom nu |
 | # | Taak | Waarom nu |
 |---|---|---|
 |---|---|---|
-| **🔒 API dichtzetten — FASE 2** | `node`, `file`, `taxonomy_term`, `taxonomy_vocabulary` | Fase 1 staat **op productie** en is nagemeten ✅. Fase 2 is een bewuste afweging — zie het blok |
 | **G** | Google Play-checklist G0–G4 | Volledige checklist hieronder |
 | **G** | Google Play-checklist G0–G4 | Volledige checklist hieronder |
 | **65** | De **ingelogde** doorloop | Kernfuncties zijn nooit op een release-build getest. Claude kan dit niet (geen wachtwoorden) |
 | **65** | De **ingelogde** doorloop | Kernfuncties zijn nooit op een release-build getest. Claude kan dit niet (geen wachtwoorden) |
 | **AdMob** | `Show Test Ads` uit | App Settings → AdMob. Wacht op de Google-registratie |
 | **AdMob** | `Show Test Ads` uit | App Settings → AdMob. Wacht op de Google-registratie |
@@ -129,7 +131,15 @@ die twee niet meer los afwerken. Vink hier af.*
       `gemeenteSelectId`), terwijl Home *Heel Nederland* zegt. Voor een event in
       `gemeenteSelectId`), terwijl Home *Heel Nederland* zegt. Voor een event in
       Steenwijk oogt dat wat vreemd; als je dat consequent wilt, is dat een aparte
       Steenwijk oogt dat wat vreemd; als je dat consequent wilt, is dat een aparte
       taak op de header-keten.
       taak op de header-keten.
-- [ ] **G0.7 — Feature graphic 1024×500** laten maken (grafisch werk, Bob).
+- [x] **G0.7 — ✅ KLAAR (Claude 2026-09-20, variant B gekozen door Bob).**
+      📁 `~/uk-play-assets/feature-graphic/DEFINITIEF-feature-graphic-1024x500.png`
+      — 1024×500, RGB zonder alpha, 88 kB; voldoet aan de Play-eisen.
+      **B = het complete app-icoon links als afgeronde tegel op leisteen
+      (`#3E454C`), rechts de naam + tagline + oranje accentstreep.** Rood op
+      leisteen geeft het icoon contrast; de twee andere merkkleuren doen de rest.
+      Variant A (merkelement groot op merkrood) staat ernaast, niet gekozen.
+      `feature.py` staat erbij: tekst of kleur wijzigen is één regel + opnieuw
+      draaien. Rest nog: uploaden bij **G3.2**.
 
 
 #
 #
 
 
@@ -274,65 +284,29 @@ taak 100 zette de padding op 40; opnieuw bekijken bij de volgende release-build)
   drawer én de data-deletion-URL voor Play) en `/nl/support/privacybeleid`.
   drawer én de data-deletion-URL voor Play) en `/nl/support/privacybeleid`.
 - **`devbob` staat niet meer in de levende app-code** (alleen in dode kopieën).
 - **`devbob` staat niet meer in de levende app-code** (alleen in dode kopieën).
 
 
-## 🔒 API-oppervlak dichtzetten — FASE 1 AF OP PRODUCTIE (2026-09-20)
-
-**Bob draaide `services-dicht.php` op productie; Claude heeft nagemeten.**
-
-| moet dicht | productie |
-|---|---|
-| `user/1.json` · `geocoder.json` · `userestablishments.json` | **404** ✅ |
-| `POST system/connect.json` | **404** ✅ |
-
-| moet blijven werken | productie |
-|---|---|
-| `plaatsen` · `plaatsen_bij_gemeente` · `horecacategorieen` | **200** ✅ |
-| alle vijf views-endpoints | **200** ✅ |
-| `POST user/login.json` | **401** ✅ (bestaat, wijst af op onzin-inloggegevens) |
-
-12 methodes uitgezet, gelogd in watchdog.
-
-### ⚠️ Correctie: het alarm was overtrokken — er stond nooit schrijftoegang open
-
-De droogdraai gaf de **werkelijke** stand: **42 methodes**, niet "tientallen
-inclusief create/update/delete". Concreet stond er nooit aan:
-`node/create|update|delete` · `system/set_variable|del_variable` ·
-`taxonomy_term/create|update|delete` · `taxonomy_vocabulary/create|update|delete`.
-
-**Oorzaak van de misser:** Claude leidde die lijst af uit de geplakte HTML van het
-resourcescherm, waar **"Enabled" een kolomlabel is en geen status**. Bob wees daar
-al op bij `system`. Les: die pagina-tekst is onbruikbaar om vinkjes uit af te
-lezen — gebruik de droogdraai van het script of een live HTTP-test.
-Wat wél hard bewezen was en blijft staan: `node/<nid>.json` geeft anoniem **200**
-met 46 velden, inclusief `name` (de inlognaam van de auteur).
+## ✅ API-oppervlak dichtgezet — KLAAR (2026-09-20)
 
 
-### 🔵 FASE 2 — nog open, vier resources
-
-Staan nog uitgecommentarieerd in het script. Wat er nu nog aanstaat:
-
-| resource | methodes | oordeel |
-|---|---|---|
-| `node` | `operations/retrieve`, `relationships/files` | 🔴 **het bewezen lek** — `node/222338.json` geeft nog 200. De app gebruikt `node/*` nergens |
-| `file` | `operations/create`, `operations/retrieve` | ⚠️ `create` is **schrijftoegang** ("Create a file with base64 encoded data"). De app uploadt via `bestand_upload/upload` |
-| `taxonomy_term` | `operations/retrieve`, `actions/selectNodes` | alleen lezen |
-| `taxonomy_vocabulary` | `operations/retrieve`, `actions/retrieveByMachineName` | alleen lezen |
+**Fase 1 én 2 staan op productie en zijn nagemeten.** Van 42 naar **22**
+methodes op endpoint `flutterdrup`. Het bewezen lek is weg: `node/<nid>.json`
+gaf anoniem 200 met 46 velden inclusief `name` (de **inlognaam** van de auteur,
+dus user enumeration) en geeft nu 404.
 
 
-**Afweging:** de app roept geen van vieren aan (geverifieerd op de export). Het
-enige risico van uitzetten is een ánder script dat ze gebruikt — Feeds niet, die
-werkt via de interne Drupal-API. Bob koos ervoor de accesslog niet te
-controleren, dus dit is een bewuste afweging: `node/retrieve` en `file/create`
-zijn de twee die het meest de moeite waard zijn.
+**Dicht (404):** `node/retrieve` + `relationships/files` · `file/create` +
+`file/retrieve` · `taxonomy_term` · `taxonomy_vocabulary` · `user/retrieve|create|
+delete|logout|token|user_pass_reset|cancel|password_reset|resend_welcome_email` ·
+`system/connect` · `geocoder` · `userestablishments`.
 
 
-**Stand 2026-09-20 (nagemeten door Claude, anoniem, Dart-UA):** `node/222338.json`
-**200** met 46 velden waaronder `name = 'bob'` · `taxonomy_term/28695.json` **200** ·
-`file/1.json` 406. Fase 2 staat dus nog volledig open.
+**Blijft aan (22):** de app-resources (`plaatsen`, `plaatsen_bij_gemeente`,
+`horecacategorieen`, `views`, de favorieten-, aanmaak- en upload-acties,
+`mijn_*`, `categorieen`, `entreeopties`, `accountdelete`) plus `user/login` en
+`user/request_new_password`.
 
 
-📁 **Het script staat weer klaar: `/home/bob/Projects/services-dicht-fase2.php`**
-(het oude stond in een vluchtige sessie-scratchpad en was weg). Droogdraaien met
-`drush @<alias> php-script /home/bob/Projects/services-dicht-fase2.php`,
-uitvoeren met `... -- doen`. Doet zelf `services_endpoint_save()` +
-`menu_rebuild()` + `cache_clear_all()` + een `watchdog()`-regel.
+**Regressiecheck na afloop, devbob én productie:** alle vijf views-endpoints 200 ·
+`plaatsen`/`plaatsen_bij_gemeente`/`horecacategorieen` 200 · `POST user/login.json`
+401 op onzin · een afbeelding uit een view 200 `image/jpeg`.
 
 
-**Daarna nameten:** `node/222338.json` moet 403/404 geven in plaats van 200.
+📁 Script: `/home/bob/Projects/services-dicht-fase2.php` (droogdraai is de default,
+`doen` als argument voert uit). Bewaren voor als er ooit een resource bij komt.
 
 
 ### 📌 Views-resource: al beperkt, niets te doen
 ### 📌 Views-resource: al beperkt, niets te doen
 
 
@@ -356,9 +330,18 @@ niet op de toegestane lijst", niet "display bestaat niet".
 
 
 - **Builder-opruimronde, laatste restanten.** Nog in de export: componenten
 - **Builder-opruimronde, laatste restanten.** Nog in de export: componenten
   `SliderUitgaanComponentSmallCurrent` en
   `SliderUitgaanComponentSmallCurrent` en
-  `PUitgaantabelKaartComponentOrgineelMetKaartjeerin`, pagina `Event` (weigert
-  structureel élke wijziging met "Invalid Action" — na één poging laten staan) en
-  de custom function `dISABLEdatumVoorApi`. Moet in de **builder**, niet met
+  `PUitgaantabelKaartComponentOrgineelMetKaartjeerin` en de custom function
+  `dISABLEdatumVoorApi`.
+
+  ⚠️ **Pagina `Event`: laten staan, dit is een doodlopend spoor.** Bob bevestigde
+  2026-09-20 dat óók *hernoemen* afketst op "Invalid Action" — verwijderen,
+  verplaatsen en hernoemen falen dus alle drie, ook in zijn eigen browser.
+  **Nagemeten op de export (2026-09-20): de pagina is onbereikbaar.** De enige
+  verwijzingen zijn de twee die FlutterFlow automatisch voor élke pagina
+  genereert (`index.dart` en de route-registratie in `nav.dart`); er is nergens
+  een `goNamed`/`pushNamed` naartoe. Geen enkele gebruiker komt er dus, behalve
+  via een handmatige deep link. Kosten van laten staan: één dode route in
+  `nav.dart` en ~30 kB in de app. Niet opnieuw agenderen. Moet in de **builder**, niet met
   `git rm` — anders staat alles na de volgende export terug.
   `git rm` — anders staat alles na de volgende export terug.
 
 
 - **P1-17 · laatste vertaling(en).** `1k52hayg` → `End date` en `22xoyrqe` →
 - **P1-17 · laatste vertaling(en).** `1k52hayg` → `End date` en `22xoyrqe` →