Qué se ha corregido
- •Errores del actualizador: fallo al escribir archivos y
version.json, parches y vista previa de cambios no admitidos, repositorio inaccesible y conexión correcta.
- •Pantalla de actualización: error de respuesta del servidor, «N archivos modificados · N commits», hecho, aplicando, tarea registrada, tiempo agotado y restauración completada.
- •Avisos de registro de tareas: el mensaje de que una tarea de actualización, parche o restauración se ha puesto en cola.
- •El worker con privilegios (cron) deja ahora sus mensajes de progreso y sus notificaciones de éxito y fallo en el idioma correcto.
Pantalla de historial de cambios (`/changelog`)
- •Los tiempos relativos como «5 months ago» solo existían en coreano e inglés; ahora están en 13 idiomas.
El aviso de lista vacía, la marca «Traducción por IA», la nota «sin traducción — se muestra en KO» y «solo se muestran las N más recientes» también están traducidos.
- •El idioma de la pantalla es ahora el idioma que el sitio usa realmente, y no solo el valor de la sesión (
?lang=, sesión, cookie, idioma predeterminado).
Antes, la primera visita caía en coreano, de modo que etiquetas en coreano se colaban en sitios en japonés e inglés.
- •Los identificadores y rutas largos (texto sin espacios) ya no se salen de la tarjeta ni se meten debajo del botón de instalación de la derecha.
Conviene saber
- •El worker con privilegios no tiene un usuario con sesión iniciada y, por tanto, no tiene idioma. Escribe sus mensajes en el idioma predeterminado del sitio.
(Antes no iniciaba el sistema de traducción, así que claves como updater.write_failed podían mostrarse tal cual. Ahora el worker lo inicia.)
- •Los registros del servidor (
error_log) y los registros de depuración para desarrolladores los leen los operadores, por lo que se dejaron como estaban.
- •En un sitio ya instalado, quien ejecuta la actualización es el actualizador antiguo ya instalado, así que estas mejoras se ven por primera vez en la actualización posterior al paso a la 2.8.2.
v2.8.1
2026-10-12 · hace 7 días
Más compatibilidad multilingüe (nombre de la app, datos de búsqueda, `?lang=`) · las actualizaciones protegen los cambios del núcleo propios de cada sitio
Multilingüe
- •El nombre y la descripción de la app en «Añadir a la pantalla de inicio» y en el aviso de instalación aparecen ahora en el idioma del visitante. Antes se mostraba tal cual el valor guardado (por ejemplo, en japonés).
Las traducciones se colocan bajo claves como pwa.front.name en rzx_translations. manifest.php se limita ahora a pasar el control a la salida oficial.
- •Datos estructurados (JSON-LD): se consultan primero las traducciones de los valores de configuración. Se corrigió el segundo elemento de la ruta de navegación, que también incluía el nombre del sitio, y «Home», que aparecía en inglés en todos los idiomas.
- •Widget de estadísticas: el número y la unidad (
suffix) aceptan ahora valores por idioma, igual que la etiqueta. Un valor único funciona exactamente como antes.
- •La pantalla sin conexión y el último mensaje del service worker se ofrecen ahora en coreano, japonés e inglés.
La versión de caché del service worker pasa de v5 a v6, para que los dispositivos que ya instalaron la app reciban la nueva pantalla.
- •«Sin resultados» en la búsqueda de mensajes estaba escrito directamente en coreano; ahora está en 13 idiomas.
Buscadores e IA
- •Se genera
<meta property="og:locale"> y los idiomas alternativos (og:locale:alternate). Al compartir en redes sociales, indica en qué idioma está la página.
- •Las páginas públicas del sistema dibujadas por código se incluyen ahora en
sitemap.xml (faltaban).
- •La primera línea de
llms.txt (la descripción del sitio) aparece en el idioma del visitante.
- •La configuración de robots por página (la pantalla de administración tenía el campo, pero nada lo leía) se aplica ahora de verdad.
Problemas corregidos
- •El valor de
?lang= se acepta solo después de compararlo con la lista de idiomas admitidos. Antes, un valor no admitido permanecía un año en la cookie,
y al abrir más tarde ese idioma se podía cambiar al visitante a esa pantalla contra su voluntad. El selector de idioma sigue la misma regla.
- •Constructor de widgets: se borraban en cada guardado las traducciones de los idiomas que no tienen campo de entrada en pantalla.
- •Búsqueda de mensajes de la comunidad: los nombres se guardan cifrados, por lo que ningún nombre daba resultados. Ahora se descifra primero y luego se compara.
Las actualizaciones protegen los cambios del núcleo propios de cada sitio
- •Si escribe rutas en
storage/custom/update-preserve.txt, una por línea (# es un comentario y se admiten carpetas), la actualización no sobrescribirá esos archivos.
Úselo para proteger lo que haya puesto directamente en archivos del núcleo, como una regla de inicio de sesión específica del sitio. No se aceptan rutas absolutas ni ..,
y si el archivo no se puede leer, la actualización no se detiene, sino que continúa con la lista de protección predeterminada.
- •Conviene saber: quien ejecuta la actualización debe tener ya esta función para aprovecharla. Se aplica desde la actualización posterior al paso a la 2.8.1.
Qué ha cambiado
El módulo de pagos ya tenía un archivo por proveedor, pero «qué proveedores existen» estaba escrito en varios puntos del código.
Para añadir un proveedor había que modificar el gestor de pagos, la pantalla de configuración y el webhook.
- •Coloque una carpeta (
gateway.json más el archivo de implementación) en rzxlib/Modules/Payment/Gateways/ y el núcleo la reconoce por sí mismo.
La elección del proveedor, los campos de claves y la dirección del webhook de la pantalla de configuración de administración se generan a partir de esa especificación.
- •Un sitio sigue eligiendo un solo proveedor en la pantalla de configuración (como antes). No es una función para usar varios proveedores a la vez.
- •La forma de añadir un proveedor está explicada en
docs/PAYMENT_GATEWAYS.md.
Square
- •Admite pagos con tarjeta, reembolsos y consultas, tarjetas guardadas (para cobros recurrentes) y webhooks (con verificación de firma).
- •El formulario de tarjeta pasa por la autenticación del titular (SCA) y muestra el motivo en pantalla si falla. 13 idiomas.
- •La pantalla de configuración de administración tiene un nuevo botón «Prueba de conexión». En modo de prueba ejecuta un pequeño pago, una consulta y un reembolso
para confirmar que sus claves son correctas. No se mueve dinero real y no se ejecuta en modo real (live).
- •Hasta que elija Square, no cambia nada para ningún sitio.
Los proveedores existentes (PAY.JP y Stripe) siguen igual
- •No se han tocado los archivos de implementación de los dos proveedores. Las configuraciones antiguas (clave única, v2, v3) se leen como antes.
- •Aunque los archivos de especificación no se puedan leer por algún motivo, una red de seguridad hace que PAY.JP y Stripe se creen a la manera antigua.
- •Con las configuraciones reales de sitios en producción y muchas configuraciones sintéticas, comparamos el código antiguo y el nuevo y confirmamos que eligen el mismo proveedor, el mismo modo y las mismas claves
(scripts/test-payment-parity.php).
Lectura de la configuración de pagos por sitio
Cuando una base de datos contiene una configuración de pagos distinta para cada sitio, el módulo de pagos lee ahora la configuración de su propio sitio.
Antes no estaba definido qué configuración de sitio se elegía. Si un sitio no tiene configuración propia, se comporta como antes.
Conviene saber
- •Se ha añadido una dirección de webhook común (
/api/webhook-payment.php?gateway=…). Las direcciones de webhook existentes de PAY.JP y Stripe no cambian.
- •La lista de proveedores de la pantalla de configuración mantiene su orden anterior; los proveedores que entran mediante una especificación se añaden después.
Cuál era el problema
El mapa del sitio (sitemap.xml) y la guía para la IA (llms.txt) solo incluían lo que el núcleo conoce.
Para que un plugin añadiera sus propias páginas, la única manera era modificar directamente archivos del núcleo, y ese cambio
desaparecía con cada actualización. Además, el núcleo adivinaba cómo leer la tabla de plugins, por lo que en algunos puntos se desviaba sin avisar.
- •Los inmuebles solo se publicaban en una dirección aparte (
/properties-sitemap.xml); si nada apuntaba a esa dirección,
los buscadores no podían encontrarla.
- •Los artículos del mercado nunca habían aparecido en el mapa del sitio, por una consulta que buscaba una columna inexistente.
El error se tragaba, así que nadie lo notó.
- •Algunos sitios publicaban las direcciones de las tiendas en una forma incorrecta, de modo que los buscadores indexaban direcciones que llevan a páginas inexistentes.
Los plugins publican ahora sus propias páginas
- •Si un plugin escribe
seo en su plugin.json, el núcleo lo lee y lo integra en el mapa del sitio y en la guía para la IA.
Funciona como los widgets, los diseños y los módulos de intranet, y si las direcciones coinciden, gana el núcleo.
- •Las direcciones para varios idiomas y las marcas de idioma las añade el núcleo. Un plugin solo tiene que dar sus direcciones.
- •Un plugin defectuoso ya no tumba todo el mapa del sitio. Solo se omite la parte de ese plugin.
- •Las direcciones de los plugins desactivados desaparecen automáticamente; las páginas de un plugin desactivado no se pueden abrir de todos modos.
Se ignoraban las direcciones alternativas en chino
El mapa del sitio escribía la marca de idioma como zh_CN, pero el estándar es zh-CN, así que Google ignoraba esa línea por completo.
Las páginas en sí estaban escritas correctamente, de modo que el mismo sitio decía dos cosas distintas en dos lugares.
Ahora las direcciones alternativas en chino simplificado y tradicional se reconocen correctamente.
Guía para la IA por idioma
- •
llms.txt solo existía en el idioma predeterminado. Ahora produce ediciones por idioma como ?lang=en.
Los nombres y las descripciones salen en ese idioma, y la cabecera de la guía indica de qué edición se trata.
- •En los sitios multilingües, los motores de respuesta de IA pueden ahora presentar este sitio a los usuarios de cada ámbito lingüístico.
Los empleados pueden ver su propia ficha de personal (intranet)
- •La ficha de personal que solo veían los responsables de RR. HH. ahora puede verla también el propio empleado. Se accede desde el menú de perfil de la parte superior.
- •Es solo de lectura. Se está preparando un flujo en el que se solicita una corrección y la aprueba RR. HH.
- •Las evaluaciones no son visibles para el empleado. Lo mismo ocurre con los premios y las sanciones. De los documentos adjuntos solo se muestra la lista.
- •No se puede abrir la ficha de otra persona modificando la barra de direcciones; de quién es la ficha lo determina únicamente la información de inicio de sesión.
- •El empleado también puede comprobar quién ha consultado su información de personal.
Usar las pantallas del foro dentro de otra envoltura
Todos los enlaces de una pantalla del foro se construyen a partir de un único valor ($boardUrl), pero el núcleo lo sobrescribía siempre.
Por eso, quien usaba un foro dentro de una pantalla de administración, como la intranet, tenía que sustituir carácter por carácter la página ya generada.
Si el núcleo cambiaba apenas la forma de un enlace, todo se desviaba sin avisar.
- •Si quien llama fija la dirección base de antemano, se usa ese valor. Si no, todo queda igual que antes.
- •El enlace del engranaje (configuración del foro) también se decide en un solo lugar. Antes, la lista, las FAQ, las preguntas y respuestas y el diseño de enlaces construían cada uno el suyo.
Los diseños pueden estar dentro de un plugin
- •Los diseños se buscan primero en
skins/layouts/ del núcleo y, si no están, en plugins/{id}/skins/layouts/ del plugin.
Es la misma regla que para los widgets y los módulos de intranet, y si los nombres coinciden, gana el núcleo.
- •Los diseños de plugin también aparecen en el selector de diseños de administración.
- •Hasta ahora, un diseño exclusivo de un plugin tenía que colocarse en la carpeta del núcleo. Una actualización no lo borra,
pero si más adelante el núcleo incluye un diseño con el mismo nombre se sobrescribe, y al mover el plugin a otro sitio había que llevarse la carpeta a mano.
Los plugins declaran ellos mismos los módulos de intranet
La lista de las pantallas de intranet (menú izquierdo y rutas de entrada) existía solo en un archivo de configuración del núcleo.
Para que un plugin añadiera su propio módulo, había que modificar un archivo del núcleo, y ese cambio desaparecía al actualizar.
- •Si un plugin escribe
intranet_module en su plugin.json, el núcleo lo lee y lo integra. Funciona como los menús y las páginas del sistema.
- •El valor puede ser un arreglo escrito directamente o una ruta de archivo como
"data/intranet-module.php".
- •Si los nombres coinciden, gana el núcleo. Un plugin no puede secuestrar un módulo del núcleo para cambiar su comportamiento predeterminado.
- •Los cinco puntos del núcleo que leían esta lista usan ahora el nuevo método. Aunque una actualización sobrescriba el archivo de configuración, los módulos de plugin se mantienen.
Eras japonesas (和暦) en la entrada de fechas
- •En las pantallas en japonés, al elegir una era (令和, 平成, 昭和, 大正, 明治) e introducir el número de año en el campo de fecha, el año gregoriano se rellena automáticamente.
No hace falta convertir cada vez un valor que se recuerda por era, como la fecha de nacimiento.
- •El campo que muestra el día de la semana se deja como está.
La configuración de diseño del foro no se reflejaba en la pantalla
- •Aunque se eligiera un diseño por foro en la administración, la pantalla mostraba el diseño predeterminado del sitio.
El lugar donde se guardaba y el lugar donde se leía eran distintos.
- •Corrección: al guardar un foro, se escribe también la configuración que lee la pantalla.
Los foros existentes se ven exactamente como ahora hasta que se vuelvan a guardar (el aspecto no cambia de repente).
Las publicaciones no se guardaban al escribirlas
- •Al guardar una publicación se omitía la columna del título, así que en los sitios donde esa columna aún existe el guardado fallaba.
En pantalla solo aparecía «Unexpected end of JSON input».
- •Corrección: si la columna existe, también se rellena. Los sitios que la eliminaron funcionan como antes.
Búsqueda del foro
- •La búsqueda miraba la columna antigua, así que en los sitios que la eliminaron, toda la lista dejaba de abrirse en cuanto se introducía un término de búsqueda.
- •Los sitios que conservaron la columna también tenían un problema. Al editar una publicación la columna no se actualizaba, así que la búsqueda solo encontraba el texto anterior a la edición.
- •Corrección: la búsqueda se hace en la tabla de traducciones. Ahora encuentra el idioma que se está viendo y también el contenido traducido.
Publicaciones que no se veían en listas, widgets y «Mi página»
- •El widget de pestañas de áreas de práctica y el widget de cuadrícula del foro mostraban siempre una lista vacía (el error se tragaba internamente).
- •En «Mis comentarios» de «Mi página» faltaban por completo los comentarios del foro.
- •El diseño del foro de enlaces tenía siempre el título vacío.
- •Corrección: todos leen ahora el título y el texto de la tabla de traducciones. Las miniaturas también se vuelven a encontrar a partir del texto.
Traducción por IA e instalaciones nuevas
- •Corregimos la traducción por IA de publicaciones, que fallaba al leer el texto original y hacía que todas las tareas de traducción fallaran.
- •Corregimos que, en una instalación nueva, el título y el texto en el idioma original de las publicaciones de ejemplo no entraban en la tabla de traducciones, por lo que el título parecía vacío.
Otros
- •Corregimos el caso en que la lista no se abría cuando la ordenación del foro estaba en «por recomendaciones» (hacía referencia a una columna inexistente).
El texto de otras personas entraba tal cual en el aviso
La ventana de aviso común del área de administración (modal de resultado) pegaba en la página los títulos de menú, los nombres de skin y los mensajes del servidor tal cual.
Si en un nombre se escribía algo como <script>, podía ejecutarse cuando otra persona administradora abría esa pantalla (XSS almacenado).
- •Corregido: todos los textos del aviso (título, cuerpo, botón, línea de confirmación) se convierten para mostrarse como caracteres normales antes de insertarse.
- •El aviso que aparece cuando falla la conexión a la base de datos usa ahora un texto traducido y pasa por la misma conversión.
Los botones del aviso salían en coreano en todos los idiomas
- •«Aceptar» y «Cancelar» estaban escritos en coreano dentro del JavaScript. Ahora salen en el idioma que se está viendo.
El servidor prepara los textos del idioma actual y el script lee esos valores.
Textos de administración en 13 idiomas
Los textos en coreano incrustados en las pantallas de diseño (skin), menús y ajustes de página se sustituyeron por claves de traducción y se completaron en 13 idiomas.
- •Skins: resultados de renombrar, duplicar y eliminar; la confirmación de que «los archivos y ajustes del skin se eliminan de forma permanente»; resultado de subir imágenes
- •Menús: aviso de que una página del sistema solo se quita del menú; confirmación de que «la página enlazada (foro) también se elimina»
- •Ajustes de página: avisos de tipo de archivo, fallo al guardar y URL duplicada
Las entradas privadas se filtraban en los listados
Una entrada marcada como privada seguía mostrando su título, su imagen y su resumen en el listado. Solo el cuerpo quedaba oculto.
- •Corregido: también en los listados, solo ven el título y la vista previa la persona autora, quien administra o quien introduce la contraseña.
- •Se cerró además la posibilidad de ver el contenido a través de la URL del adjunto: los adjuntos aplican ahora la misma comprobación.
- •Los títulos de la entrada anterior y siguiente en la pantalla de lectura también quedan ocultos.
- •Los widgets que reutilizan listados (tarjetas de foro, cuadrículas, pestañas) excluyen las entradas privadas ya en la consulta.
La primera pantalla tardaba en dibujarse
En el móvil las primeras letras tardaban en aparecer. La causa eran las tipografías.
- •El CSS de fuentes (Google Fonts 152 KB + Pretendard 29 KB) bloqueaba el dibujado.
Ahora se pinta primero con tipografías del sistema y las fuentes web se sustituyen al llegar.
- •Pretendard se carga solo en las pantallas en coreano. En japonés va primero una fuente japonesa, así que solo se descargaban fragmentos que se descartaban.
- •Dos hojas de estilo pequeñas (tamaño de letra, cuerpo del foro) van incrustadas en la página en vez de pedirse como archivo.
- •En la primera visita la caché de pantalla (service worker) hacía leer el documento dos veces; eso se eliminó.
- •El cálculo de ancho del menú de desplazamiento horizontal se aplaza hasta que la página termina de cargar (antes congelaba 0,5 s).
Para que buscadores e IA sigan las páginas por idioma
- •Los enlaces de idioma apuntan ahora a direcciones reales (
?lang=…). Antes eran enlaces JavaScript que los rastreadores no podían seguir.
Pantallas de administración
- •En la lista de páginas, una misma página aparecía una vez por idioma; esas filas se unifican en una.
- •Cambiar solo el título del menú borraba las traducciones de los demás idiomas. Ahora solo se limpian si cambia la página enlazada.
- •Programar una publicación del blog con una hora pasada se guardaba tal cual; ahora se muestra un aviso y el estado no cambia (13 idiomas).
Avisos y textos
- •El asunto de los avisos de mensajes de la comunidad estaba fijado en coreano. Ahora sale en el idioma de quien lo recibe.
- •El texto del enlace en la barra de cookies era demasiado oscuro; se ha aclarado.
Limpieza interna
- •Se eliminaron carpetas de vistas de administración duplicadas por error (
admin/site/site, admin/components/components).
Las que se usan realmente son las originales. La compilación de la distribución también las excluye.
Los tableros privados aparecían en el mapa del sitio
Incluso las direcciones de tableros que solo ven los administradores (como las bandejas de recepción) entraban en el mapa del sitio.
Los rastreadores de búsqueda solo recibían 403 (acceso prohibido) en esas direcciones, y además quedaba expuesta la existencia de tableros privados.
- •Corrección: solo se incluyen los tableros que pueden ver los no miembros. El criterio son los tableros cuyo permiso de lista y de lectura de publicaciones es «Todos».
- •También se excluyen las publicaciones secretas y los tableros configurados como excluidos de la búsqueda (noindex).
- •Una página con el mismo nombre que un tablero se evalúa según el tablero, porque el tablero ocupa la dirección primero.
- •Las páginas en varios idiomas también se incluyen en el mapa del sitio.
- •Las páginas que no quiera incluir en un sitio concreto se indican en el ajuste
sitemap_exclude. llms.txt sigue el mismo ajuste.
Los correos enviados iban a spam o eran devueltos
- •A los correos les faltaban la hora de envío (Date) y el identificador único del mensaje (Message-ID), por lo que el servidor receptor los clasificaba fácilmente como spam.
Ahora se incluyen ambos y el cuerpo se envía en base64. Así también se evita que se corten las líneas largas o que el cuerpo se interrumpa por un «.» al inicio de una línea.
- •Cuando la dirección de destino era del mismo dominio que el remitente (por ejemplo, una copia enviada a la dirección de la empresa), el buzón de ese dominio la rechazaba como suplantación.
Solo en ese caso se envía ahora desde la dirección de la cuenta SMTP, y las respuestas siguen llegando a la dirección original.
Ajustes de SEO y búsqueda con IA reforzados
- •Se añadieron campos de información del operador (datos estructurados Organization) en los ajustes de SEO del panel de administración. Los campos sin valor no se publican.
- •Ahora se usan de verdad los ajustes de SEO de cada página, y cuando el mismo contenido se ve en varias direcciones se fija una dirección canónica (canonical).
- •Se añadió un campo para el código de verificación del sitio de Bing. La búsqueda de ChatGPT usa el índice de Bing.
- •Las descripciones se recortan según el ancho de visualización de los resultados de búsqueda, al final de una frase y no a mitad.
Limpieza de la visualización de idiomas
- •La pantalla 404 siempre salía en coreano. Ahora aparece en el idioma que se está viendo (japonés, coreano o inglés; los demás idiomas, en inglés).
- •Se corrigió el «hace un momento» de la campana de notificaciones, que estaba fijo en coreano.
- •Las pantallas en japonés usan primero fuentes japonesas. El ayudante de fuentes por idioma se carga siempre, así que las pantallas que no pasan por la plantilla (como las páginas compartidas) ya no se detienen por la fuente.
- •En «Idioma preferido» de Mi página ya no aparecen idiomas que no tienen textos.
- •Al registrarse, se guarda como idioma preferido el idioma de la pantalla que se estaba viendo (antes quedaba vacío). Los correos de restablecimiento de contraseña también se envían en ese idioma.
Entrada e inicio de sesión
- •Las fechas, como la de nacimiento, se introducen ahora en tres casillas: año, mes y día. Antes la presentación cambiaba según el idioma del navegador.
- •Se corrigió que, según la plantilla, faltara la verificación reCAPTCHA en el inicio de sesión y en la recuperación de contraseña.
Pagos
- •Para cada pasarela de pago se guardan por separado las claves de prueba y las de producción. El modo de prueba solo hay que seleccionarlo, y las claves ya no se pierden al cambiarlas.
Widgets
- •El botón del widget hero ya puede abrirse en una ventana nueva.
- •Los elementos repetidos del constructor de widgets también pueden escribirse por idioma (botón del globo terráqueo).
Compilación
- •Los archivos de configuración (.env) y sus copias de seguridad nunca se incluyen en la distribución, sea cual sea su nombre.
- •Tampoco se incluyen las copias de seguridad que quedan al editar (
.bak, incluidas las que llevan una fecha tras el nombre).
- •Se han retirado de la distribución los scripts que solo usa la sede para su operación (sincronización de sitios, hosting, servidor de correo, actualización de condiciones, etc.) y los cron que solo usan plugins que no están en la distribución. Los scripts que necesitan los sitios de los clientes, como los workers de actualización, instalación y traducción, siguen incluidos.
- •Después de crear el zip se revisa de nuevo y, si contiene archivos de configuración o copias de seguridad, se detiene la publicación.
- •Los scripts de prueba ya no guardan contraseñas en el archivo; las leen de la configuración.
Tras actualizar, el nombre y la fecha de lanzamiento de la versión no cambiaban
Solo subía el número, y el título de la versión anterior se quedaba tal cual.
La pantalla de administración mostraba cosas como «2.7.9 · Fuentes japonesas · aviso de renovación de contrato…», con el número y el nombre desajustados.
Solo con la pantalla no se podía saber qué se había recibido.
- •Causa:
version.json es un archivo que a propósito no se sobrescribe, para proteger la configuración propia de cada cliente.
Por eso el actualizador lo rellena él mismo, pero cambiaba solo el número y la hora y dejaba el título y la fecha de lanzamiento.
- •Corrección: el título y la fecha de lanzamiento de la nueva versión ahora se leen del paquete recibido y se aplican también.
En las actualizaciones parciales sin paquete se leen de la lista de distribución (manifest.json).
La configuración propia de cada cliente (github, etc.) se conserva tal cual.
Atención — aunque suba a esta versión, la pantalla aún puede mostrar el nombre antiguo. Quien ejecuta la actualización es
el actualizador de la versión que está instalada ahora.
Se mostrará correctamente a partir de la versión siguiente a la que reciba la 2.7.10.
Comprobación de compilación
El fallo del constructor en la 2.7.8 (corregido en la 2.7.9) pasó las pruebas de regresión sin problema.
Las comprobaciones existentes solo miraban «si el archivo existe · si la cadena está dentro».
- •Ahora el script que entra en el constructor se genera de verdad y se somete a una comprobación de sintaxis (
node --check).
- •Si hay código que mete tal cual un archivo de varias líneas en una cadena JS, se bloquea la compilación señalando el número de línea.
- •Lo probamos con un archivo que reproduce la 2.7.8 y confirmamos que la comprobación lo detecta de verdad.
Síntomas
- •No se ve ni un solo widget en la paleta
- •Los widgets ya colocados no se dibujan en el lienzo — los vídeos, imágenes y demás parecen haber desaparecido
- •No se puede guardar
Causa
En 2.7.8 reunimos las declaraciones de fuentes en un solo lugar, partials/fonts.php. Pero el punto donde se construye la vista previa del constructor estaba
dentro de una sola línea de una cadena JS, y la declaración de fuentes de 33 líneas se metió tal cual.
En cuanto la línea se partió, la cadena se cortó y todo el script del constructor murió antes incluso de leerse.
Como es un fallo que mata el archivo entero, no parece que «algunas funciones estén raras», sino que el constructor no hace nada.
La versión 2.7.5 y las anteriores no tienen este problema.
Cómo lo arreglamos
Mantuvimos las definiciones de fuentes en ese único lugar, fonts.php, y solo al insertarlas en el constructor se pliega la salida en una línea y se tratan las comillas.
Sigue habiendo un único sitio donde modificar las fuentes.
Fuente predeterminada basada en el japonés
Hasta ahora la fuente predeterminada estaba basada en el coreano. Nuestros usuarios principales están en Japón, pero las pantallas se veían raras,
y al cambiar de idioma la fuente no cambiaba, de modo que las frases en japonés se mostraban con una fuente coreana.
- •Los valores predeterminados ahora son Mincho para los títulos y Pretendard JP para el texto.
- •En la pantalla de configuración de idiomas se puede indicar una fuente distinta para cada idioma. Si no se indica, ese idioma recibe un valor predeterminado adecuado.
- •La misma regla se aplica a todas las pantallas de administración, de miembros y del frontal, y a todos los temas (default, modern, minimal, shop).
Aviso de renovación de contrato y de fin de la gratuidad
A veces nos enterábamos del vencimiento de un contrato cuando ya había pasado. Ahora se avisa con antelación al acercarse la fecha de renovación.
- •Las renovaciones previstas y el fin del periodo gratuito se muestran como notificación antes de que llegue la fecha.
- •Funciona con una sola línea de cron (
cron.d-voscms-contract-notice), y la guía de instalación explica cómo registrarla.
Pago con tarjeta
Los pagos que pasaban por la autenticación de los emisores de tarjetas japoneses (3-D Secure) superaban la autenticación pero el cargo era rechazado.
- •PAY.jp impide usar un token creado para la autenticación si se deja tal cual. Tras superar la autenticación, el servidor debe hacer una llamada de cierre,
y solo entonces sirve para un cargo; esa única etapa faltaba.
- •También abrimos una vía para comprobar de antemano si los datos del cliente siguen existiendo en el proveedor de pagos — así se evitan los fallos al intentar cobrar a un cliente eliminado.
Campana de notificaciones y texto de la política de reembolso en todos los idiomas
- •En la campana de notificaciones de la cabecera, textos como «No hay notificaciones» y «hace minutos» que estaban fijos en coreano ahora cambian según el idioma.
- •Limpiamos las frases fijas que quedaban en el texto de la política de reembolso.
Contabilidad
En los libros de la sociedad japonesa se colaban transacciones en wones, y los totales no cuadraban.
- •Los comprobantes en una moneda distinta de la moneda contable (el yen) no generan asientos. El registro se conserva pero no entra en los libros.
Limpieza de la distribución
- •Los archivos de copia de seguridad creados durante la edición salían en la distribución (nombres como
nombre.php.bak.número).
La regla de exclusión solo filtraba .bak y se saltaba los nombres con un número añadido. Ahora bloqueamos también .bak., .orig y *~.
- •Quitamos de la distribución los materiales de venta del servicio THEVOS AI (tarifas, cálculo de paquetes, cron de conciliación de pagos).
Todo lo que los leía ya se había quitado, así que en la distribución eran archivos que nadie usaba.
«Se requiere migración de la BD»
039_ai_knowledge_base crea las tablas de la IA de consulta de tiendas de NaitoWork X,
y aun así salía tal cual en la distribución. Además, la última línea de este archivo decía
ALTER TABLE rzx_shop_inquiries ADD COLUMN IF NOT EXISTS ai_meta ...
rzx_shop_inquiries es una tabla que crea el plugin vos-store.
La distribución no incluye vos-store — el núcleo intentaba modificar la tabla de un plugin que no existe.
Así, en cada instalación nueva: se crean 4 tablas y falla en la última línea → como no llegó hasta el final,
no se registra como «aplicado» → el banner queda para siempre, y al pulsarlo vuelve a fallar en el mismo punto.
Quitamos por completo la consulta con IA (RAG) de la distribución:
039 · rzxlib/Core/AI/{Knowledge,Embedding,Generate} ·
scripts/{ai-kb-ingest,ai-rag-test,cron-ai-consult,hanesbot-ingest}.php
La traducción con IA (rzxlib/Core/AI/Translation) se mantiene. Es el motor del plugin de pago vos-ai
y sigue la misma regla que la contabilidad y el alojamiento: «el motor se queda en el núcleo, y el plugin abre la función».
«1 plugin no está autenticado (vos
El núcleo guarda una lista de «plugins incluidos que funcionan sin licencia», y esa lista estaba desactualizada.
Lo que el código conocía como incluido | vos-autoinstall · vos-marketplace |
Lo que realmente se incluye | vos-autoinstall · vos-community |
vos-marketplace salió de la distribución y vos-community ocupó su lugar,
pero solo esta lista no se corrigió. Lo incluimos y luego lanzamos un aviso porque no sabíamos que estaba incluido.
Para que no vuelva a ocurrir
La causa es que la lista de incluidos está escrita en dos sitios —
la lista de exclusión del script de compilación y LicenseStatus::BUNDLE_PLUGINS.
Añadimos a la compilación un paso de contraste. Si ambos difieren, no se crea el zip y se detiene.
No es algo que deba recordar una persona.
No se veía el botón de escribir en los foros
En Q&A y FAQ, ni siquiera quienes podían escribir tenían botón. Había dos causas.
- •Se bloqueaba a los administradores — al comprobar el permiso de escritura, primero se preguntaba si era un miembro y solo después se miraba a los administradores.
Los administradores no están en users, así que se quedaban en la primera puerta. Cambiamos el orden para que los administradores pasen primero.
- •Sin ninguna publicación, el botón desaparecía también — el botón estaba dentro de la lista,
y al pasar a la vista de «sin publicaciones» desaparecía con ella. Quien iba a escribir primero no podía escribir.
Sacamos el botón de la lista.
- •FAQ incluso consultaba
admin_id directamente — ignoraba el permiso de escritura de la configuración del foro.
Los títulos y subtítulos de los foros salían en otro idioma
Una pantalla en coreano en la que solo el subtítulo aparecía en japonés.
- •Las traducciones no se leían en absoluto. El título y el subtítulo se guardan en la configuración del tema como una sola cadena,
mientras que las traducciones están aparte en rzx_translations. La pantalla se limitaba a mostrar la cadena guardada.
- •No había distinción entre foros. La clave de traducción era
skin_config.subtitle_text, así que Q&A y FAQ
compartían el mismo hueco. Al editar uno cambiaba el otro.
La separamos en board.{número del foro}.{elemento}.
- •El cuerpo se dibujaba antes de fijar el idioma. El núcleo crea primero el cuerpo y añade la cabecera después,
pero el idioma se decidía en la cabecera. Lo trasladamos al lado del cuerpo para que se decida primero.
Faltaban las migraciones del núcleo durante la instalación
install-core.php no miraba database/migrations en absoluto.
En un sitio recién instalado faltaban algunas tablas desde el principio, y más tarde, al activar esa función, se caía.
Migraciones que se omitían en silencio (continuación de 2.7.4)
En 2.7.4 se trataron los comentarios de una línea (--), pero quedaban los comentarios de varias líneas (/ /).
Un punto y coma dentro de ese bloque unía el archivo en un solo trozo, de modo que no se ejecutaba ni una sola línea
y aun así se registraba como «aplicado». Confirmamos daños reales — en una instalación faltaban 9 tablas enteras.
Un -- o un /* dentro de una cadena no es un comentario. Eliminamos los comentarios contando las comillas.
Otros
- •Cabecera de la tienda — pusimos «Todas las categorías» en lo más alto del desplegable de categorías.
Quien quería entrar a echar un vistazo no tenía adónde ir.
- •Constructor de widgets — separamos la edición de elementos repetidos (
repeater) en un archivo aparte.
- •Barra lateral de la intranet — el texto de contabilidad se busca ahora en las tres variantes (salón, tienda online, empresa general).
Al mirar solo una, el menú mostraba la clave sin procesar cuando se usaba otra contabilidad.
- •Mi página — limpieza del panel.
- •Informe de plugins de pago — la comprobación de licencia envía también los plugins de pago que están activados.
Los que solo están instalados sin activar no se envían — el dinero se asocia únicamente a los activados.
Páginas exclusivas de la sede salían en la versión distribuida
config/system-pages.php entró entero en la versión distribuida, de modo que las «Páginas del sistema» de la administración de la instalación de un cliente
tenían seis menús en los que se puede hacer clic pero que no tienen nada detrás.
📥 downloads | Descargas de la versión distribuida — solo las distribuye la sede |
📋 changelog | Es el historial del producto de la sede |
📧 contact | Los clientes lo construyen ellos mismos con un widget (no es una página del sistema) |
💼 recruit · 💃 casts | Solo para el portal de negocios nocturnos (NaitoWork X) |
🎟 coupons | Lo proporciona el plugin del mercado |
- •Se añadió el paso de compilación
[4e/8] (strip-system-pages.php). Tras eliminar, se comprueba la sintaxis con php -l —
si este archivo se rompe, toda la pantalla de administración se cae.
- •Las páginas del sistema que quedan en la versión distribuida son ocho: home · terms · privacy · data-policy ·
refund-policy · hosting-terms · tokushoho · funds-settlement
Páginas del sistema de los plugins
- •Se añadió el texto
site.pages.blog en 13 idiomas.
Aunque un plugin registre una página del sistema, si el núcleo no tiene texto para ella, site.pages.blog aparece tal cual en la pantalla.
- •(La corrección del
plugin.json propio de vos-blog sale por el mercado — es un plugin que no forma parte de la versión distribuida)
Sitios ya instalados
Si las «Páginas del sistema» de la administración muestran un elemento que no usa, borre el bloque correspondiente de config/system-pages.php.
El contenido de las páginas (rzx_page_contents) no se borra — solo desaparece de la lista.
① Las migraciones que empiezan con un comentario se omitían por completo
- •Un comentario de
DatabaseMigrator::splitSql() decía «dividir tras eliminar los comentarios», pero
en realidad solo dividía por punto y coma.
- •Si la primera línea de un archivo SQL es
-- descripción, el archivo entero se convierte en un único fragmento,
queda atrapado por str_starts_with($stmt, '--') en la parte de ejecución, y no se ejecuta ni una sola sentencia.
- •Cuando se ejecutan 0 sentencias, hay también 0 errores, así que se trata como
success y se registra como aplicada.
Una vez registrada, no se vuelve a ejecutar, por lo que la tabla queda ausente para siempre.
- •El daño abarcaba todos los archivos que empiezan por
--. Medido: de 9 archivos, 6 ejecutaron 0 sentencias y 2 ejecutaron solo una parte.
- •Ahora elimina los comentarios de línea
-- y # y los bloques / / antes de dividir.
Un -- dentro de una cadena no se toca (si un valor como 'a -- b' se cortara, los datos se estropearían).
② Una instalación nueva no ejecutaba en absoluto las migraciones incrementales
- •El script de instalación ejecutaba solo
migrations/core y migrations/migrations
y omitía el nivel superior database/migrations/*.sql. El actualizador, en cambio, solo sigue el nivel superior.
- •Por eso, en una instalación nueva, de la 034 a la 042 faltaban desde el principio y quedaban vacías hasta la siguiente versión.
- •Al instalar, ahora ejecuta también el nivel superior y lo registra como aplicado (para que la primera actualización no ejecute otra vez las mismas).
Sitios ya instalados
Pulse una vez Ejecutar migraciones en Admin → Ajustes → Actualización.
Las entradas registradas por error no se volverán a ejecutar, así que si una tabla está vacía puede comprobarlo así.
SELECT migration FROM rzx_migrations; -- registrado
SHOW TABLES LIKE 'rzx_home_notices'; -- real
Lo que se sacó de la versión distribuida
- •
vos-cp (panel de control de servicios) y vos-shopping (tienda en línea) viajaban dentro de la versión distribuida.
Los dos son plugins recién creados que simplemente no estaban en la lista de exclusión.
- •
vos-cp es un panel para los servicios propios de THEVOS. No hay motivo para que esté en la instalación de un cliente
- •
vos-shopping sigue en desarrollo (v0.1.0), y la regla es que los plugins se compran en el mercado
- •También se sacó
vos-review — por la misma razón (instrucción del propietario).
- •A la versión distribuida solo entran dos plugins:
vos-community y vos-autoinstall.
Todo lo demás se obtiene en el mercado.
Lo que se añadió al núcleo
- •Una cabecera de tienda de dos niveles (
skins/layouts/shop) — un desplegable de todas las categorías, búsqueda, una vista previa del carrito y
un menú de avatar tras iniciar sesión. En lo más alto del desplegable de categorías pusimos «Todas las categorías» para que haya un camino de vuelta
- •Puntos acumulados, saldo a favor y nivel de miembro se muestran en «Mi página». Los nombres usan los valores que haya configurado
- •Compatibilidad con widgets de plugins — si un plugin tiene una carpeta
widgets/, el constructor del núcleo la reconoce automáticamente
- •Un componente compartido de búsqueda de dirección por código postal — el formulario de pedido y los datos del miembro usan el mismo
- •Una campana de notificaciones en la barra superior de administración
- •Ordenación de los grupos de menús de la consola de administración (
config/intranet-modules.php)
- •Compatibilidad con campos repetibles (repeater) del constructor de widgets
Lo que se corrigió
- •El problema por el que los campos de entrada multilingües sobrescribían los datos originales
- •Un error en el registro del libro de saldos a favor
- •El problema por el que la actualización de la versión distribuida convertía páginas de alojamiento en 404
Lo que hay que comprobar en las instalaciones existentes
En los sitios instalados con una versión distribuida antigua que solo han recibido actualizaciones, quedan carpetas de plugins que no se usan
(una actualización no borra las carpetas de plugins). Todo lo que aparezca como no instalado en Admin → Plugins
se puede borrar de plugins/ junto con su carpeta.
Changed
- •Enrutamiento unificado de las URL de solicitud de servicio —
/service/hosting, /service/mail y /service/domain → a /service/order mediante 302 (hasta que haya páginas de solicitud dedicadas, se guía a los visitantes a la página de solicitud integrada, y más adelante se podrán sustituir por páginas dedicadas).
Notes
- •Las correcciones del núcleo de la 2.7.1 (hreflang multilingüe, prevención de fallos silenciosos del actualizador, fusión de las tarifas
/pricing en /service/order) ya estaban correctamente incluidas en el paquete de la 2.7.1. La 2.7.2 es una reconstrucción que añade encima la ordenación del enrutamiento de solicitudes de servicio.
- •La versión distribuida incluye por norma solo el núcleo puro (se excluyen los plugins de pago y los verticales). Los sitios de la sede (voscms.com y thevos.jp) no son objetivo de la autoactualización de la versión distribuida; se despliegan desde el código fuente/git.
SEO
- •hreflang multilingüe — un canonical autorreferente por página + hreflang automático para los idiomas admitidos, indexación por idioma con
?lang=xx, normalización con 301 del ?lang del idioma predeterminado a la URL simple y sitemap ampliado.
Fixed
- •Corregidos los fallos silenciosos del actualizador — los fallos al escribir archivos del núcleo o version.json se tratan ahora como excepciones. Cuando la aplicación falla por permisos insuficientes u otras causas, se obtiene un error explícito y una reversión automática en lugar de un falso «SUCCESS».
- •Se protegió contra el fallo 500 de la página de tarifas en la que un campo multilingüe se pasaba a htmlspecialchars como un arreglo.
Changed
- •Se amplió el interruptor de reCAPTCHA v3 por formulario (blog_comment y otros).
- •La pestaña «Notificaciones de la consola del propietario» de los ajustes solo se muestra cuando vos-store está activo.
- •La página de tarifas
/pricing se fusionó en /service/order (301), y el plan «Sistema ilimitado» es solo para administradores.
Contenido de la versión distribuida
- •La versión distribuida se reduce a un núcleo puro — solo se incluyen 3 plugins del núcleo (
vos-autoinstall, vos-community, vos-review). Los plugins verticales (negocios nocturnos / salón / POS), los widgets y el tema (story) se excluyen por completo. Las funciones generales como la verificación de edad y el banner de cookies forman parte del núcleo (rzxlib) y, por tanto, se incluyen.
Added/Changed (widgets del núcleo)
- •location-map — admite la incrustación sin clave (
map_embed), eliminando la dependencia de una clave de API, y vincula el idioma del mapa a la locale
- •practice-tabs (pestañas de áreas de práctica) — un nuevo widget incluido
- •Ajuste global del ancho de página (
widget_page_width) — unifica el ancho de las páginas de widgets/documentos
Los widgets específicos de un sitio que no se incluyen (bundled:false) (hero-slideshow, overlap-cards, office-intro, page-hero, content-head, consult-cta, board-grid, clients-logos, el tema de perfil del foro) no están en la versión distribuida del núcleo.
Removed
- •Los widgets de prepago (
prepaid-charge, prepaid-pass, prepaid-purchase) — excluidos de la distribución porque vos-prepaid no se incluye
Added (ajustes y núcleo)
- •La puerta de verificación de edad se elevó a ajuste general del núcleo
- •Control de la opacidad del fondo del banner de consentimiento de cookies
- •Imágenes de logotipo por idioma
- •Datos de la cuenta bancaria en la información de la empresa + un aviso sobre la transferencia bancaria
- •Un aviso de «Tarjetas aceptadas» en la pantalla de pago (iconos de las marcas) + ajuste del administrador
Fixed
- •Localización de los títulos y subtítulos de las páginas al idioma del visitante (page-scoped db_trans), y corrección de que content_width no se aplicaba
- •Muchas correcciones de errores en widgets/administración/páginas y ampliaciones en 13 idiomas
Added
- •Limpieza automática de archivos obsoletos — una actualización que sobrescribe con el zip del paquete completo no puede borrar los archivos eliminados de la fuente, por lo que quedaban restos (huérfanos) en las versiones instaladas (por ejemplo, el
settings.php monolítico, dividido y eliminado en una refactorización, permanecía en instalaciones antiguas). Hemos añadido un mecanismo basado en una lista explícita para limpiarlos.
- •
config/obsolete-files.php — una lista acumulativa de rutas que se deben borrar (las instalaciones que se saltaron varias versiones se limpian de una vez).
- •Justo después de
applyUpdate() del actualizador, cleanupObsoleteFiles() hace un unlink seguro de los archivos de la lista. Nunca toca los destinos protegidos (.env, storage, etc.), los escapes de ruta ni los directorios.
- •Primer registro:
resources/views/admin/settings.php.
Fixed
- •El enlace «Ajustes del sistema» del panel — el enlace de las acciones rápidas apuntaba a la ruta heredada
/settings, que había sido separada y eliminada, y por eso llevaba a una página equivocada (de respaldo). Corregido hacia el punto de entrada de los ajustes /settings/general. (Omitido tras la compilación de 2.6.22.)
Fixed
- •El banner «Se requiere migración de la BD» del panel — CSRF token invalid — el botón de reintento no enviaba un token CSRF a
/update-api.php (run_migrations) y por eso era rechazado con un 419. Ahora garantiza $_SESSION['csrf_token'] y lo envía tanto en la cabecera como en el cuerpo.
- •El banner no desaparecía ni siquiera después de ejecutar las migraciones —
DatabaseMigrator::recordMigration suponía una columna batch y el prefijo patch_, de modo que en el esquema de tipo instalado (migration/version/executed_at) el INSERT de registro fallaba y el prefijo también desajustaba la coincidencia de nombres de archivo. Corregido para registrar el nombre de archivo tal cual, de acuerdo con las columnas reales. (Esa era la causa de que, al actualizar a 2.6.20, las migraciones dejaran solo un registro «OK» sin aplicarse.)
Fixed
- •Claves sin procesar en la barra lateral de páginas del núcleo como la gestión de permisos del administrador — como el diseño de la intranet se activaba solo con los route_prefixes de
config/intranet-modules.php, la página del núcleo /staff/admins de un sitio sin el plugin correspondiente (por ejemplo, sin vos-salon instalado) se mostraba con la barra lateral de la intranet de salon, y las claves salon.intranet. y salon.section. aparecían como texto sin procesar. Corregido.
- •Se añadió una función auxiliar compartida
resolve_intranet_module() — la intranet solo se activa si los recursos lang del módulo (el plugin) están realmente instalados; si no, se recurre a la barra lateral admin-sidebar del núcleo.
- •La lógica de detección duplicada en
_head.php / admin-sidebar.php se unificó en la función auxiliar. El comportamiento no cambia en los sitios con el plugin instalado (salon, store, etc.).
Fixed
- •Error al guardar niveles de puntos — se corrigió el fallo con el error
Unknown column 'group_id' al guardar un nivel en el módulo de puntos del administrador. La causa era un conflicto de esquema de point_levels/member_points entre core/004 y migrations/019(v190) (el CREATE TABLE IF NOT EXISTS de 019 no podía añadir las columnas nuevas a una tabla existente).
- •migración
034_point_system_schema_reconcile (nivel superior, se aplica automáticamente al actualizar):
- •
point_levels — se añadió group_id, se dio un valor predeterminado a name y un índice único sobre level (para que funcione el upsert).
- •
member_points — se añadieron point/balance/total_accumulated/level.
- •Todo es aditivo (sin pérdida de datos), sin efecto sobre las columnas existentes ni sobre los consumidores (como vos-prepaid).
Added
- •3 widgets de compra — uno integrado (
prepaid-purchase, visualización a elegir: todo / solo recarga / solo bonos) + uno solo para recargas (prepaid-charge) + uno solo para bonos (prepaid-pass). Un único componente compartido purchase-widget.php mueve los 3 widgets (cero duplicación), con bifurcación mediante el filtro $ppOnly.
- •Imagen de fondo del producto — carga por arrastrar y soltar (también por clic) en el modal de alta/edición de productos del administrador, con el fondo aplicado en «Mi página» y en las tarjetas de los widgets. Migración 003
image_url.
- •Entrada multilingüe del nombre del producto + traducción por IA — reutiliza el
multilang-modal compartido (botón de globo), se guarda sobre rzx_translations y se muestra en el idioma del visitante con prepaid_name(). La traducción por IA reutiliza el punto de acceso del núcleo /api/translations.
- •Se añadió al formulario de pago un campo para el nombre del titular de la tarjeta (se pasa a
createToken de PAY.jp).
- •Selección del cliente en la recarga manual — se pasó de escribir directamente un user_id a una búsqueda con autocompletado por nombre/teléfono/correo (acción
customer_search). La lista de situación de clientes también muestra el nombre + correo del cliente en lugar de un hash de user_id.
- •Interruptor de activación — la casilla de producto activo se cambió por un interruptor.
Changed
- •Cuadro de compra de 4 columnas — la cuadrícula de tarjetas de producto es adaptable 2→3→4 (
lg:grid-cols-4), y se eliminó el ancho fijo max-w-3xl del widget → se ajusta al ancho del contenedor donde se coloca.
Fixed
- •Altura de la paleta y del megamenú del constructor de widgets — la altura de la fila del constructor queda fijada en
calc(100vh - 88px), de modo que las categorías de la izquierda y el desplegable ocupan automáticamente toda la altura de la pantalla y se resuelve la superposición de los elementos del megamenú.
Added
- •Validación de link en
api/broadcast.php — si falta el esquema, se antepone https:// automáticamente (example.com → https://example.com); si no pasa filter_var FILTER_VALIDATE_URL, se devuelve un 400 con el error field: 'link'; se rechaza todo lo que supere los 500 caracteres.
- •Validación en tiempo real en el cliente —
type="url", inputmode="url", y de inmediato al escribir:
- •Esquema ausente → contorno emerald + ✓ + aviso «Se añadirá automáticamente al enviar: …»
- •Sin punto (.) en el host o formato incorrecto → contorno red + ⚠ + «No es un formato de URL válido»
- •Válido → contorno emerald + ✓ + «Es una URL válida»
- •El cliente también la completa antes de enviar → se garantiza que la vista previa y el cuerpo realmente enviado coincidan.
Added
- •Encabezados de sección: ✉ Redactar mensaje / 📡 Canales de envío — agrupación visual + texto explicativo.
- •Contadores de caracteres (título
0/255, cuerpo 0/2000) — colores automáticos: amber a partir del 70 %, red a partir del 90 %.
- •Cuadro de vista previa del mensaje (borde punteado) — muestra en tiempo real cómo se verán en LINE el título, el cuerpo y el enlace combinados + un contador del total de caracteres
n/5000 (red por encima de 4500).
- •Texto de ayuda de cada campo:
- •Título: «Se muestra tanto en el buzón de mensajes del miembro como en la notificación de LINE»
- •Cuerpo: «Si escribe una URL en el cuerpo, se acorta y se hace seguimiento de los clics automáticamente»
- •Enlace: «Se añade automáticamente al final del cuerpo tras una línea en blanco. Si también lo escribe en el cuerpo, aparecerá dos veces, tenga cuidado»
- •Icono de estado a la derecha del campo del enlace (
absolute) — ✓ / ⚠ aparecen de inmediato.
Added
🔗 Reglas de validación de URL (nueva 5.ª sección):
- •Debe empezar por
http:// / https:// → se convierte en un token de seguimiento de clics
- •Un esquema ausente se completa automáticamente
- •Se rechazan los hosts sin punto o con formato incorrecto
- •Límite de 500 caracteres
- •Cuadro de ejemplo:
✓ https://voscms.com/event / ✓ example.com/page → completado automáticamente / ✗ asdfasdf (sin punto en el host)
Notes
- •La producción (
voscms-com) y salon están sincronizados. La API, la interfaz y la i18n surten efecto de inmediato.
- •i18n en 13 idiomas:
community.broadcast.guide_url_, section_message, section_channels*, subject_hint, body_hint, link_hint, link_{invalid,autofixed,valid}, preview_{title,empty}.
Added
- •Cambio de diseño de
/theadmin/community/broadcast — la antigua tarjeta única se divide en lg:grid-cols-3. El formulario de envío principal ocupa las 2 columnas de la izquierda (lg:col-span-2), y un nuevo cuadro de ayuda ocupa la columna de la derecha (<aside>, sticky top-4).
- •Contenido de la ayuda en 6 secciones:
1. 📅 ¿Qué es el envío programado? — envío automático a una hora futura, útil para la noche, los fines de semana y los eventos
2. 🛠 Cómo se usa — 4 pasos (elegir el destino → marcar LINE → marcar la programación → enviar), interfaz con insignias numeradas
3. ⏱ Precisión del envío — explicación del intervalo de 5 minutos del cron + un cuadro de ejemplo (10:00 → 10:00 a 10:05)
4. 🆚 Inmediato frente a programado — la diferencia de comportamiento entre los dos modos en formato de tarjetas
5. 📋 Comprobar la espera en la cola — un botón de acceso directo a /history?status=queued
6. ⚠ Precauciones — al menos 30 segundos en el futuro, no se puede programar en la aplicación/push, no funciona en alojamientos sin cron, no se admite cancelar desde la interfaz
- •Fondo con degradado índigo (
from-indigo-50 to-blue-50) — el cuadro de ayuda se distingue visualmente de la tarjeta principal y a la vez se integra con suavidad.
- •i18n en 13 idiomas — unas 20 claves
community.broadcast.guide_* (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Added
- •Se añadió una tarjeta «Idioma preferido» en
/mypage/settings — un select de 14 opciones (automático / 13 idiomas). Al guardar, actualiza (UPDATE) rzx_users.locale y aplica de inmediato $_SESSION['locale'].
- •Hook de Auth::loginUser — al iniciar sesión, si existe
$user['locale'] y la locale de sesión está vacía, la rellena automáticamente. Si el usuario la cambió manualmente durante la sesión, se conserva ese valor con prioridad.
- •i18n en 13 idiomas —
auth.settings.language.{title,description,label,auto,hint,save,success,error_invalid,error_save} (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
- •Tras el cambio, la interfaz se muestra en el idioma del miembro desde la página siguiente, y si un administrador ha configurado
messages_by_locale en una regla de envío automático, el cuerpo correspondiente se envía a ese miembro por LINE.
Added
3 tarjetas nuevas en la parte superior de /theadmin/community/broadcast/history (después de las 4 tarjetas existentes):
1. Tasa de clics global (CTR) — SUM(rzx_broadcast_clicks.click_count) / SUM(rzx_line_broadcasts.recipient_count WHERE status='sent') × 100. Texto grande en emerald.
2. Prueba A/B (últimos 30 días) — para los envíos con variant IS NOT NULL, destinatarios, clics y CTR por variant. El variant con mayor CTR se resalta en emerald.
3. Distribución por locale (últimos 30 días) — solo para los envíos con source='auto_rule' AND messages_by_locale IS NOT NULL, la locale se asigna comparando las claves del JSON messages_by_locale de la regla con los primeros 30 caracteres del cuerpo del broadcast → destinatarios/clics/CTR por locale. Ordenado con ksort.
- •i18n en 13 idiomas:
community.broadcast.stat_{ctr_title,ctr_breakdown,variant_title,variant_hint,locale_title,locale_hint,empty}.
Notes
- •Los árboles
voscms-com y salon están sincronizados. No hay columnas nuevas en la base de datos (ya estaban todas incluidas en las migraciones 008/009/010 del Step 4).
- •La lógica de coincidencia de locale compara los primeros 30 caracteres del cuerpo — si el cuerpo coincide tras la sustitución de variables, se cuenta en el mismo grupo de locale. Si el cuerpo es demasiado corto o se rellena por completo con la sustitución de variables, la tasa de coincidencia puede bajar (en el futuro se podría añadir directamente una columna locale a la fila del broadcast).
Added
- •migración 010 —
rzx_auto_broadcast_rules.messages_by_locale JSON NULL + rzx_users.locale VARCHAR(10) NULL + un índice idx_locale.
- •AutoBroadcaster::lineUserIdsWithLocale — consulta pares de userId de LINE y locale del miembro. Los entornos sin la columna
users.locale recurren a lineUserIds.
- •AutoBroadcaster::pickLocaleMessage — prioridad: la user.locale del miembro → la locale predeterminada del sitio (rzx_settings.locale) → la primera clave del JSON (si todo falla, null → se recurre al cuerpo A).
- •AutoBroadcaster::fire — si existe
messages_by_locale, usa el cuerpo que coincide con la locale de cada miembro; si no, el variant A/B; y si tampoco, el A único. Cuando la locale, el variant y el predeterminado dan el mismo cuerpo, se envían como un solo grupo por multicast (eficiencia).
- •Modal de reglas del administrador — «Enrutamiento multilingüe activado» y una entrada directa de JSON, desplegables mediante
<details>. Se muestra con formato pretty-print. Validación invalid_locale_json.
- •i18n en 13 idiomas —
community.automation.locale_{toggle,placeholder,hint} + fields.messages_by_locale + error.invalid_locale_json (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Notes
- •Las etapas 1 a 4 de la infraestructura de envío por LINE están completas (2.6.9 a 2.6.15):
1. Step 1 — CTA · utility stack · envío manual · audience por nivel · selección múltiple (7872c4b, 7c94a55, c4a3325)
2. Step 2 — Clasificación automática de grupos de miembros (b24979c)
3. Step 3 — Reglas de envío automático basadas en eventos (60d47cd) + cron de envío programado (8cf02ad)
4. Step 4 — Seguimiento de clics (79c99a84) + prueba A/B (86e19b0b) + enrutamiento multilingüe (este)
- •La migración 010 se aplicó en producción (
voscms.com). users.locale es una columna nueva — todos los miembros existentes son NULL (se usa la locale predeterminada del sitio → el mismo comportamiento que antes).
- •Una interfaz en «Mi página» donde los miembros configuren por sí mismos su locale preferida es el siguiente hito.
- •Registros de cron recomendados:
/5 /usr/bin/php8.3 /var/www/voscms-com/scripts/process-scheduled-broadcasts.php, 0 3 /usr/bin/php8.3 /var/www/voscms-com/scripts/recalculate-member-grades.php.
v2.6.14
2026-05-30 · hace 5 meses
Pruebas A/B (Step 4-B)
Added
- •migración 009 —
rzx_auto_broadcast_rules.message_template_b TEXT NULL, rzx_line_broadcasts.variant CHAR(1) NULL, rzx_broadcast_clicks.variant CHAR(1) NULL + un índice (source_rule_id, variant).
- •AutoBroadcaster::fire — si
message_template_b no está vacío, los userIds de LINE se dividen 50:50 en A/B mediante crc32 mod 2. Para cada variant, el historial de envío se inserta de antemano (INSERT, logQueued) → wrap del token → multicast → finalize. El mismo usuario recibe siempre el mismo variant en envíos repetidos (consistencia del hash).
- •Modal de reglas del administrador — la opción «Prueba A/B activada» y la entrada del variant B, desplegables mediante
<details>. Por defecto es el envío único simple, y el A/B es una activación explícita.
- •ClickTracker — propaga los metadatos del variant. Si existe la columna broadcast_clicks.variant, se rellena en el INSERT. Si no, se aplica una alternativa (compatible con entornos donde no se ha aplicado la migración 009).
- •Alternativa de logQueued — funciona también en entornos sin la columna variant (cuando no se ha aplicado la migración 009).
- •i18n en 13 idiomas —
community.automation.ab_{toggle,placeholder,hint} (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Notes
- •Las 3 sentencias ALTER de la migración 009 se ejecutaron por separado en producción (
voscms.com). Las reglas existentes tienen message_template_b=NULL (se mantiene el comportamiento de envío único).
- •Editar una regla → introducir el variant B en el desplegable «Prueba A/B activada» → basta con guardar, y desde la siguiente ocurrencia el reparto A/B se hace automáticamente.
Added
- •migración 008 (
rzx_broadcast_clicks) — token (8 caracteres base62, UNIQUE), broadcast_id, source_rule_id, original_url (2048), click_count, last_clicked_at, last_user_agent. La misma combinación (broadcast_id, original_url) reutiliza el token.
- •Clase
ClickTracker — wrap($pdo, $siteId, $body, $meta) extrae las URL http/https del cuerpo con una expresión regular → INSERT o reutilización → las sustituye por /t/{token}. consume($pdo, $token, $ua) convierte un token en la URL original e incrementa click_count.
- •Ruta
/t/{token} (index.php:330) — entra en el enrutador de expresiones regulares. ClickTracker::consume → redirección 302 a la URL original, o 404 si no existe. Validación del token de URL ([A-Za-z0-9]{4,16}).
- •Integración con las reglas de envío automático —
AutoBroadcaster::fire() funciona, justo antes de enviar, en el orden logQueued → obtención de broadcast_id → ClickTracker::wrap → multicast → finalizeBroadcast. Se garantiza el vínculo entre el token y broadcast_id.
- •Integración con el envío manual — en
api/broadcast.php, wrap justo después de ensamblar el texto del envío inmediato. En scripts/process-scheduled-broadcasts.php, wrap justo antes del envío desde la cola (se conoce el broadcast_id).
- •Ajuste del dominio de seguimiento — tiene prioridad
rzx_settings.click_track_domain, y si no existe, APP_URL. Es posible un dominio corto (por ejemplo s.voscms.com).
Notes
- •La migración 008 se ha aplicado en producción (
voscms.com).
- •Prueba de integración de wrap/consume: 2 URL en el cuerpo → 2 tokens de 8 caracteres insertados (INSERT) → al llamar a consume se devuelve la URL original y click_count aumenta correctamente.
Added
- •migración 007 (
rzx_line_broadcasts): las columnas scheduled_at DATETIME NULL y processed_at DATETIME NULL más un índice (status, scheduled_at). scheduled_at NULL = envío inmediato (como antes), una hora futura = en cola.
- •
scripts/process-scheduled-broadcasts.php — el punto de entrada del cron. Recoge 50 elementos con status='queued' AND scheduled_at <= NOW() AND processed_at IS NULL → los bloquea de inmediato con processed_at = NOW() (transacción) → resuelve la audiencia (all / grade_N, igual que broadcast.php) → multicast de LineMessenger en bloques de 500 → actualiza el estado con el resultado. Frecuencia de cron recomendada: /5 *.
- •Interfaz de broadcast.php: una casilla
[Programar envío] + una entrada datetime-local. Al marcarla, el valor predeterminado es la hora actual + 1 hora (hora local del usuario). Si no está marcada o está vacía, se envía de inmediato como antes. Solo el canal LINE se puede programar (en la aplicación y push siguen siendo inmediatos).
- •api/broadcast.php: procesa el parámetro POST
scheduled_at. Para una hora futura (> NOW + 30s) con send_line, solo inserta (INSERT) rzx_line_broadcasts (status='queued', scheduled_at) sin llamar a la API de LINE y devuelve scheduled: true, scheduled_at en la respuesta.
- •i18n en 13 idiomas:
community.broadcast.schedule_{label,enable,hint,success} (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW).
Fixed
- •Se corrigieron las columnas del INSERT para que coincidan con el esquema real de
rzx_line_broadcasts (title, body, audience varchar(20), recipient_count, status enum(queued|sent|failed)). Antes, el INSERT usaba nombres de columnas que no existen (message_text, recipients_count, sent_count, failed_count, audience_value, partial), e incluso la alternativa fallaba, por lo que faltaba el historial del envío automático.
Notes
- •La migración 007 se ha aplicado en producción (
voscms.com). Un dry-run confirmó que pickup era de 0 elementos.
- •Los árboles de producción (
voscms-com) y de salon están sincronizados. .env excluido.
- •El registro del cron corresponde al lado de operaciones de alojamiento (si hace falta,
/5 * /usr/bin/php8.3 /var/www/voscms-com/scripts/process-scheduled-broadcasts.php).
Added
- •Tabla de reglas de envío automático (
rzx_auto_broadcast_rules) — migración 005_auto_broadcast_rules.sql. Reglas aisladas por site_id, con las columnas trigger_type / trigger_filter (JSON) / audience (all|grade_X|custom) / message_template / is_enabled / fired_count / last_fired_at.
- •Despachador AutoBroadcaster (
rzxlib/Core/Notification/AutoBroadcaster.php) — punto de entrada estático AutoBroadcaster::fire($triggerType, $ctx). Coincidencia con las reglas activas → sustitución de variables de la plantilla ({{title}}, {{url}}, {{author}}, {{board_name}}, {{site_name}}) → resolución de la audiencia (misma lógica que broadcast.php; en el nivel is_default se incluyen automáticamente los miembros NULL) → consulta de los userIds de LINE mediante rzx_user_social_accounts → multicast en bloques de 500 → registro del historial en rzx_line_broadcasts → actualización de las estadísticas de la regla.
- •Hook de envío automático para nuevas publicaciones del foro (api-posts.php:184) — justo después del INSERT de una publicación (excluyendo las privadas), llama a
AutoBroadcaster::fire('board_post', [post_id, board_id, title, url, author, board_name]). Aunque falle el envío, la respuesta de la publicación no se ve afectada.
- •Página de administración de reglas (
/theadmin/community/automation) — interfaz en cuadrícula de tarjetas. CRUD de reglas + interruptor de activar/desactivar + insignia de color por desencadenador (board_post=blue, review_created=amber, reservation_created=green). El desencadenador / el filtro (JSON) / el nivel de destino / la plantilla del mensaje se editan en un modal.
- •Columnas de seguimiento del origen en rzx_line_broadcasts — migración
006_line_broadcasts_add_source.sql. source ('manual' | 'auto_rule') + source_rule_id (INT) + un índice. El historial del envío automático se distingue del envío manual en la misma tabla.
- •Rutas y migraciones registradas en plugin.json — la ruta
community/automation, y las migraciones 004/005/006 indicadas tras la 003.
- •i18n en 13 idiomas — todas las claves
community.automation.* (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW). Etiquetas de desencadenadores, campos, sugerencias, mensajes de éxito/error y plantillas predeterminadas.
Notes
- •Las migraciones 005/006 se han aplicado en producción (
voscms.com). Aún no hay reglas registradas.
- •Los árboles de producción (
voscms-com) y de salon están sincronizados. .env excluido.
- •El Step 3-B (envío programado
scheduled_at + cron) es el siguiente hito.
Added
- •Asignación automática del nivel predeterminado al registrarse (
rzxlib/Core/Auth/Auth.php::register()) — justo después del registro, se consulta en rzx_member_grades el nivel con is_default=1 y se rellena de inmediato el grade_id del nuevo miembro. En los sitios sin la tabla member_grades (heredados o sin configurar), se omite en silencio mediante try-catch. Los miembros NULL existentes se rellenan con un script de recálculo o un botón aparte.
- •Script CLI de recálculo de niveles (
scripts/recalculate-member-grades.php) — tomando como base la suma de los pagos de rzx_payments y el número de reservas de rzx_reservations (solo si la tabla existe), recorre los niveles en orden sort_order DESC y asigna el nivel más alto que se pueda cumplir. Los niveles sin reglas definidas (min_spent=0 AND min_reservations=0) se omiten automáticamente — solo los niveles con reglas explícitas son candidatos a ascenso. Los miembros que no cumplen las condiciones vuelven al nivel predeterminado.
- •Opciones:
--dry-run (simulación sin UPDATE real), --user-id=UUID (solo un miembro concreto).
- •Ejemplo de cron:
0 3 * /usr/bin/php8.3 /var/www/voscms/scripts/recalculate-member-grades.php
- •Botón de administrador «Recalcular ahora» (
/theadmin/members/groups) — se añadió a la barra de acciones un botón de tono esmeralda. Al hacer clic: modal de confirmación → AJAX action=recalculate_grades → aviso del resultado (objetivo/ascendidos/descendidos/nuevos) → recarga automática. Como incluye en línea la misma lógica que el script, funciona incluso en entornos sin permiso de PHP CLI.
- •i18n en 13 idiomas — nuevas claves
members.groups.recalculate* (ko/en/ja/de/es/fr/id/mn/ru/tr/vi/zh_CN/zh_TW): recalculate, recalculate_hint, confirm_recalculate, recalculate_stat.{total,upgraded,downgraded,fallback}, success.recalculated, error.{no_grades,no_default_grade}.
Notes
- •En producción (
voscms.com) se verificó --dry-run → ejecución real → nueva ejecución tras reforzar las reglas. Los 5 miembros con grade_id NULL fueron asignados correctamente a Normal (predeterminado). VIP/Gold/Silver no tienen reglas y se mantienen en 0 miembros hasta que se añadan reglas (intencionado).
- •Los árboles de producción (
voscms-com) y de salon están sincronizados. .env excluido.
Added
- •Componente CTA para añadir amigo de LINE dirigido a los miembros (
resources/views/components/line-friend-cta.php) — 5 variants (card / button / bar / side-tab / bubble) × 6 posiciones (bottom-right/bottom-left/bottom-center/top-right/middle-right/middle-left) × 3 tamaños, colores personalizados, retraso de aparición y «recordar cierre» durante 24 horas (localStorage).
- •Interfaz de ajustes modificable — nueva sección «CTA para añadir amigo de LINE» en la parte inferior de la tarjeta del canal LINE en
/theadmin/settings/message-api. Basic ID, interruptor de activación, radio de variant, selector de posición, tamaño, selector de color y entrada de delay.
- •Se muestra automáticamente en 5 diseños — se incluye automáticamente desde
skins/layouts/{default,modern,minimal,modern-copy-260413}/footer.php. En las páginas de pago, solicitud de servicio y flujo de instalación se omite automáticamente.
Added
- •Nuevo componente
resources/views/components/utility-floating-stack.php — el patrón de diseño cameron_plugin de thevos.jp (un stack cuadrado de 80×80 en el borde derecho + 1 px de separación + un color de énfasis por icono). La posición, el tamaño y el color los ajusta el operador.
- •Ambos modos conviven —
utility_stack_mode = separate (predeterminado, cada CTA por separado) / unified (PWA + LINE mostrados juntos como un stack). En el modo integrado se desactiva la visualización automática de los 5 variants del CTA de LINE y los disparadores automáticos del modal de instalación de PWA (banner con setTimeout/DOMContentLoaded); el modal se abre solo al hacer clic en el icono de PWA del stack.
- •Ampliación natural en el futuro — con un patrón de registro de elementos (canal de Kakao, WhatsApp, suscripción al boletín, interruptor de modo oscuro, etc.), otros elementos pueden sumarse al stack simplemente añadiendo claves de configuración.
- •Salto de línea automático garantizado — el CSS en línea
word-break: break-all; overflow-wrap: anywhere; max-width: 72px evita el límite de ancho de los hijos flex de Tailwind. Las etiquetas CJK también saltan de línea de forma natural dentro del cuadro.
- •i18n en 13 idiomas — el grupo
line_cta. (22 claves) y el grupo utility_stack. (12 claves) en resources/lang/{13 locale}/messaging.php. En japonés y chino, los espacios en los límites de sentido (por ejemplo «アプリとして インストール») garantizan posiciones naturales de salto de línea.
Added
- •
LineMessenger::broadcast(messages) + broadcastText(text) — envía de una vez a todos los amigos (hasta 5 objetos de mensaje; la cuota de envío de LINE se descuenta por el número de destinatarios × el número de mensajes).
- •
LineMessenger::multicast(userIds[], messages) — envía hasta a 500 userIds indicados (infraestructura de envío por grupos).
- •Mejora de la interfaz
/theadmin/community/broadcast — se añadió la casilla «Enviar también a los amigos de LINE» (banner verde). Independientemente del envío push, envía automáticamente a los amigos de LINE por multicast/broadcast.
- •Envío por nivel de miembro —
rzx_member_grades se carga dinámicamente y las opciones radio por nivel aparecen automáticamente (punto de color + slug + recuento en tiempo real). El formato de audience es grade_<id>.
- •Los miembros con
grade_id IS NULL se incluyen automáticamente en el nivel con is_default=1 — incluso en sitios donde no se asigna nivel automáticamente al registrarse, los miembros NULL se incluyen de forma natural en el recuento y el envío del nivel predeterminado.
- •Tabla de historial de envíos
rzx_line_broadcasts — aislamiento por site_id, method (broadcast/multicast), audience, title/body, recipient_count, line_request_id, api_status/error, status. Al enviar se inserta una fila automáticamente (INSERT).
- •Con
audience=all se elige broadcast; en otros casos, multicast, automáticamente — recopila los userIds de LINE de los usuarios destinatarios con user_social_accounts.line_friend=1 y envía en bloques de 500.
- •Ancho unificado — en broadcast.php,
max-w-3xl → max-w-7xl (igual que otras páginas de administración).
- •i18n en 13 idiomas — se añadieron las claves
send_line_label/desc y audience_grade_group.
Changed
- •
resources/views/admin/dashboard.php — el comentario de cabecera, el respaldo de pageTitle y el banner de aviso de actualización RezlyX v... se corrigieron todos a «VosCMS».
- •
resources/views/admin/partials/admin-topbar.php — se corrigió el comentario de cabecera.
Fixed
- •
storage/.update_cache conservaba una caché antigua con current: "2.6.4", por lo que incluso tras actualizar a v2.6.8 el panel seguía mostrando el banner «Hay una nueva actualización» — al borrar la caché, se recalcula automáticamente en la siguiente solicitud.
- •Los cambios acumulados de dev → producción no estaban sincronizados — aparte de esta sesión, muchos archivos como install-core.php (parches de los defectos n.º 1 a n.º 7) y version.json nunca se habían sincronizado con producción. Se sincronizó dev con producción de una vez mediante rsync (342 archivos modificados + 196 nuevos, 6,3 MB, excluyendo datos de usuario,
.env, storage/, uploads/, vendor/ y .git/).
Documentation
- •
docs/LINE_FRIEND_CTA_DESIGN_GUIDE.md — se añadieron la Part 3 (implementación real del Utility Floating Stack integrado), la Part 4 (implementación real de la operación de envío de la Fase 1), la Part 5 (escenarios de usuarios externos — análisis del valor del envío con selección múltiple) y la Part 6 (hoja de ruta de las Fases 2 a 4). De unas 631 a 920 líneas.
Regression Test
- •Verifica la existencia de
utility-floating-stack.php y de los 5 archivos partial de variants de line-friend-cta.
- •Verifica la existencia de los métodos
LineMessenger::broadcast/multicast.
- •Verifica la existencia del archivo de migración
plugins/vos-community/migrations/004_line_broadcasts.sql.
- •Verifica la existencia de las claves
messaging.utility_stack. + community.broadcast.send_line_ en 13 idiomas.
- •Verifica automáticamente que no queden restos de RezlyX en dashboard.php / admin-topbar.php.
Fixed
- •Consolidación de la tabla de licencias de vos-market — 6 archivos API consultaban la tabla incorrecta
{$pfx}vos_licenses, causando "Server error" 500 en cada pago. Toda la verificación de licencias ahora se enruta al único maestro vcs_licenses (gestionado por vos-license-server).
- •Eliminación del agujero de seguridad de registro maestro no verificado en
sync.php — El código anterior hacía UPSERT en vos_licenses sin verificar vos_key, permitiendo a cualquiera registrar automáticamente claves de licencia arbitrarias en el maestro mediante un simple POST. El rastreo ahora lo gestiona el existente rzx_mkt_sync_reports (por elemento).
- •
migrations/007 obsoleto — La migración mal diseñada ahora es no-op (SELECT 1), con razón e historial de cambios en comentarios.
Added
- •4 tarjetas de estadísticas — Sitios rastreados / Sitios con problemas / Licencias multidominio / Inactivos 30+ días.
- •Pestaña de violaciones (vista a nivel de elemento) — Vea de un vistazo qué widget de pago está instalado sin licencia en qué dominio. Se muestran precio, versión, vos_key y última vez visto.
- •Pestaña multidominio — Casos donde una sola clave de licencia se reporta desde varios dominios.
- •Pestaña inactivos — Sitios sin señal de sincronización por 30+ días.
- •Definición refinada de violaciones —
unknown_product y elementos gratuitos están excluidos. Solo cuentan los artículos de pago del marketplace en estado sin licencia.
- •Lista blanca de dominios propios — Los dominios propios almacenados como matriz JSON en
settings.mkt_owner_domains se excluyen automáticamente de los recuentos.
- •i18n de 13 idiomas — Barra lateral, tarjetas, pestañas y textos de ayuda están todos debidamente traducidos.
Changed
- •
settings/system/updates — Tarjetas de versión instalada y última versión corregidas a "VosCMS vX.Y.Z".
- •
updates/ajax.php — Fallback de nombre de DB y User-Agent corregidos.
Regression Test
- •Verifica automáticamente que no haya residuos SQL de
vos_licenses en 6 archivos de vos-market
- •Detecta regresión del agujero de seguridad INSERT en
sync.php
- •Verifica que se mantenga el estado no-op de
migrations/007
- •Verifica automáticamente que no haya residuos de RezlyX en 3 archivos de la pantalla de actualizaciones
Fixed
- •Inicialización automática de
site_url — El asistente de instalación escribía el siteUrl introducido por el usuario solo en APP_URL del .env y no en rzx_settings.site_url de la base de datos, por lo que todas las funciones de un sitio recién instalado que dependen de site_url (instalación automática, SEO, enlaces de correo, etc.) se rompían. Se corrigió añadiendo site_url al arreglo $settings de install-core.php.
- •Registro automático en
rzx_site_domains — El asistente de instalación no añadía automáticamente el dominio de instalación actual a la tabla site_domains, por lo que SiteRouter no reconocía el dominio. Ahora el dominio instalado se INSERTA automáticamente como primary de site_id=1.
- •Escritura automática de
MARKET_API_URL en .env — En una instalación nueva el .env no tenía la dirección de la API del mercado, e InstallerService no encontraba el destino de la llamada. Ahora se escribe automáticamente con el valor predeterminado https://market.voscms.com/api/market (operación con un único maestro de licencias).
- •Errata en el nombre de tabla de la API de instalación del mercado —
vos-market/views/api/item/install.php consultaba la tabla inexistente rzx_vos_licenses al verificar la licencia, por lo que todas las instalaciones automáticas respondían «Server error» (HTTP 500). Se corrigió a la tabla real vcs_licenses (el maestro de licencias, fijo y sin prefijo).
- •Configuración automática de permisos de los directorios widgets/plugins/themes — Tras la instalación, el usuario de PHP-FPM no podía escribir en estos directorios, y la instalación automática fallaba en silencio. El asistente de instalación concede ahora automáticamente setgid y escritura de grupo (el grupo de ejecución de PHP se detecta automáticamente para adaptarse a la diversidad de entornos externos).
- •Múltiples alternativas en
install-handler.php — En un entorno multisitio, la consulta de site_url usaba LIMIT 1 sin filtro de site_id y era inestable. Una alternativa de tres pasos, SiteRouter → alternativa a site_id=1 → $_SERVER['HTTP_HOST'], garantiza el reconocimiento del dominio en cualquier entorno.
Added
- •
InstallerService::installTheme() — Lee el theme.json de un producto type=theme e instala de forma recursiva los plugin/widget/skin incluidos en requires. Garantiza la validación de 13 idiomas y la idempotencia (los elementos ya instalados se omiten).
- •
runPostInstall — Justo después de instalar un tema, ejecuta automáticamente las semillas post_install/settings.json / boards.json / pages.json, configurando de una vez páginas, colocación de widgets y ajustes predeterminados.
- •13 idiomas obligatorios (
REQUIRED_LOCALES) — Los objetos multilingües (name, description, title de la página, config.title de los widgets, etc.) solo pasan oficialmente si tienen las 13 claves. Se avisa de los locales que faltan.
- •Maestro
vcs_licenses único — Toda la emisión y verificación de licencias se gestiona a través del mercado de producción (market.voscms.com). Los entornos dev y test usan el mismo maestro.
Added
- •Verificación automática de estos seis defectos — 13 elementos en total, entre ellos la lógica de semillas del asistente de instalación, los permisos de directorios, la API del mercado, la accesibilidad del servidor de licencias, la rama theme de InstallerService y las múltiples alternativas de install-handler.
- •Gancho de ejecución automática en build-voscms.sh — Se invoca automáticamente al final de la compilación. Si ocurre aunque sea un FAIL, el código de salida es distinto de 0 y se bloquea la distribución. Si el mismo defecto reaparece en una compilación posterior, se detecta de inmediato.
Documentation
- •
docs/theme-package/THEME_PACKAGE_SPEC.md — Define el estándar de paquetes de temas de VosCMS (theme.json). Recoge el ejemplo del tema integrado Salon, el esquema detallado de post_install y la regla de 13 idiomas.
Fixed
- •Copias de seguridad de actualización: se conserva solo una por versión —
Backup::createFull() elimina ahora la copia existente de la misma versión antes de crear una nueva. Se resuelve el problema por el que, al repetir actualizaciones o restauraciones en la misma versión, se acumulaban duplicados de esa versión en la lista de copias (solo se conserva el último estado justo antes de la actualización).
v2.6.5
2026-05-27 · hace 5 meses
Puesta a punto del sistema de actualización (canal único, sesión/permisos, reparación de la versión distribuida)
Fixed
- •Restaurado el
update-api.php que faltaba en la versión distribuida — El script de compilación clasificaba por error update-api.php, el backend de actualización, como «archivo dev» y lo eliminaba de la versión distribuida. Como resultado, la pestaña de actualización de todas las instalaciones distribuidas no funcionaba y mostraba «File not found». Ya no se excluye de la compilación y ahora se incluye correctamente en la versión distribuida.
- •Corregido el fallo de reconocimiento de la sesión de inicio (Unauthorized) —
update-api.php miraba la ruta de sesiones predeterminada (/var/lib/php/sessions); ahora usa el mismo storage/sessions que index.php. Se resuelve el error Unauthorized que aparecía incluso con la sesión iniciada como administrador.
Changed
- •Las actualizaciones del Core usan solo el manifest de distribución propio como único canal — Se eliminaron de
update-api.php los restos de etiquetas de GitHub, tokens y diagnóstico (se bloquea la ruta por la que check sobrescribía el resultado del manifest con una etiqueta de GitHub). El mercado no tiene Core (solo plugins).
- •Seguridad reforzada — Se añadió a
update-api.php la verificación CSRF y la comprobación de permiso de maestro (propietario).
- •Lista de conservación de actualizaciones ordenada —
skins y config se quitaron de la lista de conservación. Como applyUpdate solo copia, los skins del núcleo y la config del zip distribuido se actualizan, mientras que los archivos personalizados de los clientes que no están en el zip se conservan.
Changed
- •Se eliminó el botón duplicado «Buscar actualizaciones» — al entrar en la página se comprueba automáticamente la última versión.
- •Se corrigió que la lista de copias de seguridad desapareciera ante un error temporal → la sección se mantiene y se muestra vacía.
- •Ante un error, en lugar de mensajes en bruto como
Unauthorized se muestra el aviso «Actualmente usa la última versión».
Added
- •Sección
um en 13 idiomas añadida — todos los locales de vos-community/lang/{locale}.php (ko·en·ja·de·es·fr·id·mn·ru·tr·vi·zh_CN·zh_TW): follow·following·message·block·unblock·report·report_about·report_reason/detail/placeholder·reason_spam/harassment/inappropriate/other·report_submit/sent/failed·msg_title/placeholder/send/sent/failed·block/unblock_confirm·block/unblock_done·cancel·failed·network_error
- •i18n completa de
user-mention-menu.php — todo el coreano codificado en el HTML de PHP reemplazado por __('community.um.*'); las cadenas de tiempo de ejecución de JS se emiten como un objeto PHP mediante UM_LANG = <?= json_encode(...) ?>, y el ayudante umT(key, name) sustituye el marcador :name
- •Menú de menciones permitido para invitados — se reorganizó la estructura de guardia en
community-overlays.php: las rutas de admin/API se bloquean por completo, mientras que user-mention-menu se incluye incluso para invitados (seguir/reportar invitan a iniciar sesión); las guardias de acción de página y username siguen siendo solo para usuarios autenticados
Changed
- •
boardPreloadUsers(array $rows) añadida (board/_init.php) — carga en bloque la lista de user_id en una sola consulta IN, compartida mediante la caché $GLOBALS['_boardUserCache']
- •
boardAuthorMention() ampliada — se añadieron los atributos data-user-username·data-user-avatar (vincula la URL de la historia /story/{username} con precisión, muestra la imagen del avatar). Prefiere un HIT de la caché de precarga y recurre a una consulta individual perezosa en caso de fallo
- •
board/list.php·board/read.php·skins/board/qna/_list-full.php — llaman a boardPreloadUsers() antes de renderizar para evitar consultas N+1
Fixed
- •Analizador de visualización ampliado (
resources/views/system/changelog/index.php) — se añadió el renderizado de cercas de código (``), tablas (|...|), enlaces markdown (text), subtítulos #### y líneas horizontales (---`). Resuelve que versiones anteriores (2.4.1·2.3.8·2.3.7, etc.) que usan esta sintaxis se mostraran como markdown sin procesar
- •Fallback de locale por versión — se cambió de obtener toda la lista en un solo idioma a una base por versión (
locale actual → en → ko). Ahora cada versión aparece en cualquier idioma, y las versiones aún no traducidas muestran una insignia de idioma de respaldo
- •Alineación de límites de bloque (
rzxlib/Core/Changelog/ChangelogParser.php) — se evitó que un encabezado ## [Unreleased] sin fecha fuera absorbido por la versión anterior (p. ej. 1.5.0), dividiendo los bloques en cada ## [
Added
- •Migración 056 tabla
dist_downloads (site_id·version·ip·user_agent·referrer·utm_*·is_bot·created_at)
- •Endpoint de descarga
api/dist-download.php — en lugar de un enlace directo estático /download/*.zip, las solicitudes pasan por él para registrar estadísticas por versión / día / origen (referrer·UTM) y luego redirige con 302. Los UA de bots se cuentan por separado; lista blanca de versiones (a prueba de path-traversal)
- •Página de estadísticas de administración
resources/views/admin/dist-downloads.php (solo maestro, 13 idiomas) — tarjetas de resumen (total/hoy/7d/30d), barras por versión, tendencia diaria (30d), Top-10 de fuentes, UTM. Gráficos SVG/CSS propios (sin librería externa)
- •Cambiado el enlace de descarga del widget
download-hero al endpoint de conteo
- •Añadido el menú de administración «Estadísticas de descargas» (config/admin-menu.php, solo maestro)
Added
- •
hero-cta-text — área de texto superior (escritura). Los valores por defecto coinciden con el contenido en vivo de hero-cta2 (13 idiomas); la configuración del usuario tiene prioridad
- •
hero-cta-button — botón de descarga (detecta automáticamente la última versión + cuenta las descargas, sin configuración) más un botón de marketplace opcional (un solo botón si se deja vacío)
- •Etiquetas y valores por defecto del formulario de ajustes en 13 idiomas (soporte de objeto
getLabel del constructor)
- •Unificado el color/fondo del botón de descarga con hero-cta2 (#2563eb)
Added
- •Arrastra una tarjeta de la paleta al lienzo para añadir un widget (grupo
pull:'clone' de SortableJS + reemplazo por bloque real en el onAdd del lienzo). Funciona junto al añadido por clic y el reordenamiento con ↑↓/asa existentes
- •El flyout permanece abierto durante el arrastre + resaltado de la zona de soltado (
wb-drop-active)
Changed
- •Eliminado el espacio del envoltorio de página entre widgets (
type-widget.php py-2 → 0)
- •Estabilizada la traducción de IA síncrona de comentarios — nginx
fastcgi_read_timeout 150s (vhost del foro) · PHP-FPM request_terminate_timeout 150 · set_time_limit 150, OllamaProvider keep_alive 30 min (el modelo permanece residente, eliminando arranques en frío)
- •Añadidos botones de copia para la URL del Webhook y la Redirect URI en las pestañas de Message API e Inicio de sesión social (13 idiomas, con fallback para contexto no seguro)
Added
- •Migración 036 — columnas
withholding_tax · consumption_tax · tax_basis en partner_earnings / payouts
- •Migración 037 — columnas
fx_rate · fx_amount · fx_currency en payouts (registro de transferencias convertidas a USD)
- •Migración 038 — columna
vcs_developers.paypal_email
- •Helper
rzxlib/Core/Helpers/MarketTax::calculate() — modelo de deducción estándar japonés
· El precio de venta incluye impuestos (税込); el mercado paga el IVA directamente a la NTA
· freelance → deducción de retención del 10,21% / invoice_eligible → crédito fiscal de compras / no_invoice → sin pago de IVA
· Sin categoría o sin datos → se fuerza freelance (conservador: un pago de menos puede reembolsarse; un exceso no puede recuperarse)
- •Script de relleno
scripts/market-tax-backfill.php — recálculo de earnings/payouts existentes (protege status='paid', admite --dry-run)
- •Vista de liquidación en 5 partes: ingresos → comisión → deducción de IVA → retención (freelance) → pago neto
- •Nuevas claves en
rzx_mkt_settings: min_payout_amount · consumption_tax_rate · default_commission_rate (settings tiene prioridad en todos los lugares; se elimina el fallback hardcodeado)
Added
- •Modal de liquidación en
/theadmin/market/partners/payouts — prompt() × 2 + alert() → modal único
· JOIN con vcs_developers para relleno automático de banco/sucursal/titular/número de cuenta/tipo de empresa/número T (prefill; PII enc: descifrado automático)
· Cálculo automático de las 5 partes + chips de base legal japonesa (freelance_withholding, etc.) + cita de artículos (§204, sistema de facturas)
· Toast de notificación en la página; cierra el modal y recarga automáticamente
- •Conversión automática a USD para cuentas fuera del país de operación (Migración 037)
· Tipo de cambio automático Frankfurter API (cache en storage con TTL de 24 h) + anulación manual del administrador (TTS bancario)
· Selección automática de method=paypal
· Para extranjeros: se oculta la tarjeta de cuenta bancaria y se muestra la tarjeta de correo PayPal (bank_country == site_country)
- •Especificación del importe de liquidación por el desarrollador (
/mypage/developer/earnings)
· Saldo completo → entrada de importe + botón de importe total + validación de importe mínimo
· Columnas en 5 partes para el historial de ventas, cuadro informativo de liquidación (ley fiscal aplicable según tipo de empresa)
- •Conversión a USD y tipo de cambio como información auxiliar en el historial de pagos (tanto en la pantalla del administrador como en la del desarrollador)
- •Reemplazo del gráfico de tendencia de ventas por gráfico de barras SVG (corrige el problema de visibilidad de las barras div)
Added
- •Política de bloqueo basada en
tax_review_status (perfil del desarrollador)
· not_submitted / rejected → editable
· pending / approved → bloqueo de edición (candado + aviso)
· rejected → editable + hint "al guardar, entra en la cola de revisión"
· Defensa en servidor: HTTP 423 Locked (bloquea el bypass de la interfaz)
- •
update.php actualización parcial segura — UPDATE dinámico solo de los campos enviados (anterior: sobreescritura total con riesgo de NULL en campos no enviados)
- •Enlace a sitio de verificación oficial (pantalla de verificación del administrador + perfil del desarrollador)
· 🇯🇵 Portal de publicación del número de persona jurídica de la NTA (https://www.houjin-bangou.nta.go.jp/)
· 🇯🇵 Portal de publicación de emisores de facturas calificadas de la NTA (https://www.invoice-kohyo.nta.go.jp/regno-search/)
· 🇰🇷 Consulta de estado de registro de empresa en Hometax de Corea
· Botón de copiado con un clic junto al número de empresa/número T
Added
- •Registro nuevo (
/mypage/developer) — selección de nacionalidad movida al inicio del formulario, con guía comparativa con el país de operación
· country == site_country → 🏠 formulario doméstico (tipo de empresa / cuenta bancaria)
· country != site_country → 🌐 formulario extranjero (freelance automático + correo PayPal obligatorio)
· Getter isDomestic de Alpine.js, constante global OPERATING_COUNTRY
- •Edición de perfil (
/mypage/developer/profile) — misma estructura (regla [feedback_admin_user_parity])
· Extranjero: sección de empresa oculta; información de liquidación = solo correo PayPal
· Bloqueo de nacionalidad (contactar al administrador para cambios), chip de modo en la parte superior
- •Validación en servidor del API de registro — correo PayPal obligatorio para extranjeros, freelance forzado, valores de empresa/cuenta ignorados
Added
- •Sustitución automática de tokens en
page.php — aplicada globalmente a todas las páginas del sistema
· {{COMPANY_NAME}} {{COMPANY_ADDRESS}} {{COMPANY_PHONE}} {{COMPANY_EMAIL}} {{COMPANY_REG_NUMBER}} {{COMPANY_REPRESENTATIVE}}
· {{SITE_NAME}} {{SITE_URL}} {{DOMAIN}}
· Mapeo automático desde settings business_ o site_ del administrador
· page_title / page_subtitle personalizados también pasan por sustitución de tokens
- •Prioridad de
{{DOMAIN}} — official_domain (dominio oficial para términos y políticas) → host de site_url
· Incluso en el servidor de desarrollo vos.21ces.com, el cuerpo de los términos muestra voscms.com
Added
- •Reemplazo completo de
terms / privacy en rzx_page_contents (13 filas cada uno) — Términos de la plataforma de reservas RezlyX → Términos del software libre VosCMS
- •Términos de servicio con 18 artículos: objeto · definiciones · publicación y revisión · normas complementarias · celebración del contrato · privacidad · notificaciones · obligaciones de la empresa · obligaciones del usuario · prestación del servicio · modificaciones · restricciones de uso · licencia (uso gratuito) · propiedad y conservación de datos · derechos de autor de publicaciones · exención de responsabilidad (AS-IS) · indemnización · resolución de disputas
- •Política de privacidad con 10 artículos: finalidad del tratamiento · categorías · plazos de conservación · transferencia y encargo a terceros · derechos del interesado · medidas de seguridad · cookies · responsable de protección · recurso ante vulneraciones (PPC) · modificaciones
- •Ley japonesa + jurisdicción exclusiva del Tribunal de Distrito de Fukuoka, citando APPI §33·§34·§35
- •Los datos de la empresa usan tokens → una sola edición en el administrador sincroniza automáticamente los términos y la política
- •La versión japonesa es el texto oficial; traducciones nativas formales en 13 idiomas
- •Tarjeta de información de la empresa — solo 3 elementos: nombre legal, domicilio y teléfono (se elimina representante legal/nombre para evitar revisiones de términos al cambiar el registro)
- •Scripts de conservación:
scripts/voscms-terms-update.php · scripts/voscms-privacy-update.php
Fixed
- •
menus-api.php de /theadmin/site/menus estaba 99 líneas por detrás de vos, provocando validación y traducción omitidas al guardar/editar menús
- •Sincronización de menus-api.php de vos → market:
· mn_link_target_exists() — los menús de acceso directo (enlace) solo se guardan tras verificar que la página de destino (página del sistema · rzx_page_contents) existe
· Defensa contra reutilización de ID de menú — limpieza automática de rzx_translations.menu_item.{id}.* stale para nuevos IDs
· Método INSERT para traducciones: INSERT IGNORE → INSERT … ON DUPLICATE KEY UPDATE (permite sobreescritura)
· Al cambiar title·url, limpieza completa de traducciones antiguas + reregistro automático de traducciones al coincidir con página del sistema (config/system-pages.php)
- •Añadidos 4 archivos
admin/site/menus*.php a INCLUDES de sync-to-market.sh
Fixed
- •Tabla
rzx_contact_messages faltante → añadida migración 041_create_contact_messages.sql (IF NOT EXISTS, sin efecto en vos ya existente)
- •Doble layout en
contact-messages.php — _head.php ya emite sidebar+topbar pero la vista volvía a incluirlos directamente, resultando en <body> duplicado y main anidado. Reorganizado según el patrón vos (cabecera = _head 1 vez, pie = _foot 1 vez)
Added
- •Migración 040
040_create_developer_platform.sql — creación conjunta de 7 tablas: vcs_developers + vcs_developer_earnings/payouts/activity/notes + vcs_review_queue + rzx_mp_items (IF NOT EXISTS, esquema final tras aplicar ALTER 030~038)
· vcs_developers en vos.21ces.com solo existía en el dump sin migración CREATE TABLE — corrige el fallo de ALTER 030 en sitios creados posteriormente
- •Sincronización de
mypage/profile-sidebar.php — consulta rzx_mkt_partners (antigua) → consulta vcs_developers (nueva) (integración de actividad de desarrollador en mi página)
- •Parche de rutas
index.php de market (manual, evitando sync completo):
· /developer/ → /mypage/developer/ redirección 301 (mapeo login/dashboard/my-items/submit/earnings/profile/logout)
· Rutas mypage/developer/{register|items|items/new|earnings|profile} + mypage/developer/items/{N} + mypage/developer/items/{N}/edit
- •Añadidos migración 040 + profile-sidebar.php a INCLUDES de
sync-to-market.sh
- •Copia de seguridad automática de la BD de market (
storage/backups/market-YYYYMMDD-HHMMSS/market_vos.sql) antes de aplicar la migración 040
Added
- •Los términos de market.21ces.com siguen el modelo de mercado operado por terceros, distintos de los términos del software libre VosCMS
- •Términos con 18 artículos (con reglas de operación del mercado):
· Art. 8: el contrato de compraventa se establece entre comprador y desarrollador; la empresa actúa como intermediaria en el pago y la entrega
· Art. 9: cuatro excepciones de reembolso para productos digitales (antes de la descarga · discrepancia en descripción 7 días · confirmación de malware · error del sistema) — cumple los requisitos de divulgación del art. 15-2 de la Ley de Transacciones Comerciales Específicas de Japón
· Art. 10: sin porcentaje de comisión explícito (se remite a "tasa publicada por separado"; permite ajustes sin revisar los términos)
· Art. 10: retención japonesa del 10,21% según art. 204 de la Ley del Impuesto sobre la Renta; para titulares de número T, el IVA lo paga directamente la plataforma a la NTA; cuentas fuera del país de operación → transferencia vía PayPal en USD
· Art. 14: los términos individuales del desarrollador tienen prioridad — excepto para las operaciones de la plataforma (pago, liquidación, procedimiento de reembolso)
· Art. 17: límite de indemnización: total de comisiones/liquidaciones de los 6 meses anteriores
· Art. 18: ley japonesa + jurisdicción exclusiva del Tribunal de Distrito de Fukuoka
- •Política de privacidad con 12 artículos (perspectiva de mercado de tres partes):
· Categorías de datos — comunes / compradores / desarrolladores (comunes) / residentes en Japón / residentes en el extranjero / entrega: 6 categorías
· Plazos de conservación — miembros 1 año · transacciones 7 años · liquidaciones/retenciones 7 años · verificación de identidad 5 años · logs 6 meses · consultas 3 años
· Encargados — Stripe · KOMOJU · PayPal · Frankfurter · SendGrid · NTA explicitados
· Transferencias internacionales — EE.UU. (PayPal/Stripe/SendGrid) · UE (tipos de cambio) · otros países (pasarela de pago/banco local) explicitados
- •Scripts:
scripts/market-terms-update.php · scripts/market-privacy-update.php (UPSERT — UPDATE primero; INSERT si no existe el registro)
- •BD de market actualizada (terms/privacy, 13 filas cada uno en rzx_page_contents), con títulos y subtítulos multilingües de page-i18n también aplicados
Added
- •Corrección del mapeo faltante de configuración de skin (
page_title · page_subtitle) — los valores introducidos en la pantalla de ajustes no se mostraban en la página
- •Creación del parcial
_page-title-bg.php faltante (soluciona la advertencia de include en nginx error log)
- •Opción color añadida a
title_bg_type + campo title_bg_color — fallback automático a color cuando imagen/vídeo están vacíos
- •Eliminado el fallback de caja blanca cuando
content_bg=transparent (corrige el aspecto estrecho del contenido max-w-5xl dentro de la tarjeta)
- •Variable CSS
--page-content-width generada automáticamente — componentes incluidos externamente como la barra de acciones "Sobre esta página" (vos-community) se alinean con el mismo ancho que el content_width de la página
Fixed
- •PDOException en la página de liquidaciones — eliminada la referencia a la columna
p.currency inexistente en rzx_mkt_partners
- •Gráfico de tendencia de ventas invisible — las barras div con altura en % aparecían casi en 0px según el entorno → reemplazado por gráfico basado en SVG viewBox (visibilidad mínima de 4px asegurada)
Added
- •Pantalla de detalle de artículo en el área de desarrolladores (
/mypage/developer/items/{id}) — estadísticas, historial de versiones, tendencias de descarga
- •Pantalla de edición de artículo del desarrollador (
/mypage/developer/items/{id}/edit) — misma UI de 3 pestañas que items-new, valores existentes pre-rellenados, la carga de nuevo ZIP sigue un flujo separado
- •Nueva API:
/api/developer/item-update — actualiza name(JSON), short/desc(JSON), tags(JSON), price, license, repo/demo URL, dependencias, versión mínima, icon, screenshots
- •Nueva API:
/api/developer/request-payout — solicitud de liquidación
- •Nueva API:
/api/developer/update-bank — actualizar propia cuenta de liquidación
- •Nueva API:
/api/developer/issue-reply — publicar respuesta de Q&A (canal de respuesta del propio desarrollador)
- •Pantalla de detalle de desarrollador en admin (
/theadmin/developers/show) — tarjeta de empresa/cuenta/documentos + modo de revisión de documentos (aprobación/rechazo)
- •Verificación de correo para registro e inicio de sesión (
/verify-email) mejorada + campo company guardado en Auth::register
- •Claves de traducción oficial en 13 idiomas añadidas (
show_edit_btn, items_edit_*, items_back_detail, api_updated, etc.)
Added
- •Formularios de envío de reseñas, incidencias y Q&A integrados directamente en los widgets (sin navegación a página separada)
- •Solo los compradores pueden enviar — los no compradores ven el formulario pero reciben un aviso al intentar enviarlo
- •Las respuestas de Q&A admiten hilos (respuestas a respuestas) — migración DB 035 (
parent_reply_id)
- •Nuevas APIs:
/plugins/vos-market/views/api/item/post-issue.php, post-reply.php, post-review.php
- •Core
rzxlib/Core/Helpers/Notifications.php — helper de notificaciones (complementa la migración DB 034)
Added
- •Soporte completo en 13 idiomas aplicado a todas las pantallas:
/theadmin/market/licenses, sync, orders, settings, submissions, partners/payouts, items
- •Pantalla de configuración del marketplace — etiqueta "Partner" unificada a "Developer" (solo texto visible; nombres de variables conservados por compatibilidad)
- •La entrada del nombre del artículo del marketplace usa el modal de entrada multilingüe compartido —
name_json editado simultáneamente en 13 idiomas
- •
plugins/vos-market/lang/* — nuevo grupo de idiomas a nivel de plugin
- •
resources/lang/*/marketplace.php — los 13 idiomas traducidos en su lengua nativa (cumple la regla de traducción completa i18n)
Added
- •
033_developer_notes_activity.sql — notas y registro de actividad por desarrollador
- •
034_notifications.sql — tabla rzx_notifications (notificaciones de usuario)
- •
035_issue_reply_thread.sql — columna rzx_mkt_issue_replies.parent_reply_id añadida (respuestas en hilo)
Fixed
- •
/Market?item={slug} slug faltante — corregido en items-show mediante consulta de columna única dedicada
- •Nombre de columna
icon_url en la API de actualización de artículo corregido al nombre real icon
- •Pantalla de configuración del marketplace en blanco — migración 005 faltante para
rzx_mkt_settings aplicada
Added
- •VARCHAR(12)
users.username + UsernameValidator (mínimo 6 caracteres, restricción de caracteres especiales)
- •Modal de acceso en la primera visita tras el inicio de sesión (AJAX
/api/username-set)
- •Traducciones completas en 13 idiomas (se eliminó el fallback al inglés)
Added
- •
/profile/{uuid} → /story/{username} redirección 301
- •Etiqueta del menú contextual cambiada de "Ver perfil" a "Ver historia"
Added
- •Plugin vos-story extraído del núcleo — dedicado a
/story/{username}
- •Encabezado estilo Facebook + 5 pestañas fijas (Todo / Info / Fotos / Seguidores / Siguiendo)
- •CRUD de publicaciones + feed SNS (
vos_story_posts, body_format = text/html)
- •Alternador TEXT/EDIT + integración con Summernote
- •Adjuntos de imágenes (múltiples) + cuadrícula de la pestaña Fotos
- •Sistema de me gusta + comentarios
- •Sistema de skin de Story (
skins/story/default) + token por usuario (/mypage/story)
- •Página de política de administrador (
/admin/vos-story/policy)
- •Barra de acciones de página: i18n + posición (final del contenido principal) + token de ancho de contenido
- •Cuadrícula 2:1 (solo área de contenido) — lado derecho: anuncios + amigos (pestañas Seguidores/Siguiendo)
- •Botones de acción del encabezado — solo icono ([Configuración de mi Story] / [Editar perfil] / [Seguir])
Added
- •D-1: Barra lateral de 4 menús (Panel / Historia / Cuenta / Mensajes)
- •D-2-A: Nuevo panel — 8 tarjetas de estado + navegación de 8 pestañas + pestaña de info de cuenta
- •D-2-B: Pestañas Guardados / Bandeja de entrada / Amigos (cada una con enlace "Ver todo")
- •D-2-C: Pestañas Guardado / Mis publicaciones / Mis comentarios (tablón + Story + página unificados)
- •D-2-D: Gestión de inicio de sesión automático (remember tokens)
Added
- •Helper común
rzx_mirror_external_images() — cURL + protección SSRF + redimensionado GD
- •E-1: Captura de instantáneas de guardados + absorción de imágenes externas
- •E-2: Absorción al crear una publicación de Story (columna source de story_attachments)
- •E-3: Absorción al crear una publicación en el tablón
- •E-4: Absorción al crear contenido de página
- •Marcado de imágenes:
data-source-original, data-source-mirrored
Added
- •Barra lateral de gestión de hosting — última entrada [Ajustes] que lleva a las 4 pestañas de la página de solicitud de servicio (general/domain/hosting/addons)
- •Política de reembolso → Configuración del sistema → General → tarjeta separada (aislada de los ajustes fiscales)
- •Almacenamiento de clave única
rzx_settings.refund_policy
- •Claves
settings.refund.* para 13 idiomas
Added
- •Migración 009: columnas
page_contents.created_by / updated_by
- •Registrado automáticamente en todas las pantallas de edición de página (admin/customer/system)
Added
- •Recuento de publicaciones — reconoce la estructura separada de
rzx_translations para publicaciones del tablón (board_post.{id}.title)
Added
- •Ajustes de administrador para cuota de almacenamiento por hosting / umbral de advertencia / política (block/notify/none)
- •Gráfico de rosca de uso en Mi Cuenta
- •Monitor en segundo plano
scripts/db-quota-monitor.php
Added
- •Botón de reinstalación de VosCMS — pestaña de hosting del administrador
- •Acción de cambio de [Estado] de hosting + registro
- •Eliminado el formulario de edición "Configuración de información de conexión al servidor" (evita exposición innecesaria)
Fixed
- •Error de red en scrap
add_scrap — conflicto de require del archivo API de vos-story
- •Tarjeta de scrap — contorno + iconos de eliminar/editar (papelera)
- •Discrepancia entre el recuento de publicaciones y la lista (falta el filtro de estado)
- •Eliminada la coincidencia por nick_name (evita la identificación errónea de datos semilla con user_id NULL)
- •Posición del menú Story en Mi Cuenta — top → main (reconocimiento automático del plugin)
- •Icono del menú Story en formato núcleo (etiqueta
<path> completa)
- •Falta la columna key en settings SELECT de la pestaña de complementos (dos secciones ocultas)
- •Acción hosting_status 500 — ubicación de la definición de función auxiliar
- •Barra de acciones de página — token de ancho de contenido aplicado
- •Error donde las publicaciones existentes desaparecen al publicar una nueva (actualización inmediata del feed)
Added
- •Detalle de hosting → Pestaña de dominio → Botón
+ Añadir dominio → modal
- •Opciones: subdominio gratuito / registro nuevo / conectar dominio existente
- •Búsqueda WHOIS (
/api/domain-check.php reutilizado) + visualización de precio por TLD
- •Pago: tarjeta guardada (reutilizando cliente PAY.JP del hosting) o nueva tarjeta (PAY.JP Elements)
- •Separación automática del 10% de impuesto al consumo (subtotal / impuesto / total)
- •Al completar el pago, añadir al sub del hosting
metadata.added_domains[] (sin crear sub separado)
- •Red de seguridad de reembolso automático — en caso de fallo de procesamiento de BD, el cargo de PAY.JP se reembolsa automáticamente
Added
- •Fecha de inicio~vencimiento + botón de verificación (DNS A/CNAME → coincidir con nuestra zona o rango de IP de Cloudflare)
- •Toggle de renovación automática + botón de solicitud de renovación (por dominio)
- •registrar_pending / manual_attach_pending: botón de verificación → insignia "Pendiente"
- •Insignia morada "Dominio añadido"
Added
- •Barra lateral de gestión de hosting: 2.º elemento añadido nuevo
- •Lista unificada: sub type='domain' + hosting sub.added_domains[] todos mostrados en orden cronológico
- •Tarjetas de estadísticas (total/pendiente/completado) + filtros (estado/opción/búsqueda)
- •Acciones: [Marcar como registrado] / [Procesar cancelación] (al cancelar: reembolso PAY.JP + INSERT de fila de reembolso)
- •Descifrado automático del nombre de usuario
Added
- •Barra lateral de gestión de hosting: 3.er elemento añadido nuevo
- •Libro mayor de transacciones basado en
rzx_payments — tarjetas de estadísticas de ingresos/reembolso/ingresos netos/impuesto
- •Cálculo automático del importe base para la declaración del impuesto al consumo de Japón (precio con impuesto incluido → base = total × 100/110, impuesto = total × 10/110)
- •Tabla de tendencia de los últimos 12 meses + tabla del libro mayor de transacciones + filtros/paginación
Added
- •Pestaña de complementos de mi página: sección "Disponible para solicitar" (correo empresarial excluido automáticamente)
- •Acciones por precio:
- •Gratuito (soporte de instalación): notificación de admin + order_logs
- •Presupuesto (personalización): "Solicitar presupuesto"
- •Precio fijo recurrente (soporte técnico 1 año): modal de pago con tarjeta (
_addon-pay-modal.php)
- •Al completar el pago, sub de addon creado automáticamente + entrada de pagos
- •Coincidencia de etiqueta de sub ya activo: botón de solicitud desactivado automáticamente
- •Fila install_info: botones [Ir al panel de admin / Ir al sitio] a la derecha
Added
- •Esquema BD: UNIQUE
uk_payment_order eliminado → UNIQUE uk_payment_key (charge_id) sustituido
- •Se permiten múltiples filas de pago por pedido (hosting + dominio + complemento acumulados)
- •En reembolso: fila pagada sin cambios, INSERT de fila de reembolso separada (status='refund', integridad contable)
- •Obligación de retención de 7 años de Japón + estándar de respuesta a disputas de comerciante PAY.JP cumplido
Added
- •composer require minishlink/web-push
- •Ayudante de envío de push VAPID para admin/supervisor
- •Limpieza automática de endpoints expirados + registro automático en rzx_push_messages
- •Puntos de llamada: pay_domain (pago de dominio completado), service-order (solicitud de registro de dominio), request_addon
Added
- •Coincidencia automática de subdominio de nuestra zona (CNAME comodín reconocido)
- •Coincidencia de 15 rangos CIDR de IPv4 de Cloudflare (en respuesta de proxy)
- •Respuesta "already_refunded" tratada como éxito (cuando PAY.JP ya ha reembolsado)
Added
- •Subconsulta suma totales de
rzx_payments (pagado − reembolsado)
- •Visualización acumulada de pago base de hosting + adición de dominio + pago de complemento
Added
- •
admin_cancel_addon — sub status='cancelled' + reembolso PG + INSERT de fila de reembolso
- •Enrutamiento PG: basado en la columna
gateway de la fila de pago (seguro incluso tras cambio de PG)
Removed
- •Según la obligación de retención de 7 años de Japón + política de respuesta a disputas PAY.JP, todos los botones "eliminar" de la UI de admin han sido eliminados
- •Icono de papelera + adminDeleteAddon JS + admin_delete_addon API + 4 etiquetas en 13 idiomas todos eliminados
- •Solo "Cancelar" — las filas de datos nunca son eliminadas (DELETE)
Fixed
- •Conflicto de prefijo de dos puntos en marcadores de posición del Translator —
[':count' => N] → ['count' => N] corregido masivamente
- •
metadata[refund_reason] de PayjpGateway::refund() rechazado por PAY.JP → cambiado a refund_reason
- •Verificación de permisos de admin en service-manage.php —
role === 'admin' → role IN ('admin','supervisor') corregido en 5 acciones
- •Pestaña de dominio del pedido de servicio: tras reembolso, la tarjeta contable muestra negativo → lógica SQL corregida (suma separada de status='paid' / 'refund')
Added
- •ssh.21ces.com Cloudflare Tunnel registrado (Public Hostname: SSH → localhost:22)
- •PC externo:
cloudflared access ssh --hostname ssh.21ces.com o ProxyCommand en ~/.ssh/config
- •Servidor: PermitRootLogin no, clave thevos registrada
Added
HostingProvisioner.php installVoscms() ahora llama a install-core.php de forma secuencial en el paso 3 → paso 4 tras el despliegue del zip (antes solo el paso 2):
- •Nuevo
runInstallCoreSteps() — omisión de sesión usando el fallback POST _db + verificación del marcador .installed
- •Nuevo
callInstallCore() — loopback HTTP (puerto 80, aprovechando el SSL de borde de Cloudflare; maneja la ausencia de certificado de origen cuando se aplica rate limiting)
- •Nuevo
appendHostingEnv() — reinyecta las variables HOSTING_* después de que install-core.php sobreescribe el .env
- •Elimina automáticamente welcome.html al completar la instalación (para que VosCMS index.php tenga prioridad)
- •Flujo de pago service-order.php:567-576 — al completar la instalación, registra automáticamente
install_completed_at e install_admin_url en los metadatos del addon (activa la visualización de la pestaña en Mi Página)
Added
admin/service-orders/detail.php:175-260 + partials/addon.php:
- •Dos botones en el addon "Soporte de instalación" dentro de la pestaña de servicios adicionales:
- •Instalar VosCMS (morado) — se muestra cuando existe
install_info y install_completed_at está ausente
- •Página de administración (verde) — abre la URL de admin en una pestaña nueva tras completar la instalación
- •Acción de API
admin_run_voscms_install — llama directamente a HostingProvisioner.installVoscms(), con credenciales de BD de metadata.server.db o del .env del cliente como fallback
- •Al completar la instalación: se registran los metadatos del addon + entrada
voscms_installed en order_log
Added
customer/mypage/service-partials/hosting.php:
- •🟢 Botón Backup del sitio —
service-manage.php?action=request_backup (nuevo)
- •mysqldump → volcado de BD + empaquetado zip(public_html, .env, sql)
- •URL de descarga firmada (HMAC, válida 10 minutos) — api/backup-download.php nuevo
- •Limpieza automática de backups de más de 7 días
- •🟣 Abrir phpMyAdmin — abre
https://pma.voscms.com/?lang=<user_locale> en una pestaña nueva
- •nginx vhost: pma.voscms.com.conf nuevo (enrutado automáticamente mediante el comodín
*.voscms.com de Cloudflare Tunnel)
- •El usuario inicia sesión con sus propias credenciales de BD
- •Visualización de los datos de conexión de BD (host / nombre de BD / usuario / alternancia de contraseña)
- •i18n en 13 idiomas:
btn_site_backup, btn_open_phpmyadmin, confirm_site_backup, backup_in_progress, backup_ready, f_password, password_notice, f_protocol
Added
customer/mypage/service-partials/_setup-monitor.php:
- •Configuración de hosting → paso de instalación automática de VosCMS separado
- •Los pedidos
system_imported (registros de hosting para sitios propios del propietario como vos.21ces.com) se activan inmediatamente — el cuadro de monitoreo no se muestra
- •Nueva i18n en 13 idiomas:
setup_step_voscms_install
Changed
Adiciones a la exclusión de rsync:
- •5 plugins internos (no deben incluirse en los sitios de clientes de hosting):
- •
vos-developer, vos-hosting, vos-license-server, vos-market, vos-salon
- •7 archivos SQL de migración de reservas (los clientes de hosting no tienen instalado el sistema de reservas):
- •
001_create_reservation_tables.sql, 011_seed_service_translations.sql, 012_seed_service_translations_p2.sql, 013_add_source_to_reservations.sql, 014_add_payment_status_to_reservations.sql, 015_create_reservation_core.sql (nuevo), 016_alter_reservation_services.sql, 024_reservations_bundle_columns.sql
Adiciones a la eliminación de archivos de desarrollo:
- •Directorio
admin/ (residuo de offline.html) — el directorio vacío genera un 403 por indexación de directorios nginx; sw.js solo referencia /offline.html en root, por lo que admin/ es redundante
- •Codificación predeterminada →
obfuscate.php (el ionCube EVAL anterior causaba cuelgues en tiempo de ejecución; ionCube solo se usa cuando USE_IONCUBE=1)
Changed
001_create_base_tables.sql creaba las tablas base y de reservas en un único archivo — ahora separadas:
- •Modificado:
001_create_base_tables.sql — solo las 5 tablas esenciales (rzx_admins, rzx_users, rzx_member_grades, rzx_settings, rzx_migrations) + seeds esenciales (valores predeterminados de member_grades y settings)
- •Nuevo:
015_create_reservation_core.sql — 9 tablas de reservas (rzx_categories, rzx_services, rzx_service_options, rzx_reservations, rzx_payments, rzx_point_transactions, rzx_business_hours, rzx_holidays, rzx_staff_positions, rzx_staff) + seeds de reservas (business_hours, point_*)
En las compilaciones de dist de hosting, se excluye 015 → la tabla rzx_reservations no se crea → admin/dashboard.php:25-27 $hasReservations = false → el calendario se oculta automáticamente.
Los sitios que usan el sistema de reservas (HQ voscms.com, RezlyX, etc.) aplican ambos archivos SQL → sin cambios.
Changed
config/templates/nginx-vhost.conf.tpl + HostingProvisioner addHttpsToVhost():
- •Antes: puerto 80 (redirección HTTPS) + puerto 443 (contenido real) → bucle infinito por Cloudflare Tunnel + redirección 80→443
- •Después: bloque de servidor único unificado para 80/443, mismo contenido, sin redirección
- •
fastcgi_read_timeout 60s → 300s (evita el tiempo de espera de nginx en operaciones INSERT masivas)
Changed
- •
provision-hosting.php (herramienta CLI de backfill): actualiza automáticamente los metadatos de la suscripción de hosting (hosting_provisioned, server.{ftp,db,env}) — antes solo _autoProvisionHosting en service-order.php realizaba esta actualización
- •Eliminación automática de 5 plugins internos + directorio
admin/
Added
scripts/install-cli.php nuevo:
- •Backfill para la invocación automática de install-core.php tras el aprovisionamiento de hosting
- •Credenciales de BD: prioridad a los metadatos de la suscripción, con fallback al
.env en el docroot del cliente
- •Uso:
sudo -u www-data php scripts/install-cli.php --order=<ORDER>
- •Al completar: registra automáticamente
install_completed_at en los metadatos del addon + acción voscms_installed en order_logs
Fixed
- •Llamada a
_autoProvisionHosting() ausente en el flujo de pago con tarjeta — añadida
- •
install_info.admin_pw en los metadatos de pago se mostraba en texto plano → alternancia (👁) unificada tanto en Mi Página como en el lado del administrador
Migration Notes
- •Clientes de hosting externos: sin acción necesaria. Tras la compilación de
voscms-2.4.3.zip, el flujo de instalación automática utiliza un zip limpio.
- •HQ (voscms.com): sin cambios (se aplican ambos archivos SQL de reservas).
- •Clientes de hosting existentes (dwhahn, etc.): se recomienda reinstalación limpia mediante deprovision + reprovision (aplicar el nuevo zip tras la separación).
- •Limpieza manual:
/etc/nginx/sites-available/pma.voscms.com.conf añadido como nuevo — desplegar directamente en el sistema HQ (fuera de rsync).
Added
Se llama inmediatamente después de completar el pago en service-order.php:
- •Nueva función
_autoProvisionHosting($pdo, $prefix, $orderId, $orderNumber) — se ejecuta fuera de la transacción de pago (integridad de BD aislada; el pedido en sí no se ve afectado si falla)
- •Solo
domain_option = 'free' se configura de inmediato (subdominio de Cloudflare — el registro automático de DNS ya está completado)
- •
new (esperando cambio de NS tras la compra del dominio) / existing (esperando cambio de NS del dominio propio) están en modo deferred — el administrador activa por separado en el momento de la activación del dominio
- •Si el addon 'install' tiene
install_info, se despliegan los archivos de voscms + se genera .env automáticamente (automatizar el flujo de sesión paso a paso de install-core.php (pasos 2–5) requiere rediseñar toda la canalización de instalación de voscms — diferido a la Fase 5; el usuario accede a https://<dominio>/install.php tras el pago → procede con el asistente con la información de BD pre-inyectada en .env)
- •La información ftp/db/env se guarda automáticamente en
metadata.server de la suscripción de hosting → se muestra automáticamente en la pestaña de hosting de Mi Cuenta (sin cambios de UI)
- •Acciones de order_logs:
hosting_provisioned / hosting_provision_skipped / hosting_provision_deferred / hosting_provision_failed
Orden de llamada: commit de pago → _autoProvisionHosting() → _autoProvisionMailDomain()
Added
$actionLabels en la página de detalle del pedido de servicio del administrador (detail.php:498-528) expandido de 8 a 31 entradas. Corrige los códigos de acción en bruto (p. ej. mail_account_added) que se mostraban en el registro de actividad.
Acciones recién mapeadas:
- •Correo:
mail_account_added/deleted, mail_address_changed, mail_provisioned/_skipped/_failed, mail_domain_migrated/activated, bizmail_upgrade_request
- •Hosting:
hosting_provisioned, hosting_provision_skipped/deferred/failed, hosting_deprovisioned
- •Complementos:
admin_add_storage_addon, admin_delete_addon, storage_addon_paid/requested
- •Sistema:
customer_notified, setup_email_sent, server_info_updated, bulk_extended, manual_capacity_period_fix
Semántica de colores por acción: green (completado/positivo), amber (pendiente/advertencia), red (fallido/eliminado), blue (info/notificación), violet (admin/especial), zinc (neutro).
23 claves i18n — services.admin_orders.act_* — añadidas en ko/ja/en (página de operador). Los otros 10 idiomas usan inglés como fallback.
Changed
Durante el aprovisionamiento automático de hosting, la instalación automática de VosCMS solo realiza el despliegue de archivos + generación automática de .env. Automatizar el flujo de sesión paso a paso de install-core.php (pasos 2–5) requiere rediseñar toda la canalización de instalación de voscms — diferido a la Fase 5. Tras el pago, el usuario accede a https://<dominio>/install.php → procede con el asistente (la información de BD está pre-inyectada en .env).
Valor de retorno de installVoscms() cambiado — ahora devuelve install_url + mensaje note.
Added
HostingProvisioner.php — Clase principal para la configuración automática al solicitar hosting. Transacción de 9 pasos (reversión en orden inverso en caso de fallo):
- •Creación de usuario Linux (
vos_<order_number>, dentro del límite de 32 caracteres, conservando mayúsculas y minúsculas)
- •Estructura de directorios:
/var/www/customers/<order>/{public_html,logs,tmp} (chmod 750/2775/750/1770)
- •Cuota de disco (
setquota -u <user> <soft> <hard>, hard = soft × 1.1)
- •Verificación de conflicto de directorio: rechaza si hay conflicto con nginx vhost existente /
reserved_subdomains
- •Montaje SSD 1,9 TB
/var/www (ext4 + noatime + usrquota,grpquota)
- •Sudoers central (
/etc/sudoers.d/voscms-hosting-provisioner) — www-data ejecuta comandos root (useradd/setquota/nginx, etc.) con NOPASSWD
- •Restricción de patrón de argumento por comando (
vos_, /var/www/customers/, *.conf) — supera la validación de visudo
Herramienta CLI scripts/provision-hosting.php:
php provision-hosting.php SVC260428AE7476 hotel.com 1GB
php provision-hosting.php --order SVC260428AE7476 # consulta automática en BD
php provision-hosting.php --deprovision SVC260428AE7476 hotel.com
php provision-hosting.php --install-voscms <order> <domain> <capacity>
Plantillas config/templates/:
- •
nginx-vhost.conf.tpl — HTTP + ubicación del desafío ACME + socket FPM por usuario
- •
welcome.html.tpl — página de bienvenida de activación
Added
Cada hosting ejecuta PHP bajo su propio usuario (evita compromisos entre sitios) + SSL emitido automáticamente incluso detrás del proxy de Cloudflare.
config/templates/php-fpm-pool.conf.tpl:
- •
user = vos_<order> / group = www-data — aislamiento de UID
- •
listen = /var/run/php/<order>.sock — socket por usuario
- •
pm = ondemand — eficiente en memoria para hosting con poco tráfico
- •
php_admin_value[open_basedir] — acceso limitado al directorio del sitio + /tmp + /usr/share/php
- •
disable_functions — exec, passthru, shell_exec, system, proc_open, popen, pcntl_exec
- •
session.save_path / upload_tmp_dir — aislado al directorio del usuario
- •env: HOSTING_ORDER / HOSTING_USER / HOSTING_DOMAIN
SSL de Let's Encrypt — desafío DNS-01 de Cloudflare:
- •Plugin
python3-certbot-dns-cloudflare
- •
/etc/letsencrypt/cloudflare/credentials.ini (root 0600, CLOUDFLARE_API_TOKEN de .env)
- •Se emite independientemente del proxy de Cloudflare / accesibilidad externa
- •Bloque del servidor HTTPS añadido automáticamente (TLS 1.2/1.3, cifrado fuerte, HSTS, X-Frame-Options, X-Content-Type-Options)
- •Redirección automática HTTP → HTTPS 301
- •
certbot.timer renueva automáticamente dos veces al día (ya activo)
Added
Aislamiento de BD + usuario por pedido de hosting.
- •Cuenta MySQL
hosting_admin (creada una vez, ALL PRIVILEGES + GRANT OPTION) — solo para gestión de BD
- •Nombre BD/usuario =
vos_<order> (igual que el nombre de usuario Linux)
- •Contraseña: 32 hex aleatorio (
bin2hex(random_bytes(16)))
- •
GRANT ALL PRIVILEGES ON vos_<order>.* TO vos_<order>@localhost — acceso solo a su propia BD
- •Verificación de aislamiento: acceso a otra BD de hosting / voscms_prod devuelve Access denied 1044
Instalación automática de VosCMS (installVoscms() — activado por --install-voscms o cuando el addon tiene install_info):
- •Extrae el paquete más reciente
voscms-dist/voscms-X.Y.Z.zip → public_html
- •Genera automáticamente
.env: APP_KEY=base64:<32bytes> / info BD / HOSTING_ORDER inyectado
- •Permisos: vos_:www-data, .env chmod 640
- •Llama a
install-core.php sin interfaz (actualmente hay un problema de redirección 301, a resolver en la Fase 4)
Verified
provision DEMO260429FULL demo07.21ces.com 1GB --install-voscms:
- •✅ Usuario
vos_DEMO260429FULL (aislamiento UID, grupo www-data)
- •✅ Cuota de disco 1 GB soft / 1,05 GB hard
- •✅ Pool PHP-FPM con socket propio
- •✅ nginx vhost (HTTP + HTTPS, HSTS)
- •✅ SSL de Let's Encrypt (
/etc/letsencrypt/live/demo07.21ces.com/)
- •✅ BD MySQL
vos_DEMO260429FULL + usuario (aislamiento verificado)
- •✅ Archivos VosCMS 2.3.9 + .env desplegados
deprovision: Todos los recursos eliminados limpiamente (usuario/home/BD/pool FPM/nginx/SSL).
Added
En lugar del sistema de facturación aniversario estándar del sector (desde la fecha de alta hasta la misma fecha un año después), ahora se utiliza el cierre de fin de mes por calendario (del día 1 del mes siguiente al último día del mes de vencimiento). Los usuarios que se dan de alta en el mismo mes comparten la misma fecha de vencimiento, lo que simplifica el procesamiento por lotes con cron y el cierre contable. Sigue el patrón estándar de hosting japonés (月末締め翌月1日請求).
- •BD: columna
rzx_subscriptions.billing_start DATETIME NULL añadida (fecha de inicio de facturación normal)
- •Helpers:
_proratedFirstAmount (unidad mensual ÷ 30 × días restantes), _calendarBillingStart (día 1 del mes siguiente), _calendarExpires (último día 23:59:59)
- •Formulario de pedido:
hosting_prorated se añade automáticamente a los conceptos de pago de hosting (ej.: alta el 2/4, 12 meses ¥1.000/mes → primer mes ¥967 + 12 meses normales ¥12.000 = ¥12.967)
- •Los dominios mantienen la facturación aniversario (ciclo de registro de 1 año); hosting, correo, mantenimiento y complementos usan facturación por calendario
- •El tratamiento
-1 second → -1 day en service-order.php también se unificó
- •Los servicios complementarios (almacenamiento web adicional) también usan el mismo prorrateo + suma de N meses normales, con sincronización automática con la fecha de vencimiento del hosting
Added
Configuración de Postfix + Dovecot + Rspamd + Roundcube + OpenDKIM + Let's Encrypt en ConoHa VPS (133.117.72.149). mail-tester 10/10, FCRDNS verificado. Configuración automática del dominio al realizar el pedido.
- •Registro DNS automático (
CloudflareDns.php) — A / MX / SPF / DKIM / DMARC 5 registros en un solo lote
- •Generación de clave DKIM — mx1 SSH
rspamadm dkim_keygen (clave ed25519 dedicada storage/keys/mailsync_ed25519, propietario www-data, 0700)
- •Aprovisionamiento en 3 modos (
MailDomainProvisioner.php): free (subdominio instantáneo) / new (dominio comprado, activación por admin) / existing (instrucciones de cambio de NS). Máquina de estados: existing_pending / new_pending / active
- •Contraseña: SHA512-CRYPT (
{SHA512-CRYPT}$6$...) — hash unidireccional compatible con Dovecot. Script de migración para contraseñas antiguas enc: (función encrypt): scripts/migrate-mail-passwords.php
- •Cron de sincronización con mx1 (
scripts/mail-sync-to-mx1.php) — voscms_prod → mx1 mail_lookup virtual_users cada 5 minutos. Disparador inmediato (triggerMailSyncToMx1()) para sincronización en segundo plano justo después de las acciones de correo
- •Registro automático de cron:
/etc/cron.d/voscms-mail-sync
- •Notificaciones: plantillas de correo HTML para admin (aprovisionamiento necesario) / cliente (correo disponible) (
MailNotifier.php)
Added
Correo base / correo empresarial gestionados conjuntamente con el hosting. Corregido el problema de visualización duplicada donde la misma dirección estaba almacenada tanto en la suscripción de correo como en los metadatos de hosting (fuente única de verdad: suscripción de correo).
- •Botón de webmail — Parámetro GET
?_user=dirección de Roundcube para prellenar el nombre de usuario, target="_blank". Prioridad de URL: metadata.mail_server.webmail_url → env WEBMAIL_URL → predeterminado https://mail.voscms.com/
- •Botón de cambio de correo — para corrección de errores tipográficos. El dominio es fijo; solo cambia la parte local. Se sincronizan todos los metadatos de correo + hosting del mismo pedido
- •Lógica de deduplicación (
mail.php) — _seenAddresses muestra cada dirección solo una vez; la suscripción de correo tiene prioridad
- •La conversión virtual hosting → correo en service-detail.php solo funciona cuando no existe una suscripción de correo real (compatibilidad con datos anteriores)
- •service-order.php ya no guarda hosting metadata.mail_accounts para nuevas altas (la suscripción de correo es la única fuente)
Added
Compra de almacenamiento adicional con pago en la pestaña de servicios complementarios de mi página. Registro de cliente PAY.JP + chargeCustomer.
- •Modal: selección de capacidad (1 GB / 3 GB / 5 GB) + introducción de datos de tarjeta (payjp.js v2 Elements)
- •Registrar tarjeta / pagar con otra tarjeta / volver a la tarjeta registrada — alternancia
- •Detección automática de tarjetas caducadas/rechazadas (
expired_card, card_declined, invalid_* etc., códigos de error PAY.JP) → formulario de tarjeta se abre automáticamente + banner de orientación
- •Campo de nombre del titular de la tarjeta añadido (
addon-card-holder) — almacenado como metadatos del cliente, no se pasa al token (PAY.JP v2 Elements no admite la opción name)
- •Patrón Portal:
document.body.appendChild() para salir de contenedores con transform y cubrir el viewport completo (z-[9999])
- •Si el cargo falla tras crear un nuevo cliente: reembolso automático + eliminación del cliente (prevención de huérfanos)
- •hosting metadata.extra_storage[] acumulado; el indicador de almacenamiento de la pestaña hosting muestra el total combinado
Added
Estructurados 14 artículos (definiciones, solicitud, tarifas, renovación automática, cancelación, servicio, copia de seguridad, uso prohibido, suspensión, mantenimiento, privacidad, responsabilidad, cambios, jurisdicción) siguiendo los términos estándar de los proveedores de hosting japoneses como StarServer. Se especifica la facturación por calendario + prorrateo del primer mes + reembolso de meses no utilizados al cancelar.
- •Registro de página del sistema:
config/system-pages.php slug hosting-terms, 🖥️ índigo
- •Mapeo del editor de páginas:
pages-document.php — pageMetaMap['hosting-terms']
- •Seed de BD: ja (principal) / ko / en — 3 idiomas (rzx_page_contents)
- •i18n:
site.pages.hosting_terms + hosting_terms_edit (editor_title/desc)
Added
- •4 pestañas siempre visibles (detail.php) — hosting/domain/mail/addon. Mensaje de estado vacío (empty_*) cuando no hay datos
- •Pestaña de servicios complementarios — botón
+ Añadir almacenamiento (modal de asignación gratuita por admin; al seleccionar la capacidad se crea inmediatamente una addon subscription activa, billing_amount=0, expires=fecha de vencimiento del hosting) + botón de papelera/eliminar por elemento (eliminación lógica status=cancelled, se elimina de hosting metadata.extra_storage[])
- •Lista — mejora en la visualización del solicitante (index.php) — los pedidos de administradores del sistema con rol
supervisor/admin se muestran como user.name + subtítulo en morado Superadministrador: [email protected] (evita el fallo de descifrado del antiguo applicant_name cifrado como FKzGx9SE7xG/2)
- •Botón de creación de pedido (
+ New Order) — solo UI, funcionalidad pendiente
- •i18n 13 idiomas:
role_supervisor, role_admin, btn_create_order, empty_, btn_add_storage_addon, btn_delete_addon, modal_add_storage_, confirm_, alert_ y más de 20 claves adicionales
Added
- •
subdomain-check.php — prioridad DB (reserved_subdomains) + fallback API de Cloudflare. JS checkSubdomain() llama al servidor y refleja el resultado
- •Revalidación del lado del servidor en service-order.php justo antes del pago — evita la elusión del lado del cliente
- •Importación automática de
reserved_subdomains — 11 registros existentes de la zona Cloudflare (vos/bestatech/bt/dev/ec430/ec431/salon/ssh/test/thevos/ai.21ces.com)
- •Corrección del bug INSERT IGNORE del menú (menus-api.php):
- •
add_menu_item: limpiar traducciones obsoletas inmediatamente después del INSERT + sobrescribir con INSERT ... ON DUPLICATE KEY UPDATE
- •
update_menu_item: detectar cambios en title/url, eliminar las 13 traducciones antiguas + volver a registrar si es página del sistema
- •
rename_menu_item: limpiar traducciones antiguas al cambiar el título
- •Corregida la regresión por la que el menú antiguo (id=38, "Introducción a VosCMS") apuntaba al nuevo menú de términos de hosting
Added
Flujo construido: pedido de servicio → pago completado → aprovisionamiento automático del dominio de correo → notificación al cliente.
- •Llamada a
_autoProvisionMailDomain() añadida en service-order.php — se ejecuta fuera de la transacción de pago (aislamiento de integridad de BD)
- •BD:
rzx_reserved_subdomains (zone, subdomain, record_type, reserved_by, reason) — caché de disponibilidad
- •Clave DKIM generada automáticamente, almacenada en metadata.mail_provision como mode/origin/domain/zone/subdomain/provisioned_at
- •Ramificación de UI por modo de aprovisionamiento (service-detail.php):
pending / new_pending / existing_pending — pestañas ocultas, solo se muestra monitorización (_setup-monitor.php); pestañas visibles solo cuando está active
Fixed
El $arr['key'] ?? [] de PHP devuelve un valor temporal, por lo que &$ref no afecta al array original. change_mail_password / change_mail_address devolvían respuestas de éxito pero el UPDATE de metadata no tenía efecto — los cambios de contraseña y dirección fallaban silenciosamente.
- •Cambiado al patrón de actualización directa basada en índice:
foreach ($arr as $i => $item) { $arr[$i]['k'] = ... }
- •El almacenamiento de nuevas contraseñas de correo en service-order.php también cambió de
encrypt() (bidireccional) a mail_password_hash() (SHA512-CRYPT)
Fixed
- •Resumen de pago verde fluorescente brillante → color más limpio:
text-green-400 → text-green-600 dark:text-green-400 (7 ubicaciones)
- •Clave i18n faltante en el mensaje de respuesta de disponibilidad de subdominio (mapeo PHP→JS i18n en
order.php + placeholder :fqdn unificado)
- •Cálculo de fecha de vencimiento corregido:
+N months -1 second (mismo día) → +N months -1 day (exactamente 1 día antes) → finalmente unificado al método de calendario
Database
- •
rzx_subscriptions.billing_start DATETIME NULL añadido (dev + prod)
- •Tabla
rzx_reserved_subdomains (caché de colisiones de zona Cloudflare)
- •
rzx_page_contents inicializado con hosting-terms (ja/ko/en)
Added
Todo el texto de las 6 áreas (dominio, alojamiento, complementos, solicitante, pago, resumen) y la página de pago completado ha sido traducido a 13 idiomas. El texto de alertas/modales del lado del cliente se inyecta desde PHP mediante el patrón var I18N = {...} + t(key, replace).
- •Ubicación: plugins/vos-hosting/lang/{13 locales}/services.php —
services.order.* más de 200 claves
- •Detalle de servicio en mi página:
services.detail.* más de 70 claves, service-detail.php
- •Gestión de pedidos del administrador:
services.admin_orders.* más de 115 claves, admin/service-orders/
- •Las etiquetas en coreano almacenadas en la BD (planes de alojamiento, mantenimiento, complementos) se mapean por ID de configuración mediante el closure
_localizeLabel($sub) y se aplica db_trans() → manejo consistente en mi página y en el panel de administración
Added
Al hacer clic en el botón de pago, se validan los detalles del pedido (dominio/plan/complementos/solicitante/términos) y se muestra un modal de resumen → el usuario confirma → se procede al pago. Evita errores como registrar un subdominio vacío.
- •order.js —
showOrderConfirmModal(), closeOrderConfirmModal(), confirmAndPay()
- •Eliminación mejorada de las capas de modal restantes tras completar el pago (eliminación del DOM + restablecimiento del overflow de body/documentElement)
Added
Cuando se marca "Soporte de instalación" (_id='install') en los complementos, aparece un formulario para el ID/correo/contraseña/nombre del sitio del administrador. La contraseña está enmascarada + alternancia con icono de ojo + aviso "cambiar tras la instalación". Los valores se almacenan como install_info en los metadatos de la suscripción y se muestran tanto en mi página como en el panel de administración.
- •Entrada: _addons.php
- •Visualización: mi página/admin partials/addon.php (cuadro con fondo azul + alternancia de contraseña)
Added
El memo vinculado a la pestaña de complementos ha sido movido al nivel del pedido (rzx_orders.admin_notes). Siempre se muestra como un área de texto de 7 líneas sobre el registro de actividad a la derecha, independientemente de si hay complementos. Se agregó la acción AJAX update_order_memo.
Added
Se introdujo un campo section (top/main/bottom, por defecto main) para separar visualmente los menús del núcleo de los menús de los plugins. Se inserta automáticamente un divisor entre zonas; si no hay menús visibles en una zona, toda la zona incluido el divisor se omite.
Barra lateral de administración:
- •top: Panel / Gestión de usuarios / Gestión del sitio / Instalador automático (
vos-autoinstall)
- •main: Licencia VosCMS (
vos-license-manager) / Gestión de alojamiento (vos-hosting)
- •bottom: Plugins / Configuración
- •Cambios: config/admin-menu.php, admin-sidebar.php
Barra lateral de mi página:
- •top: Panel / Perfil / Mensajes
- •main: Servicios (
vos-hosting añadido automáticamente) — se acumula automáticamente a medida que otros plugins añaden entradas
- •bottom: Configuración / Contraseña / Eliminación de cuenta / Cerrar sesión
- •Cambios: config/mypage-menu.php, mypage-sidebar.php
Registro de menú de plugins: los campos section + position en las entradas menus.admin / menus.mypage de plugin.json especifican la zona. El menú services ha sido movido de core mypage-menu.php → vos-hosting/plugin.json (registro automático).
Added
Como la API de validación de licencias y la API del portal del desarrollador estaban en el núcleo, quedarían expuestas en todos los sitios voscms desplegados. Ambas APIs se han separado en un nuevo plugin exclusivo de la sede central.
- •
vos-license-server (scope: hq_only): 7 endpoints de licencia movidos (check, register, verify, stats, updates, register-plugin, _init)
- •
vos-developer (scope: hq_only): 7 endpoints del portal de desarrolladores movidos (register, login, submit, my-items, earnings, update-version, _init)
- •
api/_plugin_dispatch.php — el wrapper del núcleo comprueba rzx_plugins.is_active antes de despachar; devuelve 404 si está inactivo
- •El núcleo
/api/license/.php y /api/developer/.php conservan solo el wrapper (compatibilidad externa mantenida)
- •
/api/notices.php con lista blanca de entorno HQ_HOSTS añadida (responde solo desde el dominio HQ voscms.com)
- •El script de compilación (
build-voscms.sh) ya excluye api/license, api/developer, api/notices.php y vos-license-manager
Fixed
- •Error de +1 día en la fecha de vencimiento del contrato de alojamiento (
+12 months → +12 months -1 second) — aplicado en 4 lugares: subscription/free/paid/bank
- •Correo electrónico corporativo: no se pueden añadir 2 o más (el selector capturaba el botón X)
- •Resumen del pedido: almacenamiento adicional no agregado (faltaba el atributo
name en el select de almacenamiento)
- •Etiqueta del plan de alojamiento no localizada — resuelto mediante la coincidencia de
service_hosting_plans._id con db_trans()
Added
_init.php ahora consulta rzx_translations directamente para board.{id}.{title|description|header_content|footer_content|seo_keywords|seo_description} (locale actual → en → fuente). En cualquier página — list/read/write/settings — $board[$field] se rellena automáticamente con el valor traducido. El botón "← Volver" en la página de configuración del tablón también se traduce automáticamente mediante el mismo mecanismo.
Seed de 13 idiomas para title + header_content del tablón de incidencias (id=6) — estructura HTML preservada, solo texto traducido, insignias de etapa de procesamiento también en cada idioma. Scripts de seed: scripts/seed_issue_i18n.php + scripts/seed_issue_i18n_data.php.
Added
Traducción de las definiciones (etiqueta/descripción/opciones/valor por defecto) de rzx_board_extra_vars a 13 idiomas. 4 campos × 13 = 52 filas.
- •
ExtraVarRenderer::renderAll($vars, $values, $mode, $boardId) — argumento boardId añadido, applyDefTranslations() carga en bloque todas las traducciones de vars en una sola consulta.
- •
ExtraVarRenderer::getOptionLabel($boardId, $varName, $value) — consulta de etiqueta traducida de una sola opción para vistas de lista (caché estático).
- •
renderStatusBadge($value, $size, $displayLabel = null) — el color usa el mapeo fuente (ko), solo el texto se traduce → color consistente en todos los idiomas (recibido = rosa, resuelto = esmeralda).
- •select/radio/checkbox: patrón
value=fuente / label=traducido. El valor fuente siempre se guarda al publicar, garantizando compatibilidad de datos.
- •Actualizaciones de llamadas:
read.php, write.php, _list-row.php, _list-card/_list-webzine/_list-gallery del skin predeterminado.
Seed de 13 idiomas para la variable extra status del tablón de incidencias: scripts/seed_ev_status_i18n.php.
Added
La API extra_var_get devuelve valores traducidos para el locale actual como objeto localized mediante db_trans(). El modal JS muestra localized.title || ev.title con prioridad → en modo japonés, al abrir el modal se muestra automáticamente "処理ステージ".
extra_var_update se ramifica por locale:
- •Locale fuente (ko) → actualizar toda la fila fuente (comportamiento existente)
- •Locale no fuente (ja/en/…) → guardar solo metadatos (
var_type/permisos/3 casillas) en la fuente; guardar 4 campos de traducción (title/description/options/default_value) en rzx_translations con el locale actual → integridad de la fuente garantizada
Los nombres de visualización en el listado de variables extra también usan db_trans('board_ev.{boardId}.{varName}.title').
Added
La sección "Permiso de edición" (etiqueta/opciones del desplegable/texto de ayuda) del modal de edición de variables extra separada en archivos lang de 13 idiomas. Nuevas claves: site.boards.ev_permission, ev_perm_all, ev_perm_member, ev_perm_admin, ev_permission_help.
Added
Normalización bidireccional del campo "Opciones" en el modal de edición de variables extra:
- •Carga del modal: convierte el array JSON de DB (
["a","b","c"]) a formato de salto de línea para mostrar en textarea (coincidiendo con las instrucciones)
- •API de guardado: el helper
ev_normalize_options() acepta entrada de salto de línea o JSON y guarda consistentemente como array JSON. Corregido un bug donde la entrada de salto de línea se guardaba como NULL.
Added
Dos nuevos skins para patrones de contenido más allá de incidencias/avisos.
FAQ (skins/board/faq/) — publicación = pregunta, cuerpo = respuesta. Incluye barra de búsqueda, filtro de categoría y paginación. Toggle de visualización del área de título, título/subtítulo multilingüe, soporte de fondo (imagen/vídeo/ninguno), ajuste de opacidad del overlay. v1.0.1.
Q&A (skins/board/qna/) — publicación = pregunta (título+cuerpo), comentario = respuesta. Insignia de sin responder, primer elemento auto-expandido, visualización de comentarios ordenada en árbol (parent_id/depth). Las respuestas a respuestas van indentadas + etiqueta "↳ Respuesta" (nivel superior = "A."). Formulario inline de edición/respuesta (solo autor/admin puede editar, permiso perm_comment para responder). v1.0.0.
Ambos skins comparten: icono de engranaje de configuración de administrador colocado abajo a la derecha de la barra de búsqueda (separado del área de título). boardSkinFile() opera con prioridad _list-full.php.
Added
Acción update añadida a api-comments.php. Solo comment.user_id == usuario actual o administrador pueden editar. Tras validación del conteo de caracteres, actualiza content + updated_at.
Added
El token {year} en el campo copyright de la configuración de layout se reemplaza automáticamente con el año actual en tiempo de renderizado. La DB conserva el original (con {year}) para que nunca necesite actualización manual. Aplicado a: default/minimal/modern/base-footer.php. La descripción de copyright en 4 archivos layout.json expandida a 13 idiomas, con instrucciones de uso de {year} mostradas en cada idioma nativo.
También corregido un bug donde $_lc['copyright'] era ignorado en los pies de página default/minimal — ahora el valor de configuración de layout se renderiza efectivamente.
Added
Seed de 13 idiomas para menu_item.38.title (Introducción a VosCMS / About VosCMS / VosCMSについて / Über VosCMS / Acerca de VosCMS / À propos de VosCMS / Tentang VosCMS / VosCMS-ийн тухай / О VosCMS / VosCMS Hakkında / Giới thiệu VosCMS / 关于 VosCMS / 關於 VosCMS).
Added
Un ejemplo por etapa de procesamiento + 1 existente resuelto = 4 en total. Traducción de título/contenido de cada publicación a 13 idiomas (104 filas).
| Etapa | Título (ko) | Estado |
|---|
| Recibido | [Bug] El botón X del menú hamburguesa en móvil no funciona | Nuevo |
| En revisión | [Solicitud de función] Notificación por correo al autor cuando se escribe un comentario | Nuevo |
| En progreso | [Rendimiento] Mejorar la velocidad de carga inicial de la página de detalle del marketplace | Nuevo |
| Resuelto | Imagen adjunta en una publicación del tablón insertada en el cuerpo... | Existente |
Eliminada la publicación 34 existente (publicación de prueba con estilos inline de Tailwind acumulados).
Fixed
customer/board/list.php hacía require del helper rzx_admin_icons en la línea 167, pero el include del archivo de skin en la línea 161 salía con return — causando un error 500 cuando rzx_admin_icons() era llamado en los skins FAQ/Q&A. Se movió require_once por encima del include del skin.
Changed
Eliminadas las referencias restantes al nombre antiguo /marketplace. Corregido un error por el que el formulario de búsqueda enviaba por GET a /marketplace, causando una redirección al dashboard (navegación fuera de la página al cambiar búsqueda/orden/filtros). Unificados todos los archivos browse.php, item-detail.php, item-submit.php, purchases.php y assets/js/marketplace.js a /autoinstall.
Added
En el Paso 4 de install.php, se añadió una pista amarilla bajo el campo de ruta del administrador, una vista previa de URL en tiempo real y una guía desplegable "¿Por qué debería cambiarlo?". Tres razones explicadas en lenguaje comprensible para cualquiera: intentos de hackeo automatizados (fuerza bruta), escaneo de vulnerabilidades y los límites de las contraseñas seguras. Aplicado a los 13 idiomas.
Added
En el paso de finalización de la instalación de install.php, el plugin vos-autoinstall se registra y activa automáticamente (llamando directamente a PluginManager::install()). Los usuarios pueden acceder al marketplace de inmediato al entrar al panel de administración sin un paso de instalación adicional.
Added
Añadido soporte del parámetro ?locale=xx a market.21ces.com/api/market/catalog y /item. El JSON multilingüe de name, description, short_description, cat_name se devuelve como una sola cadena desde el servidor (reserva: locale solicitado → en → primer valor). Eliminada toda la lógica duplicada de json_decode + locale fallback del lado de VosCMS (browse.php, item-detail.php, _components/item-card.php).
Added
Gestión integrada del TTL de la caché de respuestas de la API del marketplace (basada en archivos) en la configuración de PluginManager. 30 minutos fijo → 5 minutos por defecto, con opciones de 1/5/10/30/60 minutos. Tanto la sección de caché del marketplace en https://vos.21ces.com/theadmin/settings/general como /theadmin/autoinstall/settings leen y escriben la misma configuración, sincronizándose automáticamente. Botón de actualización inmediata de caché añadido en la cabecera de la página AutoInstall y en la página de configuración general. La caché existente se invalida automáticamente al cambiar el TTL.
Added
Muestra la misma tabla que el admin del marketplace (tipo de datos / descripción breve / slug / Product Key / versiones mínimas de VosCMS y PHP / socio / precio / licencia) inmediatamente después de la cabecera. Cambiada la proporción de contenido izquierda:derecha de 2:1 a 3:1 para ampliar el área de contenido.
Added
Botón "Escribir reseña" en la parte superior derecha de la pestaña de reseñas de la página de detalles. Modal de entrada de calificación por estrellas / apodo / texto → API de VosCMS → retransmitido al marketplace POST /api/market/item/review. Clave de licencia VosCMS + dominio adjuntados automáticamente, límite de un envío por dominio, los compradores obtienen is_verified=1 automáticamente. Las reseñas se almacenan en la BD del marketplace (rzx_mkt_reviews) como status='pending' y se publican tras la aprobación del operador. Nuevo endpoint GET /api/market/item/reviews?slug=... devuelve solo reseñas aprobadas. El dominio se muestra parcialmente enmascarado (mys***.com).
Added
La casilla anterior Solo gratuitos → ampliada a un toggle de 3 estados Todos / Gratuitos / De pago. Parámetro paid=1 añadido a la API del catálogo del mercado (i.price>0). Extraído de la barra de búsqueda y movido delante del área de toggle de vista, integrado con el selector de ordenación en una barra de herramientas del encabezado.
Changed
Borde del input de búsqueda border-2 border-emerald-300, caja de fondo verde en el icono de búsqueda de la izquierda, botón de búsqueda bg-emerald-500. Barra de búsqueda simplificada, dejando solo la entrada de palabras clave; las funciones de ordenación y filtro se movieron al encabezado del cuerpo.
Changed
| Estado | Visualización |
|---|
| Gratuito | Instalar + Descargar |
| De pago (no comprado) | Comprar (modal de pago PAY.JP del marketplace) |
| De pago (comprado) | Instalar + Descargar |
| Ya instalado | "Instalado" |
La misma ramificación aplicada a las 4 vistas de lista (lista/webzine/tarjeta/cuadrícula). El conjunto de slugs comprados se consulta una vez al entrar en la página y se reutiliza. El pago lo gestiona el marketplace POST /api/market/item/purchase, que realiza el cobro de PAY.JP + emisión de licencia.
Changed
- •Toggle de estilo de vista: etiquetas de texto eliminadas → solo iconos +
title / aria-label (accesibilidad mantenida)
- •Insignia de tipo: fondo claro
bg-<color>-100 → translúcido oscuro bg-zinc-900/85 backdrop-blur-sm + texto de color (mejor legibilidad sobre imágenes)
- •Sección de capturas de pantalla: de inmediatamente después de la cabecera → debajo del contenido de las pestañas, recuento mostrado como
(n) como en el marketplace
- •
dark:prose-invert en la página de detalles no funcionaba → se añadió el plugin de tipografía a CDN Tailwind
- •Área de descripción de la página de detalles: el mismo enfoque que el admin del marketplace — renderiza el marcado HTML tal cual, solo eliminando elementos peligrosos como
<script>/<iframe>/<style>/onXX
Fixed
- •Formulario de tarjeta PAY.JP no acepta entrada: montado en estado oculto por adelantado, causando que el iframe se dibuje a 0px. Resuelto con inicialización diferida + montaje con
requestAnimationFrame
- •Miniatura de widget faltante: 28 widgets registrados en la BD del marketplace pero solo 12 widgets gratuitos copiados a
/var/www/market/widgets/. Sincronizados los 14 faltantes. Añadida conversión de URL absoluta para screenshots + filtrado automático de archivos inexistentes a la API catalog/item
- •Advertencia de plugin de bundle no autorizado:
vos-autoinstall era detectado como unauthorizedPlugins. Añadida constante BUNDLE_PLUGINS = ['vos-autoinstall', 'vos-marketplace'] a LicenseStatus.php, filtrado aplicado en active()/fromCache()/canUsePlugin(). El mismo tratamiento aplicado al servidor de licencias verify.php
- •Posibilidad de adición duplicada de widgets: el seeding de
install.php solo usaba INSERT IGNORE, pero rzx_page_widgets no tiene restricción UNIQUE, por lo que los duplicados eran posibles. Cambiado a DELETE FROM page_widgets WHERE page_slug='home' antes del INSERT (la restricción UNIQUE de BD no se añade porque existen duplicados intencionales para casos como widgets de tablones)
Migration Notes
- •El menú/rutas/migraciones del plugin
vos-autoinstall se auto-activan en instalaciones nuevas (las instalaciones existentes necesitan activarlo una vez desde el panel de administración)
- •TTL por defecto de la caché de la API del marketplace: 30 minutos → 5 minutos (para una reflexión más rápida de los cambios de precio/contenido)
- •La configuración del plugin
vos-autoinstall.cache_ttl es nueva
Changed
- •
browse.php: cambiado de consulta a BD local a llamada API en vivo a market.21ces.com/api/market/catalog
- •
item-detail.php: cambiado de BD local a llamadas a las API /api/market/item y /api/market/item/versions
- •
CatalogClient.php: URL base de la API cambiada de /api/v1 → /api/market, campo de respuesta items[] → data[] alineado
Fixed
Problema: El bloque de aplicación de diseño en market.21ces.com/index.php no gestionaba el caso $__noLayout = true, lo que hacía que todas las rutas de la API del plugin (api/market/*) devolvieran HTTP 200 + respuesta vacía.
Corrección: Se añadió elseif ($__pageFile && $__noLayout) { include $__pageFile; } al final de index.php. api/market/catalog, api/market/item, api/market/updates/check, etc., ahora funcionan correctamente.
Added
Se añadieron los parámetros sort, free y featured a plugins/vos-market/views/api/catalog.php, con campos de respuesta ampliados (sale_price, rating_avg, is_featured, etc.).
Added
Helper mpApiFetch(): minimiza las solicitudes al servidor del marketplace con caché de archivo TTL de 30 minutos. Muestra un banner de error si falla la respuesta de la API.
Fixed
Problema: En AdminRouter, tras crear una instancia $licenseClient = new LicenseClient() e intentar el registro automático, la actualización de $_ENV['LICENSE_KEY'] no se reflejaba en el $this->licenseKey interno de la instancia — por lo que una llamada a check() dentro de la misma solicitud seguía devolviendo unregistered.
Changed
- •Al entrar en
check(), si $this->licenseKey está vacío, llamar inmediatamente a /api/license/register
- •En caso de éxito: actualizar
$this->licenseKey + actualizar $_ENV['LICENSE_KEY'] + guardar el archivo .env, y luego continuar al paso de verificación
- •Solo devolver
unregistered en caso de fallo (sin respuesta del servidor)
- •Nuevo método
saveKeyToEnv(): reemplaza una línea LICENSE_KEY= vacía (caso de fallo de install.php) con la clave real; se omite si ya existe una clave válida
Efecto
- •Usuarios que instalaron manualmente sin install.php → licencia registrada automáticamente en el primer acceso al administrador
- •Usuarios que actualizaron desde una versión anterior (sin LICENSE_KEY) → registrados automáticamente en el primer acceso al administrador después de la actualización
- •El mensaje del panel "Licencia no registrada" ahora solo aparece cuando el servidor de licencias en sí no es accesible
Fixed
El archivo semilla database/seeds/legal_pages.php tenía variables CSS de Tailwind (--tw-scale-x, --tw-pan-x, etc., 400+) incrustadas en línea en el atributo style="" de cada etiqueta <h1>/<h2>/<p>. Cada instalación propagaba esta contaminación a la BD, inflando las páginas terms y privacy (en especial, privacy/ko llegó a 128KB; lo normal es 2KB).
- •
database/seeds/legal_pages.php: 442KB → 131KB (reducción del 70%, sintaxis válida)
- •Se limpiaron 26 filas ya contaminadas en la BD de desarrollo: 1,04MB → 56KB (988KB eliminados, −95%)
- •Nuevo
scripts/clean-inline-styles.php — herramienta de limpieza masiva para style="... --tw-* ..." (reutilizable si el problema reaparece)
Changed
El CSS de .board-content contenía reglas que forzaban la conversión de cada página de documento tratando "h2 como encabezado de tarjeta y h2+p como cuerpo de tarjeta". Esto rompió la estructura hero de páginas diseñadas directamente con Tailwind (p. ej., Brand).
- •Se eliminaron 103 líneas de reglas de bloques de tarjeta (149 → 45 líneas)
- •Reglas restantes: tipografía base (p/h·/blockquote/code/ul/ol/table), tarjeta de enlace OG, anulación de texto en modo oscuro
- •Resultado: diseño original de la página
Brand restaurado (h2 con línea azul izquierda, hero centrado, etc.)
- •Los documentos legales (terms/privacy, etc.) se sirven adecuadamente con tipografía estándar
<h1><h2><p> — el estilo de tarjetas era una decoración excesiva
Changed
Contexto: En 2.3.1 se separó el contenido de páginas en rzx_page_contents para el original en coreano y rzx_translations para locales no coreanas, y en 2.3.2 se añadió una ruta de guardado adicional en el editor. Sin embargo, tras revisar el diseño se constató que las dos tablas tienen una estructura esencialmente idéntica (slug/key + locale + content) e introducen los siguientes costes:
- •El texto original en coreano se almacena duplicado en ambas tablas (para el espejo de la cadena de fallback db_trans)
- •La lógica de guardado del editor requiere una bifurcación para ko vs. no-ko
- •Mostrar el estado del locale en la interfaz de gestión de páginas requiere un JOIN entre dos tablas
- •La justificación original («cola unificada del pipeline de traducción IA») tiene poco valor en la práctica, ya que el cuerpo largo de páginas HTML (10 KB) y las cadenas cortas de interfaz (20 bytes) requieren flujos de trabajo distintos (división en fragmentos, preservación de etiquetas, etc.)
Estructura restaurada: tabla única rzx_page_contents, una fila por locale (diseño original)
Migration
- •
rzx_translations.page.{slug}.title/content × 7 slugs × 12 locales no-ko (168 filas) → restaurado en rzx_page_contents
- •Todos los claves
page.* eliminadas de rzx_translations (234 filas)
- •Objetivos: Brand, terms, privacy, refund-policy, tokushoho, funds-settlement, data-policy
- •Aplicado en las bases de datos de desarrollo y producción
Changed
- •
resources/views/admin/site/pages-edit-content.php — bifurcación ko/no-ko eliminada, UPSERT simplificado
- •
resources/views/admin/site/pages-document.php — igual
- •
resources/views/customer/page.php — db_trans('page.*.title/content') eliminado, lectura directa desde page_contents (cadena de fallback locale → en → ko mantenida)
- •
resources/views/admin/site/pages.php — los títulos de la lista de páginas también se leen directamente de page_contents.title
Kept
rzx_translations en sí se mantiene — sigue utilizándose para:
- •Tablones (
board_post.{id}.title/content)
- •Cadenas de interfaz (
menu.*, etiquetas de configuración)
- •Meta SEO de página (
page.{slug}.browser_title|meta_keywords|meta_description) — cadenas cortas, apropiadas aquí
Principio: cadenas cortas de interfaz/meta → rzx_translations; contenido largo de cuerpo de página → rzx_page_contents, almacenamiento separado según las características de cada tipo de dato.
Changed
Tras migrar en 2.3.1 la estructura de guardado de contenido de páginas a ko original (rzx_page_contents) + DB de traducción (rzx_translations), el editor de administración seguía haciendo INSERT/UPDATE de todos los locales únicamente en rzx_page_contents — causando una discrepancia con el formato de migración y fallos de consulta de db_trans() en el lado del renderizado. Esta versión lo corrige.
Reglas de bifurcación (locale fuente = $_ENV['DEFAULT_LOCALE'] ?? 'ko'):
| locale | destino de guardado |
|---|
ko (fuente) | rzx_page_contents + espejo en rzx_translations (respaldo para db_trans()) |
en/ja/zh_CN/... | solo rzx_translations (page.{slug}.title, page.{slug}.content) |
Al guardar un locale no fuente, si no hay fila fuente en rzx_page_contents, se crea automáticamente una fila stub (para preservar las conexiones de menú/configuración). is_active se aplica uniformemente a la fila fuente.
Changed
Al abrir la página de edición de un locale no fuente:
1. Buscar primero page.{slug}.title/content + locale en rzx_translations
2. Hacer fallback a la fila heredada de rzx_page_contents si no se encuentra (para compatibilidad con páginas no migradas)
Para el locale fuente, solo se usa rzx_page_contents como antes.
Changed
- •Eliminación de página: también eliminar
rzx_translations WHERE lang_key LIKE 'page.{slug}.%'
- •Cambio de slug: UPDATE masivo de
page.{old_slug}. → page.{new_slug}. en rzx_translations.lang_key
Changed
El método anterior de almacenar una fila de contenido completo por locale en rzx_page_contents se ha unificado al método ko original + BD de traducción (rzx_translations). Se logra coherencia estructural con el formato de almacenamiento del tablero (board_post.*).
7 páginas objetivo: Brand, terms, privacy, refund-policy, tokushoho, funds-settlement, data-policy
Before:
rzx_page_contents
├── Brand / ko (contenido completo)
├── Brand / en (contenido completo)
├── ... (13 filas de locale)
After:
rzx_page_contents
└── Brand / ko (1 fila original)
rzx_translations
├── page.Brand.title × 13 locale (incluido ko)
└── page.Brand.content × 13 locale
Resultados de la migración:
- •
rzx_page_contents: 7 slugs × 12 filas no-ko = 84 filas eliminadas
- •
rzx_translations: 7 slugs × 13 locales × (title + content) = 182 filas migradas
- •Aplicado a las BD de desarrollo y producción, validación de renderizado superada (visualización correcta por locale para ko/en, etc.)
Changed
- •Botón "Nueva página" del encabezado eliminado — era un marcador de posición no funcional. Las páginas se crean desde la gestión de menús como única fuente de verdad.
- •Icono de papelera 🗑 + handler AJAX + JS
deleteUserPage() eliminados — las páginas se limpian al eliminar el menú, por lo que no se necesita una ruta de eliminación de páginas independiente. Previene menús rotos por eliminaciones erróneas.
- •Se mantienen: iconos de configuración ⚙ · editar ✏ · vista previa 👁
Fixed
- •Fallo al subir imagen en
Configuración > Sitio > Imagen de logo: move_uploaded_file() fallaba silenciosamente por la ausencia del subdirectorio storage/logos/. Se añadió creación automática del subdirectorio al manejador de carga + creación del directorio en servidores de desarrollo y producción (www-data:www-data, 775).
- •Prioridad del logo de diseño restaurada: brevemente invertida a "configuración del sitio primero", luego restaurada a diseño > sitio como alternativa para cumplir el requisito de "logo específico por diseño". Si no hay logo en el diseño, se usa el logo del sitio automáticamente.
- •Afecta:
skins/layouts/{default, minimal, modern, modern-copy-260413}/header.php y footer.php
Fixed
- •Se conservó solo la fila creada primero para los slugs duplicados
bi-guide / logo-guide (id=186, 187), se eliminaron los duplicados id=193, 194
- •Aplicado a desarrollo y producción. El bug de INSERT duplicado en la API de menús ya fue corregido en v2.3.0
Added
Assets de logo creados en /var/www/_upload/ — subir a producción según sea necesario:
Icono (solo marca)
- •
voscms-logo-color-{256,512,1024}.png · color (deep·ocean·sky·ocean + deep en el centro)
- •
voscms-logo-mono-{256,512,1024}.png · sky sólido + degradado de opacidad 1/0.7/0.48/0.7
Wordmark horizontal (para encabezado) — proporción 820×256
- •
voscms-wordmark-{128h,256h,512h}.png · claro (Vos deep + CMS gris)
- •
voscms-wordmark-dark-{128h,256h,512h}.png · oscuro (Vos sky + CMS slate-300)
Wordmark vertical (para icono de app / imagen OG) — proporción 360×420
- •
voscms-stacked-light-{256h,512h,1024h}.png
- •
voscms-stacked-dark-{256h,512h,1024h}.png
Todos los SVG originales conservados. librsvg2-bin instalado proporciona el CLI rsvg-convert. El problema de compatibilidad con colores OKLCH se resolvió con la versión convertida a hex (voscms-logo-mono-hex.svg).
Added
- •Tabla DB
rzx_changelog nueva — registros independientes por versión × locale, seguimiento de translation_source (original/ai/manual) + source_hash
- •Analizador
RzxLib\Core\Changelog\ChangelogParser — estándar Keep a Changelog + lista blanca de secciones internas (Infrastructure/Internal/Refactor/Chore/Docs/Test son privadas)
- •Importador
ChangelogImporter — combinación idempotente, protección de traducciones manuales, detección de cambios mediante content_hash
- •Script CLI
scripts/import-changelog.php — opciones --file --locale --dry-run --silent, soporte de pre-release SemVer (2.1.1-hotfix)
- •Interfaz de traducción
RzxLib\Core\Translate\{TranslatorInterface, NullTranslator, TranslatorFactory} — se activa cambiando el driver en config/translator.php cuando el GPU de Gemma 4 (ai.21ces.com) esté listo
- •Página del sistema
/changelog registrada (config/system-pages.php), vista de front-end resources/views/system/changelog/index.php
- •Skin dedicado
skins/page/changelog/skin.json — estilos de tarjeta (filled/outlined/flat), colores por sección, insignia de versión, formato de fecha, visualización de tiempo relativo, alternancia de visibilidad de secciones internas
- •Página de edición
/changelog/edit separada — vista previa de carga de MD, matriz de cobertura multilingüe, alternancia y eliminación por versión, botón de traducción por IA (próximamente)
- •Automatización de despliegue — paso
1.5/5 Changelog import añadido a deploy-voscms.sh: detección y combinación automática de archivos .md ko + 12 locales
- •Clave de traducción
site.pages.changelog añadida para 13 locales
Changed
- •Orden de pestañas reorganizado:
[Info básica] [Permisos] [Avanzado] [Skin] — las páginas del sistema pueden añadir sus propias pestañas al final
- •Esquema
settings_tabs introducido (config/system-pages.php) — las páginas del sistema declaran una matriz de pestañas de nivel superior; cada elemento se renderiza dinámicamente con key/label/icon/view. Eliminado el legacy de include inferior de settings_view
- •Principio establecido: Configuración = apariencia + funcionalidad / Edición = datos — las interfaces de gestión de datos (carga, versión, traducción) se separan en
/changelog/edit
- •Estrategia de guardado MERGE — el manejador
save_settings combina con la configuración existente. Los datos de otras pestañas no se borran aunque estén separadas
- •JS
saveSettings() — envía solo los campos presentes en el DOM. Seguro cuando algunos campos no se renderizan en un entorno de pestañas separadas
- •Posición de la tarjeta de layout/skin — devuelta a la pestaña de Info básica (según feedback de usuarios). La pestaña Skin es exclusiva para variables de skin.json
- •Duplicación de ancho de página eliminada — campo
page_width eliminado de la pestaña Info básica (consolidado en content_width del skin)
- •Aplanamiento de subpestañas secundarias en service/order —
general/domain/hosting/addons de config/service-settings-tabs.php migrados a settings_tabs, promovidos a pestañas de nivel superior
Changed
- •
/{slug}/settings y /{slug}/edit ahora reconocen config/system-pages.php — antes solo se consultaba la BD rzx_page_contents, causando 404 en páginas del sistema. Ahora se consulta en orden: BD → config
- •Campo
edit_view añadido — especifica directamente la ruta del archivo de vista de edición para páginas del sistema. Al hacer clic en /{slug}/edit se incluye directamente esa vista (eliminado el salto de redirección de administrador)
- •Fallback en pages-settings.php — las páginas del sistema sin registro en BD ahora construyen
$pageData desde la configuración, evitando redirecciones a la lista de gestión de páginas
- •URL de icono de administrador unificada —
/{slug}/settings y /{slug}/edit usan la URL estándar del front-end
Fixed
- •
menus-api.php: corregida la falta de verificación de duplicados antes del INSERT para el tipo de página external — evitaba filas duplicadas en rzx_page_contents al guardar el menú dos veces
- •Corregida la omisión de limpieza de
page_contents del tipo external + widget al eliminar un menú — previene registros huérfanos
Fixed
- •Fondo del título (
title_bg_type, title_bg_image, title_bg_video, title_bg_overlay, title_text_color), show_title, show_breadcrumb, custom_css, custom_header_html, custom_footer_html — todas las variables estándar del skin de página ahora se reflejan en la vista pública
- •El partial
_page-title-bg.php ahora acepta la variable $_contentWidth — el área del título sigue el ancho del contenido
- •Icono de administrador separado del título — colocado de forma independiente en la parte superior derecha del área de contenido; siempre visible aunque
show_title=0
- •Añadido margen superior de 20px sobre el área del título (equilibrio del diseño de fondo)
Added
- •Clave de traducción
site.pages.contact añadida para 13 locales (ko/en/ja/zh_CN/zh_TW/de/es/fr/id/mn/ru/tr/vi) — resuelve la omisión anterior
- •Guía del sistema de gestión de páginas
docs/PAGE_ADMIN_GUIDE.md nueva — documenta la estructura de pestañas, esquema, enrutamiento, permisos y métodos de extensión
Added
- •Campos por elemento: número, sufijo, etiqueta (multilingüe), icono y color
- •El constructor de widgets de administrador permite agregar/editar/eliminar elementos (nueva interfaz de edición del tipo
stat_items)
- •16 tipos de icono (users, chart, star, heart, check-circle, clock, calendar, globe, shield, lightning, trophy, cart, dollar, building, chat) + 10 opciones de color
- •Animación de conteo al entrar al viewport — IntersectionObserver + easeOutCubic 1.4s
- •Solo se cuentan los valores numéricos (las cadenas como
24/7 se muestran tal cual), se conservan el formato de comas y los decimales
- •
widget.json v1.1.0 → v1.2.0
Fixed
- •Solucionado el problema en que
admin_name se almacenaba con el prefijo enc: y se mostraba sin descifrar
- •Solucionado el problema en que la consulta de avatar hacía LEFT JOIN con una tabla
staff inexistente — ahora consulta directamente rzx_users.profile_image según la estructura unificada admin_id = users.id
- •Se agrega automáticamente el prefijo baseUrl a los avatares con ruta relativa
Added
- •Botones
[Obtener miniatura] [Obtener captura de pantalla] insertados automáticamente junto a cada campo <input type="url"> (incluido el tipo de variable extendida url) en todos los tablones
- •Miniatura: renderizado nativo a 1280×960 → salida 800×600 (primer viewport de pantalla)
- •Captura de pantalla: renderizado nativo a 1280×página_completa → salida 800×reducida (página de desplazamiento completo, altura real de la página)
- •Interfaz de barra de progreso paso a paso —
Conectar al servidor → Cargar página → Capturar pantalla → Generar imagen → Guardar archivo → Esperando respuesta
- •Asignación automática de la miniatura capturada como imagen destacada — marcada como
board_files.is_primary=1, priorizada por el widget board-showcase
- •Nueva ruta
/board/api/url-capture?type={thumbnail|screenshot}&url=... + proxy api-url-capture.php
- •Acción
set_primary añadida a /board/api/files — designa uno de los archivos adjuntos existentes como imagen destacada
Changed
- •Consulta de imágenes adjuntas
ORDER BY is_primary DESC, id ASC — las miniaturas capturadas se muestran primero
- •Parámetro
primary_file_pos enviado al servidor al crear una publicación para especificar el índice del archivo destacado
Changed
- •Separación del producto Marketplace: la funcionalidad que compartía el plugin
plugins/vos-marketplace/ se ha separado como producto independiente → github.com/THEVOSjp/Marketplace nuevo repositorio, despliegue independiente en market.21ces.com
- •El plugin conserva únicamente la «interfaz del lado del cliente (explorar, comprar, instalar, licenciar)» — datos, vendedores y liquidaciones son gestionados exclusivamente por el servidor del marketplace
- •Puntos de integración de API del lado del core actualizados
Added
- •Modal tras la selección de idioma — aviso sobre la necesidad de hacer copia de seguridad de
.env + BD + storage/uploads/, y advertencia de que la pérdida del APP_KEY imposibilita el descifrado
- •Confirmación en dos pasos: casilla de verificación + botón de confirmación (se prohíbe la instalación incondicional)
- •Traducción completa añadida para los 13 idiomas (
backup_title/body/item_env/db/uploads/warn/check/confirm/cancel)
Added
- •
scripts/register-widgets-to-marketplace.php — registra en bloque 28 widgets basados en archivos en rzx_mp_items
Changed
- •
plugins/vos-marketplace/src/CatalogClient.php — URL de API por defecto cambiada a https://market.21ces.com/api/v1, ruta de fetchItem() actualizada, formato de consulta de checkUpdates() actualizado
- •
plugins/vos-marketplace/plugin.json — valor por defecto de marketplace_api_url actualizado
- •
rzxlib/Core/Modules/WidgetLoader.php — la URL de miniatura ahora incluye el cache-buster ?v={mtime} (para gestionar la caché inmutable de Cloudflare)
Fixed
- •
widgets/feature-tour/widget.json — limpieza de campos
- •Miniaturas de algunos widgets actualizadas (staff, testimonials, feature-tour, shop-map, shop-ranking, stylebook)
v2.1.5
2026-04-17 · hace 6 meses
Added
- •contact-info (v0.1.0) — tarjeta de contacto (dirección/teléfono/email/horario + imagen + mapa pequeño)
- •location-map (v0.1.0) — mapa horizontal (Google Maps + información en 3 columnas debajo, anchura ajustable)
Added
- •Refactorización completa de la página de envío de elementos — 3 pestañas (información básica / publicación / ventas)
- •Licencia (GPL/LGPL/BSD/MIT/CC/PD/comercial), URL del repositorio, URL de demo, plugins dependientes, imagen de banner
- •Historial de publicaciones, información de ventas (toggle público/de pago, precio de descuento/fecha de fin), mejoras de UX
Added
- •Menú de desarrollador (ID:20) en 13 idiomas, description/typing_words de Hero CTA complementados en 12 idiomas
Changed
- •Distinción de nombres de widgets aclarada (mapa / tarjeta de contacto), fullWidthTypes añadido a
page.php
- •contact-form categoría → solo etiqueta (código eliminado, configuración de widget editable)
Fixed
- •board-contents/board-showcase: file_type → mime_type
- •boards-api: is_shown_in_list faltante + restricción JSON de options
- •contact-form POST: vaciado completo del búfer ob
- •Hero CTA: doble escape de
\n en typing_words/description
DB Changes
- •rzx_mp_items: license, repo_url, demo_url añadidos
- •rzx_board_extra_vars: is_shown_in_list añadido
- •rzx_contact_messages: nueva tabla
v2.1.4
2026-04-17 · hace 6 meses
Added
- •Widget download-hero — página de descarga de software (integración en tiempo real con version.json, 4 idiomas integrados, características principales / requisitos del sistema / historial de versiones)
- •Página /downloads — página de sistema de tipo widget, traducciones de menú en 13 idiomas,
is_system=1
- •Widget contact-form (v0.1.0) — widget de formulario de consulta privado 1:1 (compatible con invitados, selección de categoría, protección antispam con honeypot, envío por AJAX)
- •Widget contact-info — widget de visualización de información de contacto
- •Widget location-map — widget de mapa de ubicación
- •Helper compartido rzx_send_mail() — nueva función de envío de correo SMTP en
rzxlib/Core/Helpers/mail.php
- •Tabla rzx_contact_messages — tabla de BD para almacenar consultas
- •Página /contact — página de sistema de tipo widget, traducciones de menú en 13 idiomas
Fixed
- •Protección contra eliminación de páginas del sistema — preserva el contenido/widgets de la página al eliminarla del menú (solo se elimina la entrada del menú)
- •
menus-api.php: verificación doble mediante DB is_system + slug de config/system-pages.php
- •
menus-js.php: muestra el aviso "Solo se eliminará del menú" al borrar una página del sistema
- •Menú de páginas del sistema multilingüe — al añadir una página del sistema al menú, las etiquetas en 13 idiomas se registran automáticamente desde las claves de traducción
site.pages.*
- •Traducción de la página de descargas — añadidas traducciones de
site.pages.downloads para 13 idiomas
Changed
- •Hero de la página principal — botón de descarga → descarga directa en
/download/voscms-latest.zip, versión actualizada a v2.1.1
- •Auth.php — llama primero al helper compartido
rzx_send_mail() antes de recurrir a sendSmtpMail
- •index.php — añadida carga automática del helper
mail.php
v2.1.3
2026-04-17 · hace 6 meses
Changed
- •Pestaña de alojamiento — tarjeta de resumen + barra de progreso de uso + tabla de información de conexión FTP/BD (se muestra automáticamente cuando el administrador la configura)
- •Pestaña de dominio — tarjeta de dominio + configuración principal + tabla de información de servidores de nombres (se muestra automáticamente cuando el administrador la configura)
- •Pestaña de correo — información del servidor de correo (IMAP/SMTP) + tarjeta por cuenta + cambio de contraseña en línea
- •Pestaña de mantenimiento — tarjeta de información + fecha programada + historial de trabajo (se muestra automáticamente cuando el administrador lo registra)
- •Pestaña de servicios adicionales — diseño de tarjetas unificado
- •Estructura de información unificada igual que el parcial de administrador (el administrador introduce en metadata → se refleja automáticamente en mi página)
Changed
- •Área del registro de actividad reducida de proporción 1/3 a 1/5
- •Eliminado el valor predeterminado NS hardcodeado en la pestaña de dominio del administrador (en preparación para la integración con la API de Cloudflare)
Changed
- •Flujo de trabajo de aprovisionamiento revisado por completo: subdominio primero → dominio comprado conectado después
- •Regla de ID de aprovisionamiento: nombre del subdominio o SLD del dominio comprado
- •Aprovechamiento del túnel wildcard de Cloudflare (no se necesita agregar registro A para subdominios)
- •Configuración de disco del servidor 2 cambiada: NVMe 256 GB + SSD 2 TB + HDD 8 TB (configuración de 3 discos)
v2.1.2
2026-04-16 · hace 6 meses
Added
- •Sistema de clasificación de servicios — columna
service_class añadida (recurring/one_time/free), completed_at fecha de finalización para servicios de una sola vez
- •Detalle de servicio en Mi página —
/mypage/services/{order} gestión de pestañas por tipo (alojamiento/dominio/correo/mantenimiento/servicios adicionales)
- •API de gestión de servicios —
api/service-manage.php (interruptor de renovación automática, solicitud de renovación, configuración de dominio principal, cambio de contraseña de correo)
- •Gestión de pedidos en administración —
/admin/service-orders lista de pedidos + página de detalle, modal de estado para servicios de una vez (recibido/en progreso/en espera/cancelado/completado)
- •Menú de administración — menú "Pedidos de servicio" añadido a la barra lateral (position: 65)
Added
- •phone-display — componente de visualización de número de teléfono (helper
format_phone() en servidor), sin dependencia de JS
- •format_phone() — función PHP de formato de número de teléfono internacional (Corea/Japón/EE.UU./China/Reino Unido/Francia/Alemania/Australia etc.)
- •Configuración de dominio gratuito — gestión de lista de subdominios gratuitos en ajustes de administración (
service_free_domains), soporte de selección múltiple de dominios
Fixed
- •Suscripciones de servicio gratuito faltantes — los pedidos gratuitos no creaban registros de suscripción para dominio/complemento/mantenimiento etc. → refactorizado para crear todos los servicios en bloque
- •Acuerdo de términos en sección de pago — la casilla de términos se ocultaba junto con la sección de pago en pedidos gratuitos → separada fuera de la sección
- •Detección isFree de updateSubmitButton —
btn.closest('form') NULL → corregido con document.querySelector
- •Indicador activo de barra lateral de administración —
$adminPath indefinido causaba que todos los menús estuvieran inactivos → añadida declaración de variable
- •Avatar de Mi página — faltaba
use Auth + $user = Auth::user() en services.php/service-detail.php
- •Verificación de una sola vez para servicios adicionales — el indicador
one_time en ajustes de administración ahora se refleja correctamente en service_class al realizar el pedido
- •Estado inicial de servicio de una sola vez — cambiado para crearse como
pending (recibido) en lugar de active
Changed
- •Refactorización de API de pedidos — código de creación de suscripción de las 3 rutas de pago (gratuito/tarjeta/transferencia bancaria) consolidado en función compartida
$subscriptionData + _insertSubscriptions()
- •UI de lista de servicios — expansión en línea → enlace de tarjeta + página de detalle separada
- •Visualización de número de teléfono —
format_phone() aplicado a perfil/lista de miembros/lista de personal
- •Dinamización de dominio gratuito —
.21ces.net hardcode → referencia al valor de ajustes de administración
v2.1.1-hotfix
2026-04-16 · hace 6 meses
Fixed
- •Errores 404 en el menú del pie de página — datos faltantes de páginas legales (terms, privacy, refund-policy, data-policy, tokushoho, funds-settlement) en la tabla
rzx_page_contents → restaurados desde la BD de prueba
- •Fallo en la carga de imágenes/vídeos del constructor de widgets — corregido el problema donde el POST
FormData desde el envoltorio front-end (page-widget-builder.php) no llegaba al manejador AJAX (condición de carga de archivo añadida + manejo de ob_end_clean/exit)
- •Permisos del directorio de carga — cambiado el propietario de
storage/uploads/widgets/ a www-data para garantizar acceso de escritura a PHP-FPM
Added
- •Menú de solicitud de servicio multilingüe — añadidas traducciones de
menu_item.19.title en 13 idiomas en la tabla rzx_translations
- •Widget Hero CTA multilingüe — aplicadas traducciones en 13 idiomas al título, subtítulo y texto del botón del banner hero de la página Dev
- •Vista previa del estado de carga — añadido feedback visual durante la carga de imágenes/vídeos: en curso (spinner), éxito (marca de verificación) y error (mensaje de error)
v2.1.1
2026-04-12 · hace 6 meses
Added
- •Pantalla de selección de idioma en el Paso 0 — elegir entre 13 idiomas antes de comenzar la instalación; toda la UI se muestra en el idioma seleccionado desde ese momento
- •Archivos de traducción de instalación separados —
resources/lang/install.php (13 idiomas × 45 cadenas)
- •Nivel de miembro multilingüe — Normal/Silver/Gold/VIP generado automáticamente en 13 idiomas
Added
- •config/admin-menu.php — menú de administrador principal separado en un archivo de configuración (se conserva en actualizaciones del núcleo)
- •admin-sidebar.php refactorizado — 731 líneas codificadas fijo → ~200 líneas de renderizador dinámico (basado en config + plugin.json)
- •vos-shop plugin.json — menú codificado fijo migrado al formato
menus.admin
Added
- •Menú deslizante — móvil: barra de alternancia de avatar+nombre → superposición deslizante izquierda (300ms ease-out)
- •Fondo de atenuación — cerrar haciendo clic en el fondo o en el botón X; desplazamiento del cuerpo bloqueado
Added
- •force_locale — aplicar el idioma predeterminado forzosamente (el parámetro
?lang=xx permite que el usuario lo cambie)
- •default_language — clave de configuración install.php ↔ Translator unificada
- •Parámetro URL —
?lang=xx detectado primero, guardado en sesión/cookie
Fixed
- •Skin de FAQ no aplicado — el SQL de migración ejecutaba un
INSERT sin opciones para el tablón predeterminado antes de install.php, lo que causaba que el INSERT IGNORE fuera ignorado silenciosamente; corregido
- •source_locale faltante en la tabla translations — columna ausente en la migración del núcleo → db_trans() fallando por completo → información de usuario/menú/i18n no mostrada; corregido
- •Indicador activo en la barra lateral de la pestaña de configuración — atributo
route_prefix añadido para que el menú de configuración permanezca activo en todas las subrutas de configuración
- •Bug de sesión residual en la instalación — en una nueva instalación sin .env, una sesión PHP anterior hacía que se saltara el Paso 0; corregido
- •nginx 403 — conflicto entre el directorio físico admin/ y la ruta corregido (eliminado
try_files $uri/)
- •Versión codificada fijo en el script de ofuscación — obfuscate.php ahora lee la versión dinámicamente desde version.json
Changed
- •Vista multilingüe del widget grid/board-contents — búsqueda rzx_translations añadida (locale actual → respaldo en inglés)
- •Categorías de industria del sitio — ampliadas de 15 a 26 (añadidas: corporativo, centro comercial, legal, contabilidad, TI, medios, etc.; 13 idiomas)
- •Configuración del widget grid — render.php de grid-section cambiado al tipo de celda nativo (
board-list); título en 13 idiomas
- •Selección de idioma en el Paso 4 — ampliada de 5 a 13 idiomas; rellenada previamente con el idioma de instalación
- •Selección de zona horaria — ampliada a 12 zonas horarias que cubren regiones de 13 idiomas
v1.26.0
2026-04-01 · hace 7 meses
Added
- •Servicios en paquete (conjunto) mostrados en 3 columnas en la parte superior de la selección de servicio — tema de color ámbar para distinguirlos visualmente de los servicios individuales
- •Insignia SET, etiqueta de descuento de evento, nombres de servicios incluidos, precio del paquete, precio original tachado, tasa de descuento
- •Al seleccionar un paquete se seleccionan automáticamente todos los servicios incluidos (precio calculado como precio del paquete)
- •Los servicios incluidos en un paquete no pueden activarse/desactivarse individualmente (previene cálculos de precio incorrectos)
- •Modo de designación: tabla
rzx_staff_bundles para filtrado de paquetes por personal
- •Parámetro
bundles enviado a la página de confirmación
- •Soporte multilingüe: claves de traducción de kiosk relacionadas con paquetes añadidas en 13 idiomas
Added
- •Botones de cambio de vista — alternador
[Mes] [Semana] [Día] en la barra superior (estado activo: azul)
- •Vista semanal — eje vertical izquierdo = día de la semana, eje horizontal superior = eje de tiempo
- •Desplazamiento horizontal, bloques de reserva mostrados como barras de tiempo
- •Algoritmo de carril: detecta superposición de tiempo y coloca automáticamente debajo, altura de fila variable
- •Barra lateral izquierda ↔ desplazamiento vertical de cuadrícula sincronizados
- •Vista diaria — horizontal = eje de tiempo, vertical = carriles (mismo método que la vista semanal)
- •Rango de tiempo basado en horario comercial — basado en la configuración
business_hour_start/business_hour_end; se expande automáticamente para reservas fuera de rango
- •Paso de tiempo al hacer clic en celda vacía — en vista semanal/diaria: coordenada X del clic → cálculo de tiempo en unidades de 15 minutos → hora de inicio del modal de reserva completada automáticamente
- •Barra de navegación superior fija —
overflow-x-hidden + sticky aplicados; permanece en el viewport al desplazarse horizontalmente
Added
- •Administración > Configuración > General — UI de configuración de hora de inicio/fin del negocio añadida
- •Claves de configuración DB
business_hour_start, business_hour_end
- •Aplicadas al rango de tiempo predeterminado en la vista semanal/diaria del calendario
Fixed
- •Campos faltantes en POS
add-service — se añadió la transmisión de start_time, end_time, staff_id, designation_fee, notes (antes: hora del lado del servidor hardcodeada, personal/notas faltantes)
- •Prevención de que end_time supere la medianoche — se aplicó el límite
% 24 → min(23:59) en ReservationHelper, API start-service y calcEnd() del formulario de reserva
- •Manejo de medianoche en la determinación de uso en POS —
CustomerBasedAdapter trata end_time < start_time como cruce de medianoche
- •Validación de paquete de reserva — corregido para permitir la creación de reservas cuando solo se selecciona un paquete
Changed
- •Cambios de nombre de pestaña: Recepción / Lista de recepción / Reservas
- •Lista de recepción: fuentes
walk_in, pos, kiosk, admin — todos los estados mostrados
- •Reservas: fuentes
phone, online — todos los estados mostrados
- •Orden de clasificación: En espera → Confirmado → En uso → Completado → No presentado → Cancelado; dentro del mismo estado ordenado por hora
- •Filas completadas/canceladas/no presentadas: mostradas con
opacity-50, insignia sin asignar visible
Changed
- •Insignia de completado: cambiada de gris → verde
- •Insignias de cancelado/no presentado añadidas (ocultas cuando el recuento es 0)
Added
- •Parte inferior de la tarjeta: botón de no presentado añadido para tarjetas en estado de espera (icono de prohibición), botón de cancelar cambiado a rojo
- •Modal de detalle: botón de acción de no presentado mostrado para estados
pending y confirmed
- •No presentado grupal masivo: función
POS.noShowAllServices() añadida
Fixed
- •Navegación hacia atrás en el detalle de reserva —
<a href="/reservations"> → history.back(), regresa a la página anterior
- •Multilingüe: teclas relacionadas con cambio de vista del calendario (mes/semana/día), paquete, horario comercial añadidas para 13 idiomas
v1.25.0
2026-04-01 · hace 7 meses
Architecture
- •POS separado como estructura de plugin independiente
- •
resources/views/admin/reservations/pos-*.php → movido a directorio resources/views/admin/pos/
- •La función POS se activa/desactiva automáticamente según la presencia del directorio
pos/ (comprobación is_dir())
- •Menú lateral: el menú POS se oculta automáticamente cuando no existe el directorio
pos/
- •Enrutamiento
index.php: las rutas POS se desactivan cuando no existe el directorio pos/
- •
pos/_init.php → delega al _init.php común de reservas (DB, autenticación e i18n compartidos)
- •Los endpoints de API se mantienen en
reservations/_api.php (compatibilidad con versiones anteriores garantizada)
Added
- •Modal de pago unificado — uso simultáneo de saldo + efectivo + tarjeta (pago combinado)
- •Saldo: consulta de saldo, ingreso manual o uso del importe total
- •Efectivo: ingreso de importe
- •Tarjeta: ingreso de importe + botón de auto-relleno "usar saldo restante"
- •Cuotas: pago único / selección de 2 a 12 cuotas
- •Cálculo automático en tiempo real de total, cambio e importe faltante
- •Para pagos con tarjeta Stripe, efectivo/saldo se procesan solo tras el éxito del pago con tarjeta (seguro en caso de cancelación)
- •Modal de detalle de servicio — visualización de saldo
- •El saldo de puntos del miembro se muestra siempre en el área del encabezado (incluso cuando el saldo es 0)
- •Modal de detalle de servicio — mejora de insignia de personal
- •La insignia asignado/designado se muestra delante del nombre del personal (antes: debajo del nombre)
- •Reubicación de botones de recibo/impresión
- •Movidos encima de la sección de detalles del cliente, recibo (izquierda) + impresión (derecha)
- •Mejora de la función de impresión
- •Imprime el contenido del modal de detalle de servicio actual en lugar de abrir una página externa
Changed
- •Diseño de tarjetas — basado en grid → tarjetas de tamaño fijo con flex-wrap
- •Tamaño de tarjeta fijo: predeterminado 320×240px (small: 260×200, large: 380×280)
- •Las tarjetas no cambian de tamaño según el ancho de pantalla; se ajustan automáticamente
- •Modal de pago con tarjeta — ampliado a anchura 1030px, altura 800px
Fixed
- •Error cross-origin de API resuelto
- •
$baseUrl estaba fijado al APP_URL del .env, lo que causaba fallos de fetch desde otros hosts/HTTPS
- •Panel: URL de fetch cambiada a ruta relativa (
/update-api.php)
- •
_init.php de reservas: $baseUrl cambiado a cadena vacía para que todos los fetches usen rutas relativas
- •Problema de mixed-content completamente resuelto en entorno proxy HTTPS de Cloudflare
- •Error de ejecución de migración de BD — resuelto "Failed to fetch" (problema cross-origin)
- •Bug de prepago en efectivo al cancelar pago combinado Stripe
- •Antes: se procesaba primero el efectivo → el efectivo ya estaba aplicado al cancelar el pago con tarjeta
- •Después: el efectivo/saldo se procesa solo tras el éxito del pago con tarjeta Stripe
i18n
- •
pos_pay_after_points — importe tras aplicar saldo
- •
pos_pay_proceed — proceder al pago
- •
pos_pay_remainder — usar saldo restante completo
- •
pos_pay_installment — cuotas
- •
pos_pay_lump_sum — pago único
- •
pos_pay_months — meses
- •
pos_pay_total_pay — total
- •
pos_pay_short — importe faltante
- •
show_receipt — recibo
- •
show_print — imprimir
v1.18.0
2026-03-29 · hace 7 meses
Added
- •Función de formato de fecha multilingüe —
formatReservationDate($dateString, $locale)
- •Compatible con 13 idiomas (ko, en, ja, zh_CN, zh_TW, de, es, fr, id, mn, ru, tr, vi)
- •Localiza automáticamente la fecha y el día de la semana en la página de detalles de la reserva
- •Respaldo: locales no compatibles → formato inglés
Changed
- •Procesamiento multilingüe de la página de detalles de la reserva
- •Nombre del servicio, nombre del personal y nombre del paquete localizados (basado en la tabla de traducciones de la BD)
- •Función de formato de fecha integrada
- •Etiqueta de estado de pago localizada (campo payment_status)
- •Soporte multilingüe para el estado de pago
- •Se añadió la sección
payment a todos los archivos de idioma (11 idiomas)
- •Traducciones para: pendiente, pagado, pago parcial, reembolsado
Fixed
- •Página de detalles de la reserva: el nombre del personal y del servicio que no estaban localizados ahora se muestran correctamente
- •Etiqueta de estado de pago: se corrigió el problema en que solo se mostraba en coreano
- •Procesamiento multilingüe unificado basado en la función
_tr()
Layout & UI
- •Ancho de página unificado (max-w-5xl → max-w-7xl)
- •Páginas del tablón (lista, lectura, escritura): max-w-5xl → max-w-7xl
- •Página externa (page.php): valor predeterminado y valores de configuración de BD actualizados
- •Detalle de reserva / consulta de reserva / páginas de personal: valores de configuración de BD actualizados
- •Resultado: todos los anchos de página coinciden ahora con la página de reservas
Documentation
- •Documentación detallada de
formatReservationDate() añadida en COMPONENT_MANUAL.md
- •Idiomas compatibles y tabla de formatos
- •Ejemplos de uso y estructura interna
- •Notas y funciones relacionadas
v1.16.0
2026-03-23 · hace 7 meses
Added
- •Barra de menú inferior móvil — Personal / Reservas / Inicio / Iniciar sesión (o Mi cuenta) / Registrarse / Más, con resaltado de página actual
- •Panel móvil "Más" — menú principal + Mi cuenta / Cerrar sesión
- •Configuración de diseño/skin aplicada efectivamente — site_layout, site_page_skin, site_board_skin, site_member_skin → reflejado en el renderizado
- •Parámetro no_layout — solo contenido sin diseño (para vista previa)
- •Claves de traducción añadidas — common.nav.staff, common.nav.more (13 idiomas)
Fixed
- •Diseño aplicado a las páginas de miembros (login/register/forgot/reset): encabezado duplicado eliminado extrayendo el
<main> del skin
- •Corregido error de declaración duplicada de ImageCropper/ProfilePhotoCropper en el skin de miembros
- •Corregido problema de include_once del cargador de menú: menú de base-header faltante al cargarse primero dentro del closure del skin
- •
apple-mobile-web-app-capable obsoleto → mobile-web-app-capable (8 archivos)
- •Botones de inicio de sesión/registro del encabezado ocultos en móvil (reemplazados por la barra de menú inferior)
- •Soporte de área segura de iOS para la barra de menú inferior
Changed
- •Páginas de miembros unificadas de su propio diseño al diseño predeterminado (login/register/forgot/reset)
- •Eliminado el margen inferior del pie de página (la barra de menú móvil usa posición fija)
v1.15.3
2026-03-22 · hace 7 meses
Added
- •Panel de ajustes detallados de layout — carga AJAX del formulario de configuración basado en SkinConfigRenderer
- •Ajustes de menú de layout — sección menus de layout.json → generación automática del desplegable de sitemaps en DB
- •Soporte de pestañas en SkinConfigRenderer — añadir la propiedad
tab a vars genera automáticamente la UI de pestañas
- •Refactorización de SkinConfigRenderer — método único
renderVar() unificado, separación de renderFormFlat/renderFormWithTabs, renderDependsOnJs compartido
- •Extensiones de layout.json — menus (GNB/FNB), 14 vars (cabecera/logo/color/pie/personalizado), 13 idiomas
- •Integración de vista previa de layout — al seleccionar "Deshabilitar" muestra la vista previa con ?no_layout=1
Fixed
- •Consulta del menú de gestión de layout: corrección del nombre de tabla
rzx_menus → rzx_sitemaps
- •Ajuste del ancho del panel central + whitespace-nowrap en enlaces de acción
Pending
- •Aplicación de los valores de configuración de layout/skin al renderizado real de páginas (próxima sesión)
v1.15.2
2026-03-22 · hace 7 meses
Added
- •Página de gestión de diseño — diseño de 3 paneles: izquierda vista previa+menú, derecha lista de skins, panel de configuración detallada
- •Por elemento de skin: miniatura + enlaces de acción de configuración detallada/crear copia/eliminar
- •Opciones "Desactivar" / "Instalar otro skin"
- •Guardado en 4 categorías: diseño, página, tablón, miembro (
site_layout, site_page_skin, site_board_skin, site_member_skin)
Changed
- •"Gestión de diseño" → renombrado a "Gestión de diseño" (13 idiomas: admin.php + site.php)
v1.15.1
2026-03-22 · hace 7 meses
Changed
- •15 páginas de gestión del sitio → cabecera compartida
_head.php aplicada
- •Tableros: boards, boards-create, boards-edit, boards-trash
- •Menú: menus
- •Diseño: design
- •Páginas: pages, pages-settings, pages-edit-content, pages-document, pages-compliance, pages-widget-builder
- •Widgets: widgets, widgets-create, widgets-marketplace
- •Las páginas en modo embed (boards-edit, pages-settings, pages-edit-content) solo aplican
_head.php fuera del modo embed
- •Unificación completa del encabezado y diseño de todas las páginas de administración
v1.15.0
2026-03-22 · hace 7 meses
Added
- •Módulo de widget de sistema — 4 widgets: staff (presentación del personal), booking (hacer una reserva), lookup (consulta de reservas), cta001 (banner de llamada a la acción de reserva)
- •Páginas del sistema — staff, booking, lookup registradas en la BD (is_system=1) + enrutamiento (
/staff, /booking, /lookup)
- •Renderizado de páginas basado en widgets — las páginas staff/booking/lookup renderizan widgets cuando están colocados, con retorno al renderizado legado cuando no hay ninguno
- •Registro automático de widgets en BD — llamar a
syncToDatabase() registra automáticamente los widgets al añadir un directorio de widget
- •Categoría de widget
system — traducida a 13 idiomas (widgets.categories + widget_builder.cat)
- •Unificación de la lista de páginas — renderizado en bucle de páginas del sistema + conjunto de iconos engranaje/editar/previsualizar unificado
- •Unificación del encabezado de página de administrador —
$pageSubTitle + $pageSubDesc (salida común a través de _head.php)
- •Claves de traducción añadidas — staff.admins, staff.attendance.report/stats, points.description, etc.
Fixed
- •
page_slug = 'home' en el constructor de widgets codificado fijo → manejo dinámico con $_GET['slug']
- •AJAX save/preview del constructor de widgets con
home codificado fijo → $pageSlug dinámico
- •URL de previsualización del constructor de widgets →
$pageSlug dinámico (inicio fijo → por página)
- •require de WidgetLoader en widget preview_widget faltante — corregido
- •Ruta de imagen en la vista de tarjeta de servicio (prefijo
/storage/)
- •Ancho de configuración de kiosco
max-w-3xl → 100% unificado
- •Encabezado POS "Gestión de reservas" → renombrado a "POS"
Changed
- •10 páginas de gestión de personal + 4 de gestión de miembros → encabezado
_head.php común aplicado
- •Páginas de kiosco/bundle con
_head.php aplicado
- •Configuración de idioma de kiosco grid → flex-wrap responsivo
- •Lista de páginas: páginas del sistema renderizadas mediante bucle basado en datos
v1.14.1
2026-03-22 · hace 7 meses
Added
- •Pestaña de skin de página — información básica del skin + configuración de variables extendidas (basado en SkinConfigRenderer)
- •Skin aplicado a todos los tipos de página — configuración de skin aplicada igualmente a document/widget/external
- •Sistema de fondo de título — fondo de imagen/vídeo + superposición + color de texto (partial común
_page-title-bg.php)
- •Sistema depends_on — dependencia declarativa entre campos en skin.json, el renderizador genera JS automáticamente (soporte checkbox/radio)
- •Carga de archivos de skin — carga de imágenes/vídeos mediante multipart (
storage/skins/page/{skin}/)
- •Selector de diseño/skin en tarjeta — cambiado a UI de tarjeta con miniatura en la pestaña de información del módulo
- •Sistema SEO — helper SEO, patrón de título, meta tags OG/Twitter, extracción de contenido
- •Carga de OG Image — entrada de URL + carga de archivo + vista previa
- •Modal de resultado común — UI de modal unificado de éxito/error al guardar (soporte de 13 idiomas)
- •Vista de tarjeta de servicio — tarjeta con imagen de fondo completa + botón de alternancia tabla/tarjeta
- •Ordenación de servicios por arrastrar y soltar — cambio de orden + guardado en BD en ambas vistas
- •Asignación/designación de personal en reservas — 2 botones (asignar/designar) en el detalle de reserva
- •UI de origen de reserva — selección de reserva telefónica / presencial / en línea
- •Configuración de reserva telefónica — toggle de activar/desactivar en la configuración básica del servicio
- •Cabeceras de seguridad — X-Frame-Options + CSP frame-ancestors
- •PAGE_SYSTEM.md — documentación de desarrollo del sistema de páginas
- •PRD_OrderSystem.md — documento de planificación del sistema de pedidos en mesa
- •PRD_KioskSystem.md — documento de planificación del sistema de quiosco
Fixed
- •Configuración de favicon en BD aplicada (front / admin / skin layout — 4 lugares)
- •URL de migración del dashboard
window.location.origin → $baseUrl (entornos de subdirectorio)
- •URL de API en modo embed de configuración de página → ruta de admin especificada directamente
- •Modal de resultado i18n: usa mensajes de locale al renderizar la página en lugar de la respuesta de la API
- •Fallback de valor de skin vacío en la pestaña de skin (
"" → default)
- •Problema de doble consumo de
php://input en pages-settings.php resuelto
Changed
- •Quiosco / configuración de quiosco / páginas de bundle → cabecera compartida
_head.php aplicada
- •Guardado de configuración de skin JSON → transferencia FormData (multipart) (soporte de carga de archivos)
- •Parámetro
$baseUrl añadido a SkinConfigRenderer (vista previa de imagen/vídeo)
- •Traducciones de skin en 13 idiomas (skin.json + site.php + common.php)
v1.13.0
2026-03-21 · hace 7 meses
Added
- •Sistema de páginas completado — creación automática de páginas al crear páginas de documento/widget/externas desde el menú
- •Renderizador de páginas dinámico —
page.php (documento→HTML, widget→WidgetRenderer, externo→iframe/include)
- •Configuración de entorno de página — 3 pestañas (básica / diseño y skin / SEO), guardado como JSON
page_config_{slug}
- •Edición de contenido de página — edición de título y contenido por idioma, cambio de slug, cambio de tipo, eliminación
- •Lista dinámica de gestión de páginas — carga automática de páginas de usuario desde la BD, iconos y badges por tipo
- •Iconos de administrador en el front — engranaje (configuración) + editar (contenido) mostrados por separado
- •iframe de página externa — guía "abrir en nueva ventana" mostrada si falla la carga
- •Archivos PHP/HTML internos — renderizados dentro del diseño RezlyX mediante include
Fixed
- •URL de acceso directo del menú
#menu_{id} → guarda la URL real del menú
- •La edición del menú muestra la misma estructura de UI de acceso directo que al crearla
- •El guardado del menú refleja la URL de acceso directo (
formMenuType → base de visibilidad del panel)
- •Traducciones de menú añadidas para 13 idiomas (12 elementos de menú × 13 idiomas)
- •Ruta de clave de traducción del editor de documentos de página corregida (
admin.site.pages. → site.pages.)
Changed
- •Columna
page_type añadida a rzx_page_contents en la BD (document/widget/external)
- •La eliminación del menú ahora elimina también la página asociada + los datos de traducción
v1.12.5
2026-03-21 · hace 7 meses
Fixed
- •Corregida la URL de acceso directo al menú — ahora guarda la URL real del menú en lugar de
#menu_{id}
v1.12.4
2026-03-21 · hace 7 meses
Fixed
- •Tipo de lista de configuración de skin no aplicado — se corrigió el problema por el que la caché de localStorage sobreescribía la configuración del skin
- •Cuando el cambio de estilo está desactivado, solo se usa la configuración del skin; cuando está activado, localStorage tiene prioridad
- •Se eliminó el envío duplicado de campos ocultos de checkbox (el estado no marcado ahora se gestiona en JS)
v1.12.3
2026-03-20 · hace 7 meses
Fixed
- •Claves de traducción del tablón faltantes para inglés/japonés (source_locale, reply, style, board_settings, etc.)
- •Aplicado fallback multilingüe para nombres de categoría (locale → en → original)
- •Soporte multilingüe para el cuerpo del listado del tablón (vistas previas de webzine/tarjeta/galería)
v1.12.2
2026-03-20 · hace 7 meses
Fixed
- •Reserva multilingüe del cuerpo de la lista del tablón — se aplicó el cuerpo traducido a las vistas previas de webzine/tarjeta/galería (idioma → en → original)
- •Archivos de skin de producción faltantes — se creó el directorio
skins/default/board/ por FTP y se subieron skin.json/miniatura
v1.12.1
2026-03-20 · hace 7 meses
Fixed
- •Configuración de skin no visible en producción — sincronización completa de archivos reorganizada
- •Estructura de archivos CLAUDE.md + BOARD_SYSTEM.md completamente actualizada
v1.12.0
2026-03-20 · hace 7 meses
Added
- •Sistema de configuración de skin completado — generación dinámica de formularios basada en skin.json + aplicación a los tablones reales
- •Agrupación por secciones (básico / lista / enlaces / diseño / fondo del título / personalizado)
- •Tipos de entrada radio y video añadidos
- •Carga de archivos de imagen/video (FormData multipart)
- •Función para eliminar imágenes/videos
- •Tabla de información del skin (autor / URL / correo / fecha / versión / miniatura)
- •Imagen/video de fondo del título — configuración de tipo de fondo, altura, opacidad de superposición y color del texto
- •4 estilos de diseño de lista — lista / webzine / galería / tarjeta; archivos de skin separados + soporte de anulación
- •Galería: cobertura de imagen completa + animación de deslizamiento al pasar el cursor
- •Webzine: miniatura + título + vista previa + tarjeta de metainformación
- •Publicaciones de avisos separadas en la lista superior (_list-notices.php)
- •Configuración de skin aplicada — primary_color, border_radius, posts_per_row, allow_style_switch, show_category, tablón vinculado, Font Awesome, CSS/HTML personalizado
- •Fusión automática de valores predeterminados de skin.json — los valores predeterminados de skin.json se aplican incluso sin guardado en BD
Fixed
- •skin_config no se guarda en la BD (la acción de actualización de boards-api.php no incluía skin_config)
- •Guardado del skin cambiado de URLSearchParams → FormData (multipart) para admitir carga de archivos
- •Soporte de análisis automático para el array
$_POST['skin_config']
v1.11.3
2026-03-20 · hace 7 meses
Fixed
- •Aplicado el fallback multilingüe para los títulos de entrada anterior/siguiente en la página de detalle (locale → en → original)
v1.11.2
2026-03-20 · hace 7 meses
Fixed
- •Reserva multilingüe del título de la lista del tablón — consulta de traducciones en todos los idiomas (se eliminó la restricción al coreano), reserva en inglés considerando el idioma de origen
v1.11.1
2026-03-20 · hace 7 meses
Fixed
- •Mejora en la detección de URLs independientes para tarjetas OG — ahora asciende hasta el elemento padre de bloque, por lo que las URLs envueltas en etiquetas en línea como
<font> / <span> se procesan correctamente
v1.11.0
2026-03-20 · hace 7 meses
Added
- •Enlace automático de URL — detecta automáticamente URLs en el cuerpo/editor del tablón → las convierte en etiquetas
<a>
- •Tarjeta de vista previa de enlace OG — tarjeta en formato de banner de redes sociales (miniatura + título + descripción + dominio) generada automáticamente
- •Editor: introduce/pega URL + Enter → llama a la API OG → inserta la tarjeta
- •Cuerpo: enlace URL independiente → se convierte automáticamente en tarjeta OG
- •Módulo OgFetcher — extracción de metadatos OG/Twitter Card, favicon, gestión automática de codificación
- •API OG — endpoint
/board/api/og
- •board-autolink.js — JS unificado de enlace automático + tarjeta OG (cargado globalmente desde base-header)
v1.10.2
2026-03-20 · hace 7 meses
Fixed
- •Alineación de imágenes en el editor/contenido — se sobreescribe
img { display: block } del preflight de Tailwind
- •Se creó el CSS compartido
board-content.css (unifica la visualización del editor y del contenido)
- •Cargado globalmente desde
base-header.php (todas las páginas del frontend)
- •Se agregaron estilos globales del editor a
multilang-modal.php (todas las instancias de Summernote en el panel)
- •Se eliminó la clase
prose del cuerpo del tablón; los estilos en línea del editor se preservan al 100%
Changed
- •Se documentaron las reglas de estilo del contenido del tablón en CLAUDE.md (la clase
board-content es obligatoria)
v1.10.1
2026-03-20 · hace 7 meses
Fixed
- •Corregido el error por el que al añadir una categoría al tablón se cargaban datos de la categoría anterior en el modal multilingüe (se genera una clave langKey temporal única)
- •Se añadió el valor predeterminado 3 para
comment_max_depth en la migración 021
v1.10.0
2026-03-20 · hace 7 meses
Added
- •Variables extendidas del tablón — módulo de componente ExtraVarRenderer (15 tipos: renderizado input/display por tipo)
- •Internacionalización de variables extendidas — almacenamiento independiente de valores de variables extendidas por idioma (integración rzx_translations, cadena de respaldo)
- •Almacenamiento multilingüe separado de publicaciones — al editar en el idioma original se guarda directamente en board_posts; en otros idiomas se guardan las traducciones en rzx_translations
- •Cadena de respaldo de lectura de publicaciones — muestra título/cuerpo/variables extendidas en el orden: locale actual → en → original
- •Respuestas anidadas — soporte de comentarios anidados de 3 niveles, ordenación recursiva en árbol, formulario de respuesta en línea
- •Me gusta/No me gusta en comentarios — botón de like por comentario + API
- •Acceso frontal a la configuración del tablón — icono de engranaje del administrador → /board/{slug}/settings diseño frontend
- •Enlace de título en la administración del tablón — hacer clic en el título en la lista de tablones del administrador navega a ese tablón
Fixed
- •Verificación de permisos de administrador en la API del tablón:
$currentUser['role'] → $_SESSION['admin_id'] (4 archivos actualizados)
- •Aviso en idioma original mostrado solo al autor/administrador
- •El enlace de pestaña en modo incrustado de configuración del tablón admite URL del frontend
Changed
- •Se añadió la columna JSON
extra_vars a la BD rzx_board_posts
- •Valor predeterminado de
comment_max_depth cambiado de 0 → 3
- •Barra de acciones de comentarios unificada (iconos de like/dislike/responder/eliminar)
v1.9.4
2026-03-20 · hace 7 meses
Added
- •Datos de tablón por defecto — migraciones del tablón de avisos, preguntas y respuestas, FAQ y tablón libre (020_default_boards_v193.sql)
v1.9.3
2026-03-20 · hace 7 meses
Added
- •Reinicio de grupos de miembros — inicialización con datos predeterminados (7 grupos + traducciones en 13 idiomas)
- •Reinicio de configuración de puntos — restaurar valores de configuración / puntos por nivel / vínculos de grupo a los valores predeterminados (sin afectar el saldo de los miembros)
- •Grupos de miembros multilingüe — componente común
rzx_multilang_input() aplicado al nombre del grupo / descripción de beneficios
- •Arrastrar y soltar grupos de miembros — reordenación de tarjetas con SortableJS + guardado automático
Fixed
- •Visualización de traducción de tarjeta de grupo de miembros por idioma (vista de tarjeta + modal de edición)
- •Corrupción de datos de traducción multilingüe de grupos de miembros corregida (re-guardado UTF-8 en PHP)
- •Nombre de columna del tablero de configuración del módulo de puntos corregido (board_name → title)
- •Conflicto de cotejamiento en la lista de miembros de puntos resuelto (unificado a utf8mb4_unicode_ci)
- •Migración de BD en producción (019_point_system_v190.sql)
v1.9.0
2026-03-20 · hace 7 meses
Added
- •Sistema de gestión de puntos (créditos) — estructura de 3 pestañas (Configuración básica / Configuración por módulo / Lista de puntos de miembros)
- •Estructura de 3 campos de puntos — point (puntos de actividad), balance (créditos de pago), total_accumulated (puntos acumulados, base para el cálculo de nivel)
- •Ponderación de puntos acumulados — ponderación de pago / ponderación de actividad separadas, subida de nivel preferencial para clientes que pagan
- •Canje de puntos — conversión de puntos → créditos (relación, unidad, puntos mínimos configurables)
- •17 categorías de otorgamiento/deducción de puntos — misma estructura que Rhymix (recuperación al eliminar, límite de tiempo, excluye avisos)
- •Configuración por módulo — configuración individual de 15 ítems de puntos por tablón
- •Lista de puntos de miembros — 4 columnas (puntos/créditos/acumulados/nivel), búsqueda, edición individual
- •Puntos de nivel — cálculo basado en fórmulas, ajuste dinámico del nivel máximo, vinculación de grupos
- •Documentación del sistema de puntos — POINT_SYSTEM.md documento de diseño detallado
- •Arrastrar y soltar grupos de miembros — cambiar el orden de las tarjetas con SortableJS + guardado automático
- •Grupos de miembros multilingüe — componente común
rzx_multilang_input() aplicado al nombre del grupo/descripción de beneficios
- •Datos semilla de grupos de miembros — 7 grupos × 13 idiomas de datos de traducción multilingüe
Changed
- •Se añadieron las columnas
balance, total_accumulated a la BD rzx_member_points
- •Se cambió
rzx_point_levels.group_id de INT a CHAR(36) en la BD (compatible con UUID)
- •Se unificó la intercalación de
rzx_point_levels y rzx_member_points a utf8mb4_unicode_ci
- •Se actualizó el diseño de la página del paquete (
sidebar-main-content → patrón de panel flex-1 ml-64)
- •Se corrigió la unidad de moneda de la página del paquete
$config['currency'] → $siteSettings['service_currency']
- •Se cambió la duración del servicio de select → input number + datalist
- •Se documentaron las reglas de interfaz de entrada multilingüe en CLAUDE.md (uso obligatorio de rzx_multilang_input)
v1.8.3
2026-03-20 · hace 7 meses
Added
- •Página de gestión detallada de paquetes — editar información básica del paquete, subir imagen de portada, editar composición de servicios, vincular personal, alternar activo/inactivo
- •Descuento por evento — precio de evento por paquete, período (inicio/fin), configuración de etiqueta; activación automática dentro del período
- •Diseño de tarjeta en lista de paquetes — diseño de tarjeta en cuadrícula, encabezado de imagen, insignia de evento/descuento, barra de acción fijada en la parte inferior
- •Imagen de fondo del paquete recomendado — fondo de imagen de portada + superposición de degradado en las tarjetas de paquete en la página de detalle del personal del cliente
- •Entrada directa de duración del servicio — select → entrada numérica (5–480 min, pasos de 5 minutos) + lista recomendada de datalist
Changed
- •DB
rzx_service_bundles columnas añadidas: event_price, event_start, event_end, event_label
- •API de lista de paquetes:
total_duration y columnas de evento añadidas
- •Archivos de traducción (ko, en, ja): claves de gestión detallada de paquetes + descuento por evento añadidas
v1.8.1
2026-03-19 · hace 7 meses
Added
- •Página de configuración POS — tamaño de tarjeta, visualización de imagen/precio/teléfono, opacidad, pestaña predeterminada, actualización automática, sonido de alerta, asignación de personal obligatoria, asignación automática
- •Configuración POS aplicada — tamaño de tarjeta/imagen/precio/teléfono/opacidad/actualización automática/pestaña predeterminada reflejados en POS
- •Asignación automática de personal — cuando está habilitada en la configuración POS, asigna automáticamente el personal en el check-in del quiosco (capacitado para el servicio + menor número de reservas del mismo día)
- •Insignia de personal en tarjeta POS — indicador asignado/no asignado; botón de avance deshabilitado cuando no está asignado
- •Fondo de imagen de servicio POS — imagen representativa del servicio como fondo en el encabezado + área del cliente del modal de detalle del servicio
- •Personal is_visible — controla la visibilidad en la lista; personal oculto excluido de reservas/POS/quiosco
- •Banner de tarjeta de personal — imagen de banner + insignia de estado en la tarjeta de personal del administrador
- •Asignación automática en quiosco — check-in con staff_id=NULL; asignación realizada por el administrador en POS
Fixed
- •POS asignación de personal: la lista de servicios se actualiza inmediatamente tras la asignación (prevención de caché)
- •Corrección del parámetro de la API de asignación de personal (reservation_id → reservation_ids[])
- •Múltiples tablas/columnas faltantes corregidas en producción (migración 018 reforzada)
- •Ruta de cierre de sesión de administrador añadida
- •Ubicación de la ruta previa a la autenticación del quiosco
v1.7.0
2026-03-18 · hace 7 meses
Added
- •Sistema de maquetación base —
layouts/base-header.php + base-footer.php, ob_start aplicado automáticamente en index.php, encabezado/pie de página común para todas las páginas de clientes
- •Frontend del tablero — páginas de lista, detalle, escritura/edición, editor Summernote, archivos adjuntos por arrastrar y soltar
- •URLs cortas del tablero — acceso a
/free, /notice/3, etc. sin el prefijo /board/; coincidencia automática de slugs en la BD
- •Página de consulta de reservas — URL corta
/lookup, búsqueda por número de reserva / correo / teléfono
- •Página de detalle de reserva —
/booking/detail/{número}, burbuja de saludo del personal, información de servicio/pago/cancelación
- •Página de cancelación de reserva —
/booking/cancel/{número}, campo para el motivo de cancelación
- •Detalle de reserva en mi página —
/mypage/reservations/{id}, barra lateral + información detallada
- •Tablero multilingüe —
original_locale (idioma del primer borrador) + source_locale (idioma actual del contenido), cadena de respaldo (source → en → original)
- •Saludos del personal —
greeting_before (antes del tratamiento) + greeting_after (después del tratamiento), multilingüe, mostrado según el estado de la reserva
- •Menú desplegable del perfil de administrador — foto de perfil/iniciales + nombre + enlaces a página de inicio / mi página / cerrar sesión
- •Enlace de administrador en el encabezado del cliente — al iniciar sesión como administrador, el menú desplegable muestra un enlace al panel de administración
Changed
- •Maquetación unificada para todas las páginas de clientes (se eliminaron las estructuras HTML individuales, LayoutManager aplicado automáticamente)
- •Condición de visualización de categorías del tablero cambiada de
show_category a hide_categories
- •URL del tablero cambiada de
/board/free a la URL corta /free por defecto
- •"Admin" en la barra superior del administrador reemplazado por el nombre de inicio de sesión real + foto de perfil
Fixed
- •Columna
rzx_users.role inexistente → detección de administrador ahora basada en $_SESSION['admin_id']
- •Columna
rzx_users.nick_name inexistente → uso de name + descifrado con Encryption
- •
rzx_staff.profile_image → corregido al nombre de columna avatar
- •Conversión de tipo int→string en
category_id del modelo Service
- •La búsqueda de reservas por correo/teléfono usa búsqueda en texto plano (datos sin cifrar)
v1.6.1
2026-03-18 · hace 7 meses
Added
- •6 pestañas de administración de tablón — Configuración básica, Gestión de categorías, Variables adicionales, Permisos, Configuración adicional, Skin
- •Consolidación de la pestaña de configuración básica — Información básica + SEO + Selección de layout + Selección de skin + Visualización + Lista + Avanzado (plegable/desplegable)
- •Componentes comunes — 8 archivos
components/board/section-*.php (basic, seo, layout-select, skin-select, display, list, advanced, js), reutilizables en páginas de creación/edición
- •Componente de entrada multilingüe — función
rzx_multilang_input(), modos texto/editor, botón de globo terráqueo en línea
- •Gestión de categorías mejorada — estructura en árbol (parent_id), modal de edición (nombre/color de fuente/descripción/grupos permitidos/expandir/categoría por defecto), ordenación por arrastre con SortableJS, carga dinámica de grados de miembros desde DB
- •Pestaña de variables adicionales — tabla
rzx_board_extra_vars, 15 tipos de entrada (incluidos text_multilang, textarea_multilang, textarea_editor), soporte multilingüe, ordenación por arrastre
- •Pestaña de permisos — Permisos de acceso (all/member/grade:*/admin_staff/admin), administrador de módulo (búsqueda de usuario compartida, foto de perfil, alcance de permisos), información de correo de notificación
- •Pestaña de configuración adicional — Tablón integrado, documentos (historial/recomendar/reportar), comentarios (cantidad/profundidad/aprobación/recomendar/reportar), editor WYSIWYG (matriz de permisos), archivos (grupo de descarga), feeds (RSS público/copyright)
- •Sistema de skin — formato
skin.json, SkinConfigRenderer, soporte "multilang": true, tarjeta de información de skin
- •Sistema de layout — formato
layout.json, selección en tarjeta en configuración básica
- •Búsqueda de usuario compartida — componente
user-search.php, cifrado/descifrado, foto de perfil, reutilizable en cualquier lugar
- •Administrador de módulo — tabla
rzx_board_admins, permisos de publicación/comentario/configuración, suscripción a notificaciones
- •Expansión de ajustes avanzados — excluir admin del anonimato, restricción de Data URL, abuso de Unicode, protección de publicaciones/comentarios (recuento de comentarios/respuestas/período), protección de superadministrador, dirección de correo del remitente
- •UI de interruptores de palanca — todos los elementos sí/no convertidos a interruptores (configuración básica + adicional)
- •Claves de traducción — aproximadamente 300 claves, 13 idiomas
Fixed
- •Error SQL en papelera — vinculación
LIMIT ? cadena → literal entero
- •Summernote no renderiza — carga de jQuery/Summernote JS + inicialización tras mostrar el modal
- •Error de intercalación — especificado explícitamente COLLATE utf8mb4_unicode_ci al hacer JOIN de board_admins
- •Plegar/desplegar no funciona — función
toggleSection definida primero en <head>
Changed
- •Pestañas independientes (lista/avanzado) → integradas en la pestaña de configuración básica con plegar/desplegar
- •Selección de skin → movida a la pestaña de configuración básica; la pestaña de skin solo muestra ajustes
- •Correo de administrador → cambiado a "Dirección de correo del remitente" en configuración avanzada
- •Administrador de módulo → movido a la pestaña de permisos
- •"Mostrar categoría en lista" → eliminado de configuración de categorías, delegado a ajustes por skin
- •"Mostrar categoría en lista" → eliminado de configuración de categorías, delegado a ajustes por skin
- •Grupos permitidos para escritura → cargados dinámicamente desde la tabla
rzx_member_grades en lugar de codificados
v1.6.0
2026-03-17 · hace 7 meses
Added
- •Sistema de traducción con alternativa multinivel: cadena de reserva integrada en
db_trans() (idioma configurado → en → idioma predeterminado → source_locale → cualquier traducción)
- •Botón multilingüe en configuración del tablero: botón multilingüe (globo) añadido a 6 campos en la pestaña de configuración básica
- •Alternativa multilingüe en la vista frontal del tablero: título, descripción, SEO, encabezado/pie de página multilingüe aplicado en
_init.php
- •Alternativa multilingüe de widgets: manejo de cadena vacía mejorado en
WidgetRenderer::t(), tBtn()
- •Claves de traducción PWA añadidas (13 idiomas:
admin.pwa.update_available, update, later)
Fixed
- •Problema de tarjeta vacía en el widget Features: el operador
?? trataba la cadena vacía "" como un valor válido → corregido con alternativa basada en !empty()
- •Error de Mixed Content en producción:
APP_URL http→https corregido + corrección automática de protocolo en JS
v1.5.2
2026-03-17 · hace 7 meses
Added
- •Eliminación de servicio desde el historial de servicios en POS (botón de eliminación + API + actualización inmediata del DOM)
- •Añadidas claves de traducción de eliminación de servicio para 13 idiomas
v1.5.0
2026-03-17 · hace 7 meses
Added
- •Sistema de recepción en quiosco: confirmación del servicio → datos del cliente → creación de la reserva → emisión del número de turno
- •Insignia de disponibilidad del personal: muestra ocupado/disponible en tiempo real en la selección de personal del quiosco
- •Módulo de número de teléfono internacional: se aplicó el componente phone-input al formulario de recepción del quiosco
- •Semilla de datos de ejemplo de reservas:
database/seed_reservations.php (10 registros entre POS/quiosco/en línea)
- •Se separaron los archivos comunes del quiosco (
_styles.php, _bg.php, _scripts.php)
- •Se añadieron 11 claves de traducción del quiosco en 13 idiomas
Changed
- •Selección de servicio en quiosco: filtrado según las habilidades del personal (modo de designación)
- •Se centraron las pestañas de categoría del quiosco
Fixed
- •Imposibilidad de iniciar sesión como administrador en producción (hash de contraseña dañado — problema de sustitución de la variable
$ de bash)
- •Estabilización del sistema de actualización: lista de conservación ampliada, comprobación de estado, sincronización de versión de BD
- •Unificación de la fuente única de versión (
version.json → index.php, sincronización automática de BD)