OW Agenda — Documentación
La reserva de citas online y la agenda del día a día, dentro de WordPress, sin suscripción. Autor: OptionWeb — Julien Daniel Página del plugin: https://optionweb.dev/es/addons/ow-agenda/ Licencia: GPL-2.0-or-later Versión cubierta por este documento: 1.3.1
Índice
- Visión general
- Instalación
- Inicio rápido
- El proceso de reserva
- La doble verificación por email
- La validación de las solicitudes
- La página Agenda
- El directorio de clientes
- Las invitaciones de sesión
- Emails y archivo de calendario (ICS)
- Recordatorios y tarea programada
- Anti-spam y límites de frecuencia
- RGPD
- Shortcodes
- API REST
- Referencia de ajustes
- Datos almacenados
- Internacionalización
- Desinstalación
- Solución de problemas
- FAQ
- Changelog
Visión general
OW Agenda añade a WordPress un sistema completo de citas: una página pública donde el visitante elige primero su día y luego su hora, y una página «Agenda» protegida por login, pensada para usarse desde un teléfono, en la que usted gestiona la jornada.
Todo se queda dentro de la web. Ningún servicio externo, ninguna API de terceros, ninguna
suscripción: las franjas las calcula su servidor, las citas viven en sus tablas
MySQL, los emails salen por wp_mail().
Lo que se entrega (todo gratis, GPL-2):
- Reserva online: elección del día, elección de la franja, formulario breve, casilla de consentimiento RGPD
- Doble opt-in por email: una solicitud no se vuelve real hasta que se hace clic en un enlace de validación — los bots nunca llegan a la agenda
- Validación manual (recomendada) o confirmación automática, con botones Confirmar / Rechazar directamente en el email de notificación (enlaces firmados con HMAC, sin iniciar sesión)
- Página «Agenda» mobile-first: vista del día, solicitudes pendientes, citas telefónicas añadidas a mano, bloqueo de franjas, reprogramación, estados hecho / no presentado
- Directorio de clientes: búsqueda, creación, edición, borrado; alimentado automáticamente por las citas validadas
- Invitaciones de sesión: un enlace privado permite a un cliente existente elegir la fecha de su sesión, sobre un servicio y una duración que usted fija
- Emails HTML cuidados con adjunto ICS (la cita entra en el calendario del teléfono con un gesto), recordatorio automático la víspera, anulación online por parte del cliente
- Anonimización automática de las citas pasadas, purga de las solicitudes nunca validadas
- API REST completa bajo el namespace
owag/v1
Lo que no hace, deliberadamente:
- Sin pagos online: OW Agenda concierta la cita, no cobra ninguna señal (use WooCommerce si lo necesita)
- Una sola agenda: sin gestión multiprofesional ni multirrecurso; el plugin describe un planning, el de la web
- Sin sincronización bidireccional con Google Calendar u Outlook: el calendario del cliente se actualiza por archivo ICS (solo de ida), y sus indisponibilidades personales se introducen como bloqueos
- Sin catálogo de servicios del lado del visitante: la reserva pública se refiere a un único servicio (título y duración configurables); los demás servicios pasan por las invitaciones de sesión
- Sin citas recurrentes, sin videollamada, sin sala de espera
Esta lista es una decisión de arquitectura, no una hoja de ruta encubierta: una agenda simple que nunca se equivoca de franja vale más que un artefacto enorme que nadie se atreve a tocar.
Instalación
Desde el .zip
- Descargue
ow-agenda-1.3.1.zipdesde https://optionweb.dev/es/addons/ow-agenda/ - Plugins → Añadir nuevo → Subir plugin
- Elija el archivo, haga clic en Instalar, y luego en Activar
Por FTP
Descomprima el archivo y deje la carpeta ow-agenda en /wp-content/plugins/,
y después active el plugin desde Plugins.
Requisitos
- WordPress 6.3 o posterior
- PHP 7.4 o posterior (8.1+ recomendado)
- MySQL 5.7+ / MariaDB 10.2+ — las funciones
GET_LOCK()/RELEASE_LOCK()se utilizan para serializar las reservas simultáneas - Un envío de emails que funcione. Es la única dependencia real del plugin: todo el recorrido (validación, confirmación, recordatorio, anulación) pasa por el email. En un alojamiento compartido, instale un plugin SMTP antes de poner la agenda en producción.
Qué se instala
Al activarse, OW Agenda crea tres tablas:
| Tabla | Contenido |
|---|---|
{prefix}_owag_appointments | Todos los eventos de la agenda: consultas web, sesiones surgidas de una invitación, citas introducidas a mano, bloqueos de franjas |
{prefix}_owag_invites | Los enlaces privados «elija la fecha de su sesión» |
{prefix}_owag_customers | El directorio de clientes |
…y, solo la primera vez, dos páginas publicadas:
- «Reservar una cita» — contiene el shortcode
[ow_agenda] - «Agenda» — contiene el shortcode
[ow_agenda_admin]
Sus identificadores quedan guardados en las opciones owag_page_booking y
owag_page_admin; el indicador owag_pages_created garantiza que una desactivación seguida
de una reactivación no cree duplicados. Puede renombrar estas páginas, moverlas
a un menú o cambiar su enlace permanente: el plugin las sigue por su ID.
La activación añade además:
- la capacidad
owag_manageal rol administrador — es esta, y nomanage_options, la que abre la agenda y las rutas REST de administración; - los ajustes por defecto en la opción
owag_settings; - la tarea programada horaria
owag_hourly_event(primera ejecución 5 minutos después de la activación).
Un secreto HMAC (owag_admin_secret, 64 caracteres, sin autocarga) se
genera en la primera notificación admin: es el que firma los botones
Confirmar / Rechazar de los emails.
Dar acceso a otra persona
La página Agenda es visible para cualquier usuario conectado que disponga de owag_manage.
Para abrir la agenda a un asistente sin darle las llaves de la web:
// À exécuter une fois (mu-plugin, ou plugin de gestion des rôles).
$role = get_role( 'editor' );
if ( $role ) { $role->add_cap( 'owag_manage' ); }
Inicio rápido
Tras la activación, vaya a OW Agenda (menú principal de la administración). La pantalla de ajustes cabe en una sola página; con cinco minutos basta.
1. El horario de apertura
Para cada día de la semana, introduzca un tramo (09:00 → 18:00) y, si cierra
al mediodía, un segundo tramo (14:00 → 18:30). Un día dejado en blanco es un día
cerrado. Por defecto: de lunes a viernes, 09:00–18:00; sábado y domingo cerrados.
Los horarios se interpretan en la zona horaria de la web (Ajustes → Generales → Zona horaria). Compruébela antes de abrir la reserva: todas las fechas se almacenan en hora local.
2. El servicio reservable online
- Título de la consulta — lo que el visitante ve como título del widget («Consulta», «Primera cita», «Revisión»…)
- Duración de la consulta — 60 minutos por defecto. Es esa duración la que se bloquea en la agenda.
- Granularidad de las franjas — la separación entre dos horas ofrecidas (30 minutos por defecto). Con una duración de 60 y una granularidad de 30, se ofrecen las 9:00, 9:30, 10:00… y la primera cita concertada a las 9:30 elimina las 9:00 y las 10:00 de la lista.
- Margen entre dos citas — minutos de respiro añadidos a uno y otro lado de cada franja candidata (0 por defecto).
3. Los plazos
- Antelación mínima de reserva — 24 h por defecto: un visitante no puede reservar para dentro de una hora.
- Horizonte de reserva — 60 días por defecto: más allá, ya no se ofrece nada.
- Anulación online por parte del cliente — hasta 24 h antes de la cita; pasado ese plazo, la página de anulación le invita a llamar por teléfono.
4. La validación y los emails
- Confirmación de las solicitudes: validación manual (recomendado) o confirmación automática.
- Doble verificación por email: déjela marcada. Es la mejor protección anti-bot del plugin.
- Notificaciones admin enviadas a: su dirección. Por defecto, el email de administración de la web.
Guarde y abra después la página «Reservar una cita»: el widget muestra los 14 próximos días y sus franjas libres. Ya está en marcha.
5. La agenda en el teléfono
Abra la página «Agenda» en su teléfono, inicie sesión y use «Añadir a la pantalla de inicio». El plugin publica un manifiesto de aplicación: la página se abre entonces a pantalla completa, sin barra de navegador, como una aplicación.
El proceso de reserva
Del lado del visitante, la página de reserva es un componente autónomo (JavaScript sin
framework, menos de 10 KB) que dialoga con los endpoints públicos owag/v1/public/*.
Paso 1 — el día
Al cargarse, el widget pide las disponibilidades de 14 días a partir de hoy y muestra una banda de días en los que se puede hacer clic. Un día sin franjas libres aparece en gris y no es clicable. El botón «Ver las fechas siguientes» carga los 14 días siguientes, tantas veces como haga falta hasta el horizonte configurado.
Las fechas del widget se construyen en hora local del navegador, nunca en UTC. Es una corrección de la 1.3.0: antes, un visitante situado al este de UTC que abriera la página poco después de medianoche veía la lista empezar el día anterior y perdía un día de disponibilidad.
Paso 2 — la hora
Al hacer clic en un día se llama a /public/slots y se muestran las horas de inicio libres. Una
franja se ofrece cuando se cumplen todas estas condiciones:
- cae dentro de un tramo de apertura del día en cuestión;
- la cita entera (inicio + duración) cabe antes del cierre;
- empieza después de ahora + antelación mínima;
- el día está dentro del horizonte de reserva;
- no se solapa con ningún evento activo, margen incluido — y «evento activo» incluye las citas confirmadas, las solicitudes pendientes, los bloqueos, las citas telefónicas introducidas a mano y las solicitudes aún sin verificar que siguen reteniendo su franja.
Este es el punto importante: todo lo que ocupa la agenda cierra la franja online. Una cita concertada por teléfono y anotada en la agenda desaparece de inmediato de las franjas ofrecidas en la web.
Paso 3 — el formulario
Cuatro campos, tres de ellos obligatorios: nombre (2 caracteres como mínimo), teléfono (8 dígitos como mínimo), email (validado), mensaje libre. Una casilla de consentimiento RGPD, sin marcar por defecto, es obligatoria.
El formulario incorpora además dos campos invisibles para el visitante — una trampa para bots y una marca de tiempo de apertura de página — descritos en la sección Anti-spam.
Paso 4 — la respuesta
Según su configuración, tres desenlaces:
| Configuración | Estado creado | Mensaje mostrado |
|---|---|---|
| Doble verificación por email activa (por defecto) | unverified | «Un último gesto: acabamos de enviarle un email…» |
| Verificación desactivada + validación manual | pending | «¡Hemos recibido su solicitud!» |
| Verificación desactivada + confirmación automática | confirmed | «¡Su cita está confirmada!» |
Si la franja se ha ocupado entre que se mostró la lista y se envió el formulario, el
visitante recibe un mensaje explícito (owag_slot_taken, HTTP 409) y la lista de días
se recarga automáticamente. Dos visitantes no pueden obtener la misma franja: la
sección crítica se serializa con un bloqueo con nombre de MySQL, propio de su base de datos y de
su prefijo de tablas, lo que aguanta incluso en un alojamiento compartido.
La doble verificación por email
Es la funcionalidad que distingue a OW Agenda de un formulario de contacto disfrazado.
Cuando la doble verificación por email está activa (ajuste email_verify, activado por
defecto):
- La solicitud se registra con el estado
unverified. El administrador no recibe ninguna notificación. La cita no aparece como una solicitud por tratar. - El visitante recibe un email «Un último gesto…» que contiene un botón de validación.
- La franja queda retenida 45 minutos. Pasado ese plazo sin validación, vuelve a estar disponible para todo el mundo — así, un bot no puede congelar su jornada.
- Al hacer clic en el botón se abre una página autónoma (
?owag_verify=<uid>) que muestra el resumen de la cita y un botón «Confirmo mi solicitud». La validación solo se envía al hacer clic: los antivirus y escáneres de enlaces que visitan las URL de los emails no validan nada. - La solicitud pasa entonces a
pending(validación manual) o aconfirmed(confirmación automática), el cliente recibe el email correspondiente, y solo en ese momento sale la notificación admin. - Las solicitudes nunca validadas se eliminan al cabo de 48 h mediante la tarea horaria. No dejan ningún rastro ni ningún dato personal.
Si, entre el envío y el clic, la franja la ha ocupado otra persona (la ventana de 45 minutos ya ha pasado), la validación devuelve un error claro y la solicitud se anula — nunca una cita fantasma sobre una franja ya ocupada.
Consecuencia práctica: una dirección de email inválida no produce ninguna cita. Ya no pierde tiempo llamando a números falsos. El precio a pagar es un clic adicional para el cliente; la experiencia demuestra que la tasa de abandono es muy inferior a la tasa de citas falsas que evita.
Las reservas que provienen de una invitación de sesión se saltan este paso: el cliente ya ha demostrado que lee su correo al hacer clic en el enlace privado que recibió.
La validación de las solicitudes
Validación manual (recomendada)
Ajuste por defecto: auto_confirm = 0. Una solicitud verificada llega con el estado
pending. No se le promete nada al cliente mientras usted no haga nada — pero la franja
ya está bloqueada, nadie se le puede adelantar.
Recibe un email «Por confirmar:
- Confirmar la cita — cian
- Rechazar — contorno rojo
Los botones del email: enlaces firmados con HMAC
Estos botones no exigen ningún inicio de sesión. Apuntan a su web con tres
parámetros: owag_admin_action (confirm o refuse), uid (el identificador aleatorio
de la cita) y sig, una firma HMAC-SHA256 de uid|action calculada con el
secreto privado de la web (owag_admin_secret).
- La firma se verifica antes de mostrar el menor detalle: un enlace manipulado no muestra otra cosa que «Enlace inválido».
- La comparación usa
hash_equals()— sin fuga de información por el tiempo de respuesta. - La página muestra un resumen y un botón; la acción solo se ejecuta al hacer clic. Un escáner de enlaces no confirma nada en su lugar.
- Una solicitud ya tratada devuelve «Ya tratado (estado: …)» en lugar de un error.
Confirmar envía automáticamente el email de confirmación al cliente, con el archivo de calendario. Rechazar envía el email de anulación. En ambos casos, la agenda está al día antes de que haya guardado el teléfono.
Estos enlaces siguen siendo válidos mientras la cita esté pendiente. Si elimina la opción
owag_admin_secret(restauración parcial de la base de datos, limpieza manual), todos los enlaces ya enviados dejan de funcionar: se genera un secreto nuevo y las firmas antiguas ya no coinciden. Pase entonces por la página Agenda.
Confirmación automática
Ajuste auto_confirm = 1. Una franja libre se confirma de inmediato: el cliente
recibe su confirmación con el archivo de calendario y usted recibe una notificación
«Nueva cita». Reservado para actividades en las que toda franja abierta es una franja
aceptable.
Ambos ajustes se combinan con la doble verificación: incluso en confirmación
automática, la solicitud pasa primero por unverified si email_verify está activo.
La página Agenda
La página «Agenda» ([ow_agenda_admin]) es la herramienta del día a día. Está diseñada
primero para el teléfono: botones grandes, una sola columna, sin tablas que haya que desplazar
horizontalmente, y funciona evidentemente igual de bien en la pantalla de un ordenador.
Está protegida: un visitante no conectado ve un enlace de inicio de sesión, un usuario
conectado sin la capacidad owag_manage ve «El acceso está reservado al equipo». La página
está en noindex,nofollow: nunca acabará en Google.
Pestaña Agenda — la vista del día
La jornada en curso, evento por evento, con la hora, la duración, el título, el estado y el origen (web, invitación, alta manual). Navegación de un día a otro, botón Hoy. En cada cita:
- Detalles — datos de contacto del cliente, mensaje dejado durante la reserva, y un bloque de notas internas (nunca visibles para el cliente) que se guardan sin enviar ningún email
- Confirmar ✓ — para una solicitud aún pendiente; el email de confirmación sale
- Hecho / No presentado (no-show) — estados de cierre de la cita
- Mover — nueva fecha, nueva hora, duración eventualmente modificada. El plugin le pregunta si hay que avisar al cliente por email; si es así, este recibe una confirmación actualizada y un archivo de calendario que corrige el evento ya presente en su teléfono
- Anular la cita — también aquí, con la opción de avisar o no al cliente
- Eliminar el bloqueo — para una franja bloqueada
Cuando una acción crea un solapamiento (mover a una franja ocupada), el plugin avisa y pide confirmación: usted puede forzarlo. Es su planning y sabe lo que hace — la salvaguarda solo es bloqueante para los visitantes.
Añadir una cita o un bloqueo
El botón «Añadir una cita» abre una única ficha que sirve para dos usos:
- Cita — el cliente le ha llamado. Nombre, teléfono, email (opcional), servicio, fecha, hora, duración, notas internas. Si introduce una dirección de email y marca la opción, el cliente recibe la confirmación con su archivo de calendario, como si hubiera reservado online. La cita alimenta también el directorio de clientes.
- Bloqueo (vacaciones, pausa…) — un motivo, una fecha, una hora, una duración. Ningún email, ninguna ficha de cliente, pero la franja desaparece de inmediato de la reserva online. Así es como se marca una semana de vacaciones, una formación o la franja del mediodía que no se quiere ver reservada.
Pestaña Pendientes
La lista de las solicitudes verificadas que esperan su decisión, de todos los días (las citas ya pasadas quedan excluidas). Dos botones por línea: Confirmar y Rechazar. Es la primera pantalla que abrir por la mañana.
Truco: la agenda como aplicación
En la página Agenda, el plugin declara un manifiesto de aplicación web (?owag_manifest=1)
con el nombre de la web, el icono de la web y el color de tema. Tanto en Android como en iOS,
el menú del navegador ofrece entonces «Añadir a la pantalla de inicio»: obtiene
un icono, un arranque a pantalla completa y nunca más una URL que teclear.
El directorio de clientes
La tercera pestaña de la página Agenda es un directorio ligero, deliberadamente mínimo: nombre, teléfono, email, notas libres.
- Se rellena solo. En cuanto una cita se valida (reserva online verificada, sesión por invitación, alta manual), el cliente se hace corresponder con una ficha existente por su email y después por su teléfono. Si no existe, la ficha se crea; si existe, solo se completan los campos vacíos — el plugin nunca sobrescribe lo que usted ha introducido a mano.
- Búsqueda por nombre, teléfono o email (200 fichas mostradas como máximo).
- Creación y edición manuales, con un campo de notas para el historial, las preferencias y las precauciones que haya que tomar.
- Dos atajos por ficha: Nueva cita (rellena previamente la ficha de alta manual) e Invitar a una sesión (rellena previamente la invitación).
- Borrado de una ficha: las citas pasadas se quedan en la agenda, solo desaparece la ficha del directorio.
Este directorio no es un CRM y no pretende serlo. Responde a una pregunta: «¿Quién era, la señora de las 14 h?»
Las invitaciones de sesión
El caso de uso: un cliente existente tiene derecho a una sesión — un seguimiento, un retoque, un servicio vendido por otra vía — y usted quiere que elija su fecha sin pasar por la reserva pública, sobre un servicio y una duración que usted fija.
Desde la pestaña Invitaciones:
- Introduzca el nombre del cliente y el título de la sesión (obligatorios), su duración (de 15 a 720 minutos) y, si lo desea, su email y su teléfono.
- Elija la validez del enlace: 60 días por defecto, de 1 a 365.
- Marque si quiere «Enviar el enlace al cliente por email» — el cliente recibe un email «Elija la fecha de su sesión» con el título, la duración, la fecha de caducidad y un botón.
- Si no, copie el enlace y envíelo por SMS, WhatsApp o mediante el botón de compartir nativo del teléfono.
El enlace apunta a su página de reserva con un token privado
(?owag_invite=<token>, 32 caracteres hexadecimales extraídos de un generador
criptográfico). El widget muestra entonces el título y la duración de la sesión, y no
la consulta estándar. El cliente rellena el mismo formulario breve.
Reglas que gobiernan un enlace de invitación:
- Un solo uso. Una vez reservada la sesión, el enlace responde «Este enlace ya se ha utilizado» (HTTP 410). Dos aperturas simultáneas con el mismo token no pueden crear dos sesiones: el consumo del token se hace bajo el mismo bloqueo que la reserva de la franja.
- Con fecha. Pasada la caducidad, el enlace invita a ponerse de nuevo en contacto con usted (HTTP 410).
- Sin doble opt-in. El cliente ya ha recibido este enlace por un canal que usted controla:
la sesión se crea directamente en
confirmed, con confirmación y archivo de calendario. - Reciclado en caso de anulación. Si la sesión se anula (por usted o por el cliente), la invitación queda liberada: el enlace vuelve a estar activo y la invitación reaparece en la lista de invitaciones abiertas. Es una corrección de la 1.3.0 — antes, una anulación mataba el enlace para siempre y había que rehacerlo.
- Revocable. Eliminar una invitación desde la lista desactiva el enlace de inmediato.
Emails y archivo de calendario (ICS)
Los emails transaccionales
Todos los mensajes van en HTML, sobre una maquetación en tablas compatible con Gmail y
Outlook: tarjeta blanca sobre fondo claro, nombre de la web en la cabecera, recuadro de resumen
con filete cian, botones tipo píldora. Se envían mediante wp_mail(), así que respetan su
configuración SMTP y los filtros habituales de WordPress.
| Destinatario | Disparador | |
|---|---|---|
| Validación de la solicitud | Cliente | Reserva web cuando la doble verificación está activa |
| Solicitud recibida | Cliente | Solicitud validada, a la espera de su decisión |
| Cita confirmada | Cliente | Confirmación (manual, automática, alta admin, cambio de fecha) — con adjunto ICS |
| Cita anulada | Cliente | Anulación por el cliente, rechazo o anulación por el admin — con adjunto ICS de cancelación |
| Recordatorio | Cliente | Automáticamente, entre 12 y 36 h antes de la cita |
| Elija la fecha de su sesión | Cliente | Creación de una invitación con envío por email |
| Por confirmar: … | Admin | Nueva solicitud verificada pendiente — con los botones Confirmar / Rechazar |
| Nueva cita: … | Admin | Nueva cita confirmada (confirmación automática, invitación) |
| Anulación del cliente: … | Admin | El cliente ha anulado él mismo desde su email |
La dirección de notificación admin es el ajuste notify_email (por defecto, el email
de administración de la web). Los emails al cliente solo salen si la dirección es válida, y
nunca para un bloqueo de franja.
Los emails de confirmación y de anulación le recuerdan al cliente que puede anular online, con el número de horas de preaviso realmente configurado.
El adjunto ICS
El archivo .ics adjunto a las confirmaciones es lo que convierte un email en una cita
realmente presente en el teléfono del cliente: un gesto sobre el adjunto, y
el evento está en su calendario, con su propio recordatorio.
El archivo cumple con la RFC 5545 y contiene lo necesario para que los clientes de calendario (Google, Outlook, Apple) traten correctamente las actualizaciones:
METHOD:REQUESTen la creación y en cada revisión de horario,METHOD:CANCELen la anulación;- un
UIDestable derivado del identificador de la cita y del dominio de la web — es el que permite que una revisión sustituya al evento existente en lugar de crear un segundo; - un
SEQUENCEcreciente derivado de la fecha de última modificación, reducida a un instante absoluto: el cambio al horario de invierno no puede hacer retroceder el número de revisión (y la anulación suma 1, para seguir siendo superior a una revisión ocurrida en el mismo segundo); ORGANIZER(su dirección de notificación) yATTENDEE(el cliente);DTSTART/DTENDen UTC,STATUS:CONFIRMEDoSTATUS:CANCELLED;- plegado de líneas a 75 octetos sin cortar nunca un carácter UTF-8.
Mover una cita confirmada envía por tanto un archivo nuevo que corrige el evento ya presente en el dispositivo del cliente; una anulación envía un archivo que lo elimina. Es una corrección de la 1.3.0: antes, el horario antiguo se quedaba en el calendario del cliente.
El archivo se escribe en el directorio temporal del servidor — nunca en
wp-content/uploads/, que se sirve públicamente — mediante la API de archivos de WordPress, y
se borra en cuanto termina el envío, incluso si este falla. Lleva el nombre y
el email del cliente: no tiene nada que hacer en el disco ni un segundo más de lo
necesario.
Recordatorios y tarea programada
Una sola tarea programada, owag_hourly_event, ejecutada cada hora. Hace
tres cosas.
1. El recordatorio de la víspera
El recordatorio sale cuando la cita empieza dentro de 12 a 36 horas. No es una ventana horaria fija, y es deliberado: WP-Cron solo se dispara cuando se visita una página. Una web sin tráfico por la noche perdía definitivamente los recordatorios del día siguiente. Con una ventana deslizante, el recordatorio sale en la primera ejecución disponible y recupera el retraso.
Condiciones: cita confirmada, dirección de email introducida, que no sea un bloqueo y que el recordatorio no se haya enviado ya.
Cada recordatorio se reserva de forma atómica antes del envío (un UPDATE condicional
sobre el indicador reminder_sent): dos ejecuciones de cron que se solapen no pueden
enviar dos veces el mismo mensaje. Si el envío falla, la reserva se devuelve y el
recordatorio se reintentará en la ejecución siguiente. Un bloqueo corto (10 minutos) evita además
que dos pasadas simultáneas recorran la misma lista.
Mover una cita pone el indicador a cero: el cliente recibe efectivamente un recordatorio para el nuevo horario.
2. La purga de las solicitudes sin validar
Las solicitudes que llevan unverified más de 48 horas se eliminan. (Su
franja, eso sí, ya se había liberado a los 45 minutos.)
3. La anonimización RGPD
Vea la sección RGPD.
La red de seguridad
Un evento WP-Cron puede desaparecer: optimización de base de datos, restauración
parcial, plugin de limpieza, reinicio de la opción cron. OW Agenda comprueba en
cada carga que la tarea esté bien programada y la reprograma si no. Recordatorios,
purga y retención no pueden pararse en silencio.
Usar un cron de sistema real
WP-Cron depende del tráfico. En una web poco visitada, opte por un cron de sistema:
// wp-config.php
define( 'DISABLE_WP_CRON', true );
# crontab -e — toutes les 15 minutes
*/15 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null
Anti-spam y límites de frecuencia
La reserva es pública por naturaleza: sin cuenta, sin inicio de sesión, sin nonce posible. Por eso trabajan aguas arriba cinco protecciones independientes.
1. La trampa para bots (honeypot)
El formulario contiene un campo de texto fuera de pantalla, invisible para un humano y apetecible para un bot. El campo debe estar presente y vacío. Un campo relleno — o ausente, lo que delata un envío directo a la API — devuelve un rechazo seco (HTTP 403), sin detalle aprovechable.
2. El tiempo de cumplimentación
La página transmite la marca de tiempo de su visualización. Un envío llegado menos de 4 segundos después de la carga se rechaza. También aquí, una marca de tiempo ausente o malformada es un rechazo: en la 1.3.0, estos dos campos pasaron a ser obligatorios — omitirlos desactivaba pura y simplemente el control.
3. La cuota horaria por dirección
5 reservas por hora y por ámbito. En IPv4, el ámbito es la dirección; en IPv6, es el prefijo /64 — allí un atacante dispone de 2⁶⁴ direcciones, y contar la dirección entera no limitaría nada.
El contador solo se incrementa después de la validación del formulario: un visitante que se equivoca de email tres veces seguidas ya no queda bloqueado una hora para nada.
4. El tope diario de solicitudes sin verificar
Como máximo 3 solicitudes a la espera de validación por email pueden retener una franja en un mismo día. Sin este tope, bastaban unos pocos envíos para congelar todas las franjas de una fecha. Por encima, el visitante recibe una invitación a reintentarlo más tarde o a elegir otro día (HTTP 429).
5. El bloqueo de reserva
La reserva de una franja y el consumo de una invitación se hacen dentro de una sección crítica serializada por un bloqueo con nombre de MySQL, cuyo nombre se deriva de su base de datos y de su prefijo de tablas (dos webs de un mismo alojamiento compartido no se bloquean por tanto mutuamente). Si el bloqueo no se obtiene en 5 segundos, la petición devuelve un HTTP 503 explícito en lugar de una cita duplicada.
Este bloqueo sustituye, desde la 1.3.0, a una transacción que se apoyaba en un SELECT desnudo:
bajo REPEATABLE READ, este no coloca ningún bloqueo de fila, y dos visitantes simultáneos
podían reservar el mismo horario.
Y la doble verificación
Es la capa más eficaz: incluso superando todo lo anterior, un bot nunca llega a su agenda ni a su buzón, ya que la notificación admin solo se envía tras hacer clic en un enlace recibido por email. Vea La doble verificación por email.
RGPD
Una agenda de citas maneja datos potencialmente de salud, datos de contacto e historiales. Los ajustes por defecto son deliberadamente conservadores.
Consentimiento
La casilla de consentimiento es obligatoria y no está marcada por defecto en el formulario público: «Acepto que mis datos de contacto se utilicen para la gestión de mi cita.» Una reserva sin consentimiento se rechaza en el servidor, no solo en el navegador.
Minimización
- No se almacena ninguna dirección IP en la base de datos. La cuota horaria usa un contador temporal (transient) cuya clave es un resumen del ámbito de red, purgado al cabo de una hora.
- Ningún user agent, ningún rastreador, ninguna cookie depositada por el plugin.
- Ningún dato se envía a un servicio de terceros: sin CDN, sin llamadas externas, sin telemetría.
- Las solicitudes nunca validadas se eliminan al cabo de 48 horas — no dejan ningún dato detrás.
- Las citas anuladas ya no las devuelve la API de la agenda: los datos personales innecesarios no circulan.
Anonimización automática
Ajuste retention_months, 24 meses por defecto. Cada hora, las citas
terminadas desde hace más tiempo que ese plazo se anonimizan sobre la marcha: el nombre se
sustituye por «Anonimizado», el teléfono y el email se vacían, y el mensaje del cliente y
las notas internas se borran. La fila permanece, lo que preserva el historial
estadístico de la actividad (cuántas citas, cuándo, para qué servicio) sin
conservar el menor dato personal.
Ponga 0 para desactivar la anonimización automática — con conocimiento de causa.
A tener en cuenta: la anonimización se dirige a las citas que llevan una dirección de email. Una cita introducida a mano sin email (solo nombre y teléfono) no queda alcanzada por la purga automática; elimínela manualmente si su política de retención lo exige.
Derecho de acceso y derecho de supresión
El plugin no engancha los exportadores/borradores nativos de WordPress (estos trabajan sobre cuentas de usuario, y los clientes no tienen ninguna). Para tramitar una solicitud:
- Acceso — busque a la persona en el directorio de clientes (nombre, teléfono o
email) y recorra sus citas en la agenda. Los administradores de bases de
datos pueden también consultar directamente
{prefix}_owag_appointmentsy{prefix}_owag_customerssobre la columnacustomer_email/email. - Supresión — elimine la ficha del directorio y después anule o deje que se anonimicen
las citas afectadas. Una supresión inmediata se hace en base de datos sobre
customer_email.
-- Effacement manuel des données personnelles d'une personne (adaptez le préfixe).
UPDATE wp_owag_appointments
SET customer_name = 'Anonymisé', customer_phone = '', customer_email = '',
customer_msg = NULL, admin_notes = NULL
WHERE customer_email = 'personne@example.com';
DELETE FROM wp_owag_customers WHERE email = 'personne@example.com';
Registro de actividades de tratamiento
Para su registro: finalidad gestión de las citas; base jurídica consentimiento (reserva online) o ejecución de un contrato / medidas precontractuales (cita concertada por teléfono); categorías identidad, datos de contacto, contenido del mensaje, notas internas; plazo 24 meses por defecto, después anonimización; destinatarios ningún encargado del tratamiento más allá de su proveedor de alojamiento y su servicio de envío de emails.
Shortcodes
[ow_agenda] — la reserva del visitante
Para colocar en una página pública. Sin atributos: el servicio, la duración y los horarios provienen de los ajustes. El shortcode carga su hoja de estilos y su script (menos de 12 KB en total, sin dependencias) y devuelve un contenedor que se rellena tras la primera llamada a la API.
[ow_agenda]
Es este shortcode el que interpreta el parámetro de URL ?owag_invite=<token>: la misma
página sirve la consulta estándar y las sesiones por invitación.
[ow_agenda_admin] — la página de gestión
Para colocar en una página dedicada. Sin atributos. El shortcode gestiona por sí mismo el acceso:
- visitante no conectado → enlace de inicio de sesión que devuelve a la página;
- conectado sin la capacidad
owag_manage→ mensaje de acceso restringido; - conectado con la capacidad → la interfaz completa (agenda, pendientes, clientes, invitaciones).
[ow_agenda_admin]
Ambas páginas se crean automáticamente en la activación con estos shortcodes. Si las
ha eliminado, basta con crear una página y pegar en ella el shortcode — acuérdese
entonces de actualizar la opción owag_page_admin (o owag_page_booking) para que
los enlaces de los emails y el manifiesto apunten al lugar correcto.
Las páginas autónomas
Cuatro parámetros de URL, interceptados en cualquier página de la web, producen
páginas autónomas servidas fuera del tema (renderizado mínimo, noindex,nofollow):
| URL | Función |
|---|---|
?owag_verify=<uid> | Validación de la solicitud por el cliente (doble opt-in) |
?owag_cancel=<uid> | Anulación online por el cliente |
?owag_admin_action=confirm|refuse&uid=…&sig=… | Confirmación / rechazo desde el email admin |
?owag_manifest=1 | Manifiesto de aplicación de la página Agenda |
Estas páginas nunca ejecutan ninguna acción al cargarse: muestran un resumen y un botón. Ningún bot, ningún antivirus de correo, ningún prefetch de navegador puede confirmar o anular una cita en lugar de un humano.
API REST
Todos los endpoints viven bajo el namespace owag/v1, en la raíz REST habitual
(https://example.com/wp-json/owag/v1/…).
Endpoints públicos
Sin autenticación: son las rutas del widget de reserva y de los enlaces recibidos por email. Su seguridad se apoya en tokens aleatorios (32 caracteres hexadecimales procedentes de un generador criptográfico), en firmas HMAC y en las protecciones descritas en Anti-spam.
| Método | Ruta | Parámetros | Función |
|---|---|---|---|
GET | /public/days | from (Y-m-d, por defecto: hoy), token (invitación, opcional) | Devuelve label, duration y days: 14 días a partir de from, cada uno con su número de franjas libres |
GET | /public/slots | date (Y-m-d, obligatorio), token | Devuelve slots: las horas de inicio libres (HH:MM) |
POST | /public/book | date, time, name, phone, email, message, consent, website (trampa), ts (marca de tiempo), token | Crea la solicitud; devuelve status y el mensaje que mostrar |
POST | /public/verify | uid | Valida una solicitud unverified |
POST | /public/cancel | uid | Anula una cita pending o confirmed, dentro del límite del preaviso |
POST | /public/admin-action | uid, action (confirm|refuse), sig | Confirma o rechaza desde el email admin (firma HMAC) |
Ejemplo de reserva:
curl -X POST https://example.com/wp-json/owag/v1/public/book \
-H 'Content-Type: application/json' \
-d '{
"date": "2026-09-15",
"time": "14:30",
"name": "Marie Dupont",
"phone": "0470 12 34 56",
"email": "marie@example.com",
"message": "Première visite",
"consent": 1,
"website": "",
"ts": 1757942400
}'
{
"ok": true,
"status": "unverified",
"message": "Un dernier geste : un e-mail vient de vous être envoyé…"
}
Endpoints de administración
Todos exigen la capacidad owag_manage y un nonce REST de WordPress válido
(cabecera X-WP-Nonce).
| Método | Ruta | Parámetros | Función |
|---|---|---|---|
GET | /admin/agenda | from, to (Y-m-d) | events (el rango solicitado, anuladas excluidas) y pending (todas las solicitudes pendientes futuras) |
POST | /admin/appointment | kind (manual|block|consult|seance), date, time, duration (5–720 min), label, name, phone, email, notes, force, send_email | Crea una cita manual o un bloqueo |
POST | /admin/appointment/{id} | status (pending|confirmed|cancelled|done|noshow), name, phone, email, label, notes, date, time, duration, force, silent | Actualiza, mueve, cambia el estado. silent=1 suprime cualquier envío de email |
GET | /admin/customers | q (búsqueda) | El directorio (200 fichas como máximo) |
POST | /admin/customers | name (obligatorio), phone, email, notes | Crea una ficha |
POST | /admin/customers/{id} | ídem | Actualiza una ficha |
DELETE | /admin/customers/{id} | — | Elimina una ficha |
GET | /admin/invites | — | Las invitaciones abiertas (nunca usadas o liberadas, no caducadas) |
POST | /admin/invites | name (obligatorio), label (obligatorio), duration (15–720), days_valid (1–365, por defecto 60), email, phone, send_email | Crea una invitación; devuelve url y email_sent |
DELETE | /admin/invites/{id} | — | Elimina una invitación |
GET | /admin/settings | — | Los ajustes efectivos |
POST | /admin/settings | cuerpo JSON (vea Referencia de ajustes) | Guarda los ajustes |
Cada ruta declara explícitamente su control de acceso; la capacidad se verifica antes de ejecutar el menor tratamiento.
Códigos de error
Los errores son WP_Error estándar, con un código legible y un estado HTTP.
| Código | HTTP | Significado |
|---|---|---|
owag_spam | 403 | Trampa para bots rellenada, o marca de tiempo ausente / demasiado reciente |
owag_rate | 429 | Cuota horaria alcanzada para esta dirección |
owag_day_full | 429 | Demasiadas solicitudes a la espera de validación en esta fecha |
owag_busy | 503 | Bloqueo de reserva no obtenido — reintentar en un momento |
owag_slot_taken | 409 | Franja ocupada entretanto |
owag_overlap | 409 | Solapamiento detectado (admin) — reenviar con force |
owag_invite_invalid | 404 | Token de invitación desconocido |
owag_invite_used | 410 | Invitación ya consumida |
owag_invite_expired | 410 | Invitación caducada |
owag_too_late | 403 | Anulación online fuera del plazo de preaviso |
owag_bad_sig | 403 | Firma HMAC inválida |
owag_not_found | 404 | Cita o ficha no encontrada |
owag_name, owag_phone, owag_email, owag_consent | 400 | Campo inválido o consentimiento ausente |
owag_bad_input, owag_bad_date | 400 | Fecha, hora o entrada inválida |
owag_noop | 400 | Petición de actualización vacía |
owag_insert | 500 | Fallo de escritura en base de datos |
Ganchos para desarrolladores
En la 1.3.1, OW Agenda no expone ni filtros ni acciones públicas: no hay ninguna API
de extensión que documentar, y nada de lo que sigue debe adivinarse. El único gancho
utilizable es el evento programado owag_hourly_event, al que puede enganchar
su propio mantenimiento:
add_action( 'owag_hourly_event', function () {
// Votre traitement horaire, exécuté juste après celui du plugin.
} );
Para todo lo demás, la API REST es el punto de integración: expone exactamente lo que hace la interfaz, ni más ni menos.
Referencia de ajustes
Todos los ajustes caben en una sola opción, owag_settings. Lectura y
escritura en PHP:
$s = owag_settings(); // réglages effectifs (défauts + enregistrés)
$duree = (int) $s['consult_duration'];
update_option( 'owag_settings', array_merge( owag_settings(), [
'consult_duration' => 45,
'buffer' => 10,
] ) );
Valores por defecto
[
// Horaires : clé = 1 (lundi) … 7 (dimanche), valeur = liste de plages [début, fin]
'hours' => [
'1' => [ [ '09:00', '18:00' ] ],
'2' => [ [ '09:00', '18:00' ] ],
'3' => [ [ '09:00', '18:00' ] ],
'4' => [ [ '09:00', '18:00' ] ],
'5' => [ [ '09:00', '18:00' ] ],
'6' => [], // samedi fermé
'7' => [], // dimanche fermé
],
'slot_step' => 30, // granularité des créneaux proposés (minutes)
'consult_duration' => 60, // durée d'une consultation (minutes)
'buffer' => 0, // battement autour de chaque rendez-vous (minutes)
'min_notice_hours' => 24, // délai minimum avant réservation
'max_advance_days' => 60, // horizon maximum de réservation
'auto_confirm' => 0, // 0 = validation manuelle, 1 = confirmation automatique
'email_verify' => 1, // double vérification par e-mail
'cancel_notice_hours' => 24, // préavis d'annulation en ligne par le client
'notify_email' => get_option( 'admin_email' ),
'retention_months' => 24, // anonymisation des RDV passés (0 = jamais)
'consult_label' => 'Consultation',
]
Detalle
| Ajuste | Tipo | Límites de la interfaz | Efecto |
|---|---|---|---|
hours | array | 1 o 2 tramos por día | Dos tramos permiten el cierre del mediodía. Un tramo solo se tiene en cuenta si el inicio y el fin son válidos y si inicio < fin. Un día vacío = cerrado |
slot_step | entero | 5 → 120 min | Separación entre las horas ofrecidas. Cuanto más fina, más larga es la lista |
consult_duration | entero | 15 → 480 min | Duración reservada en la agenda para una consulta web |
buffer | entero | 0 → 120 min | Margen aplicado a uno y otro lado de la franja candidata |
min_notice_hours | entero | 0 → 168 h | No se ofrece nada antes de ahora + este plazo |
max_advance_days | entero | 7 → 365 días | Horizonte más allá del cual ya no se ofrece ninguna franja |
auto_confirm | 0 / 1 | — | Validación manual o confirmación inmediata |
email_verify | 0 / 1 | — | Doble opt-in por email (déjelo activo) |
cancel_notice_hours | entero | 0 → 168 h | Más allá, la anulación online se rechaza con un mensaje que invita a llamar por teléfono |
notify_email | — | Destinatario de las notificaciones admin; sirve también de ORGANIZER en los archivos ICS | |
retention_months | entero | 0 → 120 meses | Anonimización de las citas terminadas; 0 la desactiva |
consult_label | texto | — | Título mostrado en el widget de reserva |
Dos constantes no configurables merecen conocerse, porque gobiernan el comportamiento del doble opt-in: la franja de una solicitud sin verificar queda retenida 45 minutos, y la solicitud se elimina al cabo de 48 horas.
El guardado mediante la API REST acepta las mismas claves en JSON; los enteros se llevan a cero como mínimo, la dirección de notificación solo se acepta si es válida, y cada tramo horario se vuelve a validar en el servidor.
Datos almacenados
Tres tablas, todas con el prefijo de su instalación. Las fechas son
DATETIME en hora local de la web, nunca en UTC — es lo que hace que las consultas sean
legibles y que las comparaciones de horarios resulten triviales.
{prefix}_owag_appointments
| Columna | Tipo | Notas |
|---|---|---|
id | BIGINT | Clave primaria |
uid | CHAR(32) | Token público único (hexadecimal, generador criptográfico). Sirve de clave para los enlaces de validación, de anulación y de acción admin, y de UID ICS |
kind | VARCHAR(16) | consult, seance, manual, block |
service_label | VARCHAR(120) | Título mostrado |
customer_name, customer_phone, customer_email | VARCHAR | Datos de contacto |
customer_msg | TEXT | Mensaje dejado por el cliente |
admin_notes | TEXT | Notas internas, nunca enviadas al cliente |
start_dt, end_dt | DATETIME | Límites de la franja (indexadas sobre start_dt) |
status | VARCHAR(16) | unverified, pending, confirmed, cancelled, done, noshow |
source | VARCHAR(16) | web, invite, admin |
invite_id | BIGINT | Invitación de origen, si procede |
reminder_sent | TINYINT | Indicador de recordatorio (se pone a 0 en caso de cambio de fecha) |
created_at, updated_at | DATETIME | updated_at alimenta el número de revisión ICS |
{prefix}_owag_invites
id, token (CHAR(32), único), datos de contacto del cliente, service_label,
duration_min, expires_at, appointment_id (rellenado cuando el enlace se consume,
devuelto a NULL si la cita se anula), created_at.
{prefix}_owag_customers
id, name, phone, email, notes, created_at, updated_at. Índice sobre name y
email.
Una sola opción lleva la configuración (owag_settings); las demás son técnicas:
owag_admin_secret (secreto HMAC), owag_db_version, owag_pages_created,
owag_page_booking, owag_page_admin.
Internacionalización
Toda la interfaz es traducible: ajustes de administración, widget de
reserva, página Agenda, páginas autónomas, mensajes de la API y plantillas de email. El
idioma de origen es el inglés; el plugin declara el dominio de traducción
ow-agenda y la ruta /languages.
El paquete entrega la plantilla languages/ow-agenda.pot. Se incluye una traducción francesa
completa, producida a partir de la formulación original de la interfaz.
Instalar una traducción manualmente
Compile el .po y deje el .mo en la carpeta de traducciones de WordPress:
msgfmt ow-agenda-fr_FR.po -o ow-agenda-fr_FR.mo
# puis : wp-content/languages/plugins/ow-agenda-fr_FR.mo
WordPress carga el catálogo en la primera cadena traducida; no hace falta ninguna llamada manual.
Dos trampas para los traductores
- Las cadenas destinadas al JavaScript usan marcadores numerados
(
%1$s,%2$s) y nunca un%sdesnudo: el pequeño formateador incorporado en los scripts solo entiende esos. Un marcador sin numerar se mostraría literalmente. - Dos cadenas son formatos de fecha de PHP, no frases:
l, F j, Y \a\t g:i ayl j F Y \a\t H:i. Tradúzcalas como formatos (reordenar los tokens, escapar las letras literales con una barra invertida). El francés usal j F Y \à H\hi.
Desinstalación
Desactivar el plugin solo hace una cosa: eliminar la tarea programada horaria. Sus datos, sus ajustes y sus páginas permanecen intactos, y todo vuelve a arrancar en la reactivación.
Eliminar el plugin desde WordPress ejecuta la desinstalación completa:
- eliminación de las tres tablas (
owag_appointments,owag_invites,owag_customers); - eliminación de las opciones
owag_settings,owag_admin_secret,owag_db_version,owag_pages_created,owag_page_booking,owag_page_admin; - eliminación de la tarea programada;
- retirada de la capacidad
owag_managedel rol administrador.
Las dos páginas creadas en la activación no se eliminan — pueden haber sido retocadas, puestas en un menú, indexadas. Le corresponde a usted enviarlas a la papelera si ya no las quiere.
La eliminación de las tablas es definitiva. Exporte antes su base de datos si el historial de las citas tiene valor para usted.
Solución de problemas
No aparece ninguna franja en la página de reserva
Repase la lista en orden:
- ¿Está introducido el horario del día? Un día vacío es un día cerrado.
- La antelación mínima (24 h por defecto) oculta forzosamente el día de hoy y parte de mañana.
- ¿Cabe la duración dentro del tramo? Con un tramo 09:00–12:00 y una duración de 240 minutos, no puede existir ninguna franja.
- El horizonte: los días más allá de
max_advance_daysnunca se ofrecen. - El margen: un margen amplio en una agenda cargada puede cerrarlo todo.
- Un bloqueo quizá cubra la jornada. Abra la página Agenda en el día en cuestión.
- La zona horaria de la web (Ajustes → Generales): una zona mal configurada desplaza todos los cálculos.
Los emails no llegan
Casi siempre es la entregabilidad, no el plugin.
- Compruebe que la cita existe realmente en la página Agenda: si está ahí, el plugin ha hecho su trabajo.
- Instale un plugin SMTP (Fluent SMTP, WP Mail SMTP…). La función
mail()de PHP la rechaza la inmensa mayoría de los servidores de recepción. - Compruebe el SPF, el DKIM y el DMARC de su dominio.
- Pruebe la dirección en OW Agenda → Notificaciones admin enviadas a: una dirección inválida bloquea silenciosamente las notificaciones.
El recordatorio de la víspera no ha salido
El recordatorio depende de WP-Cron, que depende del tráfico. En una web poco visitada, pase a un cron de sistema real (vea Recordatorios y tarea programada). Compruebe también que la cita esté efectivamente confirmada, que lleve una dirección de email y que no sea un bloqueo.
La página Agenda muestra «El acceso está reservado al equipo»
El usuario conectado no tiene la capacidad owag_manage. Se les da a los
administradores en la activación; para los demás roles, vea
Instalación.
El botón «Confirmar» del email muestra «Enlace inválido»
Tres causas posibles: el cliente de correo ha truncado el enlace (intente copiar la URL completa), la cita ya se ha tratado (el mensaje lo indica), o el secreto HMAC de la web se ha regenerado. En este último caso, pase por la página Agenda: los enlaces antiguos ya no pueden validarse, por construcción.
Dos citas acaban en la misma franja
Desde la 1.3.0, es imposible mediante la reserva online. En cambio, el admin
puede forzar un solapamiento desde la página Agenda (el plugin lo señala y pide
confirmación): compruebe primero que no se trate de un forzado voluntario. Compruebe
también que su base de datos soporte bien GET_LOCK() — un alojamiento exótico devolvería
entonces errores 503 en la reserva en lugar de duplicados.
Las páginas «Reservar» y «Agenda» no se han creado
Solo se crean una vez, en la primerísima activación. Si las ha
eliminado, cree dos páginas con los shortcodes [ow_agenda] y [ow_agenda_admin],
y apunte después hacia ellas las opciones owag_page_booking y owag_page_admin para que los enlaces de los
emails sigan siendo correctos:
update_option( 'owag_page_booking', 42 ); // ID de votre page de réservation
update_option( 'owag_page_admin', 43 ); // ID de votre page Agenda
El archivo de calendario no va adjunto
El archivo se escribe en el directorio temporal del servidor mediante la API de archivos de WordPress. Si ese directorio no es accesible en escritura (cuota, permisos, alojamiento bloqueado), el email sale sin adjunto en lugar de fallar. Compruebe los permisos de escritura y el espacio en disco.
Los horarios están desplazados una hora
Ajustes → Generales → Zona horaria. Elija una ciudad (Europe/Brussels) en lugar
de un desfase UTC fijo: solo una zona con nombre gestiona el cambio al horario de verano.
FAQ
¿OW Agenda es realmente gratis?
Sí. GPL-2.0-or-later, descargable en .zip desde
https://optionweb.dev/es/addons/ow-agenda/. Sin versión Pro, sin clave de licencia,
sin funciones bloqueadas, sin cuenta que crear, sin cuota de citas.
¿Las franjas online tienen en cuenta las citas concertadas por teléfono? Sí. Cualquier evento de la agenda — reserva web, cita introducida a mano, sesión por invitación, bloqueo — deja su tramo horario no disponible para la reserva online.
¿El cliente tiene que crear una cuenta? No. Ninguna cuenta, ninguna contraseña. Todas las acciones del cliente (validación, anulación) pasan por enlaces privados recibidos por email.
¿Se pueden gestionar varios profesionales o varias salas? No. OW Agenda describe un solo planning. Para dos agendas independientes, hacen falta dos webs (o dos webs de una red multisitio).
¿Se sincroniza con Google Calendar? En el sentido que importa para el cliente: sí. Cada confirmación incorpora un archivo de calendario estándar que añade — y actualiza, y elimina — el evento en Google Calendar, Outlook o el calendario del iPhone. En cambio, el plugin no lee sus agendas externas: sus indisponibilidades personales se introducen como bloqueos.
¿Se puede cobrar una señal al reservar? No, queda fuera del alcance asumido del plugin.
¿Cuántos servicios se pueden ofrecer online? Uno solo, del que usted elige el título y la duración. Los demás servicios pasan por las invitaciones de sesión, que le permiten fijar el título y la duración caso por caso. Esta decisión es deliberada: un menú de servicios en una página pública multiplica las solicitudes mal enfocadas.
¿Funciona en multisitio? Sí. Cada web de la red tiene sus propias tablas, sus propios ajustes y su propia agenda.
¿El plugin ralentiza la web? No. No se carga ningún script fuera de las dos páginas afectadas; el widget de reserva pesa menos de 12 KB, sin jQuery ni framework. El trabajo periódico cabe en una tarea horaria.
¿Qué pasa si un cliente nunca valida su email? Su franja se libera al cabo de 45 minutos y su solicitud se elimina al cabo de 48 horas. Usted nunca llega a enterarse.
¿Dónde encontrar el soporte?
- Portal de soporte: https://optionweb.dev/es/addons/support/
- Email: support@optionweb.dev
- Documentación: https://optionweb.dev/es/addons/ow-agenda/
Changelog
1.3.1
Conformidad
- Las páginas autónomas de acción, de verificación y de anulación ya no imprimen su CSS
ni su JavaScript en línea: ambos son ahora archivos registrados y servidos
por la API de enqueue de WordPress. Los dos colores de botón variables pasan por
propiedades CSS personalizadas, y el script se configura mediante
wp_localize_script(). - Los catálogos de traducción ya no van incrustados: solo se entrega
languages/ow-agenda.pot, y el francés se distribuye mediante los paquetes de idioma de WordPress.org alimentados por translate.wordpress.org. - La versión mínima de WordPress vuelve a ser la 6.3: ya nada en el paquete depende del descubrimiento de las traducciones incrustadas introducido en la 6.7.
API REST
- Cada ruta de administración declara ahora su
permission_callbackexplícitamente en lugar de compartirlo mediantearray_merge(). El control de capacidad ya se aplicaba; ahora es visible para el análisis estático.
Ningún cambio de comportamiento.
1.3.0
Internacionalización
- Plugin enteramente traducible (dominio
ow-agenda, inglés como idioma de origen): ajustes, páginas públicas, mensajes de la API, emails e interfaz JavaScript. - Plantilla
languages/ow-agenda.potentregada y traducción francesa completa.
Seguridad
- Los identificadores públicos de las citas y los tokens de invitación los genera un
generador criptográfico (
random_bytes()). Estos tokens son la única autenticación de los enlaces de anulación, de validación y de invitación. - La trampa para bots y la marca de tiempo pasan a ser obligatorias: omitirlas desactivaba los controles anti-spam.
- La cuota horaria se agrega por prefijo /64 en IPv6, y un tope diario limita las solicitudes sin verificar — un visitante ya no puede retener todas las franjas de una jornada.
- La cuota solo se descuenta después de validar el formulario: una errata ya no bloquea a un visitante legítimo durante una hora.
Fiabilidad
- Las reservas simultáneas de una misma franja se serializan mediante un bloqueo con nombre de MySQL. La transacción anterior no colocaba ningún bloqueo de fila: dos visitantes podían reservar el mismo horario.
- El recordatorio de la víspera usa una ventana deslizante de 12 a 36 h y recupera su retraso; cada envío se reserva de forma atómica, y dos ejecuciones de cron ya no pueden enviar dos veces el mismo recordatorio.
- La tarea horaria se reprograma automáticamente si desaparece: recordatorios, purga y anonimización ya no pueden pararse en silencio.
- Mover una cita actualiza el calendario del cliente (
SEQUENCE,METHOD:REQUEST, organizador y participante); la anulación envía unMETHOD:CANCELque elimina el evento. - Las fechas del widget se calculan en hora local y no en UTC: se acabó el día perdido para los visitantes al este de UTC.
- La anulación de una cita surgida de una invitación libera la invitación, que vuelve a ser utilizable.
- Las citas anuladas y las solicitudes sin validar caducadas ya no se devuelven a la agenda: menos datos personales en las respuestas y se acabaron las citas fantasma.
1.2.1
- Conformidad con Plugin Check: escapado de las salidas, saneamiento de las entradas, nonce en los ajustes, readme.
- La desinstalación elimina también la tabla del directorio de clientes y el secreto HMAC.
1.2.0
- Emails HTML (maquetación en tarjeta, color de acento de la web) para todas las notificaciones.
- Botones Confirmar / Rechazar en el email admin (enlaces firmados con HMAC).
- Directorio de clientes: búsqueda, creación, edición, borrado, atajos hacia una nueva cita y una invitación.
1.1.0
- Doble verificación por email contra los bots (franja retenida 45 minutos, purga al cabo de 48 horas).
1.0.0
- Versión inicial: reserva online, agenda de administración mobile-first, invitaciones de sesión, emails con archivo de calendario y recordatorio de la víspera.
Diseñado por OptionWeb — Julien Daniel, Châtelet, Bélgica.