Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Cerradas: **TAREA-012 / RIE-001** investigación (DEC-0019); **TAREA-009 / RIE-0
| DEC-0048 | **Aceptada** (2026-06-12) | Estrategia de cobertura de tests: mockear la red con `\curl::mock_response()` + mock parcial de `download_to_temp()` en vez de excluir; no excluir del scope código testeable (`excludelistfiles` vacío); xdebug/Codecov es la medida autoritativa (pcov local subacredita llamadas anidadas — artefacto, no límite); gate `codecov project: target: auto` (trinquete). Cobertura honesta 85.71%→87.2% (PR #65) |
| DEC-0049 | **Aceptada** (2026-06-12) | Auditoría estándar de repositorio (2026-06-11, tras DEC-0016/DEC-0044): 9 mejoras P1–P3 implementadas (PRs #46–#54: hardening XML de estilos, thirdpartylibs en el ZIP, fidelidad backup/restore, lock de intentos, participación vs grademethod, recálculo de notas en lote, `zip_utils`, descarga del informe, Behat) + registro de **hallazgos descartados** y opciones de dirección para no re-auditar |
| DEC-0050 | **Aceptada** (2026-06-12) | La herramienta de migración exeweb/exescorm vive en `mod_exelearning` (destino, dueño de los internals); orígenes como fuentes legacy de solo lectura tras `source_interface`. Endurecimiento de la rama issue #13: fix `mod_exeweb` itemid=revision (antes leía 0 → todo `nosource`); clasificación `mod_exescorm` (`.elpx` directo / 1 embebido / 0=nosource / >1=ambiguous / external+aiccurl+localsync=unsupported, `localsync` excluido por sincronización aunque tenga snapshot local); limpieza compensatoria con `course_delete_module` ante fallo parcial (sin transacción, caveat recycle bin); preservación de metadatos del cm (idnumber **nunca** se copia); validación post-extracción anti shell-vacío (`migrateextractfailed`); eventos (started/migrated/skipped/failed, patrón DEC-0041); columnas `userid`/`timemodified` (upgrade 2026061201); preflight + `\core\progress\display`. Refactor a `classes/local/migration/` (elimina `import_service`). CLI diferido |
| DEC-0051..0063 | (varias, 2026-06-12 → 2026-06-17) | **Ver índice completo en `research/docs/indices/adrs.yaml`.** Resumen: DEC-0051 eventos selectivos · DEC-0052 completion por estado · DEC-0053 búsqueda global · DEC-0054 refactor `lib.php` (extracción a clases) · DEC-0055 auditoría post-refactor · DEC-0056 tests JS (Vitest) · DEC-0057 extracción no-destructiva (BETA→STABLE) · DEC-0058 fijar tag del editor en release · DEC-0063 validación canónica del endpoint xAPI + política de versión (1.0.3 tolerante a 2.0) · DEC-0064 implementación ingesta xAPI · DEC-0065 editor solo empaquetado en release (sin instalador runtime). *(DEC-0059..0062 = iframe seguro en rama `feature/secure-iframe-scorm-bridge`, aún no en `main`.)* |
| DEC-0051..0063 | (varias, 2026-06-12 → 2026-06-17) | **Ver índice completo en `research/docs/indices/adrs.yaml`.** Resumen: DEC-0051 eventos selectivos · DEC-0052 completion por estado · DEC-0053 búsqueda global · DEC-0054 refactor `lib.php` (extracción a clases) · DEC-0055 auditoría post-refactor · DEC-0056 tests JS (Vitest) · DEC-0057 extracción no-destructiva (BETA→STABLE) · DEC-0058 fijar tag del editor en release · DEC-0063 validación canónica del endpoint xAPI + política de versión (1.0.3 tolerante a 2.0) · DEC-0064 implementación ingesta xAPI · DEC-0065 editor solo empaquetado en release (sin instalador runtime) · DEC-0066 interruptor global del editor (modo reproductor puro). *(DEC-0059..0062 = iframe seguro en rama `feature/secure-iframe-scorm-bridge`, aún no en `main`.)* |

## Restricciones inmutables

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ All settings live on a single admin page (see
rationale of dropping the eXeLearning Online integration — only the embedded
editor remains):

* **Embedded editor**: a single site-wide switch. Disabling it turns the plugin
into a pure `.elpx` player — uploads and playback keep working, but the
"Edit with eXeLearning" button is hidden and the editor endpoints refuse
requests.
* **Styles**: upload eXeLearning style packages (`.zip`), enable/disable the
editor's built-in styles, and optionally block users from importing styles
bundled inside an `.elpx`. A dedicated _Styles_ admin page lists and manages
Expand Down
10 changes: 10 additions & 0 deletions docs/EMBEDDED_EDITOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ removed installer era is obsolete and ignored; upgrading cleans the installer's
config keys (`db/upgrade.php`, stage 2026072400) but deliberately leaves the
directory for the administrator to delete.

**Site-wide toggle (DEC-0066).** Embedded editing can be switched off with the
`exelearning/editordisabled` admin setting (a deliberately negative checkbox,
unticked by default, so the unset config and the unticked box both mean
"editing on"). `exelearning_embedded_editor_enabled()` combines the toggle with bundle
validation, so the edit button, `editor/static.php` and the create-from-scratch
CTA all react to it; `editor/index.php` and `editor/save.php` additionally
refuse direct requests via `exelearning_require_embedded_editor_enabled()`.
Uploading and serving `.elpx` packages is unaffected — the plugin degrades to a
pure player.

## 2. Embedding the editor and the postMessage bridge

The editor bootstrap page is `editor/index.php`. Access requires
Expand Down
8 changes: 8 additions & 0 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ from an official release ZIP.
> `moodledata/mod_exelearning/embedded_editor/` installed by the removed
> management page. It is no longer used and can be deleted at any time.

### Disabling in-place editing

If your site only plays packages authored elsewhere, tick **Disable the
embedded editor** on the settings page. Teachers keep uploading and serving
`.elpx` packages exactly as before, but the **Edit with eXeLearning** button
disappears and the editor pages refuse requests. Untick it at any time to
restore in-place editing — no data is affected either way.

### Managing styles

On the settings page you can manage the eXeLearning styles available to the
Expand Down
6 changes: 6 additions & 0 deletions editor/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ function exelearning_editor_error_page(string $message): void {
// Direct access to dist/static/ can fail on servers that block .zip, .md, etc.
$editorbaseurl = $CFG->wwwroot . '/mod/exelearning/editor/static.php/' . $cm->id;

// Embedded editing can be switched off site-wide (DEC-0066); hiding the button
// is not enough, a direct request to this bootstrap must be refused too.
if (!exelearning_embedded_editor_enabled()) {
exelearning_editor_error_page(get_string('editordisabledbyadmin', 'mod_exelearning'));
}

// Read the editor template from the bundled installation (DEC-0065).
$editorindexsource = exelearning_get_embedded_editor_index_source();
if ($editorindexsource === null) {
Expand Down
3 changes: 3 additions & 0 deletions editor/save.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
require_sesskey();
$context = context_module::instance($cm->id);
require_capability('moodle/course:manageactivities', $context);
// Embedded editing can be switched off site-wide (DEC-0066): refuse saves too,
// not only the editor bootstrap — this is the state-changing half of the flow.
exelearning_require_embedded_editor_enabled();

header('Content-Type: application/json; charset=utf-8');

Expand Down
4 changes: 4 additions & 0 deletions lang/ca/exelearning.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
$string['deleteattempt_confirm'] = '~S\'eliminarà definitivament l\'intent {$a} d\'aquest estudiant i la seva qualificació es recalcularà a partir dels intents restants. Aquesta acció no es pot desfer.';
$string['detecteditems'] = '~iDevices qualificables detectats:';
$string['downloadreport'] = '~Baixa les dades de l\'informe com a';
$string['editordisabled'] = '~Desactiva l\'editor integrat';
$string['editordisabled_desc'] = '~Si es marca, el connector funciona com un reproductor pur: pujar i mostrar paquets .elpx continua funcionant, però el botó «Edita amb eXeLearning» s\'amaga i els punts finals de l\'editor rebutgen les peticions. Deixeu-la sense marcar (el valor per defecte) perquè els qui poden gestionar una activitat editin els paquets des de Moodle.';
$string['editordisabledbyadmin'] = '~L\'edició integrada està desactivada en aquest lloc. L\'activitat continua reproduint els paquets eXeLearning pujats; contacteu amb l\'administrador si necessiteu editar des de Moodle.';
$string['editorreaderror'] = 'No s\'han pogut llegir els fitxers de l\'editor integrat eXeLearning. Comproveu els permisos dels fitxers i contacteu amb l\'administrador.';
$string['editorsettings'] = '~Editor integrat';
$string['editwitheditor'] = '~Edita amb eXeLearning';
$string['embeddednotinstalledadmin'] = '~L\'editor integrat no està inclòs en aquesta instal·lació. Instal·leu el connector des d\'un ZIP de versió oficial, que inclou l\'editor.';
$string['embeddednotinstalledcontactadmin'] = '~L\'editor integrat no està disponible en aquest lloc. Contacteu amb l\'administrador del lloc.';
Expand Down
4 changes: 4 additions & 0 deletions lang/en/exelearning.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@
$string['deleteattempt_confirm'] = 'This will permanently delete attempt {$a} for this student and recalculate their grade from the remaining attempts. This action cannot be undone.';
$string['detecteditems'] = 'Gradable iDevices detected:';
$string['downloadreport'] = 'Download report data as';
$string['editordisabled'] = 'Disable the embedded editor';
$string['editordisabled_desc'] = 'When ticked, the plugin works as a pure player: uploading and serving .elpx packages keeps working, but the "Edit with eXeLearning" button is hidden and the editor endpoints refuse requests. Leave unticked (the default) to let users who can manage an activity author packages in place.';
$string['editordisabledbyadmin'] = 'Embedded editing is disabled on this site. The activity still plays uploaded eXeLearning packages; contact your administrator if you need in-place editing.';
$string['editorreaderror'] = 'Could not read the eXeLearning embedded editor files. Please check file permissions and contact your administrator.';
$string['editorsettings'] = 'Embedded editor';
$string['editwitheditor'] = 'Edit with eXeLearning';
$string['embeddednotinstalledadmin'] = 'The embedded editor is not included in this installation. Install the plugin from an official release ZIP, which bundles the editor.';
$string['embeddednotinstalledcontactadmin'] = 'The embedded editor is not available on this site. Please contact your site administrator.';
Expand Down
4 changes: 4 additions & 0 deletions lang/es/exelearning.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
$string['deleteattempt_confirm'] = '~Se eliminará definitivamente el intento {$a} de este estudiante y su calificación se recalculará a partir de los intentos restantes. Esta acción no se puede deshacer.';
$string['detecteditems'] = '~iDevices calificables detectados:';
$string['downloadreport'] = '~Descargar los datos del informe como';
$string['editordisabled'] = '~Desactivar el editor integrado';
$string['editordisabled_desc'] = '~Si se marca, el plugin funciona como un reproductor puro: subir y mostrar paquetes .elpx sigue funcionando, pero el botón «Editar con eXeLearning» se oculta y los endpoints del editor rechazan las peticiones. Déjelo sin marcar (el valor por defecto) para que quienes pueden gestionar una actividad editen los paquetes desde Moodle.';
$string['editordisabledbyadmin'] = '~La edición integrada está desactivada en este sitio. La actividad sigue reproduciendo los paquetes eXeLearning subidos; contacte con el administrador si necesita editar desde Moodle.';
$string['editorreaderror'] = 'No se pudieron leer los archivos del editor integrado eXeLearning. Compruebe los permisos de los archivos y contacte con el administrador.';
$string['editorsettings'] = '~Editor integrado';
$string['editwitheditor'] = 'Editar con eXeLearning';
$string['embeddednotinstalledadmin'] = '~El editor integrado no está incluido en esta instalación. Instale el plugin desde un ZIP de release oficial, que incluye el editor.';
$string['embeddednotinstalledcontactadmin'] = '~El editor integrado no está disponible en este sitio. Contacte con el administrador del sitio.';
Expand Down
4 changes: 4 additions & 0 deletions lang/eu/exelearning.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
$string['deleteattempt_confirm'] = '~Ikasle honen {$a}. saialdia behin betiko ezabatuko da, eta kalifikazioa gainerako saialdietatik birkalkulatuko da. Ekintza hau ezin da desegin.';
$string['detecteditems'] = '~Hautemandako iDevice kalifikagarriak:';
$string['downloadreport'] = '~Deskargatu txostenaren datuak honela:';
$string['editordisabled'] = '~Desaktibatu editore txertatua';
$string['editordisabled_desc'] = '~Markatzen bada, pluginak erreproduzitzaile huts gisa funtzionatzen du: .elpx paketeak igotzea eta erakustea funtzionatzen jarraitzen du, baina «Editatu eXeLearning-ekin» botoia ezkutatu egiten da eta editorearen amaiera-puntuek eskaerak ukatzen dituzte. Utzi markatu gabe (balio lehenetsia) jarduera bat kudea dezaketenek paketeak Moodletik edita ditzaten.';
$string['editordisabledbyadmin'] = '~Editatze txertatua desaktibatuta dago gune honetan. Jarduerak igotako eXeLearning paketeak erreproduzitzen jarraitzen du; jarri harremanetan administratzailearekin Moodletik editatu behar baduzu.';
$string['editorreaderror'] = 'Ezin izan dira eXeLearning editore txertatuaren fitxategiak irakurri. Egiaztatu fitxategien baimenak eta jarri harremanetan administratzailearekin.';
$string['editorsettings'] = '~Editore txertatua';
$string['editwitheditor'] = '~Editatu eXeLearning-ekin';
$string['embeddednotinstalledadmin'] = '~Editore txertatua ez dago instalazio honetan sartuta. Instalatu plugina bertsio ofizialeko ZIP batetik; horrek editorea barne dakar.';
$string['embeddednotinstalledcontactadmin'] = '~Editore txertatua ez dago erabilgarri gune honetan. Jarri harremanetan guneko administratzailearekin.';
Expand Down
4 changes: 4 additions & 0 deletions lang/gl/exelearning.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
$string['deleteattempt_confirm'] = '~Eliminarase definitivamente o intento {$a} deste estudante e a súa cualificación recalcularase a partir dos intentos restantes. Esta acción non se pode desfacer.';
$string['detecteditems'] = '~iDevices cualificables detectados:';
$string['downloadreport'] = '~Descargar os datos do informe como';
$string['editordisabled'] = '~Desactivar o editor integrado';
$string['editordisabled_desc'] = '~Se se marca, o complemento funciona como un reprodutor puro: subir e amosar paquetes .elpx segue funcionando, pero o botón «Editar con eXeLearning» ocúltase e os puntos de acceso do editor rexeitan as peticións. Déixeo sen marcar (o valor por defecto) para que quen pode xestionar unha actividade edite os paquetes desde Moodle.';
$string['editordisabledbyadmin'] = '~A edición integrada está desactivada neste sitio. A actividade segue reproducindo os paquetes eXeLearning subidos; contacte co administrador se precisa editar desde Moodle.';
$string['editorreaderror'] = 'Non se puideron ler os ficheiros do editor integrado eXeLearning. Comprobe os permisos dos ficheiros e contacte co administrador.';
$string['editorsettings'] = '~Editor integrado';
$string['editwitheditor'] = '~Editar con eXeLearning';
$string['embeddednotinstalledadmin'] = '~O editor integrado non está incluído nesta instalación. Instale o complemento desde un ZIP de versión oficial, que inclúe o editor.';
$string['embeddednotinstalledcontactadmin'] = '~O editor integrado non está dispoñible neste sitio. Contacte co administrador do sitio.';
Expand Down
34 changes: 31 additions & 3 deletions lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1055,17 +1055,45 @@ function exelearning_get_embedded_editor_index_source(): ?string {
}

/**
* Returns whether the bundled embedded editor is available (DEC-0065).
* Returns whether embedded editing is available on this site.
*
* Two conditions must hold (DEC-0065, DEC-0066): the bundled editor passes
* validation, and the administrator has not switched embedded editing off via
* the site-wide `editordisabled` setting. The toggle is deliberately negative
* (like `stylesblockimport`) so the unset state and the unticked checkbox both
* mean "editing on" — a positive default would render unticked until
* upgradesettings materialises it, contradicting the real behaviour.
*
* Used by view.php to decide whether to show the "Edit with eXeLearning" button
* and by editor/static.php before serving editor assets.
* and by editor/static.php before serving editor assets. Activities keep
* accepting `.elpx` uploads either way — the toggle only affects in-place
* editing.
*
* @return bool True when the bundled editor passes validation.
* @return bool True when the editor is bundled, valid and not disabled.
*/
function exelearning_embedded_editor_enabled(): bool {
if (!empty(get_config('exelearning', 'editordisabled'))) {
return false;
}
return \mod_exelearning\local\embedded_editor_source_resolver::is_available();
}

/**
* Aborts the request when embedded editing is disabled site-wide (DEC-0066).
*
* Guard for the editor endpoints (editor/index.php bootstrap, editor/save.php):
* hiding the button is not enough, a direct request must be refused too.
*
* @return void
* @throws moodle_exception editordisabledbyadmin when the toggle is off or no
* valid bundle is available.
*/
function exelearning_require_embedded_editor_enabled(): void {
if (!exelearning_embedded_editor_enabled()) {
throw new moodle_exception('editordisabledbyadmin', 'mod_exelearning');
}
}

/**
* Absolute path to the bundled editor static directory, used by
* editor/static.php to serve the editor's assets.
Expand Down
Loading
Loading