|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +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. |
| 8 | + |
| 9 | +**Component**: `mod_exeweb` |
| 10 | +**Moodle compatibility**: 4.2+ |
| 11 | +**License**: GNU GPL v3+ |
| 12 | + |
| 13 | +## Common Commands |
| 14 | + |
| 15 | +```bash |
| 16 | +# Development environment (Docker-based) |
| 17 | +make up # Start containers interactively |
| 18 | +make upd # Start containers in background |
| 19 | +make down # Stop containers |
| 20 | +make shell # Shell into Moodle container |
| 21 | +make clean # Stop containers + remove volumes |
| 22 | + |
| 23 | +# PHP dependencies |
| 24 | +make install-deps # composer install |
| 25 | + |
| 26 | +# Code quality |
| 27 | +make lint # PHP CodeSniffer (Moodle standard) |
| 28 | +make fix # Auto-fix CodeSniffer violations |
| 29 | +make phpmd # PHP Mess Detector |
| 30 | + |
| 31 | +# Testing |
| 32 | +make test # PHPUnit tests |
| 33 | +make behat # Behat BDD tests |
| 34 | + |
| 35 | +# Embedded editor |
| 36 | +make build-editor # Fetch exelearning source + build to dist/static/ |
| 37 | +make clean-editor # Remove built editor artifacts |
| 38 | + |
| 39 | +# Packaging |
| 40 | +make package RELEASE=1.2.3 # Create distributable ZIP |
| 41 | +``` |
| 42 | + |
| 43 | +Lint/test/phpmd/behat all delegate to `composer` scripts defined in `composer.json`. Run `make install-deps` first. |
| 44 | + |
| 45 | +## Architecture |
| 46 | + |
| 47 | +### Three Content Origins |
| 48 | + |
| 49 | +| Constant | Mode | How it works | |
| 50 | +|----------|------|-------------| |
| 51 | +| `EXEWEB_ORIGIN_LOCAL` | Upload | ZIP package uploaded via form | |
| 52 | +| `EXEWEB_ORIGIN_EXEONLINE` | Online | Redirects to remote eXeLearning; JWT-authenticated callbacks (`get_ode.php`/`set_ode.php`) sync the package | |
| 53 | +| `EXEWEB_ORIGIN_EMBEDDED` | Embedded | Static HTML5 editor in iframe from `dist/static/`; bridge script + postMessage for Moodle ↔ editor communication | |
| 54 | + |
| 55 | +### Key File Groups |
| 56 | + |
| 57 | +- **Core Moodle hooks**: `lib.php`, `locallib.php` (display/rendering), `mod_form.php` (activity form), `settings.php` (admin config) |
| 58 | +- **Editor integration**: `editor/index.php` (bootstrap), `editor/static.php` (asset server), `editor/save.php` (AJAX save) |
| 59 | +- **Online editor**: `get_ode.php`, `set_ode.php`, `classes/exeonline/` (redirector, JWT token manager) |
| 60 | +- **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) |
| 61 | +- **Package handling**: `classes/exeweb_package.php` (validation, extraction) |
| 62 | +- **Database**: `db/install.xml` (schema), `db/access.php` (capabilities), `db/upgrade.php` (migrations) |
| 63 | +- **Backup/restore**: `backup/moodle2/` |
| 64 | + |
| 65 | +### Embedded Editor: Hybrid Source Model |
| 66 | + |
| 67 | +The embedded editor uses a **hybrid source model** with two possible locations: |
| 68 | + |
| 69 | +1. **Bundled**: `dist/static/` inside the plugin (from release ZIP or `make build-editor`) |
| 70 | +2. **Admin-installed**: `$CFG->dataroot/mod_exeweb/embedded_editor/` (downloaded from GitHub Releases via the admin management page) |
| 71 | + |
| 72 | +**Source precedence**: moodledata (admin-installed) → bundled → not available |
| 73 | + |
| 74 | +Key classes: |
| 75 | +- `classes/local/embedded_editor_source_resolver.php` — single source of truth for which editor source is active |
| 76 | +- `classes/local/embedded_editor_installer.php` — download/install pipeline from GitHub Releases |
| 77 | + |
| 78 | +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. |
| 79 | + |
| 80 | +**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. |
| 81 | + |
| 82 | +Key files: |
| 83 | +- `classes/external/manage_embedded_editor.php` — AJAX external functions (`execute_action` + `get_status`), modern `\core_external\external_api` pattern (Moodle 4.2+, PSR-4 namespaced `\mod_exeweb\external\manage_embedded_editor`) |
| 84 | +- `classes/admin/admin_setting_embeddededitor.php` — Custom `admin_setting` subclass (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). |
| 85 | +- `templates/admin_embedded_editor.mustache` — Inline widget template (status card, action buttons, progress bar, result area) |
| 86 | +- `amd/src/admin_embedded_editor.js` — AMD module: calls `get_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 using `CONFIG_INSTALLING` lock, polling capped at 5 minutes |
| 87 | + |
| 88 | +**Design decisions & rationale**: |
| 89 | +- **Why AJAX instead of synchronous POST**: Keeps user on settings page; the old `manage_embedded_editor.php` navigated away. POST with `NO_OUTPUT_BUFFERING` progress bar was considered but rejected because it still navigates away. |
| 90 | +- **Why indeterminate progress bar**: The installer doesn't expose byte-level progress. Operation typically takes 10-30s. Animated Bootstrap stripes are sufficient. |
| 91 | +- **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_INSTALLING` lock (300s TTL) serves as a "still running" signal. JS polls `get_status` after timeout to distinguish "still running" / "completed" / "failed". Stale lock (>300s) is detected and reported. |
| 92 | +- **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. |
| 93 | +- **Why modern \core_external\external_api**: Plugin minimum is Moodle 4.2+ (`version.php`). Uses PSR-4 namespaced classes at `classes/external/`. The legacy `classes/external.php` (Frankenstyle) remains for existing external functions but new code uses the modern pattern. |
| 94 | +- **Both capabilities required**: `moodle/site:config` AND `mod/exeweb:manageembeddededitor`, matching the original `admin_externalpage` registration. |
| 95 | + |
| 96 | +### Embedded Editor Build (Development) |
| 97 | + |
| 98 | +`dist/static/` is **not committed**. It's built from the `exelearning/exelearning` repo: |
| 99 | +- `make build-editor` shallow-clones the source (configured via `.env` or env vars: `EXELEARNING_EDITOR_REPO_URL`, `EXELEARNING_EDITOR_REF`, `EXELEARNING_EDITOR_REF_TYPE`) |
| 100 | +- Builds with Bun (`bun install && bun run build:static`) |
| 101 | +- Copies output to `dist/static/` |
| 102 | + |
| 103 | +### File Storage |
| 104 | + |
| 105 | +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. |
| 106 | + |
| 107 | +### Capabilities |
| 108 | + |
| 109 | +`mod/exeweb:view`, `mod/exeweb:addinstance`, `mod/exeweb:manageembeddededitor` |
| 110 | + |
| 111 | +## Code Standards |
| 112 | + |
| 113 | +- **PHP**: Moodle coding standard enforced via PHP CodeSniffer (`make lint`/`make fix`) |
| 114 | +- **Strings**: All UI strings in `lang/{ca,en,es,eu,gl}/exeweb.php` — use `get_string('key', 'mod_exeweb')` |
| 115 | +- **JS**: AMD modules in `amd/src/`, compiled to `amd/build/` (exception: `moodle_exe_bridge.js` loads raw in editor iframe) |
| 116 | + |
| 117 | +## Packaging & Release |
| 118 | + |
| 119 | +- `make package RELEASE=X.Y.Z` updates `version.php`, creates ZIP excluding files in `.distignore`, then restores dev values |
| 120 | +- GitHub Actions `release.yml` triggers on git tags: fetches editor, builds, packages, uploads to GitHub Release |
| 121 | +- `check-editor-releases.yml` runs daily to auto-release when new editor versions appear |
0 commit comments