Guía de cambios
Procedimientos para modificar y ampliar la plataforma - páginas, reglas, correos, tours, scripts, parches, plugins de terceros, versiones, pruebas locales y commits.
Procedimientos que se desprenden de cómo está construido el código. Antes de cambiar algo, revise la página del subsistema correspondiente en esta sección.
Ciclo general
Cambiar el código
En plugins/ (plugins propios) o infrastructure/moodle/ (arranque, configuración, parches). Mantenga las convenciones: comentarios en español, encabezado DELTA-APP LMS — …, idempotencia, try/catch en hooks y observadores, acciones por POST con sesskey.
Si es un archivo nuevo, agregarlo a .dockerignore
.dockerignore es una lista blanca. Un script nuevo de infrastructure/moodle/ necesita su línea !infrastructure/moodle/<archivo>; un plugin propio nuevo necesita !plugins/<carpeta>/ y !plugins/<carpeta>/**. Sin eso, no llega a la imagen.
Probar en local
Con la imagen reconstruida (docker compose up -d --build) o copiando los archivos a los contenedores (ver Probar en local).
Revisar el registro del arranque
docker compose logs -f moodle: los pasos de moodle-config.sh que fallan aparecen con !!! ERROR (críticos) o ! Aviso (cosméticos), y el super administrador los ve en Moodle.
Commit y despliegue
Ver Commits. En producción, Coolify reconstruye la imagen al desplegar (Operación).
Probar en local
El código está horneado en la imagen: los contenedores delta-lms_app y delta-lms_cron tienen cada uno su propia copia de /var/www/html (solo moodledata es un volumen compartido). La forma canónica de probar es reconstruir:
docker compose up -d --build
docker compose logs -f moodlePara iterar más rápido se pueden copiar archivos a los contenedores en marcha. Hay que copiarlos a los dos (el sitio y el cron), porque el cron ejecuta tareas y observadores con su propia copia del código y, si su versión no coincide con la de la base de datos, informa "upgrade pending":
# Plugin propio (ejemplo: local_deltamodules)
docker cp plugins/local_deltamodules/. delta-lms_app:/var/www/html/local/deltamodules/
docker cp plugins/local_deltamodules/. delta-lms_cron:/var/www/html/local/deltamodules/
docker exec delta-lms_app chown -R www-data:www-data /var/www/html/local/deltamodules
docker exec delta-lms_cron chown -R www-data:www-data /var/www/html/local/deltamodules
# Si se subió version.php
docker exec delta-lms_app php /var/www/html/admin/cli/upgrade.php --non-interactive
# Siempre
docker exec delta-lms_app php /var/www/html/admin/cli/purge_caches.phpPara un script de configuración:
docker cp infrastructure/moodle/configure-tours.php delta-lms_app:/configure-tours.php
docker exec delta-lms_app php /configure-tours.phpLo copiado a mano se pierde al recrear los contenedores (la imagen manda). Antes de dar un cambio por terminado, pruébelo con docker compose up -d --build, que además ejecuta verify-patches.php y todo moodle-config.sh. En los contenedores locales, $CFG->noemailever no está fijado: si .env define MOODLE_SMTP_HOST, los correos salen de verdad (use MOODLE_MAIL_DIVERT_TO para desviarlos a una dirección de prueba).
Los nombres de contenedor dependen de COMPOSE_PROJECT_NAME (por defecto delta-lms).
Subir la versión de un plugin
Moodle solo ejecuta db/upgrade.php y vuelve a leer db/access.php, db/events.php, db/tasks.php, db/services.php, db/hooks.php y db/messages.php cuando la versión del plugin aumenta.
- En
version.php, aumente$plugin->versioncon el formatoAAAAMMDDXX(por ejemplo2026100700→2026100800). - Si cambia la base de datos: edite
db/install.xml(instalaciones nuevas) y agregue un bloque endb/upgrade.phpcon la nueva versión, que compruebetable_exists/field_existsantes de crear y termine conupgrade_plugin_savepoint(true, <versión>, 'local', 'deltamodules'). La versión del último paso no puede ser mayor que la deversion.php. - Si otro plugin depende de la nueva versión, actualice su
$plugin->dependencies(por ejemplo, losdataformat_delta*dependen delocal_deltamodules >= 2026092404). - Al arrancar,
entrypoint.shejecutaupgrade.php; si falla, el despliegue se detiene.
Agregar una página
- Cree
plugins/local_deltamodules/<pagina>.phpconrequire(__DIR__ . '/../../config.php');,require_login()(oadmin_externalpage_setup()si va en Administración) yrequire_capability()con la capacidad adecuada. - Use
ui::css(), las clasesdp-*y, si va dentro de Administración del sitio,ui::crumbs([...])antes de$OUTPUT->header(). - Acciones que cambian datos: POST con
sesskey(ui::post_button()),confirm_sesskey()al procesar, confirmación condata-delta-confirmy redirección con mensaje. - Si debe aparecer en Administración del sitio, regístrela en
settings.phpconadmin_externalpagey agregue su nombre aadmin_access::ALLOWEDsi deben verla perfiles que no son super administradores. - Si debe ir en la barra superior, agréguela en
hook_callbacks::primary_extend, su clave enPRIMARY_ORDERy su ruta en el mapa de pestaña activa deprimary_order. - Si debe tener tour, ver Agregar un recorrido guiado.
Agregar una regla de la Academia
- Agregue la clave a
rules::definitions()congroup,label,help,default(su tipo define el tipo del valor) yoptions. Si al cambiarla debe imponerse a lo configurado a mano, agregue'force' => truey trate la clave enstandard::apply_quiz()o donde corresponda. - Lea el valor con
rules::get('<clave>', $courseid)en el lugar que la aplica (standard,academy, servicios web…). - Si su cambio debe recalcular algo en los módulos, trátela en
standard::apply_course()(reciba la clave en$force). - Si debe verse en el formulario de las actividades, agréguela a la lista de
local_deltamodules_coursemodule_definition_after_dataenlib.php. - Si el manual la menciona, actualice el texto en
classes/manual.php.
No hace falta tocar la base de datos: la regla general se guarda en la configuración (rule_<clave>) y las propias de cada módulo en local_deltamodules_rule.
Agregar un tipo de correo
- Agregue el tipo a
mailer::KINDS(clave => [nombre, cuándo se envía]): así aparece en la página de correo con su interruptor. - Cree un método que arme el contenido con los ayudantes (
greet,p,badge,button,note,closing) y llame aself::queue($usuario_o_correo, '<clave>', $asunto, $contenido, $opciones). Usededupesi no debe repetirse yimmediate(trueo'after') si debe salir sin esperar a la tarea. - Si lleva enlace para crear contraseña, use
self::PWLINKen el botón ypurpose(welcomeoreset; otra vigencia exige agregarla amailer::TTL). - Agregue el caso en
mailer::sample()para poder enviar un ejemplo. - Si se dispara por un evento, agregue el observador en
db/events.php(y suba la versión), contry/catchy comprobandoself::enabled()yself::kind_on(). - Si se dispara periódicamente, cree una tarea en
classes/task/y regístrela endb/tasks.php.
Agregar un recorrido guiado
- Agregue una entrada a
$TOURSeninfrastructure/moodle/configure-tours.phpconname(único, se usa para encontrarlo),description,pathmatch,stepsy, si corresponde,roles,categoryocss. - Los pasos se arman con
$u(título, contenido)(sin elemento) o$s(selector, título, contenido, ubicación). Si el selector no está en la página, el paso se salta. - Despliegue: el script crea o actualiza el tour. Para que lo vuelvan a ver quienes ya vieron una versión anterior, agregue
'major' => true.
El observador tours::started hace que cada tour se muestre una sola vez por persona sin configuración adicional.
Agregar un script de configuración
- Cree
infrastructure/moodle/<script>.phpcondefine('CLI_SCRIPT', true); require('/var/www/html/config.php');, idempotente y con mensajes cortos en la salida. - Agréguelo a
.dockerignore. - Si debe correr en cada arranque, agréguelo a
moodle-config.shconstep critico "<descripción>" php /<script>.phpostep cosmetico …, en el lugar correcto del orden (por ejemplo, después de lo que crea los datos que necesita). - Si debe correr una sola vez, use una marca:
const FLAG = '<nombre>_AAAAMMDD';, salga siget_config('local_deltamodules', FLAG)existe y al finalset_config(FLAG, time(), 'local_deltamodules'). Si conviene permitir repetirlo, acepte--force. - Nunca escriba claves en el script: léalas con
getenv()y documente la variable en Variables de entorno.
Agregar una capacidad
- Defínala en
db/access.phpdel plugin (captype,contextlevel,riskbitmask,archetypes). Sin arquetipo, nadie la recibe hasta que se le conceda. - Agregue su nombre en
lang/esylang/en(<plugin>:<capacidad>). - Concédala por perfil en
align-access-profiles-to-manual.php($ALLOW) o retírela ($PREVENT). - Suba la versión del plugin.
Guardar datos personales
- No escriba en el código datos de personas reales (cédulas, correos, contraseñas, nombres de usuario ni listas de cuentas): las reglas deben aplicarse por cargo, cohorte o perfil, y los datos se cargan desde la plataforma o desde la importación de Excel. En comentarios y ejemplos, nombre roles ("el Jefe de Talento Humano") o datos ficticios.
- Si agrega una tabla, un campo o una preferencia con datos de personas, declárelo en el proveedor de privacidad del plugin (
classes/privacy/provider.php): metadatos enget_metadata(), exportación y eliminación. Enlocal_deltamodules, agregue la tabla aOWNED(la fila es de la persona: se borra) o aAUTHORED(la persona solo figura como autora de un registro de la empresa: se desvincula con0), con sus cadenasprivacy:metadata:*enlang/esylang/en. Ver local_deltamodules → Privacidad. - Si un dato nuevo se envía al proveedor de IA, declárelo en la ubicación externa
groqdel proveedor de privacidad y, en el agente de acciones, pase el texto porpii_hide()antes de enviarlo (ver Asistente de IA).
Agregar un parche a código de terceros
- Cree
infrastructure/moodle/patch-<nombre>.phpsiguiendo el patrón de los existentes: PHP puro, comprobar que el archivo existe, salir si ya está el marcadorDELTA: …, buscar el ancla exacta,str_replace, y terminar siempre con código 0. - Mantenga el cambio mínimo; ponga la lógica en una clase de
local_deltamodules. - Agréguelo a
.dockerignorey alRUNde parches delDockerfile(con|| truesi es cosmético). - Si es importante, agregue su archivo y marcador a
$checksenverify-patches.phppara que el build falle si deja de aplicarse.
Actualizar un plugin de terceros
- En el
Dockerfile, cambie el commit de su líneagit-at(o la etiqueta degit clone --branchen los plugins de IA). Verifique que la versión nueva siga siendo compatible con Moodle 4.5. - Revise si el plugin tiene parches (ver Parches) y si cada ancla sigue existiendo en el archivo nuevo.
- Revise si las traducciones de
infrastructure/moodle/lang/es/siguen correspondiendo a sus cadenas. - Reconstruya: si un parche importante ya no se aplica,
verify-patches.phpdetiene el build. Si un parche cosmético deja de aplicarse, el build sigue, pero el registro muestra el aviso. - Pruebe el flujo que depende del plugin (chat de IA, certificados, videos).
Al actualizar Moodle, revise además los parches al núcleo (tool_policy, login, lib/classes/user.php, user/editlib.php) y el JavaScript de additionalhtmlhead, que depende de selectores de la interfaz de Moodle 4.5.
Commits
| Regla | Detalle |
|---|---|
| Idioma | Los mensajes de commit van en inglés (comentarios del código, textos de la interfaz y documentación siguen en español). |
| Estilo | Una línea descriptiva que dice qué cambia para el usuario, por ejemplo "Backups page in Moodle; keep Moodle's per-course automated backups off" o "Quiz editor with settings and custom notice; blank modules open in edit mode". |
| Qué no se sube | .env (está en .gitignore), respaldos (backups/), credenciales, tokens, datos personales ni archivos locales de trabajo. Lo que se sube queda en el historial de Git aunque luego se borre del código: sacarlo exige reescribir el historial. |
| Rama | Los commits recientes del repositorio están en main. |
Interfaz global (CSS y JavaScript)
Estilos del tema Boost Union y el CSS y JavaScript que moodle-config.sh inyecta en todas las páginas mediante additionalhtmlhead, además de los textos personalizados.
Operación y mantenimiento
Manual para el equipo de TI de la Academia Deltamontero. Cómo desplegar, configurar, respaldar, recuperar, vigilar y mantener la plataforma.