Archivos
Archivos en las dos direcciones: guardar los archivos de un sitio (un recurso, los archivos de una carpeta, una entrega, el adjunto de un mensaje, una imagen de una página o de un mensaje) en una carpeta local con la sesión de la persona, y subir archivos locales en los formularios que los admiten (un recurso de archivo o una carpeta, una entrega, un mensaje de foro, una importación de preguntas), o dentro del HTML que escribe una función (una imagen en una página, la descripción de una sección o de un curso, un mensaje). Cualquiera puede descargar lo que alcanza su sesión; subir depende del formulario, y por tanto de quién puede usarlo (un profesor que crea un recurso, un estudiante que entrega). El driver nunca abre ni interpreta los archivos: un programa recibe sus rutas.
Las firmas de abajo son las de las funciones. Cada operación es también un método del objeto Moodle sin sus dos primeros parámetros (fn(context, site, …) es moodle.fn(…)), que es como las llaman los ejemplos; las primitivas de más bajo nivel están en @falkenslab/moodle-driver/internals (consulta Bajar de nivel).
downloadFile
Guarda un archivo del sitio en una carpeta local con la sesión de la persona.
function downloadFile(context: BrowserContext, siteUrl: string, url: string, destDir: string): Promise<DownloadedFile>;
interface DownloadedFile {
path: string;
name: string;
size: number;
contentType?: string;
}
url: una URL absoluta del sitio, normalmente un enlacepluginfile.phpde otra función (losfilesde activityContent, losfilesde una entrega en calificar,submittedFilesydescriptionFilesen tareas, los adjuntos de un mensaje en foros). Los medios que el sitio sirve fuera de la ruta de Moodle valen, siempre que estén en el mismo origen.destDir: una carpeta absoluta, que se crea cuando no existe. Como método,moodle.downloadFile(url, destDir?)usa eldownloadDirdel objeto cuando no se le da una, y lanza un error sin ninguna de las dos.
Obtiene el archivo mediante la API de peticiones del contexto del navegador, que lleva las cookies de la sesión sin abrir una pestaña, siguiendo hasta 5 redirecciones. El nombre del archivo viene del Content-Disposition de la respuesta (nombres UTF-8 incluidos), si no del último segmento de la URL, reducido a un nombre simple: nunca una ruta, y sin caracteres que un nombre de archivo no puede tener. Nunca sobrescribe: cuando el nombre está ocupado guarda name (2).ext, name (3).ext… El resultado tiene la path del archivo guardado, su name (tal como se guardó), su size en bytes y el contentType de la respuesta ("application/pdf"), sin parámetros.
Errores:
- Un
MoodleError(outside-site) cuando la URL no es del origen del sitio: las cookies de la sesión no van a ningún otro sitio, así que los enlaces a otros sitios (un vídeo de YouTube, un recurso URL externo) no se pueden descargar con ella. - Un
MoodleError(invalid-input) cuandodestDirno es absoluta. NotLoggedInErrorcuando Moodle responde con su página de inicio de sesión: la sesión caducó, o el archivo necesita una sesión que la persona no tiene.- Un
MoodleError(not-found) con el estado HTTP cuando Moodle responde con un error (Moodle answered 404 for ...): el archivo no existe, o la persona no puede verlo.
fileNameFor(url, disposition) es la regla de nombres por sí sola, exportada para un programa que obtiene los archivos él mismo.
checkLocalFiles
Comprueba que los archivos locales que subir se dan como rutas absolutas y existen.
function checkLocalFiles(files: string[]): Promise<void>;
Lanza un MoodleError para la primera ruta que no es absoluta (invalid-input: Give files as absolute paths: "brief.txt".) o no existe (not-found: There's no file at /home/ada/brief.txt.). Toda función que sube archivos la llama primero, antes de abrir ninguna página, así que una ruta errónea nunca deja un formulario a medio rellenar. Un programa puede llamarla antes, para avisar a la persona antes de empezar.
Subir en formularios
Las funciones que reciben archivos locales los reciben como rutas absolutas y los suben como lo hace "Subir un archivo" del selector de archivos: al área de borrador del formulario mediante el repositorio de subida de Moodle (repository/repository_ajax.php), y luego guardan el formulario, que los mueve a su sitio. No hace falta nada más: ni servicios web ni tokens.
| Función | Opción | Adónde van los archivos |
|---|---|---|
createActivity | files | Los archivos de un recurso de archivo o de una carpeta, o el paquete de un SCORM (actividades, SCORM). |
updateActivity | files | Archivos añadidos, quitados o sustituidos en un recurso de archivo o una carpeta (actividades). |
saveSubmission | files | La entrega de la persona, añadidos a los archivos que ya hay (tareas). |
replyToPost, startDiscussion | files | Los adjuntos del mensaje (foros). |
importQuestions | file | El archivo GIFT o Moodle XML que importar (bancos de preguntas). |
Lo que puede fallar, además de los errores de checkLocalFiles:
- El formulario no tiene área de archivos: un
MoodleErrorcon el códigonot-availabledecreateActivity(A url takes no files in its form.) o de las funciones de foros (This forum takes no attachments.), unSubmissionErrordesaveSubmission(Assignment 45 doesn't take file submissions.). - La subida falla: un
FormErrorcuyosfieldsllevan un{ field: "files", message }por archivo con la respuesta de Moodle, como un archivo por encima del límite de tamaño o de un tipo que la actividad no acepta, othe site has no upload repository enabledcuando el sitio desactivó el repositorio de subida. - Moodle rechaza el formulario al guardarlo: un
FormErrorcon los mensajes de Moodle por campo.
updateActivity cambia los archivos de un recurso de archivo o de una carpeta solo cuando su files dice qué añadir, quitar o sustituir; si no, se quedan los que hay. activitySettings los enumera. Las funciones de bajo nivel uploadToDraft, draftFiles, deleteDraftFile y removeFromDraft funcionan con cualquier formulario abierto con openForm (consulta acceso de bajo nivel).
Incrustar archivos en texto enriquecido
Las imágenes y los archivos pueden ir dentro del HTML que escribe una función, como hace una persona que inserta una imagen en el editor de Moodle. El HTML nombra cada archivo por su nombre de archivo, en un src o un href (<img src="diagram.png" alt="El bucle">, <a href="notes.pdf">los apuntes</a>), y embed da sus rutas locales absolutas:
| Función | Adónde va el HTML |
|---|---|
createActivity, updateActivity | description, content o un editor en settings ("activityeditor[text]") (actividades). |
addSection, editSection | El summary de la sección (actividades). |
startDiscussion, replyToPost, editPost | El message del mensaje (foros). |
updateCourse | El summary del curso (cursos). |
Cada editor de un formulario de Moodle guarda los archivos que muestra su texto en un área de borrador propia (el campo <editor>[itemid] junto a <editor>[text]), no en la del gestor de archivos. El driver sube cada archivo al área de borrador del editor cuyo HTML lo nombra (sustituyendo uno del mismo nombre que haya ahí) y reescribe ese src o href con la URL de borrador del archivo (draftfile.php/<user context>/user/draft/<item>/diagram.png), que es lo que escribe el editor; al guardar, Moodle guarda los archivos con el texto y convierte las URL en las suyas, así que los lectores reciben URL de pluginfile.php. Los lectores las encuentran en las images de activityContent y en las images de un mensaje de foro, y las descargan con downloadFile. Comprobado empíricamente en Moodle 5.2: la imagen de una página y la de una respuesta se muestran al estudiante y se descargan con su sesión.
Un nombre es un src o un href sin esquema (https:, mailto:) que no empieza por /, # ni ?; los escapes se interpretan (my%20diagram.png nombra my diagram.png). Antes de enviar nada, un MoodleError (invalid-input) rechaza un archivo que incrustar que ningún texto nombra, un nombre que el texto usa sin archivo dado (otro enlace relativo cuenta: da la URL completa en su lugar) y dos archivos con el mismo nombre; los errores de checkLocalFiles van primero. Un editor que no admite archivos es un MoodleError (not-available). Sin embed, el HTML se envía tal como se da.
// Una página con un diagrama, y una respuesta que muestra una captura de pantalla.
await moodle.createActivity({ courseId: 3, sectionId: 21, type: "page", name: "Bucles", content: '<p>Cómo se ejecuta un bucle:</p><p><img src="loop.png" alt="Un bucle"></p>', embed: ["/home/ada/loop.png"] });
await moodle.replyToPost(1204, { message: '<p>Este es el error que me sale: <img src="error.png" alt="El error"></p>', embed: ["/home/ada/error.png"] });
Ejemplo
import { checkLocalFiles } from "@falkenslab/moodle-driver";
// `moodle` es el objeto de createMoodle(), como en las páginas de primeros pasos.
// Subir los apuntes del tema como una carpeta, oculta hasta que el profesor la revise.
const files = ["/home/ada/unit-4/recursion.pdf", "/home/ada/unit-4/exercises.pdf"];
await checkLocalFiles(files);
const created = await moodle.createActivity({ courseId: 3, sectionId: 21, type: "folder", name: "Apuntes del tema 4", files });
console.log(created.changed);
// Guardar los archivos adjuntos a la descripción de una tarea.
const assignment = await moodle.studentAssignment(45);
for (const file of assignment.descriptionFiles) {
const saved = await moodle.downloadFile(file.url, "/home/ada/downloads");
console.log(`${saved.path} (${saved.size} bytes, ${saved.contentType ?? "tipo desconocido"})`);
}
Categorías de datos
downloadFile es course-content y others-personal: la misma función guarda los archivos de un curso y los de una entrega o un mensaje, así que un programa que no debe alcanzar el trabajo de otros comprueba de dónde vino la URL. checkLocalFiles y fileNameFor son none; uploadToDraft, draftFiles, deleteDraftFile y removeFromDraft son any. Las funciones que suben archivos se clasifican por lo que hacen: createActivity y updateActivity son course-content, saveSubmission es own, replyToPost, startDiscussion y editPost son communication (editPost también others-write, la edición de un moderador), importQuestions es course-content. Consulta categorías de datos.