Registro de cambios

NeoFrag Reborn 1.2.0

Luandre 23 sept 2026 54 min de lectura 109
Índice
Índice

Añadido

  • Las actualizaciones del núcleo, en un solo clic (2026-09-23). A partir de esta versión, Administración → Monitorización señala las nuevas versiones de NeoFrag Reborn y las instala: copia de seguridad del sitio, verificación de la huella del paquete, aplicación del paquete, actualización de la base de datos, y vuelta a la copia de seguridad si algo falla. Es la primera versión publicada por este camino; se probó de principio a fin en un sitio nuevo antes de su publicación. Un sitio en la 1.1.0 aún no tiene este botón: pasa a la 1.2.0 a mano, sustituyendo sus archivos. Para los mantenedores: tools/build-release.php produce los tres archivos que se publican juntos (neofrag-reborn-update-<v>.zip, version.json, checksum.json); el procedimiento está en docs/guide/marketplace.md.

  • Los anuncios en Discord (2026-09-23). Un webhook que apunta a un canal de Discord recibe ahora un verdadero mensaje, con título, enlace y color: nueva noticia, nuevo artículo, nuevo miembro, nuevo comentario, nuevo tema del foro. Hasta ahora Discord rechazaba lo que enviaba el módulo, aunque su descripción prometía Discord.

  • «Fulano está en directo» (2026-09-23). Cuando un canal de Twitch o de YouTube que sigue el widget «Estado en directo» empieza a emitir, los webhooks suscritos al evento «Canal en directo» lo anuncian, una sola vez por emisión, con la miniatura de la emisión.

  • La demo habla también inglés en su contenido (2026-09-23): sus noticias, artículos, páginas, categorías y galerías tienen una versión en inglés, que muestra el multilingüismo.

  • Todo el producto habla los seis idiomas (2026-09-23). La administración, los módulos, los widgets, los temas, los ajustes, los botones, los mensajes y los scripts: cerca de 800 cadenas estaban escritas directamente en francés y se quedaban así en un sitio en inglés, alemán, español, italiano o portugués. Ahora todas pasan por las traducciones, en los seis idiomas. En particular:

    • El asistente de instalación está traducido, con un selector de idioma; al principio sigue el idioma del navegador. El instalador de línea de comandos acepta --lang=en, o el idioma del terminal.
    • El marketplace muestra el nombre y la descripción de cada addon en el idioma del sitio.
    • Los países (perfil, rivales) se muestran en el idioma del sitio.
    • Los roles y las plantillas de correo incluidos se muestran traducidos; un nombre cambiado por el administrador se queda tal como lo escribió el administrador.
    • La moderación muestra el estado, el motivo y el tipo de una denuncia, y el tipo de una sanción, escritos con todas las letras — mostraba los códigos (pending, forum_message, ban_temp).
    • Un grupo creado en un idioma ya no desaparece cuando el sitio se muestra en otro — salía de la lista, y sus permisos con él.
    • El tema de la administración se declaraba escrito en inglés: en un sitio en inglés, sus cadenas francesas nunca se traducían. Está corregido, igual que los scripts de los módulos, que buscaban sus traducciones en el lugar equivocado.
    • La confirmación de eliminación de la cuenta pide escribir la palabra en el idioma del sitio («ELIMINAR» en español), y ya no «SUPPRIMER» en todas partes.

    Una comprobación, check-textes-en-dur, rechaza ahora en la CI cualquier texto de la interfaz escrito en el código.

  • Los navegadores de los visitantes pueden conservar los archivos del sitio, si lo pides (2026-09-22). El manifiesto ya hacía el sitio instalable; ahora el sitio puede conservar sus imágenes, estilos y scripts en los navegadores de los visitantes. Desactivado por defecto, se activa en Administración → Ajustes → Preferencias generales («Aplicación instalable»).

    Las páginas, en cambio, nunca se conservan. Una página guardada en memoria sobreviviría a un nuevo despliegue: a quien ya hubiera venido se le serviría un sitio de varios días, sin que nada lo indicara. Un cambio sigue así visible al instante.

    Desmarcar desinstala de verdad. Es la parte delicada de esta técnica: un componente de este tipo, una vez instalado en un navegador, se queda allí aunque el archivo se retire del servidor. El interruptor no retira, pues, nada — sirve una versión que se desinstala sola en la siguiente visita. Por eso la función está desactivada por defecto, y por eso una actualización del núcleo nunca la activa por su cuenta.

  • El sitio se puede instalar como aplicación (2026-09-21). /manifest.webmanifest lo genera el propio sitio — nombre, descripción, color del tema, dirección de inicio y favicon vienen de los ajustes — en lugar de colocarse como un archivo, que sería falso en cualquier otro sitio.

  • Las carpetas sensibles también están cerradas con Apache (2026-09-21). Los .htaccess del paquete protegían menos que la configuración de Caddy incluida: con Apache, el instalador seguía accesible una vez instalado el sitio (solo su propia comprobación le impedía ejecutarse), y en un sitio instalado desde el repositorio git, docs/, tools/ y tests/ también eran accesibles — la documentación se podía leer en texto plano. cache/, config/, install/, tools/, tests/ y docs/ tienen ahora cada uno su propia protección, la raíz bloquea también los archivos .neon y .md, y tools/check-htaccess.php vuelve a leer la lista en cada ejecución, con el motivo de cada línea.

  • Una dirección desconocida ya no escribe un error en el registro del sitio (2026-09-21). El módulo pages, que recibe como último recurso las direcciones que nada más reconoce, escribía una línea de error por cada una — cada robot que sondeaba añadía algunas. Para los desarrolladores de addons: un controlador puede ahora declarar un rechazo como corriente (Module_Checker::refus_ordinaire()); el diagnóstico se sigue mostrando en pantalla en modo de depuración. La comprobación check-journal rechaza ahora toda línea que el propio producto escriba en el registro de errores.

  • Dos comprobaciones hacen respetar las reglas del repositorio (2026-09-21), para los colaboradores. tools/check-tools.php verifica que cada herramienta sigue las reglas comunes: cabecera descriptiva, uso de la biblioteca común en lugar de código copiado, veredicto y códigos de salida estándar, puerto reservado, catálogo al día. tools/check-docs.php comprueba la documentación: cifras exactas, enlaces y anclas válidos, ningún documento huérfano, ninguna mención de una herramienta que ya no existe, ningún pasaje copiado de un documento a otro, documentos que siguen siendo legibles. Las dos se ejecutan en la integración continua.

  • Ocho addons nuevos, todos opcionales (2026-09-20). Una actualización no los instala: en un sitio existente, se añaden desde Administración → Temas y addons, y se retiran del mismo modo. Una instalación nueva los incluye con el perfil Completo, ofrecido por defecto, pero no con los demás perfiles.

    Addon Lo que hace
    Efecto de temporada (widget) Nieve, confeti u hojas por toda la pantalla, durante un intervalo de fechas. Las fechas se escriben MM-DD sin año — la temporada vuelve sola — y pueden pasar por Año Nuevo (del 12-15 al 01-06). Fuera de temporada, la página no recibe absolutamente nada: ni imagen, ni script. Se respeta el ajuste del sistema que pide menos movimiento (prefers-reduced-motion), y la animación se detiene cuando la pestaña pasa a segundo plano.
    Lector de canales (widget) Los últimos artículos de un canal RSS o Atom externo. Una página casi nunca espera a un sitio ajeno: lee una caché, e incluso sirve contenido caducado antes que hacer esperar a los visitantes; la actualización se hace en la tarea programada. Solo la primerísima visualización, con la caché vacía, espera al canal, tres segundos como máximo. Un canal caído se aparta diez minutos en lugar de volver a intentarse en cada visita.
    Citas (módulo) Una colección clasificada, con autor y fuente.
    Recetas (módulo) Ingredientes y pasos introducidos uno por línea, tiempos y número de raciones, marcado schema.org/Recipe que leen los buscadores.
    Glosario (módulo) Un léxico ordenado por letra. «Éclaireur» va en la E, «Æther» en la A, «1v1» en #. La letra se calcula, nunca se escribe.
    Mapa de lugares (módulo) Lugares en un mapa de OpenStreetMap, con dirección, descripción y enlace. La biblioteca del mapa la aloja el sitio; solo las teselas del mapa de fondo vienen de OpenStreetMap. La página sigue siendo útil sin JavaScript — la lista, las direcciones y los enlaces están ahí.
    Radio web (módulo) Un reproductor de emisión, lo que suena ahora, y la programación de la semana. Un programa puede pasar de medianoche: «sábado 22:00 → 02:00» sigue siendo el programa del sábado.
    Zona de pruebas (módulo) Solo para miembros: allí se prueba un formato y se ve la representación exacta del sitio. Sobre todo, muestra lo que el sitio ha retirado — es la única página que responde a «¿por qué ha desaparecido mi tabla?».

    Los tres módulos de contenido (citas, recetas, glosario) comparten el patrón de la FAQ: categorías, búsqueda, filtro, orden y borradores.

  • Elegir el contenido en la instalación. El instalador web ofrece ahora un perfil del sitio entre los requisitos y la base de datos: Completo, Gaming / eSports, Comunidad o Solo el núcleo. Los módulos se pueden seguir desmarcando uno a uno, y los que un módulo necesita se añaden automáticamente (el palmarés necesita los equipos), con un mensaje. Sin JavaScript, el formulario sigue funcionando — el servidor solo conserva lo que pertenece al perfil elegido.

    Ninguna lista de addons se escribe a mano: los perfiles se construyen a partir de las declaraciones presets de cada addon. Es la diferencia con el intento de junio de 2026, abandonado porque una instalación aligerada terminaba en un error del servidor (500): unos módulos del núcleo consultaban las tablas de módulos opcionales sin comprobar que existían. Esas comprobaciones existen ahora y se verifican en cada ejecución de la CI.

  • Cada módulo, widget y tema incluido declara su papel: en su __info(), dice si pertenece al núcleo, de qué perfiles de instalación forma parte, y qué módulos necesita. La pertenencia al núcleo era hasta ahora un efecto secundario de estar en el paquete — así acabaron allí emojis, files y webhooks sin que se tomara ninguna decisión.

  • Tres nuevas salvaguardas de integración continua:

    Comprobación Lo que rechaza
    check-addon-declarations.php un addon que no declara nada, una dependencia inexistente, un addon del núcleo que depende de uno opcional, un addon no distribuible publicado en el catálogo, una declaración en desacuerdo con seed.sql
    check-addon-coupling.php un acoplamiento fatal (tabla, clase) ni declarado ni anotado; un ciclo duro. Leído en el código con el tokenizador de PHP, no con expresiones regulares — así que sin falsos positivos en los comentarios
    check-install-profiles.php un perfil que no arranca. Instala de verdad en una base de datos desechable, sirve el sitio y falla al menor 5xx — también en las rutas de los módulos ausentes, que deben devolver un 404 limpio
  • Encontrar a un miembro desde la barra de búsqueda. La búsqueda del sitio solo encontraba contenido — foro, noticias, páginas. Buscar a alguien obligaba a abrir el directorio de miembros y recorrerlo, aunque es una de las cosas que más se buscan en un sitio comunitario. Los miembros aparecen ahora en la búsqueda global y en las sugerencias instantáneas, por nombre de usuario, nombre o apellido.

    No se expone nada nuevo de paso: estos tres campos ya se muestran en el perfil público de cada miembro, y la búsqueda aplica exactamente el mismo filtro que el directorio — una cuenta eliminada no aparece.

  • Gestionar una serie de eventos de una vez. Crear un evento recurrente ya generaba todas sus repeticiones con un clic — volver sobre ellas obligaba luego a abrirlas una por una.

    Ahora, una repetición de una serie tiene un botón «Eliminar toda la serie», cuya confirmación indica cuántas repeticiones se irán, y el formulario de edición ofrece «Aplicar a todas las repeticiones de la serie». El título, el tipo, las descripciones, el lugar, la imagen y el estado de publicación se copian en cada una; las fechas nunca — son lo que distingue una sesión de la siguiente.

  • Deshacer una actualización y restaurar una copia de seguridad. El CMS ya hacía una copia de seguridad completa — archivos y base de datos — justo antes de cada actualización del núcleo. No sabía volver a usarla: la red de seguridad estaba ahí, pero nadie sabía caer en ella.

    Ahora, si la colocación de los archivos o una migración falla, el sitio vuelve automáticamente al estado en que estaba; el mensaje dice a la vez qué falló y si la vuelta atrás tuvo éxito. Cada copia de seguridad de la lista tiene también un botón Restaurar, para los casos en que el daño no viene de una actualización — un módulo de terceros, una acción de la que uno se arrepiente. La confirmación indica lo que se perderá.

    Lo que la restauración no toca, a propósito: la configuración del sitio (volver a poner credenciales de base de datos caducadas cortaría el sitio de su propia base), los registros (son la memoria del incidente que se está reparando) y la caché (se vacía para que se reconstruya, en lugar de volver al estado de otra versión).

    De paso, la copia de seguridad incluye ahora vendor/, que el paquete de actualización incluye y que ella no incluía: sin él, una vuelta atrás habría puesto el código antiguo sobre las dependencias nuevas.

  • Responder a un comentario. El botón «Responder», desactivado en el código original, funciona ahora: la respuesta se muestra sangrada bajo el comentario al que responde. Solo se puede responder a un comentario de primer nivel del mismo contenido que no haya sido eliminado (un nivel de respuestas); el servidor lo comprueba.

  • Para los creadores de temas: zonas llamadas por su nombre (idea tomada de HiddenCMS). Una vista llama ahora a una zona por su nombre — $this->output->region('content') — en lugar de por su número — zone(2). El tema declara la correspondencia entre nombres y zonas en su __info() (regions). La antigua llamada zone() sigue funcionando, y un tema sin esta declaración no cambia. Los cinco temas públicos (Nebula, Forge, Granite, Blockcraft, Extend) usan los nombres.

  • Instalar por línea de comandos (install/cli.php, idea tomada de HiddenCMS): una alternativa al asistente web, práctica para instalar un servidor de forma reproducible o automatizada. Sus opciones cubren la base de datos (--db-*, --create-db), la cuenta de administrador (--admin-*), el nombre y la dirección del sitio (--site-name, --site-url), el idioma (--lang), los datos de demostración (--demo) y la ejecución (--force, --yes, --dry-run, --no-lock). Sin opciones, hace las preguntas una a una y oculta la contraseña al teclearla; --admin-pass-env lee la contraseña de una variable de entorno para que no aparezca en la lista de procesos. Usa el mismo código que el asistente web e instala todos los addons incluidos, como el perfil Completo.

  • Un lanzador para toda la batería: php tools/check-all.php. Las comprobaciones no tenían un punto de entrada común — la CI las llama una a una, y en local cada uno escribía su propio bucle a mano, nunca el mismo. El lanzador descubre las comprobaciones presentes (una comprobación nueva se suma sin que nadie lo toque), ejecuta primero composer audit, limita la duración de cada comprobación, e indica al final lo que no ha ejecutado: las pruebas en navegador (--navigateur) y las comprobaciones con un objetivo explícito, entre ellas check-restauration, que daña el sitio a propósito.

  • Cada addon muestra cómo es (2026-09-22). La página «Temas y addons» de la administración y el marketplace muestran una miniatura: una verdadera captura del módulo, widget, tema o idioma tal como funciona en la demo. Los 61 addons del marketplace tienen una, en 960×600 para los módulos y los widgets. Para ello, se instalaron en la demo seis módulos y dos widgets con contenido de ejemplo — un glosario, citas, recetas, un mapa de lugares y una programación de radio web —, y los widgets que no muestran nada sin ajustes se capturaron con ajustes de ejemplo. Lo que no tiene nada que mostrar conserva su icono, como los conectores de Discord, GitHub y Google, cuyo icono es su logo. La demo lleva además por fin la identidad de su comunidad — nombre, tipo, fecha de creación y descripción —, que se había quedado vacía.

  • Diez addons tienen por fin una descripción (2026-09-22): los seis idiomas y los cuatro addons de inicio de sesión externo (el de base y sus conectores Discord, GitHub y Google). Se muestra en el idioma del sitio.

  • Campos de perfil definidos por el administrador (2026-09-20). El perfil de un miembro solo tenía campos fijos. Administración → Usuarios → Campos del perfil permite añadir los propios — nombre en el juego, plataforma, rango… —, de entre ocho tipos de campo. Los miembros los rellenan en el panel «Información adicional» de su perfil. Un campo es privado por defecto; una vez hecho público, se muestra en la página del miembro. Los valores se incluyen en la exportación de los datos personales del miembro y se eliminan con su cuenta.

  • La fuente del sitio se puede elegir en la administración (2026-09-20). Administración → Ajustes → Preferencias generales → Fuente del sitio ofrece doce fuentes, que sustituyen la de cada tema. Por defecto, «Fuente del tema» conserva la fuente propia de cada tema y no llama a ningún servicio externo; las doce fuentes ofrecidas las sirve Google Fonts.

  • El historial de inicios de sesión ya no se guarda para siempre (2026-09-20). Anota la dirección IP, el navegador y la fecha de cada inicio de sesión, y nada lo borraba nunca. Ahora se purga más allá de 395 días (trece meses), una duración que se ajusta en Administración → Ajustes → Preferencias generales; 0 desactiva la purga.

  • Recordatorios de los eventos seguidos del calendario (2026-09-20). Un miembro que sigue un evento del calendario recibe un recordatorio antes de su inicio, como los participantes de un evento del módulo Eventos. El plazo se ajusta en la configuración del calendario («Recordatorio antes de un evento seguido», 24 horas por defecto, 0 para desactivarlo).

  • El paquete de actualización dice lo que protege y lo que retira (2026-09-20). Lleva un archivo nf-manifest.json: las carpetas del sitio en las que una actualización nunca escribe (config/, install/, upload/, logs/, backups/, cache/), y los archivos que la versión retira. Hasta ahora la actualización deducía sola lo que había que eliminar; un paquete sin este archivo sigue aceptándose.

