Skip to content

fix: upgrade TinyMCE to v7 and remove the premium plugin stub package - #3245

Open
bradenmacdonald wants to merge 4 commits into
openedx:masterfrom
open-craft:braden/tinymce-7-upgrade
Open

bradenmacdonald wants to merge 4 commits into
openedx:masterfrom
open-craft:braden/tinymce-7-upgrade

Conversation

@bradenmacdonald

@bradenmacdonald bradenmacdonald commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Description

Upgrades TinyMCE from 5.10 to 7.9 (supersedes #3176, renovate's security bump, which fails tests and would break the editor at runtime) and removes the frontend-components-tinymce-advanced-plugins dependency, replacing it with a configuration-based way to load premium plugins.

This affects Course Authors and Library Authors (all rich text editors: HTML/Text, Problem, hints, feedback, expandable text areas, course updates, schedule & details) and Operators (new configuration keys, license change, dependency removal).

Why the renovate PR alone was not mergeable

  1. tinymce.editors was removed in v7. The problem editor used it to collect answer/hint/feedback content and to check dirty state. This was the cause of the two failing tests. It now uses the public tinymce.get() API and reads each editor's id.
  2. A second TinyMCE core was being bundled. frontend-components-tinymce-advanced-plugins pins tinymce ^5.10.4, so with v7 at the root npm nested a separate TinyMCE 5 copy under it. The app imported all of its standard plugins through that package, so the bundle contained two cores, and the v5 one overwrote window.tinymce, which is what @tinymce/tinymce-react reads. The editor would have been broken in the browser even though the unit tests didn't catch it.
  3. Other v5 to v7 breaking changes in our config:
    • The hr and imagetools plugins no longer exist (hr is now a core button). The "Edit Image Settings" context toolbar that imagetools_toolbar used to provide is re-registered as a plain context toolbar on <img> nodes.
    • The formatselect toolbar item was renamed to blocks.
    • The dom model and each core plugin now have to be imported explicitly.
    • v7 logs a "running in evaluation mode" warning unless a license key is declared (licenseKey="gpl" on the React Editor).

Removal of frontend-components-tinymce-advanced-plugins

That package only shipped no-op stubs for the paid a11ychecker and powerpaste plugins, is still on TinyMCE 5 upstream (a renovate PR bumping it was closed), and re-exports plugins that no longer exist. It is removed entirely. Operators who have licensed the premium plugins can now load them through env.config.jsx:

  • TINYMCE_EXTERNAL_PLUGINS: { pluginName: 'https://.../plugin.min.js' }, passed to TinyMCE's built-in external_plugins loader. Configuring a11ychecker restores its toolbar button; configuring powerpaste restores the paste defaults the app previously set.
  • TINYMCE_LICENSE_KEY: the commercial license key that premium plugins in v7 require (defaults to gpl).
  • TINYMCE_PLUGIN_OPTIONS: extra init options for those plugins.

Documented in the README under "Feature: New React XBlock Editors", with a test in pluginConfig.test.

Behavior changes to be aware of

  • License: TinyMCE 7 is GPL-2.0-or-later (v5 was LGPL-2.1). Compatible with this AGPL project, but worth a deliberate acknowledgement.
  • Content sanitization (the CVE fix): I ran v7's parser against course-style HTML with this app's permissive valid_elements: '*[*]'. <script> tags and inline event handlers are preserved exactly as v5 did, so existing course content is not stripped. The one change is the fix for CVE-2024-29881 itself: convert_unsafe_embeds is on by default, so <object>/<embed> are rewritten to <iframe>/<img>/<video>/<audio> (by MIME type) when content is loaded and re-saved.
  • Pasting images: paste moved into core in v6 with paste_data_images defaulting to true, so pasted screenshots now land in the content as base64 data URIs (v5 dropped them). Left at the default; easy to set to false if we would rather not allow it.
  • Focus outline: v7 enables highlight_on_focus by default, adding a blue outline around the focused editor.

Test-suite note

Removing the stub plugin names exposed that the jest suite has always relied on TinyMCE never finishing initialization under JSDOM: previously the editor silently hung trying to fetch the (mocked) a11ychecker/powerpaste scripts over the network. With every plugin genuinely registered, the editor initialized for real and crashed inside the theme's sizing code, which JSDOM can't support. src/setupTest.js now stubs tinymce/themes/silver to make that dependency explicit. Browser behavior is unaffected.

Supporting information

Manual Testing instructions

  1. Open the Text/HTML editor on a course unit. Check the toolbar renders (including the block format dropdown, horizontal rule, table, emoji, charmap, code sample, "HTML" source button, embed iframe), that formatting works, and that content saves and reloads correctly. There should be no "evaluation mode" or "failed to load plugin" messages in the console or editor.
  2. Insert an image, then click it: the "Edit Image Settings" context toolbar should appear and open the image settings modal. Resizing the image should still sync dimensions.
  3. Edit a Text component in a library as well; make sure it works fine.
  4. Open the Problem editor for a multiple choice problem: edit the question, answers, hints and feedback; add/delete answers (deleting shifts later answers' content into the earlier editors); switch to the advanced editor and back; save. Also check the "unsaved changes" warning on close still triggers only after editing.
  5. Try the expandable text areas (answers/feedback) and confirm the floating quick toolbar still works.
  6. Course Updates and Schedule & Details pages also embed the editor; do a quick check there.

Other information

  • No dependency on other changes. The upstream frontend-components-tinymce-advanced-plugins package no longer needs a TinyMCE 7 release for this MFE.
  • Operators who were substituting their own build of that package to get the premium plugins must switch to the new TINYMCE_* configuration keys.

Best Practices Checklist

  • Any new files are using TypeScript (.ts, .tsx).
  • Avoid propTypes and defaultProps in any new or modified code. (No new usages; the pre-existing ones in TinyMceWidget are untouched.)
  • Tests should use the helpers in src/testUtils.tsx (specifically initializeMocks) (N/A: the new test is a pure config test; existing tests were adapted in place.)
  • Do not add new fields to the Redux state/store.
  • Use React Query to load data from REST APIs. (N/A)
  • All new i18n messages in messages.ts files have a description. (N/A)
  • Avoid using ../ in import paths. (No new relative imports added.)

🤖 Generated with Claude Code, with edits from me.

Private ref: MNG-4670

renovate Bot and others added 2 commits September 11, 2026 21:56
TinyMCE 5 -> 7 removed several APIs and plugins this app relied on:

- `tinymce.editors` is gone; the problem editor now uses `tinymce.get()`
  to collect answer/hint/feedback content and check dirty state.
- The `hr` and `imagetools` plugins were removed (the `hr` button is now
  core). The "Edit Image Settings" context toolbar that imagetools used to
  provide is re-registered as a plain context toolbar.
- The `formatselect` toolbar item was renamed to `blocks`.
- The `dom` model and each core plugin must now be imported explicitly.
- TinyMCE 7 is GPL-2.0-or-later; `licenseKey="gpl"` silences its
  evaluation-mode warning.

`frontend-components-tinymce-advanced-plugins` is removed as a dependency.
It pinned TinyMCE 5 (pulling a second core into the bundle that clobbered
`window.tinymce`) and only shipped no-op stubs for the paid a11ychecker and
powerpaste plugins. Operators who have licensed those plugins can now load
them via `TINYMCE_EXTERNAL_PLUGINS`, `TINYMCE_LICENSE_KEY` and
`TINYMCE_PLUGIN_OPTIONS` in env.config.jsx (see README).

The jest setup now stubs `tinymce/themes/silver` so the editor never
finishes initializing under JSDOM, which the test suite has always
implicitly relied on (previously the stubbed plugins hung initialization).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @bradenmacdonald!

This repository is currently maintained by @bradenmacdonald.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Sep 11, 2026
@github-project-automation github-project-automation Bot moved this to Needs Triage in Contributions Sep 11, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.03922% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.03%. Comparing base (87cba01) to head (0c5a998).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...rc/editors/sharedComponents/TinyMceWidget/hooks.ts 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3245      +/-   ##
==========================================
- Coverage   96.03%   96.03%   -0.01%     
==========================================
  Files        1407     1407              
  Lines       34287    34296       +9     
  Branches     7882     7879       -3     
==========================================
+ Hits        32928    32936       +8     
- Misses       1318     1319       +1     
  Partials       41       41              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

@mphilbrick211 mphilbrick211 moved this from Needs Triage to Ready for Review in Contributions Sep 17, 2026
@bradenmacdonald

Copy link
Copy Markdown
Contributor Author

@ChrisChV Could you please review this?

@arbrandes @brian-smith-tcril Do you also want to review this, and/or are there any other implications for our other MFEs for bumping TinyMCE up two major versions?

@ChrisChV

Copy link
Copy Markdown
Contributor

Could you please review this?

Sure 👍

@arbrandes

Copy link
Copy Markdown
Contributor

@bradenmacdonald

are there any other implications for our other MFEs

Discussions and Communications also use it, so ideally they'd be bumped, too. If we were already fully in frontend-base, I'd suggest tracking this as a global peer dependency so we'd have to bump them.

@bradenmacdonald

Copy link
Copy Markdown
Contributor Author

Discussions and Communications also use it, so ideally they'd be bumped, too. If we were already fully in frontend-base, I'd suggest tracking this as a global peer dependency so we'd have to bump them.

That's not something I have time to take on at the moment, but hopefully it's easier to bump them using the learning from this PR.

And yeah, based on this forum discussion, I think we should eventually have a paragon-rich-editor package that our MFEs can reuse, but I wouldn't make it part of frontend-base or paragon directly.

@ChrisChV ChrisChV left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I commented some nits 👍

  • I tested this: I followed the testing instructions. Also I tested the iframes
  • I read through the code and considered the security, stability and performance implications of the changes.
  • I tested that the UI can be used with a keyboard only (tab order, keyboard controls).
  • Includes tests for bugfixes and/or features added.
  • Includes documentation

.tox:not(.tox-tinymce-inline) .tox-editor-header {
box-shadow: none;
padding: 0;
border-bottom: 1px solid #E3E3E3;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a root var for this color? Or can we use var(--pgn-color-gray-100)?

powerpaste_html_import: 'prompt',
powerpaste_googledoc_import: 'prompt',
autoresize_bottom_margin: autoresizeBottomMargin,
external_plugins: externalPlugins,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description says:

Pasting images: pasting moved into core in v6 with paste_data_images defaulting to true, so pasted screenshots now land in the content as base64 data URIs (v5 dropped them). Left at the default; easy to set to false if we would rather not allow it.

I think it would be good to add the parameter here with a comment so that the change is reflected and documented in the code.

Suggested change
external_plugins: externalPlugins,
external_plugins: externalPlugins,
// Paste moved into core in v6 with paste_data_images defaulting to true, so pasted screenshots
// now land in the content as base64 data URIs (v5 dropped them). Left at the default; easy to set to
// false if we would rather not allow it.
paste_data_images: true,

powerpaste_word_import: 'prompt',
powerpaste_html_import: 'prompt',
powerpaste_googledoc_import: 'prompt',
autoresize_bottom_margin: autoresizeBottomMargin,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration guide 6 to 7 says that a new security mechanism for iframes, sandbox_iframes, has been added and is set to true by default. This changes the editor's behavior. Previously, you could embed iframes, such as Google Forms, and they worked, but now that same iframe no longer functions. This issue only affects newly added iframes, but it would be good to add a comment here noting this change in behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Ready for Review

Development

Successfully merging this pull request may close these issues.

5 participants