feat(themes): runtime themeRegistryOverride hook for host integrations#1722
Merged
eXeLearningProject merged 1 commit intomainfrom Apr 23, 2026
Conversation
Host integrations (WordPress/Moodle/Omeka-S) need to let administrators
upload approved style ZIPs and hide built-in ones without rebuilding the
static editor bundle. Expose a tiny, additive hook:
window.eXeLearning.config.themeRegistryOverride = {
disabledBuiltins: ['zen'], // hide these built-ins
uploaded: [ { name, title, ... } ], // append admin-approved themes
blockImportInstall: true, // refuse installs from content
fallbackTheme: 'base', // used when a project references
// a style the admin disabled
}
- StaticDataProvider.getThemes() merges override with the bundle payload
(pure, null-safe helper applyThemeRegistryOverride exported for tests).
- ThemeList.addUserTheme / loadUserThemesFromIndexedDB bail out with a
console.warn when blockImportInstall is set, closing the
import-from-content install path that embedded hosts must not expose.
- ThemesManager.selectTheme uses the configured fallbackTheme (with a
warning) when both the requested theme and the default are missing,
so projects referencing a removed style still open.
All changes are opt-in: with no override present, behavior is identical
to today. Unit tests cover the merge, block-import guards, and fallback.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1722 +/- ##
==========================================
- Coverage 91.34% 91.34% -0.01%
==========================================
Files 353 353
Lines 76270 76295 +25
==========================================
+ Hits 69672 69692 +20
- Misses 6598 6603 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
4 tasks
Deploying exelearning with
|
| Latest commit: |
ad6fb11
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://c1f54eb8.exelearning.pages.dev |
| Branch Preview URL: | https://feature-allow-installation-a.exelearning.pages.dev |
eXeLearningProject
approved these changes
Apr 23, 2026
3 tasks
erseco
added a commit
to exelearning/mod_exeweb
that referenced
this pull request
Apr 23, 2026
…ditor
Adds an admin Styles management page (Site administration → Plugins →
Activity modules → eXeLearning (website) → Styles) where managers can
upload eXeLearning style .zip packages, enable/disable built-in styles,
and enable/disable/delete uploaded ones — without rebuilding the
static editor bundle.
Architecture
- `mod_exeweb\local\styles_service` owns the pure logic: ZIP
validation (path traversal, absolute paths, size cap, extension
allow-list, single config.xml), slug allocation with collision
suffix, registry persistence in config_plugin(exeweb), and the
`build_theme_registry_override()` payload the editor consumes.
- `admin/styles.php` is a standard Moodle admin_externalpage that
renders the upload form and lists, using HTTP POST + sesskey
(no custom AJAX).
- `editor/styles.php/{slug}/{file}` serves the extracted style
assets from moodledata with PATH_INFO, gated by site capability
checks and a registry membership check.
- Uploaded style bundles extract to
`{dataroot}/mod_exeweb/styles/{slug}/` — a sibling of
`mod_exeweb/embedded_editor/` so reinstalling the embedded editor
never destroys admin-managed styles.
- `editor/index.php` injects
`window.eXeLearning.config.themeRegistryOverride` and mirrors
`blockImportInstall` onto the pre-existing `userStyles`
(ONLINE_THEMES_INSTALL) flag so the 'Import this project style?'
modal is suppressed end-to-end.
Admin toggle
- `stylesblockimport` (default: 1) controls whether imported
project styles are refused. When on, the editor hides the
'Imported' tab (see companion core PR) and silently falls back
to the default style instead of offering to install.
Behavior
- Disabled built-ins disappear from the editor's selector.
- Uploaded styles show up alongside built-ins with stable ids.
- Projects referencing a missing/disabled style fall back to `base`.
- The admin link appears only when editor mode is 'embedded'.
Tests
- `tests/local/styles_service_test.php`: ZIP validator edge cases,
install, unique-slug on collision, delete, override enabled flag,
import-blocked config contract.
Language
- Adds strings under `styles*` in `lang/en/exeweb.php`.
Depends on
- Core editor hook: exelearning/exelearning#1722 (merged).
- Core editor UI follow-up: exelearning/exelearning#1724 (hides
the 'Imported' tab when blockImportInstall is set).
This was referenced Apr 23, 2026
feat(styles): administrator-managed style registry for the embedded editor
exelearning/mod_exeweb#32
Open
erseco
added a commit
to exelearning/wp-exelearning
that referenced
this pull request
Apr 23, 2026
…ditor Adds a Settings → eXeLearning → Styles section where administrators can upload eXeLearning style ZIPs, enable/disable built-in styles, and enable/disable/delete uploaded ones — without rebuilding the static editor bundle or touching dist/static. Architecture - `ExeLearning_Styles_Service` owns the pure logic: ZIP validation (path traversal, absolute paths, size cap, extension allow-list, single config.xml), slug allocation with collision suffix, registry persistence in the `exelearning_styles_registry` option, and the `build_theme_registry_override()` payload the editor consumes. - `ExeLearning_Admin_Styles` exposes nonce-protected admin-ajax endpoints for upload/toggle/delete, gated on `manage_options`. - Uploaded style bundles extract to `wp-content/uploads/exelearning-styles/<slug>/` — a sibling of the editor install, so `make build-editor` / the installer cannot destroy admin-managed styles. - `admin/views/editor-bootstrap.php` injects the approved registry into the page as `window.eXeLearning.config.themeRegistryOverride` before the editor scripts load. The static editor merges/filters the bundle themes and refuses install-from-content (core hook landed upstream in exelearning/exelearning#1722). Behavior - Disabled built-ins disappear from the editor's style selector. - Uploaded styles show up alongside built-ins with stable ids. - Projects referencing a missing/disabled style fall back to `base`. - Default max ZIP size is 20 MB, filterable via `exelearning_styles_max_zip_size`. - No mandatory platform-wide "default style" setting is introduced. Tests - `tests/unit/StylesServiceTest.php` covers: reject-missing-config, reject-traversal, reject-disallowed-ext, accept-valid-root, accept-single-root-folder, install extracts + registers, unique-slug on collision, delete removes files + entry, override respects enabled flag and includes the block-import flag. - A standalone smoke run of the ZIP validator + registry math (7 assertions) was executed locally during development. Docs - README "Managing styles" section describes the admin UX, storage location, fallback behavior, and the size filter.
Bundle ReportChanges will increase total bundle size by 935 bytes (0.07%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: app-bundleAssets Changed:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in, backward-compatible runtime hook that lets host
integrations (WordPress, Moodle, Omeka-S) inject an admin-approved style
registry into the static editor without rebuilding the bundle.
Host pages (e.g. the editor bootstrap in each plugin) may set:
With no override present, behavior is unchanged.
Changes
StaticDataProvider.getThemes()merges the override with thebundled themes payload. A pure helper (
applyThemeRegistryOverride)is exported so plugin smoke tests can reuse the exact contract.
ThemeList.addUserThemeandloadUserThemesFromIndexedDBbail out with
console.warnwhenblockImportInstallis truthy,closing the install-from-content path that embedded hosts must not
expose.
ThemesManager.selectThemeusesfallbackTheme(with a warning)when both the requested theme and the configured default are missing,
so existing projects that reference a removed style still open.
Test plan
public/app/core/providers/StaticDataProvider.test.js— unitcoverage of the merge helper and the
getThemes()path(hide builtins, append uploads, override shadows on id collision,
malformed override is ignored).
public/app/workarea/themes/themeList.test.js— guards againstaddUserThemeandloadUserThemesFromIndexedDBwhenblockImportInstallis set.public/app/workarea/themes/themesManager.test.js— fallbackto
themeRegistryOverride.fallbackThemewhen default theme isalso unavailable, with warning.
three affected files).
Companion work
The admin UIs that will consume this hook live in the four integration
repos, each on the matching feature branch:
feature/allow-installation-and-management-of-styles28-allow-installation-and-management-of-stylesfeature/allow-installation-and-management-of-stylesfeature/allow-installation-and-management-of-stylesThose PRs bump the editor version once this one merges.