Cambiado

  • Toda la interfaz habla Bootstrap 5 (2026-09-23). Quedaban unos 470 usos de clases de Bootstrap 3 y 4, mantenidos en funcionamiento por una hoja de estilos de compatibilidad: campos de formulario, pastillas de color, botones de cerrar, botones a todo el ancho, bloques «avatar y texto», grupos de entrada, casillas, rejillas de formulario. Todo el marcado está migrado a sus equivalentes de Bootstrap 5; lo que es propio del producto lleva ahora un nombre propio del producto. Una comprobación rechaza cualquier nombre antiguo que vuelva, incluso redefinido. Lo que se ve:

    • las listas desplegables recuperan su flecha — un centenar, en la administración y en el sitio, parecían campos de texto;
    • las pastillas de color (estados, grupos, roles, sanciones) son legibles en todos los temas, en claro como en oscuro: su texto suspendía la comprobación de contraste;
    • los grupos «Moderador» y «Moderador sénior» se muestran: su nombre se escribía en blanco sobre fondo blanco;
    • los botones de cerrar de los diálogos tienen una zona de clic suficiente en los teléfonos.
  • El JavaScript lo revisa un linter (2026-09-22). Setenta y un archivos del proyecto escritos a lo largo de los años, sin que nada los comprobara más allá de su sintaxis y de la ausencia de jQuery. ESLint ve lo que la sintaxis no dice: una variable global creada por olvidar un var, un eval disfrazado, código después de un return, un innerHTML alimentado por un valor calculado. No se despliega ningún paquete npm: es una herramienta de desarrollo, igual que el análisis estático de PHP.

    Las reglas se eligen con un único criterio — haber mordido ya aquí. Es la familia de «$ is not defined», que había dejado sin funcionar la ordenación de tablas en toda la administración durante tres meses. El desorden existente (usos de innerHTML que sanear) se congela tal cual en lugar de reescribirse de golpe: su número ya no puede subir.

  • La línea de base de PHPStan se regenera con una orden, y la clase Config del núcleo está anotada (2026-09-21). Tres errores nuevos llevaron a reabrir phpstan-baseline.neon: las entradas añadidas a mano estaban escritas de otra forma que las del propio PHPStan, y cerca de trescientos bloques ya no correspondían a nada — toda una familia «Function NeoFrag not found», muerta desde que el archivo de arranque declara la función. La configuración vive en phpstan-base.neon, phpstan.neon solo le añade la línea de base, y composer stan:baseline la regenera entera. Config anota ahora el idioma actual, la lista de idiomas y los ajustes del núcleo que leen los módulos: estos accesos se analizan en lugar de quedar congelados.

  • Las herramientas de tools/ rehechas sobre una biblioteca común (2026-09-21). Una sesentena de herramientas, escritas cada una a su manera, copiaban la misma fontanería: arrancar un servidor PHP, abrir una sesión de administrador, lanzar Chrome, leer config/db.php. Una lección aprendida en una no llegaba a las demás. tools/lib/ lleva ahora esa base — conexión, sesión, servidor, navegador, recorrido del repositorio, SQL, opciones, veredictos —, y cada herramienta se reduce a su propia lógica. Quedan cincuenta y dos, sin pérdida de funciones: check-js-syntax y check-js-jquery forman check-js-sources; check-docs-counts y check-docs-liens forman check-docs; check-lang-args se integra en check-langs; seed-wiki-docs y dump-wiki forman wiki-docs; smoke-test pasa a ser check-smoke, como toda comprobación; addons-manifest y table-map, que son datos, viven en la biblioteca. Cada herramienta tiene ahora su propio puerto reservado (varias compartían uno), tres códigos de salida que distinguen «nada que señalar», «rechazado» y «no ha podido juzgar», y una cabecera a partir de la cual se genera el catálogo de tools/README.md. bs5-codemod, la herramienta de la migración de Bootstrap 4 a 5 de junio, se retira: check-classes-bs4 cubre todo lo que comprobaba, incluidos los puntos de ruptura de las utilidades direccionales.

  • declare(strict_types=1) en todo el ámbito útil (2026-09-21): 1365 archivos frente a 78 el día anterior. El trabajo avanzaba en pequeños lotes desde junio; está terminado. Los 209 archivos restantes son las plantillas views/**.tpl.php, donde la declaración sería sintácticamente válida y no protegería nada.

    La conversión se hizo por orden de riesgo decreciente — el número de llamadas a funciones nativas por archivo —, con la batería de pruebas pasada entre cada lote.

    Una consecuencia de fondo, propia de este producto: PHP en modo estricto rechaza __toString() para las funciones internas. Ahora bien, lang() devuelve un objeto de traducción diferido, resuelto en el momento de mostrar, cuando el idioma por fin se conoce, y las fechas también son objetos. La barra de la administración, la página de cada noticia y varias pantallas fallaban, pues, con un error fatal — módulo fuera de combate, página truncada, sin decir nada. La conversión se hace ahora en el punto de escape (htmlspecialchars), es decir, allí donde el valor debe convertirse en cadena por definición: cerca de 500 lugares en 86 archivos.

    La oleada exigió también once conversiones explícitas, tres de ellas en caminos que se toman en cada página — el orden de presentación de los idiomas y de los grupos, leído al empezar cada sesión, y el cálculo del token que protege cada formulario contra las peticiones falsificadas (CSRF): sin ellas, un sitio con dos idiomas activos habría devuelto un error 500 en todas sus páginas.

  • El registro dice ahora DÓNDE, no solo qué (2026-09-21). Cuando un módulo lanza una excepción, su representación se abandona y la página se muestra truncada — en silencio. La línea del registro daba el mensaje sin nombrar nunca un archivo. Ahora nombra el primer lugar del código propio del producto en la pila de llamadas. Encontrar el origen de los errores anteriores pasó de ser una investigación a una simple lectura.

  • El compilador SCSS pasa a la 2.1 (2026-09-20). En PHP 8.5, cada compilación de estilos escribía diez avisos de obsolescencia en el registro; ya no queda ninguno. La 2.x minimiza un poco más (.25rem en lugar de 0.25rem, #ccc en lugar de #CCCCCC), lo que cambia tres hojas de estilos — las de los módulos addons y palmarés y la de la página de mantenimiento. La equivalencia no se dedujo del texto: los dos juegos de CSS se dieron al motor de Chrome, que ve en ellos exactamente las mismas reglas, propiedades y valores calculados.

  • La documentación se ha releído entera frente al código (2026-09-17). Las guías de instalación, de conceptos y de desarrollo (crear un módulo, un widget, un tema, el framework) databan de junio y describían el CMS de antes: una instalación sin elección de perfil, un tema que cargaba jQuery, rejillas de Bootstrap 4. Ahora dicen lo que hace el producto — perfiles de instalación, declaraciones de los addons, regiones con nombre, front end sin jQuery bajo una política de seguridad estricta, ajustes de widget con valores de respaldo, vocabulario de colores compartido. El README, la guía de contribución, la política de seguridad y la referencia técnica están alineados igualmente.

  • Lo que permite un addon se lee en su declaración, ya no en listas de nombres escritas en el código. Tres de ellas decidían la suerte de los addons, y las tres eran falsas: la lista de los widgets protegía siete nombres que no son widgets; la de los temas protegía un tema default inexistente dejando nebula, el único tema público del núcleo, eliminable en cuanto estaba inactivo; la de los módulos duplicaba, en desacuerdo, lo que ya decía __info(). Comprobado addon por addon frente a la lógica antigua: ningún cambio en lo que se puede desactivar, ningún cambio de estado activo, y 39 addons pasan a no poder eliminarse — que es el objetivo de la corrección.

  • La Papelera ya no conoce a los demás módulos. Llevaba escrita en el código la lista de las tablas, claves primarias y métodos de restauración de news, articles, gallery, comments y forum: no podía, pues, pertenecer al núcleo sin arrastrar consigo cuatro módulos opcionales — comments pertenece él mismo al núcleo. Cada módulo declara ahora sus tipos restaurables; el núcleo los recoge. Misma inversión para los descriptores de contenido (reacciones, suscripciones, revisiones).

  • La lista de los addons del núcleo y opcionales ya no se mantiene a mano. tools/lib/addons-manifest.php, una tabla escrita a mano y congelada en junio, se deduce ahora de las declaraciones. Las dos ya se habían separado: emojis estaba listado como núcleo cuando se declara a la carta — se podía desmarcar en la instalación pero faltaba en el catálogo, y era por tanto imposible de reinstalar. Catálogo: 52 → 53 addons ese día (61 al salir la 1.2.0).

  • El instalador, rehecho visualmente: disposición en dos columnas, tokens de estilo tomados de nebula (azul marino y verde azulado, acento #2dd4bf), degradado sacado de detrás del texto, logo puesto en una pastilla.

Corregido

  • La copia de seguridad de la Monitorización vuelve a funcionar (2026-09-23). Desde el 21 de septiembre, se detenía en la primerísima fila de la base de datos: el botón «Iniciar copia de seguridad», y la actualización del núcleo, que empieza por una copia de seguridad, fallaban en todos los sitios. Encontrado al probar la actualización antes de publicarla.

  • Cambiar de idioma en una noticia traducida ya no lleva a una página no encontrada (2026-09-23). El selector de idioma conserva la dirección, y con ella el título en el idioma anterior: la versión traducida lo rechazaba. Ahora redirige a su propia dirección.

  • Una página traducida se muestra en el idioma correcto (2026-09-23). Las páginas públicas se servían en la versión que la base de datos devolvía primero, fuera cual fuera el idioma del visitante.

  • El tema elegido por un visitante ya no se extiende a otro sitio del mismo dominio (2026-09-23). La elección se recordaba para todo el dominio: un visitante que pasaba un sitio a Forge veía también en Forge otro sitio instalado en el mismo dominio. La elección se recuerda ahora por cada sitio por separado, y un ajuste nuevo, Preferencias generales → Elección del tema, permite desactivarla: el menú desaparece y el tema por defecto se aplica a todos.

  • La demo vuelve a restablecerse (2026-09-23). Un error en su conjunto de datos hacía fallar el restablecimiento de cada cuarto de hora desde la víspera: lo que cambiaban los visitantes allí se quedaba. Una comprobación rechaza ahora este defecto antes de que nada se publique, y un restablecimiento fallido se escribe en el registro en lugar de pasar desapercibido.

  • La cuenta de emergencia de la demo ya no aparece en ninguna parte (2026-09-23): ni en la lista de miembros, ni en la búsqueda, ni entre los miembros en línea, ni en la administración; su página responde «no encontrado».

  • El sitio en un teléfono (2026-09-23), a partir de capturas tomadas en un teléfono:

    • El menú siempre está al alcance. En el tema Nebula, desaparecía por debajo de 860 píxeles de ancho sin ningún botón para volver a abrirlo: ahora lo abre un botón «hamburguesa». En los demás temas, un menú demasiado largo para una línea se repliega tras un botón «Menú», en lugar de pasar de línea hasta llenar la pantalla; un menú corto sigue desplegado.
    • Los bloques apilados ya no se tocan: el área de miembros estaba pegada al bloque de encima.
    • El texto ya no se pega a los avatares de los miembros (comentarios, conversaciones, autor de una noticia, candidaturas, perfil).
    • Los puntos del carrusel son verdaderos botones, centrados bajo la imagen, y siguen siendo claros en los temas oscuros, como las flechas y la leyenda: Bootstrap los volvía negros.
    • La búsqueda del tema Extend conserva su botón junto al campo.
    • Las fotos de un álbum llenan su tarjeta.
    • La cabecera de los comentarios muestra el título antes del contador, que antes lo precedía.
    • Las fechas abreviadas se escriben en el idioma del sitio («30 août 2026», «Sonntag»), y ya no en inglés; la línea del autor de una noticia recupera un tamaño de texto normal.
  • La página de una foto de la galería existe (2026-09-23). El widget «Imagen aleatoria», el carrusel de la galería y el enlace de un comentario escrito sobre una foto llevaban todos a una página no encontrada: nunca se había declarado. Muestra la foto, su descripción, el camino de vuelta al álbum y los comentarios. Una foto sigue además las reglas de su álbum: ya no hay acceso por su dirección cuando el álbum está en la papelera o aún no está publicado.

  • Los últimos restos de Bootstrap 3 y 4 (2026-09-23), que sus nombres de clase no delataban: la estructura del carrusel, las cruces de cerrar que mostraban una «×» encima del icono, dos acordeones (FAQ, enlaces de navegación) todavía construidos a la manera de Bootstrap 4 (el de la FAQ se abría, pero sin el aspecto ni la flecha del componente), campos que se envolvían en un grupo vacío, la flecha de las burbujas que se quedaba blanca bajo una burbuja oscura, y dos reglas del widget de los socios. check-classes-bs4 lee ahora también la ESTRUCTURA de los componentes y los estilos escritos en las vistas.

  • Páginas de error que no lo eran (2026-09-23). En la moderación, y en la gestión de los roles, las plantillas de correo, los miembros y el carrusel, una dirección que apuntaba a un elemento retirado debía responder «página no encontrada»: llamaba a una función que no existe, escribía una advertencia en el registro, y la página de moderación se mostraba vacía. Encontrado al enseñar al análisis estático las funciones «mágicas» del framework — lo que quitó 455 apariciones de su lista de excepciones.

  • El botón de los correos enviados a los miembros es legible (2026-09-23): la validación de la cuenta, la contraseña perdida y las demás plantillas tenían un botón blanco sobre turquesa claro (contraste de 2,3:1). La corrección se aplica a las plantillas incluidas y a las ya instaladas, salvo las que un administrador ha personalizado.

  • Últimos retoques de presentación (2026-09-23): los enlaces del wiki en el tono legible del tema; los botones de «éxito» en los temas oscuros; el estado de una candidatura, cuyo recuadro no tenía fondo (clases de un antiguo tema de administración); las redes sociales en las tarjetas de miembro; los títulos truncados en la administración; un visitante que sigue el enlace para participar en un evento recibe «Acceso no autorizado» en lugar de una página no encontrada.

  • El texto es legible en todas partes, en modo claro como en modo oscuro (2026-09-23). Los enlaces y botones de la administración, los avisos, las fechas y las cabeceras de tabla de los temas, las categorías de las noticias, la página del marketplace en los temas claros, el botón «Aceptar todo» del banner de cookies, las pastillas de reputación y de tipo de evento: todos alcanzan ahora el contraste mínimo recomendado (4,5:1). En modo oscuro, la administración tiene sus propios colores de estado, y el panel «Salud del sitio» y la zona de subida de imágenes de la galería siguen al tema.

  • La administración en un teléfono (2026-09-23). La barra superior pasa a dos líneas — la miga de pan completa, y luego los botones — en lugar de truncar el título de la página; los objetivos pequeños (icono de inicio, contadores) son lo bastante grandes para un dedo; la cabecera de las categorías del foro ya no se desliza bajo sus botones.

  • El foro en un teléfono (2026-09-23): en un tema, el autor pasa encima del mensaje en lugar de dejarle solo 200 px de ancho, y los botones de un mensaje ya no tapan su fecha.

  • Los plurales traducidos (2026-09-23). 47 cadenas habían perdido su forma plural en la traducción: un sitio en inglés mostraba «3 topic», «5 image», «2 year». El contador de las categorías del foro, escrito en francés en todos los idiomas, está ahora traducido.

  • La documentación del sitio está al día (2026-09-23). Las diez páginas del wiki incluidas con el producto y las de la demo llevaban una semana de retraso respecto a las guías; «Crear un widget» todavía enseñaba una clase de Bootstrap 4. Una comprobación las compara ahora con las guías.

  • El formulario de respuesta del foro cabe en un teléfono (2026-09-23): la lista de los tipos de adjuntos permitidos, escrita sin espacios, formaba una sola palabra demasiado ancha para la pantalla.

  • Todo el sitio revisado, en cada tema, cada modo y cada ancho (2026-09-23). Una comprobación automática muestra ahora cada página de la administración en su tema y cada página pública en cada tema público, en claro y en oscuro, conectado como administrador y como visitante, en ocho anchos de 360 a 2560 px, con y sin contenido. Esta primera pasada ha corregido:

    • iconos rotos en las listas de categorías de la galería, los juegos, los equipos y las noticias, cuando una categoría no tenía icono;
    • el país de los perfiles de la demo, escrito con todas las letras: el perfil no mostraba ningún país y buscaba una bandera que no se encontraba;
    • los asuntos de las plantillas de correo, que mostraban {{site_name}} tal cual en la lista;
    • tablas demasiado anchas para un teléfono (gestor de errores, descargas, historial del wiki, moderación, sesiones, participantes de un evento, palmarés): ahora se desplazan dentro de su marco en lugar de ensanchar toda la página;
    • las listas del foro en un teléfono: cuatro columnas se repartían 360 px y el último mensaje se cortaba; cada entrada se apila ahora — icono y título, y luego estadísticas y último mensaje;
    • la barra del tema Nebula en un teléfono, que desbordaba la pantalla;
    • tres archivos pedidos por páginas sin existir, y dos líneas escritas en el registro en cada comprobación de actualizaciones mientras no hay ninguna versión publicada.
  • Los botones «Modificar» y «Eliminar» ya no están desalineados (2026-09-22). En las listas de la administración a dos columnas — categorías de las noticias, categorías de la galería… — el segundo botón pasaba a la línea siguiente, desplazado bajo el primero. Ahora se quedan uno al lado del otro.

  • La tarjeta «Almacenamiento» de la monitorización se muestra bien (2026-09-22). El indicador se dibujaba como un círculo completo en lugar del semicírculo previsto, con una cifra en bruto en su centro, y el valor y el porcentaje, colocados arriba, se solapaban con el trazo; ahora se sitúan en el hueco del indicador. El título «Información del servidor» de la misma página pasa a dos líneas cuando la columna es estrecha, en lugar de cortarse. En el grupo «Envío de correo», la línea «Transporte de correo» mostraba [object Object] en lugar del método usado.

  • Un widget colocado sin ajustes se muestra con sus valores por defecto (2026-09-22). Cuando un tema coloca un widget en la instalación, o una disposición antigua lo restaura, llega sin ajustes: escribía entonces advertencias en el registro en cada página mostrada, y el menú de navegación simplemente desaparecía. Ahora recibe los valores que su formulario habría guardado por defecto. Los widgets ya configurados no cambian.

  • La página de los eventos vuelve a mostrarse (2026-09-22). Mostraba una página de error «no encontrado» en cuanto tenía eventos que repartir en varias páginas — con un título perfectamente normal, lo que la hacía difícil de notar. El ajuste del número de eventos por página se guardaba como texto, y el código, hecho más estricto la víspera, lo rechazaba. Todas las listas paginadas aceptan ahora este ajuste en sus dos formas.

  • Los votos de las encuestas se registran (2026-09-22). El votante leía «¡Gracias por tu voto!», y el voto nunca se contaba: ninguna opción se reconocía como perteneciente a la encuesta. Un voto que no apunta a ninguna opción de la encuesta se rechaza ahora con un mensaje que lo dice, en lugar de ser bien recibido.

  • El widget «Foro» muestra por fin temas y mensajes (2026-09-22). Sus dos tipos, «Últimos temas» y «Últimas publicaciones», no seleccionaban ninguna categoría del foro, y por eso se quedaban vacíos en todos los sitios, hubiera los mensajes que hubiera.

  • Otras tres funciones caían en la misma trampa que las encuestas (2026-09-22), encontradas por la comprobación escrita para este defecto:

    • abrir una candidatura con campos personalizados hacía fallar la página, del lado del candidato como en la administración;
    • una notificación que enlaza a un evento del calendario fallaba al construir su enlace;
    • la profundidad máxima de las respuestas del foro nunca se aplicaba: el cálculo se detenía en el primer nivel.
  • Volver a la página 1 de una lista ya no lleva a una página no encontrada (2026-09-22). En las listas de la administración cuyo número de elementos por página se puede ajustar, el botón «1» construía una dirección que el sitio no reconocía.

  • La pantalla de edición de un campo de perfil personalizado se abre (2026-09-22). La pantalla fallaba cada vez que se abría: no daba etiqueta a su botón de guardar.

  • Dos botones «Volver» llevaban a una página no encontrada (2026-09-22): desde la página de una imagen, hacia su álbum; desde un modo de juego, hacia su juego.

  • «Postular» ya no aparece donde la candidatura sería rechazada (2026-09-22): en una oferta cerrada o completa, y para quien no tiene derecho a presentarse. Del mismo modo, un administrador que lee una conversación reservada al equipo puede ahora invitar a ella y denunciar un mensaje en ella, en lugar de ser rechazado tras haber visto el botón.

  • Los submenús del widget de navegación se abren (2026-09-22). Un enlace que agrupa otros enlaces no desplegaba nada al hacer clic: seguía llevando el atributo de la versión antigua de Bootstrap, que su propio script ya no buscaba.

  • Las demás burbujas de ayuda de los formularios aparecen al pasar el ratón (2026-09-22). La corrección de la víspera solo cubría una parte de los formularios: el icono (i) junto a los campos de las demás pantallas de la administración, y las burbujas de las etiquetas, seguían sin mostrar nada — el mismo defecto de atributo, en las dos bibliotecas que los construyen.

  • Las etiquetas de menú con acentos se traducen (2026-09-22). Un enlace de menú guardado desde la administración — «Actualités», «Équipes» — se almacena codificado, y su traducción ya no se encontraba: se quedaba en francés en los otros cinco idiomas, con una advertencia en el registro en cada página mostrada.

  • Los archivos del marketplace son reproducibles (2026-09-22). Llevaban los mapas de fuente .map, que el compilador de estilos regenera en cada instalación y que el repositorio ignora: dos archivos del mismo addon, construidos en dos máquinas, diferían aunque el código no hubiera cambiado. Los archivos ya no llevan estos ficheros.

  • El widget «Palmarés» se puede instalar por fin subiendo un archivo (2026-09-22). Era el único de los 61 addons distribuibles que no declaraba su dependencia del núcleo; el instalador, que la exige para reconocer un addon, se lo saltaba SIN DECIR NADA — ni mensaje, ni rastro.

  • La página de mantenimiento ya no está en blanco (2026-09-22). Los dos campos «Título» y «Contenido» se incluyen VACÍOS, así que la página no mostraba NADA más que el nombre del sitio — aunque la vista previa de la administración prometía un título y un texto. Un título y un mensaje por defecto, traducidos a los seis idiomas, llenan el hueco; en cuanto se rellenan los campos, son ellos los que se muestran.

  • Los botones de contorno recuperan su borde (2026-09-22). Los temas solo redefinían la variante «primary» del botón de contorno, y su propia regla borraba el borde de las demás: las otras seis variantes usadas por el producto — 90 usos en el código — parecían simples enlaces. El más visible era «Abrir el sitio», en el banner de mantenimiento, que solo tenía borde al pasar el ratón.

  • El código técnico ya no es rosa en la administración (2026-09-22). El tema solo fijaba la fuente de los fragmentos <code>, que conservaban por tanto el rosa de Bootstrap — un color ajeno a todas las paletas del producto. Muy visible en la lista de los roles.

  • El botón «Cerrar» de las ventanas cierra por fin la ventana (2026-09-22). En TODAS las ventanas del producto, el botón «Cerrar» (o «Cancelar») de abajo no hacía nada: hacía falta la cruz de arriba a la derecha. La causa: Bootstrap 5 renombró el atributo que desencadena el cierre, y el núcleo seguía poniendo el nombre antiguo; un navegador ignora un atributo desconocido sin decir nada. El botón también se ha convertido en un verdadero botón — se representaba como un <span>, y era por tanto inaccesible con el teclado. Dos salvaguardas impiden que vuelva: la comprobación del código rechaza el nombre antiguo, y una pasada en un navegador, lanzada a mano, HACE CLIC en el botón.

  • El tema Nebula ya no muestra su menú dos veces (2026-09-22). Cada instalación nueva mostraba dos veces el nombre del sitio y dos veces el menú: el tema dibuja su propia barra, y la instalación colocaba ADEMÁS un título y un menú justo debajo. El menú vive ahora en la barra superior, donde solo aparece una vez — y es configurable desde la administración, lo que antes no ocurría: los enlaces de la barra estaban escritos en el código, así que faltaba «Contacto» y un módulo desactivado conservaba su enlace. Los sitios ya instalados se corrigen con una migración, que solo toca la cabecera si se ha quedado como se incluyó.

  • Cambiar de idioma ya no lleva a una página de código (2026-09-22). El selector de idioma del pie de página — el de cinco temas, entre ellos el tema por defecto — enviaba al visitante a una página que mostraba {"redirect":"/en/…"} como texto en bruto, en lugar de llevarlo a la página traducida. Había que volver atrás para salir.

    La causa: este selector es un verdadero formulario, que nada interceptaba, y apuntaba a una dirección técnica que siempre responde en JSON. El servidor distingue ahora una llamada de la ventana «Elegir mi idioma», que espera JSON, de un envío de formulario corriente, que espera ser llevado a la página. Encontrado siguiendo el gesto en un navegador real, cosa que ninguna comprobación había hecho hasta entonces.

  • El contenido escrito en un solo idioma ya no envía los demás idiomas a una página de error (2026-09-21). Una noticia escrita en francés seguía ofreciendo los otros cinco idiomas en su selector, y los cinco respondían 404: medido en la demo, 60 direcciones muertas de 72. El gesto más natural de un visitante extranjero llevaba a un error. Ahora se sirve la versión que existe, con un aviso que lo dice en el idioma del visitante y un enlace al original. Esto se aplica a las noticias, los artículos y los álbumes (y sus categorías), así como a los equipos.

    Dos precauciones inseparables, sin las cuales el remedio costaría más que la enfermedad: la página solo anuncia ya a los buscadores los idiomas que existen de verdad, y designa el original como dirección canónica — si no, se indexarían seis direcciones para un solo texto. En la administración, nada de repliegue: una versión vacía debe mostrarse vacía, es lo que uno viene a rellenar. tools/check-langues-contenu.php hace cumplir las tres propiedades, en la integración continua.

  • La integración continua vuelve a estar en verde (2026-09-21); en la rama principal, estaba en rojo desde el 10 de junio. Casi todas las causas estaban en las herramientas de prueba y la configuración de la CI, no en el producto. La excepción: con el servidor integrado de PHP 8.3, el sitio podía calcular mal su dirección base y redirigir hojas de estilo y scripts; ahora solo la deduce de un SCRIPT_NAME que termine en index.php. Una batería de pruebas que no puede ejecutarse falla ahora en lugar de saltarse sin decir nada, y la mayoría de los trabajos de la CI tienen un límite de tiempo.

  • El logo del instalador no se mostraba en una instalación nueva (2026-09-21): la ruta era relativa a la carpeta install/, que el servidor no sirve en esa dirección. La imagen está ahora incrustada en la página.

  • Componentes que ya no se abrían desde el paso a Bootstrap 5 (2026-09-21). Bootstrap 5 puso el prefijo bs- a todos sus atributos de datos. Un atributo que se queda con el nombre antiguo no provoca ningún error: el componente simplemente no hace nada. Estos estaban muertos en silencio:

    Lo que ya no funcionaba Dónde
    El acordeón de la FAQ no se abría modules/faq
    La lista de participantes de un evento no se desplegaba modules/events
    Las ayudas emergentes de una parte de los formularios estaban vacías núcleo — neofrag/libraries/form.php
    La ventana emergente del perfil del foro, la de eliminar un enlace de navegación modules/forum, widgets/navigation
    La colocación de las burbujas en el editor en vivo, el intervalo del carrusel modules/live_editor, widgets/slider
  • Disposiciones rotas, también silenciosas (2026-09-21). Bootstrap 5 renombró sus utilidades direccionales; una clase renombrada ya no está definida en ninguna parte, y el elemento conserva su disposición por defecto sin que nada lo señale.

    • Los pies de los paneles y de las ventanas modales no se alineaban a la derecha. El núcleo construía la clase por concatenación ('float-'.$align), así que ninguna búsqueda de float-right podía encontrarla. El único parche de compatibilidad del producto solo cubría las tablas del tema de la administración: nada para los temas públicos. Misma causa para la alineación de las celdas de tabla, en las dos bibliotecas.
    • Ningún campo de formulario rechazado se marcaba, en ningún tema. Las dos bibliotecas ponían un nombre heredado — has-error (Bootstrap 3) y has-danger (una versión preliminar de Bootstrap 4) — que ninguna hoja de estilos servida define. El mensaje sí aparecía, pero nada indicaba el campo afectado. Se sustituyen por un marcador propio, definido una sola vez.
    • El pie de página llevaba <div class="float-right">: «Desarrollado por NeoFrag Reborn» bajaba a una nueva línea, a la izquierda. El valor viene de lejos — una migración de Alpha 0.2.2 lo había cambiado de pull-right a float-right en tiempos de Bootstrap 4, y el paso siguiente nunca se dio. Una migración (2026_09_20_bootstrap5_float) corrige los sitios existentes; la semilla incluye ahora float-end.
    • Y las últimas clases muertas en las vistas: la barra de depuración en un teléfono, el margen de las listas de eventos, las etiquetas del editor de menús, las dos barras del voto de reclutamiento — que salían del mismo color, de modo que el gráfico ya no distinguía las opiniones favorables de las desfavorables.
  • El cuerpo de un artículo podía borrarse al mostrarse (2026-09-21). La construcción del índice sustituía el texto por el resultado de un tratamiento que puede rendirse ante un artículo largo: la página respondía entonces con normalidad, con un artículo vacío, y nada en los registros.

  • Un widget colocado arriba o abajo de la página desaparecía. El tema Nebula ofrecía las ubicaciones «Cabecera» y «Pie de página» en el editor de disposiciones, pero no las mostraba. El widget sí se guardaba, y no aparecía nada — sin ningún mensaje. Los otros cuatro temas públicos no se veían afectados.

  • El widget de navegación ofrecía dos módulos internos (live_editor y pages) que debía ocultar. El mismo defecto hacía perder su nombre de módulo a las estadísticas de la administración, sin efecto visible mientras dos módulos no usen el mismo nombre de estadística.

  • El registro de depuración crecía sin límite cuando estaba activado. Ahora tiene un tope: más allá de 64 MB, vuelve a empezar conservando la generación anterior.

  • En un sitio en inglés, «No» se quedaba en francés («Non»).

  • Cinco páginas de la administración no ofrecían ningún camino de vuelta (2026-09-20): la ficha de un miembro, el registro de auditoría, añadir un grupo, la lista de sesiones y los archivos de la monitorización. La miga de pan solo hace clicable el nombre del módulo si el módulo declara tener una página de inicio de administración; user y monitoring declaraban lo contrario aunque la tienen. Una vez en esas páginas, había que pasar por el menú lateral o por el botón de volver del navegador.

  • Los buscadores recibían JSON en lugar del mapa del sitio. Pedido en la raíz, sin prefijo de idioma — la única forma en que lo pide un robot —, /sitemap.xml respondía {"redirect":"/fr/sitemap.xml"} con el código de estado 200, y robots.txt y humans.txt hacían lo mismo. La redirección que añade el prefijo de idioma respondía en JSON siempre que la dirección terminaba en .txt, .xml o .json. Como estos archivos no tienen versión por idioma, ya no pasan por esta redirección: se sirven directamente, con su tipo de contenido real.

  • /favicon.ico devolvía 404. Los navegadores piden esta dirección declare lo que declare la página; ahora lleva al favicon configurado en los ajustes, o al del CMS.

  • Activar la autenticación en dos pasos fallaba. Al guardar los códigos de recuperación, un error interrumpía la página: la cuenta acababa marcada como «protegida» sin un solo código de emergencia. Encontrado por una prueba escrita al reforzar el tipado de esta biblioteca; corregido, y la prueba se queda.

  • Un límite de «un solo intento» nunca bloqueaba. El umbral solo se comprobaba a partir del segundo intento. Sin efecto en los umbrales incluidos, pero el límite de denuncias de la moderación, que se puede bajar hasta 1, nunca bloqueaba entonces. El umbral se aplica ahora desde el primer intento.

  • Algunos textos se quedaban en francés en los demás idiomas, y el registro se seguía llenando. Tres causas, todas corregidas: el título de una página del wiki, de un tipo de evento o de una campaña de donaciones pasaba por la traducción como si fuera texto de la interfaz (no tiene traducción: es contenido); un texto ya traducido volvía a pasar por la traducción — «Espace membre» se convertía en «Member area», y luego «Member area» se buscaba como clave y se señalaba como ausente en cada visualización; y treinta cadenas de la interfaz (el campo «Nombre de usuario o dirección de correo» del formulario de inicio de sesión, títulos de panel en la administración de los juegos y del reclutamiento, las redes sociales del perfil…) no tenían traducción en ningún idioma, porque la comprobación que verifica los seis idiomas solo miraba las llamadas de traducción explícitas, no los títulos de campos y cabeceras traducidos más adelante. La comprobación los ve ahora; las treinta cadenas están traducidas al inglés, alemán, español, italiano y portugués.

  • Google Analytics espera ahora tu consentimiento. Si hay un identificador de Analytics configurado, el script de Google solo se carga ahora tras un clic en «Aceptar todo» en el banner de cookies, nunca antes. Además, la política de seguridad del sitio bloqueaba este script en todos los casos: Analytics no funcionaba, y nada lo decía. Ahora funciona, y solo con el consentimiento del visitante.

  • Tres funciones de la administración habían dejado de responder desde junio. Ordenar las tablas haciendo clic en una cabecera de columna, reordenar arrastrando las categorías, foros y subforos, y los botones Abierto/Cerrado de las páginas de Mantenimiento y Registros: sus scripts aún suponían la presencia de jQuery, aunque se retiró del CMS en junio de 2026, y se detenían en seco al cargar la página — sin ningún mensaje, los botones simplemente se quedaban inertes. Los tres se han reescrito sin jQuery, están cubiertos por pruebas en navegador, y una comprobación automática rechaza ahora cualquier script que vuelva a llamar a jQuery.

  • Una dirección mal formada provocaba un error del servidor. Una petición como /fr/a:80 o /:80, de las que envían los robots, hacía fallar el cálculo de la extensión de la dirección y devolvía un error 500 al visitante. El sitio responde ahora con un 404 corriente.

  • El formulario de contacto escribía «De:» con la dirección del visitante. Enviar un mensaje desde tu servidor en nombre de visiteur@example.org es exactamente lo que rechazan las protecciones contra la suplantación de los proveedores de correo (SPF, DMARC): el mensaje acababa en spam o era rechazado. El remitente es ahora la dirección de contacto del sitio, y la dirección del visitante se pone en «Responder a», lo que en la práctica viene a ser lo mismo. Misma corrección para las respuestas a una candidatura, que podían salir con la dirección personal del administrador.

  • El instalador podía guardar una dirección de contacto no válida. Cuando el sitio se instalaba accediendo a él por su dirección IP, la dirección de contacto pasaba a ser noreply@<IP>, que los proveedores de correo rechazan — y ya no salía ningún correo, sin ningún mensaje de error visible. La dirección solo se deduce ya de un verdadero nombre de dominio.

  • Aparecía un banner vacío en las páginas en las que una zona de la disposición está vacía. En el tema Extend, todas las páginas salvo la de inicio mostraban una ancha banda de color que no contenía nada, así como una sección vacía encima del contenido.

    La causa no era el tema: una zona declarada pero sin widget devolvía espacios en lugar de nada en absoluto, así que los temas dibujaban su marco alrededor de nada. Corregido una sola vez para todos los temas. Si construyes un tema, puedes escribir if ($zone = $this->output->region('banner')) (o zone(n)): la condición dice ahora la verdad.

  • El tema Extend mostraba dos veces su mención «Desarrollado por NeoFrag Reborn». Su disposición incluida colocaba en el pie de página un bloque que repetía lo que el tema ya escribe él mismo. Los sitios ya instalados se corrigen automáticamente al actualizar; los nuevos nunca lo tendrán.

  • Buscar una palabra común devolvía una página de error. Una búsqueda como «le» o «de» mostraba sus resultados en pantalla, pero el servidor respondía 404 — que es lo que ven los buscadores, las herramientas de supervisión y los navegadores, y que hacía parecer desaparecida una página perfectamente válida.

    Resaltar las palabras encontradas suponía que la palabra buscada aparecía necesariamente en el texto mostrado. Es falso por partida doble: la búsqueda examina varios campos y solo muestra uno — un tema del foro titulado «Salut tout le monde !» coincide con «le» por su título, no por el mensaje mostrado debajo — y la base de datos compara sin tener en cuenta los acentos, mientras que el código sí los tenía en cuenta: «éléphant» salía en la búsqueda «ele», sin que se resaltara en él ninguna palabra.

    Los dos casos están resueltos: el resaltado reconoce ahora las variantes acentuadas de una letra, como hace la base de datos, y cuando la palabra de verdad no está en el texto mostrado, el extracto empieza simplemente por su comienzo.

  • Las actualizaciones automáticas del núcleo no podían funcionar — cinco defectos, cada uno suficiente por sí solo: la dirección para comprobar las nuevas versiones no estaba definida en ninguna parte, así que el botón nunca aparecía; un interruptor global, que había que poner en la configuración, bloqueaba la operación; la descarga apuntaba a las versiones del NeoFrag original, que habrían sobrescrito NeoFrag Reborn; el paquete guardaba sus archivos en una subcarpeta, así que una actualización no habría sustituido nada, sin el menor error; y el archivo se extraía con una interfaz de PHP obsoleta desde PHP 8.0.

    El interruptor se sustituye por cuatro garantías: el origen de las versiones debe estar en una lista de direcciones autorizadas (la misma que para el marketplace); el archivo de versión solo da un nombre de archivo, nunca una dirección; la huella SHA-256 del paquete se comprueba antes de tocar un solo archivo; cada archivo del paquete se controla (ninguna ruta que salga del sitio, ningún enlace simbólico). Las carpetas config/ e install/ nunca se reescriben si ya existen.

  • El catálogo del marketplace era inaccesible desde cualquier sitio. El servidor que lo distribuye respondía {"redirect":"/fr/…"} con el código de estado 200 en lugar del archivo estático — un cuerpo perfectamente válido como JSON, y perfectamente falso, así que un fallo silencioso que hacía que todos los sitios recurrieran a su catálogo local. La causa era la configuración de ese servidor, ya corregida: no hay nada que hacer en los sitios. En el código, la actualización del núcleo valida ahora la forma de sus manifiestos, y no solo si se leen como JSON.

  • El botón «Eliminar» de un tema, en «Temas y addons», solo protegía el tema de la administración, por su nombre escrito en el código. Ahora sigue la declaración del tema, y protege por tanto también a Nebula.

  • Acoplamientos del núcleo hacia lo opcional. Siete módulos consultaban las tablas de módulos opcionales: cinco del núcleo (admin, moderation, settings, statistics, user), más games y teams. Dos consultas estaban sin protección: en teams, la comprobación de existencia de recruits estaba colocada después de la consulta que debía proteger, y en games, eliminar un juego consultaba la tabla de los equipos sin comprobar que existe. Las demás estaban protegidas pero mudas: llevan ahora una anotación que verifica la CI. El único ciclo duro del paquete (games ↔ teams) está roto.

  • Editor en vivo: los cambios aparecen sin recargar la página. Eliminar un widget lo dejaba en pantalla y añadir una fila no la mostraba hasta recargar la página, aunque el servidor había guardado el cambio. El script esperaba otro formato de respuesta que el que envía el servidor; el defecto venía de la retirada de jQuery.

  • Editor en vivo: arrastrar y soltar widgets vuelve a funcionar. Con el modo Columnas activado, un widget añadido se quedaba atascado al final de su columna y no se podía mover por encima del contenido de la página. Las filas y las columnas no se veían afectadas.

  • Editor en vivo: los widgets sin ajustes se pueden volver a añadir. Diez widgets que no tienen pantalla de ajustes (breadcrumb, copyright, downloads, forum, module, news, newsletter, slider, surveys, teams) no se podían añadir: añadirlos fallaba con un error 404 y sin ningún mensaje útil.

  • Un error del servidor ya no se muestra como contenido. Cuando una acción hecha sin recargar la página recibía un error del servidor (una página 404, por ejemplo), algunas pantallas insertaban esa página de error en la interfaz. El error se trata ahora como un fallo.

  • Editor en vivo: el formulario de ajustes corresponde siempre al widget elegido. Al cambiar rápido de widget o de tipo, la respuesta más lenta podía mostrar el formulario de otro widget. La petición superada se anula ahora, y el formulario ya no se recarga (ni se pierde lo que se está escribiendo) cuando la selección no ha cambiado.

  • El puerto de la base de datos se respeta ahora. El CMS se conectaba siempre por el puerto 3306, fuera cual fuera el puerto introducido en el asistente de instalación: no se podía instalar ni hacer funcionar en un servidor MySQL o MariaDB que escuchara en otro. El puerto se guarda ahora en config/db.php (solo si difiere de 3306) y se usa en cada conexión.

  • Un tema activado ya no está vacío. Activar Forge, Granite, Blockcraft o Extend cambiaba el tema sin aplicar su disposición por defecto, que solo se aplicaba con el botón «Reinstalar predeterminado»: el sitio no mostraba ningún widget. La activación aplica ahora la disposición por defecto del tema si aún no tiene ninguna, sin sobrescribir nunca una disposición personalizada.

  • Cambiar el tema por defecto se aplica ahora también a los visitantes que habían elegido uno. El selector de tema del pie de página recordaba la elección de un visitante durante un año, y esa elección prevalecía sobre el tema por defecto: cuando el administrador cambiaba de tema, los visitantes, y también el administrador, se quedaban en el antiguo, sin salida desde un tema sin selector. Una elección hecha antes del último cambio de tema por defecto se olvida ahora; una hecha después se sigue respetando.

  • El navegador ya no conserva copias caducadas de las páginas. A falta de instrucciones, podía volver a mostrar una versión antigua de una página: un cambio de tema, por ejemplo, solo aparecía tras una recarga forzada (Ctrl+F5). Las páginas y respuestas del sitio prohíben ahora esta caché (Cache-Control: no-store); las imágenes, estilos y scripts, servidos directamente por el servidor web, no se ven afectados.

Seguridad

  • Un valor guardado ya no puede inyectar etiquetas en un formulario (2026-09-23). El formulario de edición colocaba el valor de un campo en la página escapando las comillas como lo haría una cadena de PHP — lo que no tiene ningún efecto en HTML. Un valor que no hubiera pasado por el propio formulario (importación, datos de demostración, otro módulo) podía por tanto salirse de su campo y añadir código a la pantalla de administración: una cita cuya fuente era "><img src=x> lo demostró. Los campos de texto, las áreas de texto, las listas, las casillas y los botones de opción codifican ahora su valor; la representación de un valor introducido normalmente no cambia. Las notificaciones («Cita modificada», etc.) se pasan también al script de la página en una forma que ya no puede romperlo.

  • La política de seguridad declara ahora una regla para los medios (media-src). No tenía ninguna y recurría a la regla general (default-src 'self'). Ahora permite los medios del propio sitio y los medios incrustados en la página ('self' data:); la dirección de la emisión de una radio web se añade solo si hay una emisión configurada: un sitio sin radio web no permite ningún origen externo adicional.

  • Once avisos de seguridad cerrados en las dependencias (2026-09-20): league/commonmark pasa de la 2.8.2 a la 2.10.1 (diez avisos, ocho de ellos graves) y phpseclib de la 3.0.52 a la 3.0.57 (un aviso de gravedad media).

    Nueve de los diez avisos de commonmark son denegaciones de servicio mediante Markdown preparado — análisis en tiempo cuadrático, títulos con anclas que colisionan, notas al pie duplicadas, bloques de atributos adyacentes, salida XML profundamente anidada — y el décimo es una vulnerabilidad XSS: el filtro de los atributos on* se podía saltar con un salto de página U+000C. La biblioteca representa el Markdown del wiki, de los artículos y de la FAQ, escrito por los miembros. Varios de estos avisos afectan a extensiones que el producto no activa (notas al pie, atributos, anclas de títulos, salida XML); la actualización los cierra todos.

    La representación no cambia, y no es solo una impresión: doce casos representativos — títulos, tabla, lista de tareas, bloque de código, HTML inyectado, enlace javascript:, cita, texto tachado, enlace automático, listas anidadas, imagen, entidades y nota al pie, anidamiento profundo —, representados por las dos versiones con la configuración real del producto, dan el mismo HTML byte a byte (1568 bytes, la misma huella).

    Nota: Composer se niega ya a instalar la 2.8.2, precisamente por estos diez avisos. Hubo que desactivar la política en una carpeta desechable para hacer la comparación.

Lee también

0
Comentarios

Gestionar mis cookies

El sitio solo instala las cookies necesarias para su funcionamiento: no requieren su consentimiento. Los demás servicios pertenecen a otras empresas; solo se cargan si los acepta. Su elección se guarda seis meses, y puede cambiarla en cualquier momento.

Necesarias para el sitio Siempre activas

  • __Host-session Mantiene su sesión iniciada y protege los formularios contra envíos fraudulentos. Hasta que se cierra el navegador; un año con «Recordarme»
  • nf_consent Guarda las elecciones que hace en esta ventana. 6 meses
  • nf_fuseau Muestra las fechas y las horas en su zona horaria. 1 año
  • Almacenamiento del navegador Guarda su elección entre el modo día y el modo noche, y la presentación elegida para la lista de artículos. Hasta que lo vacíe

Vídeos

Los vídeos integrados en las páginas: el reproductor viene de la plataforma, que recibe su dirección IP y puede instalar sus cookies.

Música

Las canciones y listas de reproducción integradas en las páginas: el reproductor viene de la plataforma, que recibe su dirección IP y puede instalar sus cookies.