Skip to content

feat(download): make the package's own .elpx download button work in embeds - #156

Merged
erseco merged 2 commits into
mainfrom
feature/route-content-elpx-download-to-original
Sep 29, 2026
Merged

erseco merged 2 commits into
mainfrom
feature/route-content-elpx-download-to-original

Conversation

@erseco

@erseco erseco commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Refs exelearning/exelearning#2488. Same fix as exelearning/omeka-s-exelearning#63; complements exelearning/exelearning#2489.

Thanks to smorros for reporting it and to @biyayo for forwarding it.

Problem

In a published package, the download-source-file iDevice button ("Download .elpx" inside the content) never saves a file in a WordPress embed. It fails in two independent places:

  1. CSP. ExeLearning_Content_Proxy serves HTML with script-src 'self' 'unsafe-inline' 'unsafe-eval' and no worker-src. The button rebuilds the .elpx with the async fflate.zip(), which compresses in blob: workers. The workers are blocked, fflate's callback never fires, and the button stays at "Processing... 100%".
  2. Sandbox. The embed iframes use sandbox="allow-scripts allow-same-origin allow-popups", without allow-downloads. Even once the rebuild completes (fix(elpx-download): fall back to zipSync when the CSP blocks blob: workers exelearning#2489 adds a zipSync fallback), Chrome drops the download: "Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag 'allow-downloads' is not set". I reproduced this on the Omeka S equivalent, which uses the same CSP and sandbox.

Even when it works, rebuilding refetches the whole package and holds it in memory 2–3 times over. Packages exported before exelearning/exelearning#2196 also rebuild without content.xml, so the result cannot be re-imported.

Change

Serve the original (assets/js/wp-exe-download.js). While an embed's toolbar offers an enabled .elpx item, the script replaces the package's global downloadElpx() (called by the iDevice's inline onclick) with one that downloads the original attachment. It goes through the same downloadFormat() path as the toolbar button.

  • It is scoped per embed (.exelearning-preview / .exelearning-block-frontend).
  • A capturing load listener reapplies it on every page the frame navigates to, and init() covers frames that loaded earlier.
  • A cross-origin frame (exelearning_content_origin) raises an access error, which is caught, and the package keeps its own download.

Let the rebuild finish when routing does not apply. That covers the .elpx not offered (for example show_download off, the default), "Open in new tab", or a cross-origin content origin.

  • HTML content gets worker-src 'self' blob: (includes/class-content-proxy.php).
  • The block, shortcode, Media Library and Gutenberg preview iframes get allow-downloads.

Neither relaxation grants package scripts anything new. They already run with 'unsafe-inline'/'unsafe-eval' in the site origin and can start downloads through the same-origin parent. The decision is recorded in ADR-156-01.

Tests

  • tests/js/wp_exe_download.test.js: new routing the package's own .elpx button block. It covers the original download, block embeds, in-frame navigation, the .elpx not offered or disabled, packages without the button, a cross-origin frame, and several embeds on one page. 4 of these tests fail on main.
  • PHPUnit: the shortcode, block and Media Library sandbox tests pin allow-downloads. 3 of these tests fail on main.
npm run test:js   -> Test Files 8 passed, Tests 266 passed
make test         -> OK (895 tests, 2024 assertions)
make lint         -> OK
make architecture-check -> OK

…embeds

The download-source-file iDevice rebuilds the .elpx in the browser. In an
embed that never saved a file: the content CSP blocks fflate's blob:
workers (exelearning/exelearning#2488) and the iframe sandbox drops the
download. While the embed offers the .elpx, route the package's
downloadElpx() to the original attachment; otherwise let the rebuild
finish by allowing blob: workers and downloads in the frame.
@erseco erseco added bug Something isn't working enhancement New feature or request javascript Pull requests that update javascript code labels Sep 29, 2026
@erseco erseco self-assigned this Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Test in WordPress Playground

Test the plugin with the code from this branch:

Preview in WordPress Playground

ℹ️ The eXeLearning editor is fetched from the shared release and unpacked into the plugin when the playground boots, so the first load may take a few extra seconds. ELP upload, shortcode, Gutenberg block and preview work normally.

@codecov-commenter

codecov-commenter commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.73684% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.42%. Comparing base (7ec2e82) to head (fd324c9).

Files with missing lines Patch % Lines
assets/js/wp-exe-download.js 94.11% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #156      +/-   ##
============================================
- Coverage     96.43%   96.42%   -0.01%     
  Complexity      806      806              
============================================
  Files            36       36              
  Lines          4238     4256      +18     
============================================
+ Hits           4087     4104      +17     
- Misses          151      152       +1     
Flag Coverage Δ
javascript 95.81% <94.11%> (-0.03%) ⬇️
php 96.64% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
assets/js/elp-upload.js 96.73% <ø> (ø)
includes/class-content-proxy.php 94.89% <100.00%> (+0.01%) ⬆️
includes/class-elp-upload-block.php 100.00% <ø> (ø)
includes/integrations/class-media-library.php 100.00% <100.00%> (ø)
public/class-shortcodes.php 100.00% <ø> (ø)
assets/js/wp-exe-download.js 98.04% <94.11%> (-0.36%) ⬇️

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7ec2e82...fd324c9. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@erseco
erseco merged commit a586650 into main Sep 29, 2026
5 checks passed
@erseco
erseco deleted the feature/route-content-elpx-download-to-original branch September 29, 2026 10:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants