This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Moodle activity module (mod_exeweb) for creating and editing web sites with eXeLearning. Teachers can author educational content via three modes: local upload, eXeLearning Online (remote editor), or an embedded static editor.
Component: mod_exeweb
Moodle compatibility: 4.2+
License: GNU GPL v3+
# Development environment (Docker-based)
make up # Start containers interactively
make upd # Start containers in background
make down # Stop containers
make shell # Shell into Moodle container
make clean # Stop containers + remove volumes
# PHP dependencies
make install-deps # composer install
# Code quality
make lint # PHP CodeSniffer (Moodle standard)
make fix # Auto-fix CodeSniffer violations
make phpmd # PHP Mess Detector
# Testing
make test # PHPUnit tests
make behat # Behat BDD tests
# Embedded editor
make build-editor # Fetch exelearning source + build to dist/static/
make clean-editor # Remove built editor artifacts
# Packaging
make package RELEASE=1.2.3 # Create distributable ZIPLint/test/phpmd/behat all delegate to composer scripts defined in composer.json. Run make install-deps first.
| Constant | Mode | How it works |
|---|---|---|
EXEWEB_ORIGIN_LOCAL |
Upload | ZIP package uploaded via form |
EXEWEB_ORIGIN_EXEONLINE |
Online | Redirects to remote eXeLearning; JWT-authenticated callbacks (get_ode.php/set_ode.php) sync the package |
EXEWEB_ORIGIN_EMBEDDED |
Embedded | Static HTML5 editor in iframe from dist/static/; bridge script + postMessage for Moodle ↔ editor communication |
- Core Moodle hooks:
lib.php,locallib.php(display/rendering),mod_form.php(activity form),settings.php(admin config) - Editor integration:
editor/index.php(bootstrap),editor/static.php(asset server),editor/save.php(AJAX save) - Online editor:
get_ode.php,set_ode.php,classes/exeonline/(redirector, JWT token manager) - Frontend JS (AMD):
amd/src/editor_modal.js(fullscreen overlay),amd/src/admin_embedded_editor.js(settings page AJAX + progress),amd/src/moodle_exe_bridge.js(runs inside editor iframe, raw — not AMD) - Package handling:
classes/exeweb_package.php(validation, extraction) - Database:
db/install.xml(schema),db/access.php(capabilities),db/upgrade.php(migrations) - Backup/restore:
backup/moodle2/
The embedded editor uses a hybrid source model with two possible locations:
- Bundled:
dist/static/inside the plugin (from release ZIP ormake build-editor) - Admin-installed:
$CFG->dataroot/mod_exeweb/embedded_editor/(downloaded from GitHub Releases via the admin management page)
Source precedence: moodledata (admin-installed) → bundled → not available
Key classes:
classes/local/embedded_editor_source_resolver.php— single source of truth for which editor source is activeclasses/local/embedded_editor_installer.php— download/install pipeline from GitHub Releases
The resolver is used by lib.php helper functions (exeweb_get_embedded_editor_local_static_dir(), exeweb_embedded_editor_uses_local_assets(), exeweb_get_embedded_editor_index_source()), which in turn are used by editor/static.php (asset proxy) and editor/index.php (bootstrap). This makes the source transparent to the rest of the codebase.
Admin management (inline settings widget): Editor management is integrated directly into the plugin's admin settings page via a custom admin_setting subclass — no separate page. Actions (install/update/repair/uninstall) use AJAX external functions; a JS AMD module handles progress display and timeout resilience.
Key files:
classes/external/manage_embedded_editor.php— AJAX external functions (execute_action+get_status), modern\core_external\external_apipattern (Moodle 4.2+, PSR-4 namespaced\mod_exeweb\external\manage_embedded_editor)classes/admin/admin_setting_embeddededitor.php— Customadmin_settingsubclass (PSR-4 namespaced\mod_exeweb\admin\admin_setting_embeddededitor), renders inline widget in settings page.get_setting()/write_setting()return empty string (display-only, no config stored).templates/admin_embedded_editor.mustache— Inline widget template (status card, action buttons, progress bar, result area)amd/src/admin_embedded_editor.js— AMD module: callsget_status(checklatest=true)on page load (async GitHub API check), handles action AJAX calls with 120s JS timeout, falls back to status polling every 10s usingCONFIG_INSTALLINGlock, polling capped at 5 minutes
Design decisions & rationale:
- Why AJAX instead of synchronous POST: Keeps user on settings page; the old
manage_embedded_editor.phpnavigated away. POST withNO_OUTPUT_BUFFERINGprogress bar was considered but rejected because it still navigates away. - Why indeterminate progress bar: The installer doesn't expose byte-level progress. Operation typically takes 10-30s. Animated Bootstrap stripes are sufficient.
- Why 120s JS timeout + polling: Reverse proxies (nginx default 60s, Apache 60-120s) may kill long AJAX requests for ~50MB ZIP downloads. The existing
CONFIG_INSTALLINGlock (300s TTL) serves as a "still running" signal. JS pollsget_statusafter timeout to distinguish "still running" / "completed" / "failed". Stale lock (>300s) is detected and reported. - Why GitHub API only from JS:
discover_latest_version()hits GitHub API (60 req/hr unauthenticated). Calling it on PHP render would fire on every settings page visit. JS calls it once async after page load. - Why modern \core_external\external_api: Plugin minimum is Moodle 4.2+ (
version.php). Uses PSR-4 namespaced classes atclasses/external/. The legacyclasses/external.php(Frankenstyle) remains for existing external functions but new code uses the modern pattern. - Both capabilities required:
moodle/site:configANDmod/exeweb:manageembeddededitor, matching the originaladmin_externalpageregistration.
dist/static/ is not committed. It's built from the exelearning/exelearning repo:
make build-editorshallow-clones the source (configured via.envor env vars:EXELEARNING_EDITOR_REPO_URL,EXELEARNING_EDITOR_REF,EXELEARNING_EDITOR_REF_TYPE)- Builds with Bun (
bun install && bun run build:static) - Copies output to
dist/static/
Uses Moodle's file API — packages stored in mod_exeweb/package filearea, expanded content in mod_exeweb/content. Revision number in URLs for cache busting. Never serve files directly from disk.
mod/exeweb:view, mod/exeweb:addinstance, mod/exeweb:manageembeddededitor
- PHP: Moodle coding standard enforced via PHP CodeSniffer (
make lint/make fix) - Strings: All UI strings in
lang/{ca,en,es,eu,gl}/exeweb.php— useget_string('key', 'mod_exeweb') - JS: AMD modules in
amd/src/, compiled toamd/build/(exception:moodle_exe_bridge.jsloads raw in editor iframe)
make package RELEASE=X.Y.Zupdatesversion.php, creates ZIP excluding files in.distignore, then restores dev values- GitHub Actions
release.ymltriggers on git tags: fetches editor, builds, packages, uploads to GitHub Release check-editor-releases.ymlruns daily to auto-release when new editor versions appear