Browse Source

TASKS: 112/132-K/128-A-tikfout bleken al gedaan; 123-code herschreven en klaargezet

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bob 1 day ago
parent
commit
c03c57780b
2 changed files with 117 additions and 89 deletions
  1. 18 89
      TASKS.md
  2. 99 0
      snippets/mijn-stadsrechten-keten.inc.txt

+ 18 - 89
TASKS.md

@@ -32,7 +32,6 @@ Google Play en één doorloop die alleen Bob kan doen.
 | **136** | Taalkeuze onder Mijn Profiel | **AF** (2026-09-23), op één verificatie na. **(1)** App Settings -> Languages -> **Persist Selection** staat aan; export bevestigt `await FFLocalizations.initialize()`, `Locale? _locale = FFLocalizations.getStoredLocale()` en `FFLocalizations.storeLocale(language)` in `main.dart` — zonder die drie overleefde een keuze het afsluiten niet. **(2)** In de **Account**-tab van `mijnProfiel` (`ColumnTab3`) staat nu, tussen `userName` en "Wachtwoord wijzigen": een `Text` **"Taal / Language"** (Body Small, Secondary Text, NL+EN beide gevuld, sleutel `zlpb2nvy`) plus **`FlutterFlowLanguageSelector`** met `hideFlags: false` (vlag erbij, want zonder label/vlag leest "Nederlands" in deze app als een plaatskeuze — Home toont "Heel Nederland"). Export: `currentLanguage: FFLocalizations.of(context).languageCode`, `languages: FFLocalizations.languages()`, `onChanged: (lang) => setAppLanguage(context, lang)`; `dart analyze` 0 errors. Verse export bevestigt ook de vertaling (`'zlpb2nvy'` = "Taal / Language" in nl én en). ⏳ **Nog te doen: testen op een toestel** — Claude kan dat niet, `mijnProfiel` vereist ingelogd zijn. Zet de app op Engels, sluit hem af (`force-stop`) en start opnieuw: de keuze hoort te blijven staan. ⚠️ Blijft Nederlands ook in de EN-UI: de content zelf, de server-geformatteerde `datum` (alle API-URL's zijn hardcoded `/nl/`, 10 bindingen in levende code), de 5 `LegeLijstComponent`-teksten (parameterwaarden, geen i18n-sleutel) en Drupal-foutmeldingen |
 | **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 |
 
-| **112** | Restje zoek-stad: lege API-call opruimen | **Eigenaar: Bob.** `SelectStateDropDownComponent` → Actions → On Page Load → *Conditional Action 5* (`gemeenteSelectId is set/non-empty`) → TRUE-tak → **Action 13, Custom Action `drupalRequest`** met url/sessionName/sessionId/token/body allemaal Unset. Staat sinds 2026-09-20 op **disabled**; kan weg via ⋮ → Delete Action. Vlak eronder staat **Conditional Action 16** (`apiGemeenteResultInitalize -> Succeeded`) met twee lege takken — mag in dezelfde moeite weg |
 
 
 
@@ -161,7 +160,7 @@ schoon. De prijs van disablen is één warning per actie.
 | **122** | Teksten aanmaakpagina's | ✅ **A/B/C af** (Claude 2026-09-20, 19 teksten, export-geverifieerd): verkeerde woorden, typefouten, technische veldnamen en het sterretje bij de categoriekeuze. Open blijven D (hints die het label herhalen), E-restje en F (`Select...` op een NL-pagina) — eigen blok hieronder |
 | **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 |
-| **128-A** | ✅ **KLAAR** — nieuwsbrieven-resource staat live | Gedeployd en getest op productie 2026-09-21. Rest: één tikfout in de omschrijving van tid 36676 (*verstuurt* → verstuurd), zie het blok hieronder |
+| **128-A** | ✅ **KLAAR** — nieuwsbrieven-resource staat live | Gedeployd en getest op productie 2026-09-21; de tikfout in de omschrijving van tid 36676 is ook weg (gemeten 2026-09-24) |
 | **129** | Sessie-verlopen-melding afmaken/verifiëren | **Niet in deze chat** (Bob 2026-09-21). De afhandeling bestaat: `drupalRequest` herkent 401 en 403-met-"anonymous", zet `sessieVerlopen` en `VerbindingsBanner` hangt in `HeaderButtonsComponent`. Staat alleen **in de builder, niet in de gecommitte `lib/`** — dus nooit geëxporteerd/gecommit. Nalopen of het af is en of de banner ook echt verschijnt. Eigen blok hieronder |
 | **130** | **Inloggen met Facebook/Google (OneAll)** | Wens Bob 2026-09-21. Onderzocht en akkoord: OneAll-abonnement gaat naar **Personal Advanced** ($27/mnd jaarlijks) voor *Direct Connect*. Geen Play-blocker, maar wél de privacyverklaring/Data Safety als het meegaat in de eerste release. Volledig stappenplan in een eigen blok onderaan |
 
@@ -325,9 +324,6 @@ lijkt te werken en er gebeurt niets. Controleer een nieuwe actie daarom met
 
 - `unsubscribe.json` is nog niet één keer aangeroepen. Zelfde route als
   subscribe, dus laag risico; de app-test dekt het vanzelf af.
-- **Tikfout in de omschrijving van tid 36676**, die letterlijk in de app komt:
-  *"Deze wordt donderdagavond **verstuurt**"* → *verstuurd*. Te wijzigen op
-  `/admin/config/services/simplenews` (Description van de nieuwsbrief).
 - Alles met `source = app` in `{simplenews_subscription}` is voortaan via de
   app binnengekomen — gratis telling, stond op 0 vóór de eerste test.
 
@@ -684,86 +680,6 @@ de gemeenten die je volgt"*) staat er altijd en is de algemene toelichting. Met
 0 gemeenten staan er dus twee bijna gelijke regels onder elkaar — overweeg er één
 van te maken.
 
-### 132-K · `&amp;` in de categorie · ✅ GEFIXT OP DEVBOB, productie nog te doen
-
-**Stand 2026-09-22: Bob heeft beide regels handmatig aangepast op devbob;
-Claude heeft het daar geverifieerd. Alleen de uitrol naar productie rest.**
-
-Geverifieerd op devbob (alles read-only gemeten):
-- beide regels staan goed, `php -l` schoon op allebei;
-- `diff` tegen productie toont **alleen** de bedoelde wijziging — plus twee
-  regels commentaar, één weggehaalde trailing space en één lege regel aan het
-  eind. Niets anders is bij het plakken meegekomen;
-- de logica: `'Kunst &amp; Cultuur, Theater'` → `["Kunst & Cultuur","Theater"]`,
-  en de dubbel geëscapete `'R&amp;amp;B'` → `["R&B"]`. De komma-split blijft dus
-  intact en de herhaalde decode-lus werkt;
-- **`favorieten_horeca` live gedraaid** voor uid 3621 (bobcity): *Luxor Live* →
-  `["Kunst & Cultuur","Theater"]` — precies het geval dat op productie nog fout
-  gaat. Accenten en slashes blijven heel (`Muziekcafé/Poppodium`, `Theatercafé`),
-  en een zaak met 7 categorieën blijft netjes gesplitst;
-- **`mijn_horecagelegenheden` live gedraaid** voor uid 2 en 603: ook daar komen
-  `Kunst & Cultuur` en `Feesten & partijen` schoon door.
-
-Uitrollen naar productie: dezelfde twee regels in
-`/data/disk/o1/static/uitgaanskrant/sites/all/modules/custom/`, daarna
-`drush @uitgaanskrant.com cc all`. Geen Cloudflare-purge nodig — beide endpoints
-zijn sessie-gebonden en matchen geen Cache Rule.
-
-Opruimen: `/data/disk/o1/tmp/fix-132k.py` staat er nog (een script dat Claude had
-klaargezet en dat Bob niet wilde gebruiken; het heeft niets gewijzigd, alleen een
-droogdraai gedaan). Mag weg met `rm`.
-
-#### Oorspronkelijke analyse
-
-Favorieten tab 3 toont bij Luxor Live het label **`Kunst &amp; Cultuur`**.
-Nagemeten in de rauwe respons:
-`"categorie":["Kunst &amp; Cultuur","Theater"]`. Dit is het bekende
-`_custom_clean_html()`-patroon uit `CLAUDE.md`: het veld `categorie` staat voor
-dit endpoint niet in de `$fields_to_clean`-lijst. De views leveren diezelfde
-categorie wél schoon uit ("Kunst & Cultuur"), dus het is endpoint-specifiek.
-
-**Vindplaats opgezocht op de server (2026-09-22, alleen gelezen).** Het zit op
-**twee** plekken, met identieke code — beide bouwen hun resultaat zélf op uit
-`views_get_view('flutterflowmobiel_establishments')` en draaien dus niet langs de
-opschoning in `custom_services_request_postprocess_alter()`, ondanks wat de
-docblock erboven suggereert:
-
-| bestand | regel | resource |
-|---|---|---|
-| `custom.favorites_flag.inc` | **316** | `favorieten_horeca` (Favorieten tab 3) |
-| `custom.evenementen_aanmaken.inc` | **306** | `mijn_horecagelegenheden` (Mijn Profiel) |
-
-In beide gevallen krijgen `titel`, `adres` en `plaats` netjes
-`_custom_clean_html()` mee en **`categorie` als enige niet**:
-
-```php
-'plaats'    => _custom_clean_html($plaats),
-'logo'      => str_replace('devbob.', '', $logo),
-'categorie' => _custom_parse_komma_to_array($categorie_raw),   // <- hier
-```
-
-Fix, één regel per bestand:
-
-```php
-'categorie' => _custom_clean_html(_custom_parse_komma_to_array($categorie_raw)),
-```
-
-**Decodeer ná het splitsen, niet ervóór.** `_custom_clean_html()` handelt arrays
-recursief af (`custom.module:1860`), dus dit werkt direct op de lijst. Zou je
-`$categorie_raw` eerst decoderen, dan kan een `&#44;` een extra komma opleveren
-en daarmee de split op `', '` stukmaken.
-
-Het tweede geval (`mijn_horecagelegenheden`) is niet visueel bevestigd — daarvoor
-moet je eigen zaak toevallig een `&` in een categorie hebben — maar de code is
-regel voor regel dezelfde, dus fix ze samen.
-
-De volledige path op de server:
-`/data/disk/o1/static/uitgaanskrant/sites/all/modules/custom/`.
-
-Controleer in dezelfde moeite de andere favorieten-resources
-(`favorieten_agenda.json`, `favorieten_gemeenten.json`) — in de opgevangen
-agenda-respons zaten geen entiteiten, maar daar zat toevallig ook geen `&` in.
-
 ### Wat expliciet GOED was
 
 Offline starten (rode banner *Geen verbinding met uitgaanskrant.com* + werkende
@@ -1266,10 +1182,23 @@ sterretje bij de categoriekeuze) zijn gedaan op béide pagina's:
 
 ### 123 · Stadsrechten-cascade op `stadsactiviteitAanmaken` — Drupal-deel · Eigenaar: Bob
 
-**Code staat klaar en is getest** (2026-09-20, Claude): vervang in
-`sites/all/modules/custom/custom.evenementen_aanmaken.inc` de **regels 316 t/m
-372** (docblock t/m sluitende accolade van `_custom_mijn_stadsrechten_service()`)
-door de aangeleverde versie. Daarna `drush @<alias> cc all`.
+**Code staat klaar en is getest** (herschreven 2026-09-24, want de versie van
+2026-09-20 was nergens bewaard): `snippets/mijn-stadsrechten-keten.inc.txt` in
+dit repo, én al op de server in `~/tmp/` (o1). Daar staat ook
+`~/tmp/evenementen_aanmaken.NIEUW.inc`: het complete bestand met de nieuwe
+functie er al in (regels 316-373 vervangen), `php -l` schoon, 16 functies zoals
+het origineel. Read-only doorgedraaid tegen bobcity (4× gemeente), bobhoreca
+(provincie + gemeente) en Bee (0 rechten): klopt.
+
+**Deploy (Bob, één blok):**
+```bash
+cd /data/disk/o1/static/uitgaanskrant/sites/all/modules/custom
+cp custom.evenementen_aanmaken.inc ~/tmp/custom.evenementen_aanmaken.inc.bak-$(date +%F)
+diff custom.evenementen_aanmaken.inc ~/tmp/evenementen_aanmaken.NIEUW.inc | grep "^[0-9]"   # alleen 316-373 hoort te verschillen
+cp ~/tmp/evenementen_aanmaken.NIEUW.inc custom.evenementen_aanmaken.inc
+drush @uitgaanskrant.com cc all
+```
+Terugrollen: de `.bak` terugkopiëren + `cc all`.
 
 **Waarom:** `field_town_access` mag een term op **elk** van de drie niveaus van
 de town-vocabulary bevatten — plaats (tot 2026-08-27), gemeente (huidige

+ 99 - 0
snippets/mijn-stadsrechten-keten.inc.txt

@@ -0,0 +1,99 @@
+/**
+ * 'mijn_stadsrechten' / index.
+ *
+ * Gemeenten/plaatsen/provincies waarvoor de ingelogde gebruiker
+ * redactierechten heeft (field_town_access op het gebruikersaccount).
+ * PUUR INFORMATIEF -- dit endpoint is GEEN toegangscontrole,
+ * stadsactiviteiten/create hieronder blijft bewust voor iedereen open.
+ *
+ * field_town_access mag een term op ELK van de drie niveaus van de
+ * town-vocabulary bevatten: provincie (niveau 1), gemeente (2) of plaats (3).
+ * De app kan een provincie- of plaats-tid niet rechtstreeks in
+ * plaatsen_bij_gemeente stoppen (geeft dan []), daarom krijgt elk recht hier
+ * de volledig uitgeklapte keten mee. Zelfde 'keten'-formaat als
+ * _custom_aanm_keten() in custom.mijn_aanmeldingen.inc.
+ *
+ * tid/titel/parent_tid/parent_titel blijven ongewijzigd (bestaande
+ * consumenten merken niets); nieuw zijn niveau, provincie_tid/_titel,
+ * gemeente_tid/_titel, plaats_tid/_titel, keten en label.
+ *
+ * @return array
+ *   Lijst van rechten, hierarchisch gesorteerd (provincie, gemeente, plaats).
+ */
+function _custom_mijn_stadsrechten_service() {
+  global $user;
+
+  if (empty($user->uid)) {
+    return services_error('Niet ingelogd', 401);
+  }
+
+  $account = user_load($user->uid);
+  if (empty($account->field_town_access[LANGUAGE_NONE])) {
+    return array();
+  }
+
+  $result = array();
+  foreach ($account->field_town_access[LANGUAGE_NONE] as $item) {
+    if (empty($item['tid'])) {
+      continue;
+    }
+    $term = taxonomy_term_load($item['tid']);
+    if (!$term) {
+      continue;
+    }
+
+    // Kind -> ... -> wortel; omdraaien naar provincie, gemeente, plaats.
+    $pad = array_reverse(taxonomy_get_parents_all($term->tid));
+    $niveau = count($pad);
+    $provincie = isset($pad[0]) ? $pad[0] : NULL;
+    $gemeente  = isset($pad[1]) ? $pad[1] : NULL;
+    $plaats    = isset($pad[2]) ? $pad[2] : NULL;
+
+    $parents = taxonomy_get_parents($term->tid);
+    $parent = $parents ? reset($parents) : NULL;
+
+    // Sommige termen heten al "X (gemeente)"; dan geen tweede toevoeging.
+    $label = $term->name;
+    if ($niveau === 1 && stripos($label, 'provincie') === FALSE) {
+      $label .= ' (hele provincie)';
+    }
+    elseif ($niveau === 2 && stripos($label, 'gemeente') === FALSE) {
+      $label .= ' (hele gemeente)';
+    }
+
+    $result[] = array(
+      'tid' => (string) $term->tid,
+      'titel' => $term->name,
+      'parent_tid' => $parent ? (string) $parent->tid : '',
+      'parent_titel' => $parent ? $parent->name : '',
+      'niveau' => $niveau,
+      'provincie_tid' => $provincie ? (string) $provincie->tid : '',
+      'provincie_titel' => $provincie ? $provincie->name : '',
+      'gemeente_tid' => $gemeente ? (string) $gemeente->tid : '',
+      'gemeente_titel' => $gemeente ? $gemeente->name : '',
+      'plaats_tid' => $plaats ? (string) $plaats->tid : '',
+      'plaats_titel' => $plaats ? $plaats->name : '',
+      'keten' => implode('|', array(
+        $provincie ? (string) $provincie->tid : '',
+        $gemeente ? (string) $gemeente->tid : '',
+        $plaats ? (string) $plaats->tid : '',
+      )),
+      'label' => $label,
+    );
+  }
+
+  // Hierarchisch: eerst op provincienaam, dan gemeente, dan plaats.
+  usort($result, function ($a, $b) {
+    $c = strcasecmp($a['provincie_titel'], $b['provincie_titel']);
+    if ($c !== 0) {
+      return $c;
+    }
+    $c = strcasecmp($a['gemeente_titel'], $b['gemeente_titel']);
+    if ($c !== 0) {
+      return $c;
+    }
+    return strcasecmp($a['plaats_titel'], $b['plaats_titel']);
+  });
+
+  return $result;
+}