Changeset c06816e in Klonkt


Ignore:
Timestamp:
07/01/2026 09:49:52 PM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
5a27ba4
Parents:
8cf8b5f
Message:

feat(audio): sharing openly on the fediverse is one-way + lock the editor choice

Once a track has federated openly the file is out there — re-gating would be false
security. Opening is now permanent: the flag can be set but never cleared, the editor
locks the checkbox once open, and warns before you tick it. Default stays closed
(safe default; self-hosters choose per track).

  • src/routes/posts.js — setAudioFediOpen never writes fedi_open back to 0 (one-way).
  • src/views/pages/post-edit.ejs — locked (checked+disabled) checkbox with an explainer once open; a red permanence warning appears when ticking it on a gated post. Also fixes the poll fields showing while "Add a poll" was unchecked: the conditional style attribute was emitted through an escaping tag, turning it into entity soup — the condition now lives inside the quoted attribute.
  • src/services/i18n.js — pedit.fedi_audio_oneway + pedit.fedi_audio_locked (nl/en/de).
  • CHANGELOG(.nl/.de).md — "Sharing audio openly is now one-way" under Changed.

Co-Authored-By: Claude <noreply@…>

Files:
6 edited

Legend:

Unmodified
Added
Removed
  • CHANGELOG.de.md

    r8cf8b5f rc06816e  
    3030  Mastodon-Follower aus ihrer eigenen App abstimmen können; die Live-Ergebnisse stehen am Beitrag und
    3131  die Umfrage schließt sich selbst, sobald die Zeit abgelaufen ist.
     32
     33### Geändert
     34- **Audio offen zu teilen ist jetzt unumkehrbar.** Sobald ein Track offen im Fediverse geteilt wurde,
     35  ist die Datei verbreitet — erneutes "Schließen" wäre Scheinsicherheit. Der Editor sperrt die Wahl
     36  nach dem Öffnen und warnt dich, bevor du sie ankreuzt.
    3237
    3338## [1.2.0] — 2026-07-01
  • CHANGELOG.md

    r8cf8b5f rc06816e  
    2525  duration). It federates as a real fediverse poll, so your Mastodon followers can vote from their own
    2626  app; the live results show on the post and the poll closes itself when the time is up.
     27
     28### Changed
     29- **Sharing audio openly is now one-way.** Once a track is shared openly on the fediverse the file
     30  has spread, so "closing" it again would be false security — the editor now locks the choice after
     31  opening and warns you before you tick it.
    2732
    2833## [1.2.0] — 2026-07-01
  • CHANGELOG.nl.md

    r8cf8b5f rc06816e  
    2828  Die federeert als een echte fediverse-poll, dus je Mastodon-volgers kunnen stemmen vanuit hun eigen
    2929  app; de live-resultaten staan op de post en de poll sluit zichzelf zodra de tijd om is.
     30
     31### Gewijzigd
     32- **Audio openbaar delen is nu onomkeerbaar.** Zodra een track openbaar op de fediverse is gedeeld,
     33  is het bestand verspreid — weer "sluiten" zou schijnveiligheid zijn. De editor vergrendelt de keuze
     34  na het openen en waarschuwt je voordat je 'm aanvinkt.
    3035
    3136## [1.2.0] — 2026-07-01
  • src/routes/posts.js

    r8cf8b5f rc06816e  
    212212// attachment). NB: the file gate is per file, so opening a track in one post makes its file
    213213// fetchable for every post that reuses it.
     214// ONE-WAY: opening is permanent. Once the file has federated it's out there — re-gating
     215// would be false security (remote copies keep the URL), so we never write fedi_open back to 0.
    214216function setAudioFediOpen(siteId, content, open) {
    215   const val = open ? 1 : 0;
     217  if (!open) return; // never close — see one-way note above
    216218  const c = content || '';
    217219  try {
    218     for (const m of c.matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE id = ? AND site_id = ?').run(val, m[1], siteId);
    219     for (const m of c.matchAll(/\[\[album:([^\]]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE site_id = ? AND album = ?').run(val, siteId, m[1].trim());
    220     for (const m of c.matchAll(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = ? WHERE id IN (SELECT track_id FROM playlist_tracks WHERE playlist_id = ?)').run(val, m[1]);
     220    for (const m of c.matchAll(/\[\[track:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = 1 WHERE id = ? AND site_id = ?').run(m[1], siteId);
     221    for (const m of c.matchAll(/\[\[album:([^\]]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = 1 WHERE site_id = ? AND album = ?').run(siteId, m[1].trim());
     222    for (const m of c.matchAll(/\[\[playlist:([A-Za-z0-9_-]+)\]\]/g)) db.prepare('UPDATE audio_tracks SET fedi_open = 1 WHERE id IN (SELECT track_id FROM playlist_tracks WHERE playlist_id = ?)').run(m[1]);
    221223  } catch { /* non-fatal */ }
    222224}
  • src/services/i18n.js

    r8cf8b5f rc06816e  
    701701    'pedit.pin_top': 'bovenaan',
    702702    'pedit.pin_nth_suffix': 'e van boven',
    703     'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.fedi_audio_label': 'Audio openbaar delen op de fediverse (speelt inline in apps; bestand downloadbaar)', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen', 'post.share': 'Deel', 'post.share_copied': 'Link gekopieerd ✓', 'pedit.poll_label': 'Peiling toevoegen', 'pedit.poll_locked': 'Er is al gestemd — de opties kunnen niet meer wijzigen.', 'pedit.poll_option_ph': 'Optie', 'pedit.poll_add': 'Optie toevoegen', 'pedit.poll_remove': 'Optie verwijderen', 'pedit.poll_multiple': 'Meerdere keuzes toestaan', 'pedit.poll_duration': 'Looptijd', 'pedit.poll_dur_5m': '5 minuten', 'pedit.poll_dur_30m': '30 minuten', 'pedit.poll_dur_1h': '1 uur', 'pedit.poll_dur_6h': '6 uur', 'pedit.poll_dur_12h': '12 uur', 'pedit.poll_dur_1d': '1 dag', 'pedit.poll_dur_3d': '3 dagen', 'pedit.poll_dur_7d': '7 dagen',
     703    'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.fedi_audio_label': 'Audio openbaar delen op de fediverse (speelt inline in apps; bestand downloadbaar)', 'pedit.fedi_audio_oneway': 'Let op: openen is permanent — eenmaal openbaar gedeeld is het bestand verspreid en kan het niet meer worden afgeschermd.', 'pedit.fedi_audio_locked': 'Deze audio is openbaar gedeeld op de fediverse. Dat is permanent — het bestand is al verspreid.', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen', 'post.share': 'Deel', 'post.share_copied': 'Link gekopieerd ✓', 'pedit.poll_label': 'Peiling toevoegen', 'pedit.poll_locked': 'Er is al gestemd — de opties kunnen niet meer wijzigen.', 'pedit.poll_option_ph': 'Optie', 'pedit.poll_add': 'Optie toevoegen', 'pedit.poll_remove': 'Optie verwijderen', 'pedit.poll_multiple': 'Meerdere keuzes toestaan', 'pedit.poll_duration': 'Looptijd', 'pedit.poll_dur_5m': '5 minuten', 'pedit.poll_dur_30m': '30 minuten', 'pedit.poll_dur_1h': '1 uur', 'pedit.poll_dur_6h': '6 uur', 'pedit.poll_dur_12h': '12 uur', 'pedit.poll_dur_1d': '1 dag', 'pedit.poll_dur_3d': '3 dagen', 'pedit.poll_dur_7d': '7 dagen',
    704704    'pedit.fan_only_label': 'Alleen voor ingelogde fans',
    705705    'pedit.schedule_label': 'Publicatie inplannen',
     
    16231623    'pedit.pin_top': 'at the top',
    16241624    'pedit.pin_nth_suffix': 'th from top',
    1625     'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.fedi_audio_label': 'Share audio openly on the fediverse (plays inline in apps; file downloadable)', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show', 'post.share': 'Share', 'post.share_copied': 'Link copied ✓', 'pedit.poll_label': 'Add a poll', 'pedit.poll_locked': 'Votes are in — the options can no longer change.', 'pedit.poll_option_ph': 'Option', 'pedit.poll_add': 'Add option', 'pedit.poll_remove': 'Remove option', 'pedit.poll_multiple': 'Allow multiple choices', 'pedit.poll_duration': 'Duration', 'pedit.poll_dur_5m': '5 minutes', 'pedit.poll_dur_30m': '30 minutes', 'pedit.poll_dur_1h': '1 hour', 'pedit.poll_dur_6h': '6 hours', 'pedit.poll_dur_12h': '12 hours', 'pedit.poll_dur_1d': '1 day', 'pedit.poll_dur_3d': '3 days', 'pedit.poll_dur_7d': '7 days',
     1625    'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.fedi_audio_label': 'Share audio openly on the fediverse (plays inline in apps; file downloadable)', 'pedit.fedi_audio_oneway': 'Note: opening is permanent — once shared openly the file has spread and cannot be gated again.', 'pedit.fedi_audio_locked': 'This audio has been shared openly on the fediverse. That is permanent — the file is already out there.', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show', 'post.share': 'Share', 'post.share_copied': 'Link copied ✓', 'pedit.poll_label': 'Add a poll', 'pedit.poll_locked': 'Votes are in — the options can no longer change.', 'pedit.poll_option_ph': 'Option', 'pedit.poll_add': 'Add option', 'pedit.poll_remove': 'Remove option', 'pedit.poll_multiple': 'Allow multiple choices', 'pedit.poll_duration': 'Duration', 'pedit.poll_dur_5m': '5 minutes', 'pedit.poll_dur_30m': '30 minutes', 'pedit.poll_dur_1h': '1 hour', 'pedit.poll_dur_6h': '6 hours', 'pedit.poll_dur_12h': '12 hours', 'pedit.poll_dur_1d': '1 day', 'pedit.poll_dur_3d': '3 days', 'pedit.poll_dur_7d': '7 days',
    16261626    'pedit.fan_only_label': 'Logged-in fans only',
    16271627    'pedit.schedule_label': 'Schedule publication',
     
    25452545    'pedit.pin_top': 'ganz oben',
    25462546    'pedit.pin_nth_suffix': '. von oben',
    2547     'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.fedi_audio_label': 'Audio offen im Fediverse teilen (spielt inline in Apps; Datei herunterladbar)', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen', 'post.share': 'Teilen', 'post.share_copied': 'Link kopiert ✓', 'pedit.poll_label': 'Umfrage hinzufügen', 'pedit.poll_locked': 'Es wurde bereits abgestimmt — die Optionen lassen sich nicht mehr ändern.', 'pedit.poll_option_ph': 'Option', 'pedit.poll_add': 'Option hinzufügen', 'pedit.poll_remove': 'Option entfernen', 'pedit.poll_multiple': 'Mehrfachauswahl erlauben', 'pedit.poll_duration': 'Laufzeit', 'pedit.poll_dur_5m': '5 Minuten', 'pedit.poll_dur_30m': '30 Minuten', 'pedit.poll_dur_1h': '1 Stunde', 'pedit.poll_dur_6h': '6 Stunden', 'pedit.poll_dur_12h': '12 Stunden', 'pedit.poll_dur_1d': '1 Tag', 'pedit.poll_dur_3d': '3 Tage', 'pedit.poll_dur_7d': '7 Tage',
     2547    'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.fedi_audio_label': 'Audio offen im Fediverse teilen (spielt inline in Apps; Datei herunterladbar)', 'pedit.fedi_audio_oneway': 'Achtung: Öffnen ist dauerhaft — einmal offen geteilt ist die Datei verbreitet und lässt sich nicht wieder sperren.', 'pedit.fedi_audio_locked': 'Dieses Audio wurde offen im Fediverse geteilt. Das ist dauerhaft — die Datei ist bereits verbreitet.', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen', 'post.share': 'Teilen', 'post.share_copied': 'Link kopiert ✓', 'pedit.poll_label': 'Umfrage hinzufügen', 'pedit.poll_locked': 'Es wurde bereits abgestimmt — die Optionen lassen sich nicht mehr ändern.', 'pedit.poll_option_ph': 'Option', 'pedit.poll_add': 'Option hinzufügen', 'pedit.poll_remove': 'Option entfernen', 'pedit.poll_multiple': 'Mehrfachauswahl erlauben', 'pedit.poll_duration': 'Laufzeit', 'pedit.poll_dur_5m': '5 Minuten', 'pedit.poll_dur_30m': '30 Minuten', 'pedit.poll_dur_1h': '1 Stunde', 'pedit.poll_dur_6h': '6 Stunden', 'pedit.poll_dur_12h': '12 Stunden', 'pedit.poll_dur_1d': '1 Tag', 'pedit.poll_dur_3d': '3 Tage', 'pedit.poll_dur_7d': '7 Tage',
    25482548    'pedit.fan_only_label': 'Nur für angemeldete Fans',
    25492549    'pedit.schedule_label': 'Veröffentlichung planen',
  • src/views/pages/post-edit.ejs

    r8cf8b5f rc06816e  
    262262               placeholder="<%= t('pedit.nsfw_cw_ph') %>"
    263263               style="width:100%;box-sizing:border-box;margin:6px 0 2px;font-size:13px;padding:7px 9px">
     264        <% var _fediOpen = (typeof fediOpenAudio !== 'undefined' && fediOpenAudio); %>
    264265        <label class="pe-checkbox">
    265           <input type="checkbox" name="fedi_open_audio" value="1" <%= (typeof fediOpenAudio !== 'undefined' && fediOpenAudio) ? 'checked' : '' %>>
     266          <%# One-way: once opened, the file has federated — re-gating would be false security, so the box locks. %>
     267          <input type="checkbox" name="fedi_open_audio" value="1" id="pe-fedi-audio" <%= _fediOpen ? 'checked disabled' : '' %>>
    266268          <span>🌐 <%= t('pedit.fedi_audio_label') %></span>
    267269        </label>
     270        <% if (_fediOpen) { %>
     271          <p style="font-size:12px;opacity:.7;margin:2px 0 0 26px"><%= t('pedit.fedi_audio_locked') %></p>
     272        <% } else { %>
     273          <p id="pe-fedi-audio-warn" style="font-size:12px;color:#c0392b;margin:2px 0 0 26px" hidden>⚠️ <%= t('pedit.fedi_audio_oneway') %></p>
     274          <script>
     275          (function () {
     276            var cb = document.getElementById('pe-fedi-audio'), w = document.getElementById('pe-fedi-audio-warn');
     277            if (cb && w && !cb.__wired) { cb.__wired = true; cb.addEventListener('change', function () { w.hidden = !cb.checked; }); }
     278          })();
     279          </script>
     280        <% } %>
    268281        <script>
    269282        (function () {
     
    298311          .pe-poll-dur { padding: 7px 9px; border-radius: 7px; border: 1px solid var(--rule, rgba(128,128,128,.35)); background: transparent; color: inherit; font-size: 13px; }
    299312        </style>
    300         <div id="pe-poll-fields" class="pe-poll"<%= _poll ? '' : ' style="display:none"' %>>
     313        <%# NB: keep the condition INSIDE the quoted attribute — escaped output (equals-tag)
     314            turns a whole emitted style attribute into entity soup (fields showed always). %>
     315        <div id="pe-poll-fields" class="pe-poll" style="<%= _poll ? '' : 'display:none' %>">
    301316          <% if (_pollLocked) { %><p class="pe-poll-locked"><%= t('pedit.poll_locked') %></p><% } %>
    302317          <div id="pe-poll-opts" data-ph="<%= t('pedit.poll_option_ph') %>" data-del="<%= t('pedit.poll_remove') %>">
Note: See TracChangeset for help on using the changeset viewer.