diff --git a/AGENTS.md b/AGENTS.md index 4b5c0f6..027f644 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,21 +1,69 @@ # AGENTS.md — eXeLearning -WordPress integration for uploading, managing, editing and embedding eXeLearning -ELPX content. Keep the WordPress 6.1 / PHP 8.0 minimum declared in `exelearning.php`. +WordPress plugin for uploading, managing, editing and embedding eXeLearning `.elpx` +packages. Keep the WordPress 6.1 / PHP 8.0 minimum declared in `exelearning.php`. Use the existing Bootstrap 5/jQuery UI and registered block; do not scaffold a second plugin or replace the build pipeline when following a generic skill. +This is the single guide for every coding agent; `CLAUDE.md` only imports it. + +## How the plugin works + +Packages are plain WordPress **attachments** — there is no custom post type. + +1. An `.elpx` is uploaded to the Media Library (`includes/class-mime-types.php` + registers the type; legacy `.elp` v2 files are rejected). +2. `ExeLearning_Elp_File_Service` validates it (ZIP + `content.xml`, zip-slip, + entry-count and size limits) and extracts it to + `wp-content/uploads/exelearning/{sha1}/`. +3. Package metadata is stored as attachment post meta. +4. Content is served by the REST content proxy and embedded with the + `[exelearning]` shortcode or the `exelearning/elp-upload` block. + +| Area | Files | +|------|-------| +| Bootstrap | `exelearning.php` (`require_once` for every class), `includes/class-exelearning.php` (creates components; most register their own hooks), `includes/class-upgrader.php` (option migrations keyed on `exelearning_db_version`) | +| Ingestion | `includes/class-elp-upload-handler.php` (upload, extraction, delete cleanup), `includes/class-elp-file-service.php` | +| Reprocessing | `includes/class-elp-reprocessor.php` (class `ExeLearning_Reprocessor`), used by REST save/reprocess, the Media Library bulk action and `wp exelearning reprocess` (`includes/class-cli-command.php`); `includes/class-content-hash-aliases.php` keeps retired hashes as redirects (ADR-68-01) | +| Content delivery | `includes/class-content-proxy.php`: `GET /wp-json/exelearning/v1/content/{hash}/{file}` with security headers and CSP. A generated `.htaccess` blocks direct HTML/SVG/XML access under `uploads/exelearning/` (Apache only); the `exelearning_content_origin` filter serves content from a separate host | +| Frontend | `public/class-shortcodes.php`, `includes/class-elp-upload-block.php` (Block API v3), `includes/class-viewer-enhancements.php` (shortcode only), `includes/class-download-button-renderer.php` + `includes/class-export-bootstrap.php` (exports through the editor in a hidden `?exe_export=1` iframe) | +| Admin | `admin/class-admin-settings.php`, `includes/integrations/class-media-library.php` (columns, meta boxes, previews, bulk reprocess), styles: `admin/class-admin-styles.php`, `includes/class-styles-service.php`, `includes/class-style-package.php` (`uploads/exelearning-styles/{slug}/`) | +| Embedded editor | `includes/class-exelearning-editor.php`, `admin/views/editor-bootstrap.php` (loads `dist/static/index.html` with WordPress config), `assets/js/exelearning-editor.js` (modal) ↔ postMessage ↔ `assets/js/wp-exe-bridge.js` (inside the editor iframe), `includes/class-editor-bundle.php` | + +### REST API (`exelearning/v1`) + +| Route | Method | Permission | +|-------|--------|------------| +| `/content/{hash}/{file}` | GET | public (hash is the capability) | +| `/save/{id}` | POST | `edit_post` on the attachment | +| `/create` | POST | `upload_files` | +| `/elp-data/{id}` | GET | `edit_post` | +| `/reprocess/{id}` | POST | `edit_post` | + +### Data + +- Post meta: `_exelearning_title`, `_exelearning_description`, + `_exelearning_license`, `_exelearning_language`, `_exelearning_resource_type`, + `_exelearning_version`, `_exelearning_extracted` (SHA1 of the extraction folder), + `_exelearning_has_preview` (`1` when `index.html` exists), + `_exelearning_obsolete_hash` (retired hashes kept as redirect aliases). +- Options: `exelearning_db_version`, `exelearning_proxy_assets`, + `exelearning_styles_registry`, `exelearning_styles_block_import`, + `exelearning_disabled_styles`; `uninstall.php` deletes them and keeps user content. + ## Project boundaries -- `exelearning.php` bootstraps `includes/`; admin screens live in `admin/`, - shortcode rendering in `public/class-shortcodes.php`. - Archive processing, styles and content delivery go through the existing file-service, style-service and content-proxy classes. Preserve capability, nonce, path-validation and content-delivery boundaries when changing them. + Package HTML is untrusted author content served on the site origin. Do not + drop `allow-same-origin` from its iframes piecemeal: packages need storage and + cookies, embedded video needs the parent relay, and Playground cannot serve + opaque-origin subframes. The opaque-origin viewer lands as a whole in #56. - Update `docs/SHORTCODES.md` with shortcode attributes and `docs/HOOKS.md` with public actions/filters in the same change. -- The embedded editor is built with `make build-editor`; `npm run build` is only - a reminder, not a build. Do not hand-edit generated editor output. +- The block uses Block API version 3 (change 89), which WordPress 6.1 still loads. + Do not move it to script modules while WordPress 6.1 remains supported. - `.distignore` controls `wp dist-archive` releases; `.gitattributes` controls source archives used by Playground. They are different contracts, not lists to synchronize. Root-only dist rules need `/` so they do not strip editor assets. @@ -23,6 +71,26 @@ second plugin or replace the build pipeline when following a generic skill. ADR/change guides. Records use the carrying PR number (issues are disabled). Preserve accepted history, record `ai_assistance`, and run `make architecture-check`. +### Embedded editor build + +`exelearning/` is a gitignored clone of `exelearning/exelearning`, not a submodule; +`dist/static/` is its generated build. Do not hand-edit either. `npm run build` +is only a reminder, not a build. + +- `make build-editor`: fetch the source and build it (needs Bun). +- `make build-editor-if-needed`: build only when `dist/static/.build-commit` differs + from the clone's HEAD (used by `make up`; keeps the existing build when offline). +- `EXELEARNING_EDITOR_REF` / `_REF_TYPE` (`auto`, `branch`, `tag`; env or `.env`) + choose the ref, default `main`. Releases pin the editor tag in `.editor-version`. + +## Local environment + +- `make up` / `make down`: wp-env on http://localhost:8888 (admin/password); the + tests site is on 8889. `make clean` resets it, `make destroy` removes it. +- The tests container's uploads directory persists between runs: register every + file or folder a test creates and delete it in `tear_down()`, never inline after + the assertions. + ## Verification `composer install` and the committed dependency configuration provide tooling; @@ -31,10 +99,14 @@ do not add coding-standard packages just because an upstream example does. formatting needs correction. Do not replace the repository ruleset with a bare `--standard=WordPress .` scan. -- PHP: `make lint`, `make test`, `make phpmd`; do not raise `phpmd.xml` budgets. +- PHP: `make lint`, `make test` (`FILTER=Name` narrows it), `make phpmd`; do not + raise `phpmd.xml` budgets. - Browser JS/block behavior: `npm run test:js`; affected UI flows: `make test-e2e`. -- Translation changes: `make check-untranslated` and commit the generated catalogs. +- Translations: `make translations` (`composer make-translations`, vendored WP-CLI; + never the wp-env container's `wp`), then `make check-untranslated`; commit the + generated catalogs. - Architecture records: `make architecture-check`. +- Workflows: `actionlint`. - Plugin distribution: `make check-plugin`; inspect the actual release archive. - `make check` also applies automatic fixes; it is not a read-only verification command. @@ -43,7 +115,9 @@ commands, read [testing notes](.agents/references/testing.md). ## Working conventions -- Branches use English names with `feature/` or `hotfix/`; PRs target `main`. +- Branches use English names with `feature/` or `hotfix/`. Standalone PRs target + `main`; a stacked PR may target the previous branch of its stack, and the stack + is merged in order. - Follow the repository PHPCS ruleset and current source. English PHPDoc precedes functions/methods. Unslash request data before sanitizing; escape at output. - Check capabilities and resource ownership as well as nonces at write boundaries; @@ -55,10 +129,10 @@ commands, read [testing notes](.agents/references/testing.md). - No production deployment, release publication or data mutation is implied by a local implementation task. Respect authorization already given in the session. -English source strings use the plugin text domain; Spanish translations and -assertions preserve the user-facing language. Update catalogs with string changes, -use plural-aware translation functions, and add `translators:` comments for -placeholders. JS strings/nonces/URLs use the existing localization pipeline. +English source strings use the plugin text domain; every shipped locale gets a +translation, and assertions preserve the user-facing language (the test site runs +`es_ES`). Use plural-aware translation functions and add `translators:` comments +for placeholders. JS strings/nonces/URLs use the existing localization pipeline. ## Skills @@ -80,23 +154,24 @@ Install upstream skills with `gh skills install OWNER/REPO skills/NAME --dir .ag Keep upstream text and `metadata.github-*` unchanged; fix upstream and reinstall. Local skills have no GitHub provenance and the updater skips them. Put project exceptions in local guidance, not inside installed upstream folders. +New Claude entries are symlinks to `../../.agents/skills/NAME`, so Claude reads +the same skill directories. WordPress skills may target 7.0+: verify APIs against this project's supported versions. Do not upgrade requirements, scaffold new packages or change architecture merely because a generic skill recommends it. Resolve example `skills/...` paths under the actual `.agents/skills/` installation; use existing commands first. -New Claude entries are symlinks to `../../.agents/skills/NAME`. -Claude reads the same skill directories through those symlinks. - `.github/workflows/update-agent-skills.yml` checks weekly/on dispatch, scoped to installed skills, and opens a review PR on `main`. It never merges updates. Review prompt diffs as behavior changes. PRs made with the default GitHub token may not trigger CI; do not assume green checks will appear automatically. -The block skill must not force apiVersion 3 or script modules while WordPress 6.1 -remains supported. Preserve the existing block registration/build compatibility. +### GitHub Actions -Maintainer preference: use `actions/checkout@v7` and -`devantler-tech/actions/update-agent-skills@v13.3.3`; prefer the floating major -`v13` when upstream provides it. Use `peter-evans/create-pull-request@v8` too. Keep all actions in the skill-update workflow on version tags, not SHAs. +Keep actions on version tags maintained by Renovate, not SHAs: `actions/checkout@v7`, +`peter-evans/create-pull-request@v8`, and `devantler-tech/actions/update-agent-skills@v13.3.3` +(prefer the floating `v13` once upstream provides it). Pass external values +(release tags, dispatch inputs, event fields) to `run:` steps through `env:`, never +`${{ }}` inside the script, and use `persist-credentials: false` on checkouts in +jobs that install third-party packages. diff --git a/CLAUDE.md b/CLAUDE.md index 6beda88..43c994c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,150 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -It describes *how the plugin works*. The coding conventions — WPCS, tabs, translations, PHPMD -thresholds, the ADR/change-document policy — live in [`AGENTS.md`](AGENTS.md); read that before writing code. - -## Skills - -`.agents/skills/` holds vendored third-party skills; `.claude/skills/` symlinks to them. -Read the relevant one before working in its area — `wp-plugin-development` (hooks, Settings API, -packaging), `wp-rest-api` (the `/exelearning/v1/` routes below), `wp-plugin-directory-guidelines` -(`readme.txt`, licensing, what `make check-plugin` enforces), `blueprint` (`blueprint.json`, -Playground), `security-audit`. Never reformat or edit a skill in place. Details in `AGENTS.md`. - -## Project Overview - -eXeLearning is a WordPress plugin for managing eXeLearning .elp files. It allows uploading, extracting, and embedding eXeLearning content in WordPress pages and posts. - -**Key principle**: The plugin works directly with WordPress **attachments** (Media Library). No custom post type is needed - ELP files are uploaded as attachments and their metadata is stored in attachment post meta. - -## Development Commands - -### Static Editor Build -```bash -make build-editor # Build static eXeLearning editor from submodule (requires Bun) -make build-editor-no-update # Build without updating submodule (for CI/CD) -make update-submodule # Update eXeLearning submodule to correct branch -make clean-editor # Remove static editor build and node_modules -``` - -**Note**: The static editor is built from the `exelearning/` submodule (branch `release/3.1-embedable-version-refactor`). Output is placed in `dist/static/`. - -### Environment Setup -```bash -make up # Start wp-env Docker containers (http://localhost:8888, admin/password) -make down # Stop containers -make clean # Reset WordPress environment -make destroy # Completely remove wp-env -``` - -### Testing -```bash -make test # Run all PHPUnit tests -make test FILTER=MyTest # Run tests matching pattern -``` - -### Code Quality -```bash -make fix # Auto-fix code style with PHPCBF -make lint # Check code style with PHPCS -``` - -### Translations -```bash -make pot # Generate .pot file -make po # Update .po files -make mo # Generate .mo files -``` - -## Architecture - -### How It Works - -1. User uploads `.elp` file to Media Library -2. Plugin validates the file using ElpParser -3. File is extracted to `wp-content/uploads/exelearning/{sha1_hash}/` -4. Metadata from ELP is stored in attachment post meta -5. Content is embedded via shortcode or Gutenberg block - -### Core Components - -- **exelearning.php**: Main plugin file -- **includes/class-exelearning.php**: Core class that initializes all components -- **includes/class-hooks.php**: WordPress action registration -- **includes/class-filters.php**: WordPress filter registration - -### ELP File Handling - -- **includes/class-elp-upload-handler.php**: Handles ELP file upload and extraction -- **includes/class-elp-file-service.php**: Validates, parses, and extracts ELP files (inline ZipArchive + SimpleXML) -- **includes/class-elp-upload-block.php**: Gutenberg block for embedding ELP content -- **includes/class-mime-types.php**: Registers `.elp` MIME type for WordPress uploads - -### Admin - -- **admin/class-admin-settings.php**: Settings page -- **admin/class-admin-upload.php**: Admin upload handler - -### Public/Frontend - -- **public/class-shortcodes.php**: `[exelearning]` shortcode handler -- **includes/integrations/class-media-library.php**: Media library integration (columns, meta boxes) - -## Data Storage - -ELP files use WordPress attachments. Metadata is stored in post meta: - -| Meta Key | Description | -|----------|-------------| -| `_exelearning_title` | Title from ELP file | -| `_exelearning_description` | Description from ELP file | -| `_exelearning_license` | License information | -| `_exelearning_language` | Content language | -| `_exelearning_resource_type` | Learning resource type | -| `_exelearning_extracted` | SHA1 hash pointing to extraction folder | - -## File Storage - -When an ELP file is uploaded: -1. Original `.elp` file stored in Media Library -2. Extracted to `wp-content/uploads/exelearning/{sha1_hash}/` -3. Content accessible via `index.html` in extraction folder - -## Embedded Editor - -The plugin includes a fully embedded eXeLearning editor built from the static PWA version. - -### Key Files - -- **dist/static/**: Static build of eXeLearning editor (generated by `make build-editor`) -- **admin/views/editor-bootstrap.php**: Loads static editor with WordPress configuration -- **assets/js/wp-exe-bridge.js**: Bridge JavaScript connecting editor with WordPress -- **includes/class-exelearning-rest-api.php**: REST endpoints for saving/creating ELP files - -### How the Editor Works - -1. User clicks "Edit in eXeLearning" on an attachment -2. Plugin loads `dist/static/index.html` and injects WordPress config -3. Bridge JS (`wp-exe-bridge.js`) handles: - - Loading ELP file from WordPress into editor - - Saving edited content back to WordPress via REST API - - Keyboard shortcuts (Ctrl+S to save) -4. Editor runs 100% client-side (static PWA) - -### REST API Endpoints - -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/exelearning/v1/save/{id}` | POST | Update existing ELP file | -| `/exelearning/v1/create` | POST | Create new ELP file | -| `/exelearning/v1/elp-data/{id}` | GET | Get ELP file metadata | - -## Key Patterns - -- WordPress Coding Standards enforced via PHPCS -- Tests run inside wp-env container -- ELP files are ZIP archives with XML metadata -- No custom post type - uses WordPress attachments -- Static editor (PWA) runs client-side, no backend required +@AGENTS.md diff --git a/README.md b/README.md index 77a163d..33078e9 100644 --- a/README.md +++ b/README.md @@ -167,8 +167,8 @@ index and `make architecture-check` validates it. ### Working with AI coding agents -Coding conventions live in [`AGENTS.md`](AGENTS.md); [`CLAUDE.md`](CLAUDE.md) -describes how the plugin is put together. +[`AGENTS.md`](AGENTS.md) is the single guide for coding agents: how the plugin is +put together, commands and conventions. [`CLAUDE.md`](CLAUDE.md) only imports it. Reusable procedures ship as agent skills in `.agents/skills/` (symlinked from `.claude/skills/`): diff --git a/exelearning.php b/exelearning.php index 27ff2a1..01bebc5 100644 --- a/exelearning.php +++ b/exelearning.php @@ -65,7 +65,7 @@ // Integration classes. require_once EXELEARNING_PLUGIN_DIR . 'includes/integrations/class-media-library.php'; -// ELP File Service (validates, parses and extracts .elp files). +// ELP File Service (validates, parses and extracts .elpx packages). require_once EXELEARNING_PLUGIN_DIR . 'includes/class-elp-file-service.php'; // Reprocessor for existing attachments (reused by the REST API and entry points).