Parches
Los once parches que se aplican al código de Moodle y de plugins de terceros durante la construcción de la imagen, por qué existen y cómo los verifica verify-patches.php.
Algunos comportamientos necesitan cambiar código que no es propio. Para no mantener copias modificadas de Moodle ni de los plugins, se usan parches en tiempo de construcción: scripts PHP puros (sin cargar Moodle) en infrastructure/moodle/patch-*.php que buscan un texto exacto (el ancla) y lo reemplazan con str_replace.
Reglas de los parches
| Regla | Detalle |
|---|---|
| Mínimos | Una o pocas líneas; si hace falta lógica, el parche llama a una clase de local_deltamodules. |
| Idempotentes | Cada parche busca primero su marcador (normalmente un comentario DELTA:) y, si ya está, no hace nada. |
| No rompen el build | Si el archivo no existe o el ancla no aparece, escriben un aviso en la salida de error y terminan con código 0. |
| Verificados | verify-patches.php comprueba al final que los importantes quedaron aplicados y, si falta uno, el build falla. |
| Orden fijo | El Dockerfile los ejecuta en un solo RUN, en el orden de la tabla de abajo. |
php /patch-ai-role-injection.php \
&& php /patch-ai-agent.php \
&& php /patch-policy-consentpage.php \
&& php /patch-forgot-password.php \
&& php /patch-welcome-name.php \
&& php /patch-ai-chat-everywhere.php \
&& php /patch-cedula-field.php \
&& php /patch-cert-completion-gate.php \
&& (php /patch-ai-chat-copy.php || true) \
&& (php /patch-ai-chat-ui.php || true) \
&& (php /patch-video-completion.php || true) \
&& php /verify-patches.phpLos tres últimos son cosméticos: van con || true y no detienen el build aunque fallen.
Detalle de cada parche
patch-ai-role-injection.php
| Elemento | Detalle |
|---|---|
| Archivo | local/ai_manager/classes/external/submit_query.php |
| Ancla | El bloque que decodifica $options con json_decode($options, true) |
| Cambio | Después del bloque, si $options es un arreglo y existe la clase, $options = \local_deltamodules\ai_role_injector::inject($options); |
| Para qué | El chat conoce el perfil del usuario (Asistente de IA). Sin el parche, el chat funciona sin esa información. |
| Marcador verificado | local_deltamodules\ai_role_injector |
patch-ai-agent.php
| Elemento | Detalle |
|---|---|
| Archivo | local/ai_manager/classes/external/submit_query.php |
| Ancla | El bloque insertado por el parche anterior (por eso debe correr después) |
| Cambio | $deltaagent = \local_deltamodules\ai_agent::maybe_handle($prompt, $options, $contextid); y, si no es null, return ['code' => 200, 'string' => 'ok', 'result' => $deltaagent]; |
| Para qué | Agente de acciones con permisos reales |
| Marcador verificado | local_deltamodules\ai_agent |
patch-policy-consentpage.php
| Elemento | Detalle |
|---|---|
| Archivos | admin/tool/policy/classes/output/page_agreedocs.php y admin/tool/policy/templates/page_agreedocs.mustache |
| Cambios | 1) Marca como vistas las políticas vigentes en la caché, para ir directo a la página de consentimiento (el texto completo queda en una ventana modal). 2) Quita el mensaje de error "debe aceptar" de la primera carga. 3) Quita de la plantilla la línea "acceda al texto completo". 4) Quita los encabezados "Aceptar" y "Por favor, acepte las siguientes políticas". |
| Para qué | Aceptación del aviso de datos en una sola página, más limpia |
| Marcador verificado | DELTA: ir directo a la página de consentimiento (en el PHP) |
patch-forgot-password.php
| Elemento | Detalle |
|---|---|
| Archivo | login/forgot_password_form.php |
| Cambio | Quita la sección "Buscar por dirección email" (encabezado, campo y botón) |
| Para qué | La recuperación nativa queda solo por nombre de usuario. En la práctica el enlace del login apunta a la página propia local/candidate_enrollment/recover.php (forgottenpasswordurl). |
| Marcador verificado | DELTA: sin búsqueda por email |
patch-welcome-name.php
| Elemento | Detalle |
|---|---|
| Archivo | lib/classes/user.php |
| Ancla | return new lang_string($messagekey, 'core', $namefields); |
| Cambio | Agrega a $namefields las claves firstfirstname y firstlastname (primera palabra del nombre y del apellido) |
| Para qué | El saludo del Área personal muestra "primer nombre + primer apellido"; se combina con las cadenas welcomeback y welcometosite que redefine customize-strings.php |
| Marcador verificado | firstfirstname |
patch-ai-chat-everywhere.php
| Elemento | Detalle |
|---|---|
| Archivo | local/ai_manager/classes/local/userinfo.php |
| Cambio | get_default_scope() devuelve siempre SCOPE_EVERYWHERE (antes, el nivel básico recibía "solo cursos") |
| Para qué | Un colaborador nuevo ve el chat flotante en todas las páginas, respetando su cuota |
| Marcador verificado | DELTA: chat disponible en todas las páginas |
patch-cedula-field.php
| Elemento | Detalle |
|---|---|
| Archivo | user/editlib.php |
| Cambio | Agrega un campo idnumber con la etiqueta "Cédula", maxlength 10 y obligatorio, en la sección General (después del correo), y quita la definición original de "Número de ID" de la sección Opcional |
| Para qué | La cédula es el identificador estable de cada persona y base de la recuperación de contraseña. Solo afecta al formulario de usuario. |
| Marcador verificado | DELTA: Cédula |
patch-cert-completion-gate.php
| Elemento | Detalle |
|---|---|
| Archivo | mod/customcert/view.php |
| Cambios | 1) Calcula $deltacomplete con info_module::is_available(…, false, …) (sin el atajo de gestores; el super administrador siempre true; ante un error, true). 2) Muestra el botón de descarga solo si $canreceive && $deltacomplete. 3) Con downloadown y sin completar, redirige a la vista. |
| Para qué | Candado real del certificado propio, también para gestores (Certificados) |
| Marcador verificado | $deltacomplete |
patch-ai-chat-copy.php (cosmético)
| Elemento | Detalle |
|---|---|
| Archivos | blocks/ai_chat/amd/src/helper.js y blocks/ai_chat/amd/build/helper.min.js |
| Cambio | Reemplaza navigator.clipboard.writeText(textToCopy) por una versión que, fuera de un contexto seguro, copia con un textarea y document.execCommand('copy') |
| Para qué | El botón copiar fallaba al entrar por http://IP |
patch-ai-chat-ui.php (cosmético)
| Elemento | Detalle |
|---|---|
| Archivos | blocks/ai_chat/classes/output/renderer.php, blocks/ai_chat/templates/floatingbutton.mustache, blocks/ai_chat/classes/external/get_conversationcontext_limit.php |
| Cambios | 1) El renderizador agrega deltafirstname (primer nombre) y deltaailimit (límite diario según el nivel del usuario). 2) La plantilla agrega un span oculto data-delta-aichat con contextid, userid, nombre y límite. 3) El contexto por defecto pasa de 5 a 50 mensajes. |
| Para qué | El JavaScript propio del chat (saludo, contador, borrar una conversación) y más memoria de la conversación |
patch-video-completion.php (cosmético)
| Elemento | Detalle |
|---|---|
| Archivos | mod/interactivevideo/amd/build/viewannotation.min.js (y, si coincide, amd/src/viewannotation.js) |
| Cambio | Quita la llamada a fireConfetti() y al aviso "congratulationsyoucompletethisactivity" |
| Para qué | El video solo se marca como hecho, sin confeti ni anuncio |
verify-patches.php
Revisa ocho parches importantes buscando su marcador en el archivo parcheado:
| Comprobación | Archivo | Marcador |
|---|---|---|
| Perfil del usuario en el chat de IA | local/ai_manager/classes/external/submit_query.php | local_deltamodules\ai_role_injector |
| Asistente de gestión del chat de IA | local/ai_manager/classes/external/submit_query.php | local_deltamodules\ai_agent |
| Política del sitio en una sola página | admin/tool/policy/classes/output/page_agreedocs.php | DELTA: ir directo a la página de consentimiento |
| "¿Olvidó su contraseña?" sin correo | login/forgot_password_form.php | DELTA: sin búsqueda por email |
| Saludo con nombre y apellido | lib/classes/user.php | firstfirstname |
| Chat de IA en todas las páginas | local/ai_manager/classes/local/userinfo.php | DELTA: chat disponible en todas las páginas |
| Campo Cédula en el perfil | user/editlib.php | DELTA: Cédula |
| Certificado solo con el módulo completo | mod/customcert/view.php | $deltacomplete |
Si falta alguno, escribe la lista en la salida de error y termina con código 1: el build se detiene y el error se ve en Coolify. Los parches cosméticos no se verifican.
Como Moodle y casi todos los plugins están fijados a un commit, un parche solo deja de aplicarse cuando alguien cambia esa versión. Al actualizar Moodle o un plugin parcheado, revise que cada ancla siga existiendo en el archivo nuevo (ver Guía de cambios).
Plugins de terceros
Moodle y los plugins de terceros incluidos en la imagen, con su versión o commit fijado, los ajustes de instalación y los parches que se les aplican.
Scripts de configuración
Todos los scripts de infrastructure/moodle - arranque, configuración que corre en cada despliegue (con su clasificación crítica o cosmética), scripts de provisionamiento y lectores en Python.