{"id":"6a2d2e75672fe32974b1e303","title":"2026 06 13","path":"how-to/committee/release-notes/2026-06-13","contentMarkdown":"# 13-Jun-2026 — per-event email overrides with ContentBlockEditor, merge field fixes, and walk pipeline... [#273](https://github.com/nbarrett/ngx-ramblers/issues/273)\n\n## [build 691](https://github.com/nbarrett/ngx-ramblers/actions/runs/27463649147) — [commit cc5c688](https://github.com/nbarrett/ngx-ramblers/commit/cc5c688a5c83456b835c34908b6f9ee33d9f57b9)\n\n_____\n\n## What's new\n\n### Booking email overrides now use ContentBlockEditor\n\n- Rewrote the per-event email override section in the Bookings admin page to use\nthe same ContentBlockEditor component used by site-level email templates\n- Removed the old markdown editor with override/revert buttons\n- Added section-toggle tabs (Standard / Customise / Leave Out) matching the\nsite-level template editor experience\n- Removed the disabled booking-default-preview showing the raw default text\n- Preview toggle button redesigned with sunrise colour scheme\n- ContentBlockEditor gained new inputs to support this reuse:\n- `omitAllowed` — controls whether the \"Leave out\" tab appears\n- `stateQueryParamKey` — persists the active tab choice in URL query params\n- `frameless` — removes the heading, description, and accordion wrapper for\nan inline editing mode\n- `blockDefaults` — when a block is in DEFAULT state, renders the default\ncontent in a disabled tiptap editor so the admin can see what would be sent\n- MailNotificationTemplateEditor now merges booking block defaults from\nDEFAULT_BOOKING_EMAIL_BLOCKS into the discovered defaults so the disabled\npreview works for booking email types\n\n### Booking merge fields preserved in markdown link URLs\n\n- booking-template-resolver.ts now uses renderMarkdownPreservingTokens instead of\nrenderMarkdownToHtml when resolving booking email body content (default, site\noverride, and per-event override)\n- Merge field placeholders like {{params.bookingMergeFields.EVENT_LINK}}\ninside markdown link URLs are no longer percent-encoded during HTML rendering\n- messages.ts template substitution now runs 3 passes (was 1) to resolve\nparameters that might contain other parameter references\n- Booking merge field values are now included in the test params for the\nwelcome-to-the-group template spec, confirming they resolve correctly\n\n### Booking reminder email uses correct NGX domain link\n\n- booking-reminder-job.ts passes null instead of event.groupEvent?.url for the\nevent link parameter, so publicEventLink() constructs the group's own domain\nURL rather than the Ramblers national URL\n\n### Walk description diff in publish status\n\n- Added LCS-based word diff that compares the transformed website description\nagainst the Ramblers-stored description in toPublishStatus()\n- Words only in the Ramblers version are shown with red strikethrough\n- Words only in the website version are shown in green\n- Output is trusted HTML rendered via [innerHTML] so coloured spans display\n- RamblersEventSummaryResponse interface and the list-events.ts mapper now\ninclude the description field\n\n### CSV export contraction expansion and character cleanup\n\n- replaceSpecialCharacters now:\n- Decodes HTML entities &#039; / &#39; back to apostrophes\n- Expands 30+ common contractions via lookup map (don't -> do not, etc.)\n- Strips apostrophes on unrecognised words (possessive forms)\n- Converts & to and (avoids double-encoding by Ramblers CSV import)\n- walkLeader() no longer calls replaceSpecialCharacters so names like\n\"O'Grady\" preserve their apostrophe in the CSV export\n\n### Styling: disabled tiptap editor state\n\n- Added .tiptap-editor-shell-disabled and child classes for a visually distinct\ndisabled appearance\n- Activated via [class.tiptap-editor-shell-disabled]=\"!editable\" on the shell\n\n### Code quality\n\n- Removed unused BrevoButtonComponent imports from\nmail-notification-template-editor.ts and notification-config-selector.ts\n- Added EMAIL_STATE to the StoredValue enum for content block editor state\npersistence\n\n## Test changes\n\n- Added booking merge field fixture (ATTENDEE_NAME, EVENT_TITLE, EVENT_DATE,\nEVENT_LINK, ATTENDEE_LIST, PLACES_COUNT) to messages.spec.ts params\n- Added spec confirming booking merge fields in markdown link URLs are\npreserved during template rendering\n- Updated resolveBookingBody spec to expect HTML rendering and preserved\nmerge field hrefs instead of raw DEFAULT_BOOKING_EMAIL_BLOCKS value\n- template-diff.ts returns booking block defaults as raw trimmed markdown\n(not HTML)\n\n## Ramblers Walks Manager E2E\n\n- DRY: combined disableCookieBannerPermanently, forceDismissCookieBanners,\nand cookieBannerIfVisible into a single dismissCookieBanners() method\n- Updated all call sites (ClickWhenReady, Login, Start, UploadWalks) to use\nthe combined method\n\n## Further fixes\n\n- Section-toggle tabs and Preview button now sit on the same row (removed\nflex-wrap from the container)\n- Preview now refreshes when switching email type tabs while preview is\nactive (made onEmailTypeTabChange async with await Promise.resolve()\nso Angular change detection settles the view before calling preview)\n- Batch-loaded events in loadEvents() using a single all() call with the\nids parameter instead of N+1 individual queryById() calls — cuts\ngroup-event/all requests from N (one per unique event referenced in\nbookings) to 1","contentHtml":"<h1>13-Jun-2026 — per-event email overrides with ContentBlockEditor, merge field fixes, and walk pipeline... <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/273\">#273</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/27463649147\">build 691</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/cc5c688a5c83456b835c34908b6f9ee33d9f57b9\">commit cc5c688</a></h2>\n<hr>\n<h2>What&#39;s new</h2>\n<h3>Booking email overrides now use ContentBlockEditor</h3>\n<ul>\n<li>Rewrote the per-event email override section in the Bookings admin page to use\nthe same ContentBlockEditor component used by site-level email templates</li>\n<li>Removed the old markdown editor with override/revert buttons</li>\n<li>Added section-toggle tabs (Standard / Customise / Leave Out) matching the\nsite-level template editor experience</li>\n<li>Removed the disabled booking-default-preview showing the raw default text</li>\n<li>Preview toggle button redesigned with sunrise colour scheme</li>\n<li>ContentBlockEditor gained new inputs to support this reuse:</li>\n<li><code>omitAllowed</code> — controls whether the &quot;Leave out&quot; tab appears</li>\n<li><code>stateQueryParamKey</code> — persists the active tab choice in URL query params</li>\n<li><code>frameless</code> — removes the heading, description, and accordion wrapper for\nan inline editing mode</li>\n<li><code>blockDefaults</code> — when a block is in DEFAULT state, renders the default\ncontent in a disabled tiptap editor so the admin can see what would be sent</li>\n<li>MailNotificationTemplateEditor now merges booking block defaults from\nDEFAULT_BOOKING_EMAIL_BLOCKS into the discovered defaults so the disabled\npreview works for booking email types</li>\n</ul>\n<h3>Booking merge fields preserved in markdown link URLs</h3>\n<ul>\n<li>booking-template-resolver.ts now uses renderMarkdownPreservingTokens instead of\nrenderMarkdownToHtml when resolving booking email body content (default, site\noverride, and per-event override)</li>\n<li>Merge field placeholders like {{params.bookingMergeFields.EVENT_LINK}}\ninside markdown link URLs are no longer percent-encoded during HTML rendering</li>\n<li>messages.ts template substitution now runs 3 passes (was 1) to resolve\nparameters that might contain other parameter references</li>\n<li>Booking merge field values are now included in the test params for the\nwelcome-to-the-group template spec, confirming they resolve correctly</li>\n</ul>\n<h3>Booking reminder email uses correct NGX domain link</h3>\n<ul>\n<li>booking-reminder-job.ts passes null instead of event.groupEvent?.url for the\nevent link parameter, so publicEventLink() constructs the group&#39;s own domain\nURL rather than the Ramblers national URL</li>\n</ul>\n<h3>Walk description diff in publish status</h3>\n<ul>\n<li>Added LCS-based word diff that compares the transformed website description\nagainst the Ramblers-stored description in toPublishStatus()</li>\n<li>Words only in the Ramblers version are shown with red strikethrough</li>\n<li>Words only in the website version are shown in green</li>\n<li>Output is trusted HTML rendered via [innerHTML] so coloured spans display</li>\n<li>RamblersEventSummaryResponse interface and the list-events.ts mapper now\ninclude the description field</li>\n</ul>\n<h3>CSV export contraction expansion and character cleanup</h3>\n<ul>\n<li>replaceSpecialCharacters now:</li>\n<li>Decodes HTML entities &#039; / &#39; back to apostrophes</li>\n<li>Expands 30+ common contractions via lookup map (don&#39;t -&gt; do not, etc.)</li>\n<li>Strips apostrophes on unrecognised words (possessive forms)</li>\n<li>Converts &amp; to and (avoids double-encoding by Ramblers CSV import)</li>\n<li>walkLeader() no longer calls replaceSpecialCharacters so names like\n&quot;O&#39;Grady&quot; preserve their apostrophe in the CSV export</li>\n</ul>\n<h3>Styling: disabled tiptap editor state</h3>\n<ul>\n<li>Added .tiptap-editor-shell-disabled and child classes for a visually distinct\ndisabled appearance</li>\n<li>Activated via [class.tiptap-editor-shell-disabled]=&quot;!editable&quot; on the shell</li>\n</ul>\n<h3>Code quality</h3>\n<ul>\n<li>Removed unused BrevoButtonComponent imports from\nmail-notification-template-editor.ts and notification-config-selector.ts</li>\n<li>Added EMAIL_STATE to the StoredValue enum for content block editor state\npersistence</li>\n</ul>\n<h2>Test changes</h2>\n<ul>\n<li>Added booking merge field fixture (ATTENDEE_NAME, EVENT_TITLE, EVENT_DATE,\nEVENT_LINK, ATTENDEE_LIST, PLACES_COUNT) to messages.spec.ts params</li>\n<li>Added spec confirming booking merge fields in markdown link URLs are\npreserved during template rendering</li>\n<li>Updated resolveBookingBody spec to expect HTML rendering and preserved\nmerge field hrefs instead of raw DEFAULT_BOOKING_EMAIL_BLOCKS value</li>\n<li>template-diff.ts returns booking block defaults as raw trimmed markdown\n(not HTML)</li>\n</ul>\n<h2>Ramblers Walks Manager E2E</h2>\n<ul>\n<li>DRY: combined disableCookieBannerPermanently, forceDismissCookieBanners,\nand cookieBannerIfVisible into a single dismissCookieBanners() method</li>\n<li>Updated all call sites (ClickWhenReady, Login, Start, UploadWalks) to use\nthe combined method</li>\n</ul>\n<h2>Further fixes</h2>\n<ul>\n<li>Section-toggle tabs and Preview button now sit on the same row (removed\nflex-wrap from the container)</li>\n<li>Preview now refreshes when switching email type tabs while preview is\nactive (made onEmailTypeTabChange async with await Promise.resolve()\nso Angular change detection settles the view before calling preview)</li>\n<li>Batch-loaded events in loadEvents() using a single all() call with the\nids parameter instead of N+1 individual queryById() calls — cuts\ngroup-event/all requests from N (one per unique event referenced in\nbookings) to 1</li>\n</ul>\n"}