Problem. Andrew wanted PDF rows in a [listatt] box to be openable via Adobe Acrobat, not just the built-in PDF.js viewer. Several UI approaches were discussed and ruled out on the way to this one:
Resolution. A checkbox is the one part of this that's fully within a webpage's control. Added "Download PDFs instead of viewing" next to the existing theme switcher in each listatt box header:
action=dlattach URL the DL button already uses, which SMF serves with Content-Disposition: attachment). Once the file is on disk, the OS's own "Open With" — a genuine, real app picker — is one native right-click away, Acrobat included.localStorage under listatt_pdf_download_pref, mirroring the existing ise_misc_theme pattern, and is synced across every listatt box on the page (checking it in one box checks it everywhere, same as the theme selector already does)..md, .html, audio/video/image, plain text) is completely unaffected.| Risk | Assessment |
|---|---|
| Regression to non-PDF rows | None — pdfAttrs/data-pdf-view is only ever set when fileExt() === 'pdf'; listattFileClick() falls through to the original popup behavior for every other row exactly as before. |
| Regression to PDF viewing when unchecked | None by design — listattApplyPdfMode() only swaps href when listattPdfDownloadMode is true; default state (no localStorage key set) evaluates false, so first-time visitors see identical behavior to before this change. |
| Multiple listatt boxes on one page (common on volume posts) | Handled — the toggle and the mode flag are page-global (window.listattPdfDownloadMode, one shared localStorage key), and both the checkbox sync loop and listattApplyPdfMode() iterate every box's toggle/every PDF link on the page, not just the one clicked. |
XSS via filename in new data-pdf-view/data-pdf-dl attributes |
None — both are passed through htmlspecialchars(..., ENT_QUOTES) before being embedded, same discipline as the existing htmlspecialchars($a['filename']) call two lines below. |
Browser compatibility of hasAttribute / querySelectorAll / localStorage |
None — all three are already used elsewhere in this same file's script block (theme switcher), so no new browser-support surface is introduced. |
| Popup blockers | Unchanged — window.open() is still only called from a real user click handler (listattFileClick), same as before; no new popup call sites were added. |
| Migration / existing posts | None needed — this is a display-time behavior change in the mod itself, not a per-post attribute; the ~4000 already-migrated [listatt] posts pick it up automatically on next render, no bulk edit or re-migration required. |
Overall risk: low. Purely additive — new attribute, new checkbox, new JS functions — with the previous behavior left completely intact as the default (unchecked) behavior.
Full unified diff (ListAttBBC.diff, included alongside this report) for v7.0. Summary of the five touch points:
@version 7.0, @date 2026-09-09, new v7.0 changelog entry explaining what was tried and why the checkbox is the ceiling of what's achievable.VERSION/BUILD_DATE bumped to match..listatt-pdf-toggle-label and .listatt-pdf-toggle, styled consistently with the existing .listatt-switcher block.data-pdf-view="<pdfjs url>" data-pdf-dl="<raw dlattach url>"; the filename <a>'s inline onclick was refactored out into a single shared listattFileClick(this, event) function (previously identical inline JS was duplicated per row via string concatenation — same logic, no functional change for non-PDF rows).listattApplyPdfMode, listattSetPdfMode, listattFileClick) plus an extension to the existing page-load IIFE to sync toggle state and apply the saved preference on first paint, matching the theme switcher's own init pattern exactly.Files changed: ListAttBBC.php only. No changes to listatt.css, listatt_display_patch.xml, CollabCore/*, or any installer script.