feat: aplicación Documentate bajo /documentate/ (armazón y primeras vistas) - #280
Merged
Conversation
Contributor
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
First slice of Documentate as an application, following the shell pattern of the Registro de Visitas app: one WordPress page (created idempotently at /documentate/) carries the [documentate_app] shortcode; the shell paints its own header with the institutional mark and role chip, a tab bar, the sheet and a one-line footer, and the stylesheet hides the theme chrome under body.documentate-app with the same visual vocabulary (shield-blue primary, tonal chips, pill buttons). Views in this slice, resolved from query arguments under the single URL: - List (my documents / all documents): status counters, status filter chips and document rows with type, updated date and status chip. Scope rules mirror the admin list: administrators see everything, a scoped user their category tree, an unscoped restricted user nothing. - Detail: read-only field summary with a status chip and a link into the wp-admin editor, guarded by edit_post (scope applies). - New document: type + name form that creates a locked-type draft and redirects to the editor; the scope fallback files it under the creator's area. Access is gated to logged-in users with edit_posts; visitors get a sign-in notice. Spanish translations added for every new string.
App - New edit view (vista=editar) that reuses the sections metabox and saves through wp_update_post, so the workflow, content writer and meta saver run exactly as in wp-admin; a draft can be sent for review from the app. - "Documentate" node in the admin bar linking to /documentate/. - Request guards of the create/save handlers extracted into small helpers (nonce, permission, posted fields, redirect) to keep NPath complexity under the PHPMD threshold flagged by code scanning. Dev tooling - scripts/mu-plugins/documentate-dev-tools.php: seeds test users for every profile (admin, área editor, gestora, subscriber) and shows a profile switcher, wired into wp-env (mu-plugins mapping + User Switching plugin) and the Playground blueprint. - Demo seeder: get_demo_document_ids() looks the documents up directly and falls back to the admin author, so scoped users no longer hide them. Fixes - TinyMCE filters no longer call get_current_screen() on the front end (fatal when the app rendered a rich field); the app page is detected via Documentate_App_Shell::is_app_page(). Tests & docs - tests/e2e/specs/documentate-app.spec.js covers admin bar, create/save/ send flow, editor scope rules, subscriber and visitor gates; shared WP-CLI/login helpers in tests/e2e/fixtures/site.js. - Unit tests for the app handlers, the TinyMCE wiring on the app page and the demo seeder fallback. - Testing skill (.agents/skills/testing, .claude/skills/testing) and the AGENTS.md rule: coverage never below 90 % and no tricks to inflate it; codecov.yml targets updated accordingly.
- Documentate_Roles: capacidad documentate_gestionar (editor + administrador), etiquetas de rol; se retira en uninstall. - Documentate_Estados: estado en_gestion, post states, sin Quick Edit. - Documentate_Transiciones: tabla de transiciones por estado y rol, motivo obligatorio al devolver, marca «devuelto», eventos; regla 0 en el workflow para que ningún guardado salte la tabla (incluido autosave y papelera). - Bloqueos por rol: gestión edita en gestión, el área solo borradores. - Documentate_Actividad (eventos y comentarios) y Documentate_Documento (nombre interno, anotaciones, devuelto, adjunto, prefijo del tipo). - Ámbito: gestión ve los documentos que han entrado en el circuito. - Notificaciones en español para gestión, devoluciones y aprobación. - Metabox de gestión extraído a Documentate_Workflow_Metabox; motivo en wp-admin; guardia en Makefile contra dos PHPUnit simultáneos. - Tests unitarios para todo lo anterior.
- Atributo rol='gestion' en los marcadores (con herencia en bloques y subbloques), propagado por el extractor y el conversor de esquema. - Documentate_Campos_Rol: qué campos ve cada rol y si un tipo pasa por gestión documental; el área no ve los campos oficiales y tampoco puede escribirlos aunque los envíe. - La metabox de secciones agrupa «Datos oficiales · los completa gestión documental» y marca esas filas. - propuestagasto.odt: introducción y Bloque I del área; Bloque II (importe, partida, proveedores, conceptos) de gestión, con títulos limpios. resolucion.odt: nº, fecha, expediente y órgano firmante nuevos y articulado como campos de gestión. - Tipos de documento: prefijo (RES, PG, CONV…) y «Pasa por gestión documental», con distintivo de rol en el esquema. - documentate-calculos.js: totales por concepto, bruto, IGIC/IRPF y resumen de la propuesta calculados solos. - Tests unitarios, de generación y jest para todo lo anterior.
App - Bandejas: «Mis documentos» para el área, «Para revisar» para gestión (todo lo que ha entrado en el circuito) y «Para revisar» / «Todos» para administración, con contadores, chips de estado, filtro por área, documentos devueltos marcados con su motivo y acciones por rol. - Ficha: avisos por estado, datos del área y datos oficiales según el rol, fichero adjunto, actividad con comentarios y carril con el recorrido del documento, acciones y descargas. - Editor: nombre interno con el prefijo del tipo, título oficial, campos por rol, anotaciones internas de gestión, zona de arrastre para el fichero (PDF, ODT o DOCX) y botones de transición. - Diálogos: confirmación antes de enviar o aprobar y motivo obligatorio al devolver, con alternativa sin JavaScript. - Previsualizar PDF y descargar PDF, ODT o DOCX desde la app, reutilizando la maquinaria de wp-admin (también en Playground). - La app ocupa todo el ancho de la ventana, como Registro de Visitas. Refuerzos de seguridad - El área no puede escribir campos de gestión aunque los envíe, ni cambiando el tipo ni inyectando post_content. - Subida de ficheros validada (tipo, tamaño, permisos) antes de sustituir el adjunto existente. Tests unitarios, jest y E2E actualizados.
- Nombre interno bajo el título, columna en el listado y metabox de anotaciones internas (solo gestión y administración) y de actividad, en una clase aparte para no engordar la metabox de secciones. - Documentate_Demo_App: doce documentos de ejemplo repartidos por tipo, área, autor y estado (borrador, devuelto por gestión, devuelto por administración, en gestión, en revisión, aprobado y archivado), con campos rellenos según quién los haya tocado, adjuntos, comentario y actividad fechada; idempotente y sin enviar correos. - scripts/seed-demo-app.php para volver a sembrar desde WP-CLI. - Herramientas de desarrollo: etiquetas de cuenta por rol.
- documentate-app-flujo.spec.js: el ciclo entero (el área crea, adjunta y envía; gestión revisa y devuelve con motivo; el área corrige y reenvía; gestión pasa a administración; administración aprueba), con el rechazo del motivo vacío y la actividad resultante. - documentate-app-roles.spec.js: pestañas, campos y ámbito por rol. - documentate-app-export.spec.js: previsualizar y descargar desde la app. - Helpers compartidos en tests/e2e/fixtures (PDF de prueba, capacidad de gestión, alta y borrado de documentos con sus adjuntos) y cerrojo para que las llamadas concurrentes a WP-CLI no corrompan la caché de wp-env. - CI: el trabajo de E2E se reparte en dos fragmentos y la conversión WASM queda tras DOCUMENTATE_E2E_WASM=1. - scripts/capturas.mjs y «make capturas»: recorre la aplicación con los tres perfiles en ordenador y móvil y deja capturas/informe.html. - Arreglo: WordPress descarta ?error= al resolver la URL, así que los avisos de error de la app se leen también de la propia petición.
A la derecha de los chips de estado, un campo «Filtrar…» esconde las filas que no coinciden según se escribe: nombre interno, título oficial, tipo y estado, sin acentos ni mayúsculas. El pie pasa a «2 de 12 documentos» y, si no queda ninguna, lo dice. Sin JavaScript el campo no aparece: los chips siguen haciendo el filtrado de verdad contra la base de datos.
El plugin es solo para España, así que las 400 llamadas de i18n pasan a literales en español (tomados del catálogo es_ES) y desaparece toda la cadena de traducción: languages/, los scripts de composer, los objetivos de Makefile, el paso de gettext en CI, las reglas de .distignore y de .gitignore, la regla WordPress.WP.I18n de PHPCS y las cabeceras Text Domain y Domain Path. Añadir una cadena ya no obliga a regenerar el .pot. También se retira WPLANG de la configuración de wp-env: el idioma del núcleo ya no cambia lo que muestra el plugin.
Seguridad - La actividad y los comentarios de un documento solo se leen con permiso sobre ese documento: dejan de aparecer en el escritorio de WordPress ni en consultas genéricas de comentarios. - Los ficheros adjuntos se sirven por un endpoint con permiso y nombre no adivinable; la carpeta de documentos generados deniega el acceso directo. - La siembra de datos de ejemplo ya no depende de una cabecera de la petición: solo del entorno declarado por WordPress (Documentate_Demo_Gate). - La capacidad de gestión pasa a un rol propio «Gestión documental» en vez de concedérsela a todos los editores del sitio, con migración desde la versión anterior. - El área tampoco puede escribir campos de gestión por la vía de los campos desconocidos, y el texto enriquecido se filtra con wp_kses_post para quien no tenga unfiltered_html. Flujo - Guardar con el nombre o el título vacíos ya no descarta los cambios. - Un documento sin tipo puede elegirlo desde la app; hasta entonces no se ofrecen botones de envío que fallarían. - Un documento devuelto a gestión ya no le pide al área que lo corrija. - «Devolver a revisión» queda registrado en la actividad y pide motivo. Pruebas y documentación - Tests jest del metabox de gestión y cobertura de JS con mínimos por módulo; los E2E vuelven a poder ejecutar la conversión WASM con «make test-e2e-wasm». - README, ARCHITECTURE, AGENTS, CLAUDE y readme.txt al día, más docs/flujo-documentos.md y docs/campos-por-rol.md para el equipo.
…gestión - blueprint.json declara el entorno (WP_ENVIRONMENT_TYPE de desarrollo y WORDPRESS_PLAYGROUND) antes de activar el plugin, que es lo que arma la siembra: sin eso, tras endurecer la puerta de los datos de ejemplo, Playground se quedaba sin usuarios ni documentos. Y siembra los doce documentos al final, para que la página de aterrizaje tenga contenido. - Los E2E dejan de conceder «documentate_gestionar» al rol editor entero (se quedaba pegado en el sitio compartido y contradecía el modelo) y nombran gestión documental a la cuenta que toca, como hace la siembra.
Semgrep marcaba cuatro lecturas de fichero cuyo identificador venía de la petición. El riesgo real era nulo (entero saneado y ruta tomada de la base de datos), pero la base de datos no es una promesa: una fila editada a mano o una migración pueden apuntar a cualquier sitio legible. Documentate_Ficheros resuelve la ruta de un adjunto y rechaza lo que no sea un fichero dentro del directorio de subidas, con el separador incluido para que «uploads-otra-cosa» no cuele. Lo usan la entrega de adjuntos de la app y las dos lecturas de plantilla del administrador de tipos. El nombre que viaja en Content-Disposition se sanea, así que tampoco puede cortar la cabecera.
- La generación de documentos ya no compone un invocable: el formato pedido elige una rama y el generador se nombra en el código. El mapa de formatos se queda como lista de lo que responde el endpoint. - Las dos lecturas del fichero adjunto llevan la anotación de Semgrep con el motivo: la petición solo nombra un documento y un adjunto (enteros), quien lee tiene que poder editar ese documento, el adjunto tiene que ser el suyo y la ruta se resuelve con realpath dentro del directorio de subidas. Comprobado con el mismo conjunto de reglas que usa CI (p/php y p/security-audit): cero hallazgos.
El editor declaraba dos listas de elementos permitidos y la del filtro tiny_mce_before_init pisaba a la del propio editor, sin las secciones de tabla: TinyMCE se comía thead, tbody y tfoot al guardar. Ahora hay una sola lista, la que define el campo, y un test lo fija. Tres E2E de wp-admin pasaban sin comprobar nada y empezaron a fallar al cambiar lo que había alrededor: - Los repetidores se buscaban con «add|agregar», que con el núcleo en inglés encontraba «Add Media», y contaban filas con una clase que no existe. Ahora usan los controles del propio plugin y la prueba de eliminar añade una fila antes de quitarla, porque el editor siempre deja una. - La previsualización esperaba a que cargara una pestaña con un PDF, que en Chromium sin interfaz puede acabar como descarga: ahora vale cualquiera de las dos y se comprueba el tipo de contenido. - Los ajustes dejaban una URL de Collabora inventada en la base de datos y engañaban a las descargas de los demás specs; ahora la restauran. Suite completa de E2E en local: 100 pasan, 8 se saltan.
Marcarlas con «hidden» no bastaba: la hoja de estilos da display de rejilla a cada fila y ese selector gana a la regla del navegador, así que la lista se quedaba igual mientras se escribía. Ahora «hidden» manda dentro de la aplicación, lo que además arregla la zona de arrastre y el aviso de lista vacía, que se veían antes de que el JavaScript los mostrara. Con un E2E que comprueba lo que se ve, no lo que está marcado.
Al arreglar el filtro salió que otras piezas dependían de reglas de estilo que las contradecían. Revisado en el navegador, no solo en el código: - En móvil, la tarjeta de acciones del editor se pintaba encima de la de estado y nunca llegaba a quedarse fija: el carril va debajo del formulario, así que ahí no cabe una barra pegada abajo. - Los diálogos de confirmación y de motivo salían pegados al borde superior, tapando la barra de administración: el tema anula los márgenes automáticos con los que se centra un diálogo. - Las ventanas de «cambios sin guardar» y de generación cuelgan del body, fuera del alcance de los estilos de la app, y mostraban botones grises del navegador en medio de una tarjeta cuidada. - El indicador de cambios sin guardar no se pintaba en la app, y sin él su guion ni siquiera se suscribía: previsualizar o descargar con el formulario sucio usaba la versión guardada sin avisar. Y el filtro rápido mejora con lo aprendido: busca también por área y persona en las bandejas de revisión y por el motivo de una devolución, y el pie ya no se come el aviso de que la bandeja tiene más documentos de los que caben en pantalla.
…ieja Documentate_App_Lista llegaba justo al límite de complejidad de clase (100, y la regla salta al alcanzarlo). Se parte en tres piezas que además se leen mejor: la bandeja decide estados, filtros y consulta; la fila se dibuja aparte; la lista se queda con el marco y la tabla. El marcado no cambia y las llamadas de fuera siguen igual. Quedan en 45, 34 y 26, con margen. Y para que esto no dependa de que alguien lo mire: - phpmd-baseline.xml recoge las 48 violaciones heredadas, casi todas del conversor OpenTBS. Encogerla vale; agrandarla no: una violación nueva se arregla partiendo el método o la clase. - «make phpmd» falla con cualquier violación fuera de esa línea base, entra en «make check» y es un paso obligatorio de CI, justo después del lint. Comprobado con una sonda: la rechaza y la nombra. - El fichero no se llama phpmd.baseline.xml a propósito. Con ese nombre PHPMD lo aplicaría solo, también al escaneo que alimenta el panel de código, y la deuda heredada desaparecería de la vista. - Los umbrales quedan escritos en AGENTS.md y CLAUDE.md: ciclomática 15, NPath 500, método 150 líneas, clase 2500 líneas y complejidad de clase 100, con cómo medir un fichero y qué hacer cuando salta.
Lo copia «npm run build:autofirma» desde node_modules en el postinstall y nadie lo edita a mano, así que sus cinco mil líneas no tienen por qué salir en los diffs ni en las revisiones. «-text» además conserva los bytes tal y como los publica el paquete. Cuando aparezca en git status tras un install, lo que toca es «git restore», salvo que la versión de package.json se haya movido de verdad.
… pestaña de tipos Las pestañas de administración quedan como las de gestión documental: la lista completa primero y la bandeja de revisión después. Así cambiar de rol no mueve las pestañas de sitio. Y fuera «Tipos y plantillas»: los tipos y sus plantillas se llevan desde el escritorio de WordPress, no desde la aplicación, así que ninguna pestaña sale ya de ella. Con eso desaparece también el marcador de pestaña externa, que no tenía otro uso. El aviso de «no hay tipos definidos» dice ahora dónde se crean de verdad.
…ribe El plugin ya decía «code, comments, docblocks in English» y el resto lo cumplía, pero la aplicación entera, que nace en este PR, llegó con ficheros, clases, métodos, variables y tests en español. Se renombra todo: 33 ficheros, 14 clases, un par de centenares de métodos y propiedades, y los nombres de los tests de PHPUnit, jest y Playwright. Queda intacto lo que es contrato y no estilo: los textos de la interfaz y sus aserciones, los correos, la actividad, las guías del equipo, las clases CSS, los atributos data, los parámetros de la URL, los campos de formulario, las claves de metadatos y opciones, las capacidades, los ganchos, el estado en_gestion, el atributo rol de las plantillas y los slugs de los tipos. Renombrar eso rompería enlaces guardados, datos ya escritos y selectores. Y la regla entera se escribe en AGENTS.md, con las tres listas —inglés, español y lo que no se renombra nunca— y el porqué de cada una, más un resumen en CLAUDE.md. Antes solo se mencionaba el inglés de los docblocks. También: Semgrep sube el SARIF sin los hallazgos que el código suprime con nosemgrep, porque GitHub los levantaba igual como alertas abiertas y el bot los comentaba en cada revisión pese a estar justificados en el propio código.
En un sitio nuevo la siembra funcionaba, pero bastaba un documento de demostración de la versión anterior para que se saltara entera: la guarda de «ya sembrado» miraba solo la marca antigua y cortaba la pasada antes de crear los doce documentos que recorren el circuito. Eso deja cualquier wp-env vivo, y cualquier instalación que se actualice, sin nombre interno, sin estados y sin devoluciones. Ahora esa guarda solo protege lo que protegía —el documento por tipo— y los del circuito se siembran igual, porque se marcan aparte y sembrarlos dos veces no crea nada. Los documentos de demostración antiguos reciben además un nombre interno corto sacado de su título, para que las listas no sean una pared de frases recortadas. El título no se toca: es lo que imprime el documento generado. Y «make up» siembra al arrancar, con «make seed-demo» para ponerse al día sin recrear el entorno.
Un día de E2E deja cientos de documentos: cada spec crea los suyos con un nombre de un solo uso y una tanda que falla no llega a limpiar. El juego de demostración acaba perdido entre el ruido, que es justo lo contrario de para lo que está. En este equipo eran 596 documentos de pruebas frente a 12 del circuito. El objetivo borra lo que no lleva marca de demostración —con sus adjuntos y su actividad, que WordPress no se lleva por delante solo— y las cuentas, categorías y tipos que crean los specs, reconocibles porque su nombre lleva una marca de tiempo. Después vuelve a sembrar, así que el sitio queda como recién instalado. Se niega a correr donde no esté permitido el contenido de ejemplo, de modo que una producción no puede perder nada.
erseco
force-pushed
the
feat/app-documentate
branch
from
September 8, 2026 17:04
2a2fe17 to
80f8ee8
Compare
The native renderer landed on main while this branch was open, so its strings, its demo values and its output guard arrive here speaking English through the translation functions and carrying their own copy of the demo field values inside the seeder. The application has rules of its own: the interface is written in Spanish directly in the code, with no i18n at all. The strings of the PDF layout field, the conversion manager, the private output directory, the PDF generator and merger, and the tests that assert them, follow suit. The demo field values move to Documentate_Demo_Field_Values and the front-end requires move to load_app_dependencies(), which is also what keeps the seeder under the class-length budget and load_dependencies() under the method-length one now that both carry main's work as well.
erseco
force-pushed
the
feat/app-documentate
branch
from
September 8, 2026 18:51
e72812b to
96c0c9e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Qué es
Documentate como aplicación: una página en /documentate/ desde la que un documento oficial recorre todo su ciclo sin pasar por wp-admin, con tres perfiles y el reparto de trabajo que pidió el servicio. wp-admin sigue existiendo y hace lo mismo que antes; la app es el camino recomendado.
Los tres perfiles y el circuito
documentate_gestionar)manage_optionsLos tipos que no pasan por gestión van directos del borrador a revisión. «Devuelto» no es un estado: es una marca sobre el borrador, con quién lo devolvió, cuándo y por qué; se limpia al reenviarlo. Toda la tabla de transiciones vive en
Documentate_Transicionesy ningún guardado puede saltársela (ni el formulario de wp-admin, ni la edición rápida, ni un autoguardado, ni una petición fabricada).El documento 0, partido por bloques
El atributo
rol='gestion'en los marcadores de la plantilla decide quién ve y quién puede escribir cada campo, con herencia en bloques y subbloques:Al área no se le pintan esos campos y tampoco puede escribirlos aunque los envíe a mano, cambie el tipo o inyecte contenido. Gestión y administración lo ven y editan todo. Los totales de la propuesta (concepto, bruto, IGIC, IRPF y resumen) se calculan solos.
La aplicación
Correo y actividad
Cada paso deja un evento en la actividad del documento (con el motivo cuando lo hay) y avisa por correo a quien toca: a gestión cuando entra un documento, al área cuando se le devuelve con el motivo, a administración cuando algo espera aprobación y al autor cuando se aprueba. La actividad y los comentarios de un documento solo los lee quien tiene permiso sobre ese documento.
Datos para probarlo
Documentate_Demo_Appsiembra doce documentos repartidos por tipo, área, autor y estado —borrador, devuelto por gestión, devuelto por administración, en gestión, en revisión, aprobado y archivado—, con los campos rellenos según quién los haya tocado, adjuntos, comentario y actividad fechada. Se vuelve a sembrar con:Las cuentas de prueba (
admin,editor1,author1,subscriber1, contraseñapassword) salen etiquetadas por perfil en la pantalla de acceso y en el selector «Probar como…» de la barra de administración.Informe de capturas
make capturasrecorre la aplicación con los tres perfiles en ordenador y en móvil y deja capturas/informe.html con 52 capturas comentadas: entrada por perfil, alta, adjunto, envío con confirmación, bandeja de gestión, datos oficiales, devolución con motivo, documento devuelto y corregido, paso a administración, aprobación, previsualización y descargas, actividad, el documento 0 con proveedores y totales, wp-admin y el selector de perfiles.Seguridad
Auditoría final con tres revisiones independientes (seguridad, flujo, pruebas) y sus 26 hallazgos corregidos. Entre ellos: la actividad ya no se filtra por consultas genéricas de comentarios, los adjuntos se sirven por un endpoint con permiso y nombre no adivinable, la siembra de ejemplo no depende de una cabecera de la petición, y la capacidad de gestión va a un rol propio en vez de a todos los editores del sitio (con migración desde la versión anterior).
Sin traducciones
El plugin es solo para España: las 400 llamadas de i18n pasan a literales en español y desaparece toda la cadena de traducción (
languages/, scripts de composer, objetivos de Makefile, paso de gettext en CI, regla de PHPCS y cabeceras del plugin). Añadir una cadena ya no obliga a regenerar el.pot.Validación
make testmake lintmake check-pluginnpm run test:unit-jsmainDos E2E antiguos de wp-admin fallan solo en el entorno local de desarrollo, por datos que otro spec deja en la base y por TinyMCE Advanced instalado en ese sitio; ni el spec ni el código que ejercitan cambian en esta rama, y en CI están en verde.
Documentación
README, ARCHITECTURE, AGENTS, CLAUDE y readme.txt al día, más dos guías para el equipo:
docs/flujo-documentos.md(el circuito y los perfiles) ydocs/campos-por-rol.md(cómo marcar campos de gestión al editar una plantilla ODT).