{"id":"69f26baabd28ba8b2142b8f5","title":"2026 04 29 Issue 234","path":"how-to/committee/release-notes/2026-04-29-issue-234","contentMarkdown":"# 29-Apr-2026 — Committee page title override and original filename downloads [#234](https://github.com/nbarrett/ngx-ramblers/issues/234)\n\n## [build 629](https://github.com/nbarrett/ngx-ramblers/actions/runs/25131677331) — [commit 143d908](https://github.com/nbarrett/ngx-ramblers/commit/143d908ca4d4e3b40686072ff029e50ca14eb503)\n\n_____\n\nTwo changes requested by Stag Walkers for the\ncommittee documents feature, plus a few small improvements made while testing.\n\n### Custom page heading on committee documents pages\n\nEditors can now set a \"Page Title Override\" on any committee\ndocuments row. When set, the page heading uses that text directly;\nwhen blank, the existing \"Committee events for {url-derived-name}\"\ndefault is preserved. Useful for replacing auto-generated headings\nlike \"Committee events for Agm Meetings\" with something more\nappropriate to the audience.\n\n![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/ff95bf3c-3218-4022-b3af-b211eb9c5dec.png)\n\n*The new **Page Title Override** input at the top of the committee documents row editor. Leave it blank to keep the existing URL-derived heading; type any text to replace it on the public page.*\n\n![](https://ngx-ramblers.org.uk/api/aws/s3/site-content/514002a9-b375-41c7-9e4b-55b6fb85b9e4.png)\n\n*The same row rendered on the live page. The override text \"Some override\" replaces the auto-generated \"Committee events for 2025\" heading. The file links beneath now use the original uploaded filenames (\"AGM Agenda\", \"AGM Minutes\").*\n\n### Downloads use the original uploaded filename\n\nCommittee file downloads now suggest the original uploaded name in\nthe browser's save dialog instead of the hashed S3 object key.\nExisting files work without re-upload because the original filename\nwas already captured at upload time.\n\n### File editor tweaks\n\n- The link preview in the file editor no longer shows\n\"{date} - (none)\" when a file has no display title; it shows just\nthe date.\n- When a display title is set, it is joined to the date with an em\ndash with spaces (matching the convention used elsewhere in the\napp for event time ranges).\n- The \"File or Event Date\" and \"File Type\" fields in the file\neditor are now aligned on the same horizontal line.\n\n## Other changes in this release\n\n### **env-setup**: clone-mode wizard fixes, memory dropdown, streamed deploy\n\nCluster of fixes to the environment-setup wizard surfaced while\ncreating a new environment via \"Create from Existing\":\n\n### Ramblers API key entry in clone mode\n\n- The clone branch of step 1 had no API key UI, so when the source\nenvironment's secrets did not include RAMBLERS_API_KEY (older\nenvs predate it being persisted), step 4 validation failed with\n\"Ramblers API Key: API key is required\" with no way to recover.\n- Add a Ramblers API Key secret-input plus Validate API Key button\nto the clone branch, scoped to a selected source environment.\nEditing the key resets apiKeyValid so the user can re-validate.\n- Fall back to the current admin's\nsystemConfig.national.walksManager.apiKey when the cloned source's\napiKey is empty, so the request is seeded with something usable\nrather than the empty string that createEmptySetupRequest left\nbehind after the clone reset.\n- Drive the post-clone apiKeyValid flag from the resulting request\nvalue rather than the raw details payload, so the systemConfig\nfallback is reflected in the indicator.\n\n### Auto-load group/area info on clone\n\n- environment-details/:environmentName: when any of\nRAMBLERS_GROUP_CODE / RAMBLERS_GROUP_NAME / RAMBLERS_AREA_CODE /\nRAMBLERS_AREA_NAME are missing from the source's secrets, connect\nto its MongoDB and fall back to systemConfig.group.{groupCode,\nlongName} and systemConfig.area.{shortName, longName}. Existing\nsecret-derived values still take precedence.\n- validateSetupRequest: add a Ramblers Group entry that fails when\ngroupCode is empty so the wizard stops at validation rather than\nexploding with \"Failed to fetch group details for\" during deploy.\n\n### Memory configuration UX\n\n- Replace the free-text \"Memory (MB)\" input with a select bound to a\nnew FlyioMemory enum (MB_256/MB_512/MB_1024/MB_2048/MB_4096/\nMB_8192), iterated via the existing enumKeyValues helper to match\nother dropdowns in the app. FLYIO_DEFAULTS.MEMORY now points at\nFlyioMemory.MB_512.\n- Drop the trailing \"MB\" in the review summary so the display reads\n\"512mb\" instead of \"512mbMB\".\n\n### Streamed flyctl deploy output\n\n- The streaming infrastructure already existed (runCommandStreaming +\nDeployOutputCallback, used by the resume-environment WS path), but\nthe create-environment path was wiring only a progress callback,\nso the wizard saw \"[running] deploy-app: Deploying to Fly.io\"\nfollowed by silence until \"[completed] deploy-app\". The actual\nflyctl output (Verifying app config, Building image, Provisioning\nips, Launching new machine, etc.) only went to the server stdout.\n- createEnvironment: accept an optional onDeployOutput callback and\nforward it to deployToFlyioCommand using the options-object form\nalongside the existing onProgress handler.\n- handleEnvironmentCreate (WebSocket handler): pass an onDeployOutput\nthat emits each streamed line as a PROGRESS message prefixed with\n\"[deploy] \", matching the prefix the resume-environment handler\nalready uses. The HTTP /create route is unchanged because it\nreturns a single response and cannot stream.","contentHtml":"<h1>29-Apr-2026 — Committee page title override and original filename downloads <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/234\">#234</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/25131677331\">build 629</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/143d908ca4d4e3b40686072ff029e50ca14eb503\">commit 143d908</a></h2>\n<hr>\n<p>Two changes requested by Stag Walkers for the\ncommittee documents feature, plus a few small improvements made while testing.</p>\n<h3>Custom page heading on committee documents pages</h3>\n<p>Editors can now set a &quot;Page Title Override&quot; on any committee\ndocuments row. When set, the page heading uses that text directly;\nwhen blank, the existing &quot;Committee events for {url-derived-name}&quot;\ndefault is preserved. Useful for replacing auto-generated headings\nlike &quot;Committee events for Agm Meetings&quot; with something more\nappropriate to the audience.</p>\n<p><img src=\"https://ngx-ramblers.org.uk/api/aws/s3/site-content/ff95bf3c-3218-4022-b3af-b211eb9c5dec.png\" alt=\"\"></p>\n<p><em>The new <strong>Page Title Override</strong> input at the top of the committee documents row editor. Leave it blank to keep the existing URL-derived heading; type any text to replace it on the public page.</em></p>\n<p><img src=\"https://ngx-ramblers.org.uk/api/aws/s3/site-content/514002a9-b375-41c7-9e4b-55b6fb85b9e4.png\" alt=\"\"></p>\n<p><em>The same row rendered on the live page. The override text &quot;Some override&quot; replaces the auto-generated &quot;Committee events for 2025&quot; heading. The file links beneath now use the original uploaded filenames (&quot;AGM Agenda&quot;, &quot;AGM Minutes&quot;).</em></p>\n<h3>Downloads use the original uploaded filename</h3>\n<p>Committee file downloads now suggest the original uploaded name in\nthe browser&#39;s save dialog instead of the hashed S3 object key.\nExisting files work without re-upload because the original filename\nwas already captured at upload time.</p>\n<h3>File editor tweaks</h3>\n<ul>\n<li>The link preview in the file editor no longer shows\n&quot;{date} - (none)&quot; when a file has no display title; it shows just\nthe date.</li>\n<li>When a display title is set, it is joined to the date with an em\ndash with spaces (matching the convention used elsewhere in the\napp for event time ranges).</li>\n<li>The &quot;File or Event Date&quot; and &quot;File Type&quot; fields in the file\neditor are now aligned on the same horizontal line.</li>\n</ul>\n<h2>Other changes in this release</h2>\n<h3><strong>env-setup</strong>: clone-mode wizard fixes, memory dropdown, streamed deploy</h3>\n<p>Cluster of fixes to the environment-setup wizard surfaced while\ncreating a new environment via &quot;Create from Existing&quot;:</p>\n<h3>Ramblers API key entry in clone mode</h3>\n<ul>\n<li>The clone branch of step 1 had no API key UI, so when the source\nenvironment&#39;s secrets did not include RAMBLERS_API_KEY (older\nenvs predate it being persisted), step 4 validation failed with\n&quot;Ramblers API Key: API key is required&quot; with no way to recover.</li>\n<li>Add a Ramblers API Key secret-input plus Validate API Key button\nto the clone branch, scoped to a selected source environment.\nEditing the key resets apiKeyValid so the user can re-validate.</li>\n<li>Fall back to the current admin&#39;s\nsystemConfig.national.walksManager.apiKey when the cloned source&#39;s\napiKey is empty, so the request is seeded with something usable\nrather than the empty string that createEmptySetupRequest left\nbehind after the clone reset.</li>\n<li>Drive the post-clone apiKeyValid flag from the resulting request\nvalue rather than the raw details payload, so the systemConfig\nfallback is reflected in the indicator.</li>\n</ul>\n<h3>Auto-load group/area info on clone</h3>\n<ul>\n<li>environment-details/:environmentName: when any of\nRAMBLERS_GROUP_CODE / RAMBLERS_GROUP_NAME / RAMBLERS_AREA_CODE /\nRAMBLERS_AREA_NAME are missing from the source&#39;s secrets, connect\nto its MongoDB and fall back to systemConfig.group.{groupCode,\nlongName} and systemConfig.area.{shortName, longName}. Existing\nsecret-derived values still take precedence.</li>\n<li>validateSetupRequest: add a Ramblers Group entry that fails when\ngroupCode is empty so the wizard stops at validation rather than\nexploding with &quot;Failed to fetch group details for&quot; during deploy.</li>\n</ul>\n<h3>Memory configuration UX</h3>\n<ul>\n<li>Replace the free-text &quot;Memory (MB)&quot; input with a select bound to a\nnew FlyioMemory enum (MB_256/MB_512/MB_1024/MB_2048/MB_4096/\nMB_8192), iterated via the existing enumKeyValues helper to match\nother dropdowns in the app. FLYIO_DEFAULTS.MEMORY now points at\nFlyioMemory.MB_512.</li>\n<li>Drop the trailing &quot;MB&quot; in the review summary so the display reads\n&quot;512mb&quot; instead of &quot;512mbMB&quot;.</li>\n</ul>\n<h3>Streamed flyctl deploy output</h3>\n<ul>\n<li>The streaming infrastructure already existed (runCommandStreaming +\nDeployOutputCallback, used by the resume-environment WS path), but\nthe create-environment path was wiring only a progress callback,\nso the wizard saw &quot;[running] deploy-app: Deploying to Fly.io&quot;\nfollowed by silence until &quot;[completed] deploy-app&quot;. The actual\nflyctl output (Verifying app config, Building image, Provisioning\nips, Launching new machine, etc.) only went to the server stdout.</li>\n<li>createEnvironment: accept an optional onDeployOutput callback and\nforward it to deployToFlyioCommand using the options-object form\nalongside the existing onProgress handler.</li>\n<li>handleEnvironmentCreate (WebSocket handler): pass an onDeployOutput\nthat emits each streamed line as a PROGRESS message prefixed with\n&quot;[deploy] &quot;, matching the prefix the resume-environment handler\nalready uses. The HTTP /create route is unchanged because it\nreturns a single response and cannot stream.</li>\n</ul>\n"}