From b8a340cb7fa1c4d49e5e25f2ba8adc082fea98f4 Mon Sep 17 00:00:00 2001 From: erseco Date: Fri, 24 Jul 2026 12:46:16 +0100 Subject: [PATCH 1/3] Add a site-wide switch to disable embedded editing (DEC-0066) Some sites use the plugin as a pure player: teachers upload .elpx packages authored elsewhere and the site does not want to offer in-place editing. Until now the "Edit with eXeLearning" button appeared for anyone with moodle/course:manageactivities whenever the bundled editor was valid, with no supported way to switch it off. Add the exelearning/editorenabled admin setting (checkbox, default on; an unset config counts as on so upgrades keep today's behaviour). exelearning_embedded_editor_enabled() now combines the toggle with bundle validation, so the edit button, editor/static.php and the create-from-scratch CTA all react to it. Because hiding a button is not an access control, the editor endpoints refuse direct requests too: editor/index.php shows an explanatory error page and editor/save.php aborts through the new exelearning_require_embedded_editor_enabled() guard. Uploading and serving packages is unaffected either way. A site-wide setting was chosen over a new capability (ADR DEC-0066): the "who" is already governed by moodle/course:manageactivities, the real use case is binary and site-wide, and feature switches in admin settings are how activity plugins usually disable whole features. A per-role capability can still be added on top later without breaking anything. Strings added to the five language packs (machine translations carry the ~ review marker). Tests cover the default-on behaviour, the toggle disabling the helper, and the endpoint guard throwing. --- AGENTS.md | 2 +- README.md | 4 + docs/EMBEDDED_EDITOR.md | 9 +++ docs/USER_GUIDE.md | 8 ++ editor/index.php | 6 ++ editor/save.php | 3 + lang/ca/exelearning.php | 5 ++ lang/en/exelearning.php | 5 ++ lang/es/exelearning.php | 5 ++ lang/eu/exelearning.php | 5 ++ lang/gl/exelearning.php | 5 ++ lib.php | 33 +++++++- ...0066-interruptor-global-editor-embebido.md | 75 +++++++++++++++++++ research/docs/indices/adrs.yaml | 1 + settings.php | 17 +++++ tests/lib_helpers_test.php | 26 +++++++ 16 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md diff --git a/AGENTS.md b/AGENTS.md index e1a07f5..3ff7ad2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 diff --git a/README.md b/README.md index cc1e357..300ebe9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/EMBEDDED_EDITOR.md b/docs/EMBEDDED_EDITOR.md index fbde066..b9d0d5c 100644 --- a/docs/EMBEDDED_EDITOR.md +++ b/docs/EMBEDDED_EDITOR.md @@ -49,6 +49,15 @@ 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/editorenabled` admin setting (default on; an unset config counts as +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 diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 72d93f4..ad9be66 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -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, untick **Enable 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. Tick it again 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 diff --git a/editor/index.php b/editor/index.php index b8c48b9..3d5b1cb 100644 --- a/editor/index.php +++ b/editor/index.php @@ -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) { diff --git a/editor/save.php b/editor/save.php index 69baeee..460f165 100644 --- a/editor/save.php +++ b/editor/save.php @@ -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'); diff --git a/lang/ca/exelearning.php b/lang/ca/exelearning.php index d574e16..d73a64e 100644 --- a/lang/ca/exelearning.php +++ b/lang/ca/exelearning.php @@ -51,7 +51,12 @@ $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['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['editorenabled'] = '~Activa l\'editor integrat'; +$string['editorenabled_desc'] = '~Si està activat, els qui poden gestionar una activitat veuen el botó «Edita amb eXeLearning» i poden crear o modificar paquets des de Moodle. Si està desactivat, el connector funciona com un reproductor pur: pujar i mostrar paquets .elpx continua funcionant, però l\'editor s\'amaga i els seus punts finals rebutgen les peticions.'; $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['editorsettings_desc'] = '~L\'editor d\'eXeLearning ve inclòs al paquet de la versió del connector i no es pot instal·lar ni actualitzar per separat. Aquest interruptor només controla si s\'ofereix l\'edició des de Moodle.'; $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.'; diff --git a/lang/en/exelearning.php b/lang/en/exelearning.php index 1ddfc46..c69e5fb 100644 --- a/lang/en/exelearning.php +++ b/lang/en/exelearning.php @@ -47,7 +47,12 @@ $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['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['editorenabled'] = 'Enable the embedded editor'; +$string['editorenabled_desc'] = 'When enabled, users who can manage an activity see the "Edit with eXeLearning" button and can author packages in place. When disabled, the plugin works as a pure player: uploading and serving .elpx packages keeps working, but the editor is hidden and its endpoints refuse requests.'; $string['editorreaderror'] = 'Could not read the eXeLearning embedded editor files. Please check file permissions and contact your administrator.'; +$string['editorsettings'] = 'Embedded editor'; +$string['editorsettings_desc'] = 'The eXeLearning editor ships inside the plugin release package and cannot be installed or updated separately. This switch only controls whether in-place editing is offered.'; $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.'; diff --git a/lang/es/exelearning.php b/lang/es/exelearning.php index 554a6d2..c43b647 100644 --- a/lang/es/exelearning.php +++ b/lang/es/exelearning.php @@ -51,7 +51,12 @@ $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['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['editorenabled'] = '~Activar el editor integrado'; +$string['editorenabled_desc'] = '~Si está activado, quienes pueden gestionar una actividad ven el botón «Editar con eXeLearning» y pueden crear o modificar paquetes desde Moodle. Si está desactivado, el plugin funciona como un reproductor puro: subir y mostrar paquetes .elpx sigue funcionando, pero el editor se oculta y sus endpoints rechazan las peticiones.'; $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['editorsettings_desc'] = '~El editor de eXeLearning viene incluido en el paquete de la release del plugin y no puede instalarse ni actualizarse por separado. Este interruptor solo controla si se ofrece la edición desde Moodle.'; $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.'; diff --git a/lang/eu/exelearning.php b/lang/eu/exelearning.php index 9c80e2c..b3105c6 100644 --- a/lang/eu/exelearning.php +++ b/lang/eu/exelearning.php @@ -51,7 +51,12 @@ $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['editordisabledbyadmin'] = '~Editatze txertatua desaktibatuta dago gune honetan. Jarduerak igotako eXeLearning paketeak erreproduzitzen jarraitzen du; jarri harremanetan administratzailearekin Moodletik editatu behar baduzu.'; +$string['editorenabled'] = '~Aktibatu editore txertatua'; +$string['editorenabled_desc'] = '~Aktibatuta badago, jarduera bat kudea dezaketenek «Editatu eXeLearning-ekin» botoia ikusten dute eta paketeak Moodletik sortu edo aldatu ditzakete. Desaktibatuta badago, pluginak erreproduzitzaile huts gisa funtzionatzen du: .elpx paketeak igotzea eta erakustea funtzionatzen jarraitzen du, baina editorea ezkutatu egiten da eta haren amaiera-puntuek eskaerak ukatzen dituzte.'; $string['editorreaderror'] = 'Ezin izan dira eXeLearning editore txertatuaren fitxategiak irakurri. Egiaztatu fitxategien baimenak eta jarri harremanetan administratzailearekin.'; +$string['editorsettings'] = '~Editore txertatua'; +$string['editorsettings_desc'] = '~eXeLearning editorea pluginaren bertsio-paketearen barruan dator eta ezin da bereizita instalatu edo eguneratu. Etengailu honek Moodletik editatzea eskaintzen den ala ez baino ez du kontrolatzen.'; $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.'; diff --git a/lang/gl/exelearning.php b/lang/gl/exelearning.php index 86ba4ee..263cbca 100644 --- a/lang/gl/exelearning.php +++ b/lang/gl/exelearning.php @@ -51,7 +51,12 @@ $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['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['editorenabled'] = '~Activar o editor integrado'; +$string['editorenabled_desc'] = '~Se está activado, quen pode xestionar unha actividade ve o botón «Editar con eXeLearning» e pode crear ou modificar paquetes desde Moodle. Se está desactivado, o complemento funciona como un reprodutor puro: subir e amosar paquetes .elpx segue funcionando, pero o editor ocúltase e os seus puntos de acceso rexeitan as peticións.'; $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['editorsettings_desc'] = '~O editor de eXeLearning vén incluído no paquete da versión do complemento e non se pode instalar nin actualizar por separado. Este interruptor só controla se se ofrece a edición desde Moodle.'; $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.'; diff --git a/lib.php b/lib.php index a0bde7a..bf9f001 100644 --- a/lib.php +++ b/lib.php @@ -1055,17 +1055,44 @@ 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 `editorenabled` setting. An unset config (a site that never + * saved the settings page) counts as enabled, preserving the default. * * 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 { + $setting = get_config('exelearning', 'editorenabled'); + if ($setting !== false && !$setting) { + 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. diff --git a/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md b/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md new file mode 100644 index 0000000..26445ee --- /dev/null +++ b/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md @@ -0,0 +1,75 @@ +--- +id: DEC-0066 +titulo: "Interruptor global del editor embebido: modo reproductor puro vía ajuste de sitio" +estado: Aceptada +fecha: 2026-07-24 +agentes: + - erseco + - claude-code +fuentes: + - REPO-002 + - REPO-004 +relacionados: + - DEC-0009 + - DEC-0065 + - DEC-0024 +herramienta_ia: + interfaz: claude-code + modelo: claude-fable-5 +--- + +## Contexto + +Hay sitios que quieren usar `mod_exelearning` como **reproductor puro**: los docentes suben +paquetes `.elpx` producidos fuera (eXeLearning de escritorio, repositorios institucionales) +y el sitio no quiere ofrecer la edición en Moodle — por política editorial, por flujo de +trabajo (el contenido se autoriza fuera) o por simplicidad de soporte. Hoy el botón +"Editar con eXeLearning" aparece para cualquiera con `moodle/course:manageactivities` +siempre que el bundle del editor sea válido ([[DEC-0065]]); no existe forma soportada de +apagarlo sin mutilar el paquete. + +Se valoraron dos mecanismos: + +1. **Ajuste global de sitio** (checkbox en la página de ajustes del plugin). +2. **Capability nueva** (`mod/exelearning:useeditor`) concedida por defecto a + editingteacher/manager — un "perfil mínimo de edición". + +## Decisión + +**Ajuste global `exelearning/editorenabled`** (checkbox, activado por defecto), por ser lo +más sencillo y lo más parecido a cómo los plugins de actividad de Moodle apagan +funcionalidades completas de sitio (los ajustes `scorm_*` de mod_scorm, los toggles de +características en admin settings). La capability se descarta como mecanismo primario: el +"quién" ya lo gobierna `moodle/course:manageactivities` (paridad con el resto del plugin), +y una capability nueva obligaría a editar roles para lograr el caso de uso real, que es +binario y de sitio ("aquí no se edita"), no por-rol. + +Alcance del interruptor: + +- **Apagado**: el botón de edición no se muestra (`view.php` vía + `exelearning_embedded_editor_enabled()`, que ahora combina ajuste + validez del bundle); + los endpoints del editor **rechazan** peticiones directas — `editor/index.php` con página + de error explicativa y `editor/save.php` con `exelearning_require_embedded_editor_enabled()` + (ocultar el botón no es un control de acceso); `editor/static.php` deja de servir assets + (ya pasaba por el helper). La subida y reproducción de `.elpx` no cambian. +- **Encendido** (default): comportamiento actual. Config ausente (sitio que nunca guardó + ajustes) cuenta como encendido para no cambiar el comportamiento en upgrade. + +## Consecuencias + +- Positivas: caso "reproductor puro" soportado con un clic; superficie del editor + desactivable de golpe (bootstrap, guardado y assets); sin cambios de roles ni upgrade de + BD (es un config, no una columna). +- Negativas (asumidas): no permite granularidad por rol o por curso; si mañana hiciera + falta, la capability del punto 2 puede añadirse *encima* del interruptor sin romper nada + (el ajuste seguiría siendo el interruptor maestro). +- El CTA de "crear desde cero" ([[DEC-0024]]) degrada solo: con el editor apagado el + docente ve el aviso de "sube un paquete" en lugar del de "créalo con el editor". + +## Alternativas consideradas + +| Alternativa | Por qué se rechaza | +|---|---| +| Capability `mod/exelearning:useeditor` | Resuelve un problema distinto (quién, no si); exige editar roles para el caso real; más superficie de configuración. Puede añadirse después si aparece demanda por-rol. | +| Ajuste por actividad | Dispersa la política editorial en N actividades; el caso de uso es de sitio. | +| No hacer nada (quitar el bundle del paquete) | Rompe [[DEC-0065]] (el empaquetado exige el editor) y castiga a los sitios que sí editan. | diff --git a/research/docs/indices/adrs.yaml b/research/docs/indices/adrs.yaml index ce19edf..ba11318 100644 --- a/research/docs/indices/adrs.yaml +++ b/research/docs/indices/adrs.yaml @@ -61,3 +61,4 @@ items: - id: 'DEC-0063', titulo: 'Reglas de validación canónica del endpoint xAPI y política de versión (1.0.3 con tolerancia a 2.0)', estado: 'Propuesta', fecha: '2026-06-17', ruta: 'decisiones/adr/DEC-0063-validacion-canonica-endpoint-xapi-y-version.md' - id: 'DEC-0064', titulo: 'Implementación de la ingesta xAPI (TAREA-015): xAPI-primary para paquetes nuevos, SCORM inerte, overall desde el statement de paquete, siempre activo', estado: 'Aceptada', fecha: '2026-06-18', ruta: 'decisiones/adr/DEC-0064-implementacion-ingesta-xapi.md' - id: 'DEC-0065', titulo: 'Editor embebido exclusivamente empaquetado en la release: eliminar el instalador/actualizador en runtime', estado: 'Aceptada', fecha: '2026-07-24', ruta: 'decisiones/adr/DEC-0065-editor-empaquetado-solo-en-release.md' + - id: 'DEC-0066', titulo: 'Interruptor global del editor embebido: modo reproductor puro vía ajuste de sitio', estado: 'Aceptada', fecha: '2026-07-24', ruta: 'decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md' diff --git a/settings.php b/settings.php index 1648bac..6f6428b 100644 --- a/settings.php +++ b/settings.php @@ -54,6 +54,23 @@ } if ($ADMIN->fulltree) { + // Embedded editor (DEC-0066): a single site-wide toggle. The editor itself + // ships inside the release package (DEC-0065) and has no runtime management; + // this switch lets a site use the plugin as a pure .elpx player — uploads + // keep working, only in-place editing is hidden and refused. + $settings->add(new admin_setting_heading( + 'mod_exelearning/editorsettingsheading', + get_string('editorsettings', 'mod_exelearning'), + get_string('editorsettings_desc', 'mod_exelearning') + )); + + $settings->add(new admin_setting_configcheckbox( + 'exelearning/editorenabled', + get_string('editorenabled', 'mod_exelearning'), + get_string('editorenabled_desc', 'mod_exelearning'), + 1 + )); + // Defined styles management (upload / enable / disable / lockdown). $settings->add(new admin_setting_heading( 'mod_exelearning/stylesheading', diff --git a/tests/lib_helpers_test.php b/tests/lib_helpers_test.php index 417657e..35c8c09 100644 --- a/tests/lib_helpers_test.php +++ b/tests/lib_helpers_test.php @@ -35,6 +35,7 @@ * @covers ::exelearning_grade_item_view_url * @covers ::exelearning_grade_analysis_url * @covers ::exelearning_embedded_editor_enabled + * @covers ::exelearning_require_embedded_editor_enabled * @covers ::exelearning_get_embedded_editor_index_source * @covers ::exelearning_get_embedded_editor_local_static_dir * @covers ::exelearning_xapi_primary_enabled @@ -163,5 +164,30 @@ public function test_embedded_editor_wrappers_reflect_bundle(): void { $this->assertTrue(exelearning_embedded_editor_enabled()); $this->assertSame($dir . '/index.html', exelearning_get_embedded_editor_index_source()); $this->assertSame($dir, exelearning_get_embedded_editor_local_static_dir()); + + // With no editorenabled config at all (fresh site), editing stays on and + // the endpoint guard passes. + exelearning_require_embedded_editor_enabled(); + } + + /** + * The site-wide editorenabled toggle disables embedded editing even when a + * valid bundle is present (DEC-0066): the button helper reports false and the + * editor endpoints' guard throws. + */ + public function test_admin_toggle_disables_embedded_editing(): void { + global $CFG; + $this->resetAfterTest(); + + $dir = make_temp_directory('mod_exelearning/lw-' . random_string(6)) . '/static'; + make_writable_directory($dir . '/app'); + file_put_contents($dir . '/index.html', 'x'); + $CFG->mod_exelearning_bundled_editor_dir = $dir; + + set_config('editorenabled', 0, 'exelearning'); + + $this->assertFalse(exelearning_embedded_editor_enabled()); + $this->expectException(\moodle_exception::class); + exelearning_require_embedded_editor_enabled(); } } From 6ce60d84b5dc8ecf1224e13678acc20301db9c8b Mon Sep 17 00:00:00 2001 From: erseco Date: Fri, 24 Jul 2026 12:49:37 +0100 Subject: [PATCH 2/3] Drop the redundant heading description from the editor settings The checkbox's own description already explains the switch; repeating a condensed version in the heading added noise without information. --- lang/ca/exelearning.php | 1 - lang/en/exelearning.php | 1 - lang/es/exelearning.php | 1 - lang/eu/exelearning.php | 1 - lang/gl/exelearning.php | 1 - settings.php | 2 +- 6 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lang/ca/exelearning.php b/lang/ca/exelearning.php index d73a64e..0a620ae 100644 --- a/lang/ca/exelearning.php +++ b/lang/ca/exelearning.php @@ -56,7 +56,6 @@ $string['editorenabled_desc'] = '~Si està activat, els qui poden gestionar una activitat veuen el botó «Edita amb eXeLearning» i poden crear o modificar paquets des de Moodle. Si està desactivat, el connector funciona com un reproductor pur: pujar i mostrar paquets .elpx continua funcionant, però l\'editor s\'amaga i els seus punts finals rebutgen les peticions.'; $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['editorsettings_desc'] = '~L\'editor d\'eXeLearning ve inclòs al paquet de la versió del connector i no es pot instal·lar ni actualitzar per separat. Aquest interruptor només controla si s\'ofereix l\'edició des de Moodle.'; $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.'; diff --git a/lang/en/exelearning.php b/lang/en/exelearning.php index c69e5fb..14a0c74 100644 --- a/lang/en/exelearning.php +++ b/lang/en/exelearning.php @@ -52,7 +52,6 @@ $string['editorenabled_desc'] = 'When enabled, users who can manage an activity see the "Edit with eXeLearning" button and can author packages in place. When disabled, the plugin works as a pure player: uploading and serving .elpx packages keeps working, but the editor is hidden and its endpoints refuse requests.'; $string['editorreaderror'] = 'Could not read the eXeLearning embedded editor files. Please check file permissions and contact your administrator.'; $string['editorsettings'] = 'Embedded editor'; -$string['editorsettings_desc'] = 'The eXeLearning editor ships inside the plugin release package and cannot be installed or updated separately. This switch only controls whether in-place editing is offered.'; $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.'; diff --git a/lang/es/exelearning.php b/lang/es/exelearning.php index c43b647..8df5a5d 100644 --- a/lang/es/exelearning.php +++ b/lang/es/exelearning.php @@ -56,7 +56,6 @@ $string['editorenabled_desc'] = '~Si está activado, quienes pueden gestionar una actividad ven el botón «Editar con eXeLearning» y pueden crear o modificar paquetes desde Moodle. Si está desactivado, el plugin funciona como un reproductor puro: subir y mostrar paquetes .elpx sigue funcionando, pero el editor se oculta y sus endpoints rechazan las peticiones.'; $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['editorsettings_desc'] = '~El editor de eXeLearning viene incluido en el paquete de la release del plugin y no puede instalarse ni actualizarse por separado. Este interruptor solo controla si se ofrece la edición desde Moodle.'; $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.'; diff --git a/lang/eu/exelearning.php b/lang/eu/exelearning.php index b3105c6..165b894 100644 --- a/lang/eu/exelearning.php +++ b/lang/eu/exelearning.php @@ -56,7 +56,6 @@ $string['editorenabled_desc'] = '~Aktibatuta badago, jarduera bat kudea dezaketenek «Editatu eXeLearning-ekin» botoia ikusten dute eta paketeak Moodletik sortu edo aldatu ditzakete. Desaktibatuta badago, pluginak erreproduzitzaile huts gisa funtzionatzen du: .elpx paketeak igotzea eta erakustea funtzionatzen jarraitzen du, baina editorea ezkutatu egiten da eta haren amaiera-puntuek eskaerak ukatzen dituzte.'; $string['editorreaderror'] = 'Ezin izan dira eXeLearning editore txertatuaren fitxategiak irakurri. Egiaztatu fitxategien baimenak eta jarri harremanetan administratzailearekin.'; $string['editorsettings'] = '~Editore txertatua'; -$string['editorsettings_desc'] = '~eXeLearning editorea pluginaren bertsio-paketearen barruan dator eta ezin da bereizita instalatu edo eguneratu. Etengailu honek Moodletik editatzea eskaintzen den ala ez baino ez du kontrolatzen.'; $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.'; diff --git a/lang/gl/exelearning.php b/lang/gl/exelearning.php index 263cbca..e267c0c 100644 --- a/lang/gl/exelearning.php +++ b/lang/gl/exelearning.php @@ -56,7 +56,6 @@ $string['editorenabled_desc'] = '~Se está activado, quen pode xestionar unha actividade ve o botón «Editar con eXeLearning» e pode crear ou modificar paquetes desde Moodle. Se está desactivado, o complemento funciona como un reprodutor puro: subir e amosar paquetes .elpx segue funcionando, pero o editor ocúltase e os seus puntos de acceso rexeitan as peticións.'; $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['editorsettings_desc'] = '~O editor de eXeLearning vén incluído no paquete da versión do complemento e non se pode instalar nin actualizar por separado. Este interruptor só controla se se ofrece a edición desde Moodle.'; $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.'; diff --git a/settings.php b/settings.php index 6f6428b..0bd422a 100644 --- a/settings.php +++ b/settings.php @@ -61,7 +61,7 @@ $settings->add(new admin_setting_heading( 'mod_exelearning/editorsettingsheading', get_string('editorsettings', 'mod_exelearning'), - get_string('editorsettings_desc', 'mod_exelearning') + '' )); $settings->add(new admin_setting_configcheckbox( From 7c4a140cba83c302158bd431f6d0125f6735162b Mon Sep 17 00:00:00 2001 From: erseco Date: Fri, 24 Jul 2026 13:00:49 +0100 Subject: [PATCH 3/3] Flip the toggle to a negative checkbox so the UI matches reality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review caught the classic new-setting confusion: "Enable the embedded editor" rendered UNTICKED next to "Default: Yes", because a positive default only materialises when upgradesettings runs — until then the stored value is unset and the box contradicts the actual behaviour. Replace editorenabled with editordisabled (default 0, unticked): now the unset config and the unticked checkbox both mean "editing on", on every site and at every moment, with no dependency on when settings were saved. Same pattern as the plugin's existing stylesblockimport toggle. Default behaviour is unchanged: the embedded editor stays enabled unless an admin ticks the box. --- docs/EMBEDDED_EDITOR.md | 5 +++-- docs/USER_GUIDE.md | 4 ++-- lang/ca/exelearning.php | 4 ++-- lang/en/exelearning.php | 4 ++-- lang/es/exelearning.php | 4 ++-- lang/eu/exelearning.php | 4 ++-- lang/gl/exelearning.php | 4 ++-- lib.php | 9 +++++---- .../DEC-0066-interruptor-global-editor-embebido.md | 9 ++++++--- settings.php | 13 ++++++++----- tests/lib_helpers_test.php | 10 +++++----- 11 files changed, 39 insertions(+), 31 deletions(-) diff --git a/docs/EMBEDDED_EDITOR.md b/docs/EMBEDDED_EDITOR.md index b9d0d5c..5cd05cd 100644 --- a/docs/EMBEDDED_EDITOR.md +++ b/docs/EMBEDDED_EDITOR.md @@ -50,8 +50,9 @@ 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/editorenabled` admin setting (default on; an unset config counts as -on). `exelearning_embedded_editor_enabled()` combines the toggle with bundle +`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()`. diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index ad9be66..5f7b13d 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -242,10 +242,10 @@ from an official release ZIP. ### Disabling in-place editing -If your site only plays packages authored elsewhere, untick **Enable the +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. Tick it again at any time to +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 diff --git a/lang/ca/exelearning.php b/lang/ca/exelearning.php index 0a620ae..44cf772 100644 --- a/lang/ca/exelearning.php +++ b/lang/ca/exelearning.php @@ -51,9 +51,9 @@ $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['editorenabled'] = '~Activa l\'editor integrat'; -$string['editorenabled_desc'] = '~Si està activat, els qui poden gestionar una activitat veuen el botó «Edita amb eXeLearning» i poden crear o modificar paquets des de Moodle. Si està desactivat, el connector funciona com un reproductor pur: pujar i mostrar paquets .elpx continua funcionant, però l\'editor s\'amaga i els seus punts finals rebutgen les peticions.'; $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'; diff --git a/lang/en/exelearning.php b/lang/en/exelearning.php index 14a0c74..3a204ff 100644 --- a/lang/en/exelearning.php +++ b/lang/en/exelearning.php @@ -47,9 +47,9 @@ $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['editorenabled'] = 'Enable the embedded editor'; -$string['editorenabled_desc'] = 'When enabled, users who can manage an activity see the "Edit with eXeLearning" button and can author packages in place. When disabled, the plugin works as a pure player: uploading and serving .elpx packages keeps working, but the editor is hidden and its endpoints refuse requests.'; $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'; diff --git a/lang/es/exelearning.php b/lang/es/exelearning.php index 8df5a5d..62789e7 100644 --- a/lang/es/exelearning.php +++ b/lang/es/exelearning.php @@ -51,9 +51,9 @@ $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['editorenabled'] = '~Activar el editor integrado'; -$string['editorenabled_desc'] = '~Si está activado, quienes pueden gestionar una actividad ven el botón «Editar con eXeLearning» y pueden crear o modificar paquetes desde Moodle. Si está desactivado, el plugin funciona como un reproductor puro: subir y mostrar paquetes .elpx sigue funcionando, pero el editor se oculta y sus endpoints rechazan las peticiones.'; $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'; diff --git a/lang/eu/exelearning.php b/lang/eu/exelearning.php index 165b894..2869cfd 100644 --- a/lang/eu/exelearning.php +++ b/lang/eu/exelearning.php @@ -51,9 +51,9 @@ $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['editorenabled'] = '~Aktibatu editore txertatua'; -$string['editorenabled_desc'] = '~Aktibatuta badago, jarduera bat kudea dezaketenek «Editatu eXeLearning-ekin» botoia ikusten dute eta paketeak Moodletik sortu edo aldatu ditzakete. Desaktibatuta badago, pluginak erreproduzitzaile huts gisa funtzionatzen du: .elpx paketeak igotzea eta erakustea funtzionatzen jarraitzen du, baina editorea ezkutatu egiten da eta haren amaiera-puntuek eskaerak ukatzen dituzte.'; $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'; diff --git a/lang/gl/exelearning.php b/lang/gl/exelearning.php index e267c0c..99c75d1 100644 --- a/lang/gl/exelearning.php +++ b/lang/gl/exelearning.php @@ -51,9 +51,9 @@ $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['editorenabled'] = '~Activar o editor integrado'; -$string['editorenabled_desc'] = '~Se está activado, quen pode xestionar unha actividade ve o botón «Editar con eXeLearning» e pode crear ou modificar paquetes desde Moodle. Se está desactivado, o complemento funciona como un reprodutor puro: subir e amosar paquetes .elpx segue funcionando, pero o editor ocúltase e os seus puntos de acceso rexeitan as peticións.'; $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'; diff --git a/lib.php b/lib.php index bf9f001..dae06b3 100644 --- a/lib.php +++ b/lib.php @@ -1059,8 +1059,10 @@ function exelearning_get_embedded_editor_index_source(): ?string { * * 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 `editorenabled` setting. An unset config (a site that never - * saved the settings page) counts as enabled, preserving the default. + * 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. Activities keep @@ -1070,8 +1072,7 @@ function exelearning_get_embedded_editor_index_source(): ?string { * @return bool True when the editor is bundled, valid and not disabled. */ function exelearning_embedded_editor_enabled(): bool { - $setting = get_config('exelearning', 'editorenabled'); - if ($setting !== false && !$setting) { + if (!empty(get_config('exelearning', 'editordisabled'))) { return false; } return \mod_exelearning\local\embedded_editor_source_resolver::is_available(); diff --git a/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md b/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md index 26445ee..a1d7a76 100644 --- a/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md +++ b/research/decisiones/adr/DEC-0066-interruptor-global-editor-embebido.md @@ -36,7 +36,7 @@ Se valoraron dos mecanismos: ## Decisión -**Ajuste global `exelearning/editorenabled`** (checkbox, activado por defecto), por ser lo +**Ajuste global `exelearning/editordisabled`** (checkbox negativo, desmarcado por defecto = edición activa), por ser lo más sencillo y lo más parecido a cómo los plugins de actividad de Moodle apagan funcionalidades completas de sitio (los ajustes `scorm_*` de mod_scorm, los toggles de características en admin settings). La capability se descarta como mecanismo primario: el @@ -52,8 +52,11 @@ Alcance del interruptor: de error explicativa y `editor/save.php` con `exelearning_require_embedded_editor_enabled()` (ocultar el botón no es un control de acceso); `editor/static.php` deja de servir assets (ya pasaba por el helper). La subida y reproducción de `.elpx` no cambian. -- **Encendido** (default): comportamiento actual. Config ausente (sitio que nunca guardó - ajustes) cuenta como encendido para no cambiar el comportamiento en upgrade. +- **Encendido** (default): comportamiento actual. El checkbox es **negativo a propósito** + ("Desactivar el editor integrado", patrón de `stylesblockimport`): así config ausente y + casilla desmarcada significan lo mismo (edición activa) y se evita la confusión de un + default positivo que se muestra desmarcado hasta que `upgradesettings` lo materializa + (observada en la revisión del PR). ## Consecuencias diff --git a/settings.php b/settings.php index 0bd422a..2a06bce 100644 --- a/settings.php +++ b/settings.php @@ -57,7 +57,10 @@ // Embedded editor (DEC-0066): a single site-wide toggle. The editor itself // ships inside the release package (DEC-0065) and has no runtime management; // this switch lets a site use the plugin as a pure .elpx player — uploads - // keep working, only in-place editing is hidden and refused. + // keep working, only in-place editing is hidden and refused. The checkbox is + // negative (disable) on purpose: unset config and unticked box then agree, + // avoiding the "Default: Yes but unticked" confusion before upgradesettings + // materialises a positive default (same pattern as stylesblockimport). $settings->add(new admin_setting_heading( 'mod_exelearning/editorsettingsheading', get_string('editorsettings', 'mod_exelearning'), @@ -65,10 +68,10 @@ )); $settings->add(new admin_setting_configcheckbox( - 'exelearning/editorenabled', - get_string('editorenabled', 'mod_exelearning'), - get_string('editorenabled_desc', 'mod_exelearning'), - 1 + 'exelearning/editordisabled', + get_string('editordisabled', 'mod_exelearning'), + get_string('editordisabled_desc', 'mod_exelearning'), + 0 )); // Defined styles management (upload / enable / disable / lockdown). diff --git a/tests/lib_helpers_test.php b/tests/lib_helpers_test.php index 35c8c09..faaaa95 100644 --- a/tests/lib_helpers_test.php +++ b/tests/lib_helpers_test.php @@ -165,15 +165,15 @@ public function test_embedded_editor_wrappers_reflect_bundle(): void { $this->assertSame($dir . '/index.html', exelearning_get_embedded_editor_index_source()); $this->assertSame($dir, exelearning_get_embedded_editor_local_static_dir()); - // With no editorenabled config at all (fresh site), editing stays on and + // With no editordisabled config at all (fresh site), editing stays on and // the endpoint guard passes. exelearning_require_embedded_editor_enabled(); } /** - * The site-wide editorenabled toggle disables embedded editing even when a - * valid bundle is present (DEC-0066): the button helper reports false and the - * editor endpoints' guard throws. + * The site-wide editordisabled toggle switches embedded editing off even when + * a valid bundle is present (DEC-0066): the button helper reports false and + * the editor endpoints' guard throws. */ public function test_admin_toggle_disables_embedded_editing(): void { global $CFG; @@ -184,7 +184,7 @@ public function test_admin_toggle_disables_embedded_editing(): void { file_put_contents($dir . '/index.html', 'x'); $CFG->mod_exelearning_bundled_editor_dir = $dir; - set_config('editorenabled', 0, 'exelearning'); + set_config('editordisabled', 1, 'exelearning'); $this->assertFalse(exelearning_embedded_editor_enabled()); $this->expectException(\moodle_exception::class);