En vivo · estado OK
Documentación · OW Agenda v1.3.1

OW Agenda
Documentación

La reserva de citas en WordPress que no deja que nadie te ocupe una franja para nada.

v1.3.1GPL-2.0-or-laterDocumentación

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

  1. Visión general
  2. Instalación
  3. Inicio rápido
  4. El proceso de reserva
  5. La doble verificación por email
  6. La validación de las solicitudes
  7. La página Agenda
  8. El directorio de clientes
  9. Las invitaciones de sesión
  10. Emails y archivo de calendario (ICS)
  11. Recordatorios y tarea programada
  12. Anti-spam y límites de frecuencia
  13. RGPD
  14. Shortcodes
  15. API REST
  16. Referencia de ajustes
  17. Datos almacenados
  18. Internacionalización
  19. Desinstalación
  20. Solución de problemas
  21. FAQ
  22. 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

  1. Descargue ow-agenda-1.3.1.zip desde https://optionweb.dev/es/addons/ow-agenda/
  2. Plugins → Añadir nuevo → Subir plugin
  3. 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:

TablaContenido
{prefix}_owag_appointmentsTodos los eventos de la agenda: consultas web, sesiones surgidas de una invitación, citas introducidas a mano, bloqueos de franjas
{prefix}_owag_invitesLos enlaces privados «elija la fecha de su sesión»
{prefix}_owag_customersEl 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_manage al rol administrador — es esta, y no manage_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:

  1. cae dentro de un tramo de apertura del día en cuestión;
  2. la cita entera (inicio + duración) cabe antes del cierre;
  3. empieza después de ahora + antelación mínima;
  4. el día está dentro del horizonte de reserva;
  5. 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ónEstado creadoMensaje mostrado
Doble verificación por email activa (por defecto)unverified«Un último gesto: acabamos de enviarle un email…»
Verificación desactivada + validación manualpending«¡Hemos recibido su solicitud!»
Verificación desactivada + confirmación automáticaconfirmed«¡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):

  1. La solicitud se registra con el estado unverified. El administrador no recibe ninguna notificación. La cita no aparece como una solicitud por tratar.
  2. El visitante recibe un email «Un último gesto…» que contiene un botón de validación.
  3. 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.
  4. 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.
  5. La solicitud pasa entonces a pending (validación manual) o a confirmed (confirmación automática), el cliente recibe el email correspondiente, y solo en ese momento sale la notificación admin.
  6. 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: » con el resumen completo (servicio, fecha, nombre, teléfono, email, mensaje del cliente) y dos botones:

  • 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:

  1. 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.
  2. Elija la validez del enlace: 60 días por defecto, de 1 a 365.
  3. 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.
  4. 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.

EmailDestinatarioDisparador
Validación de la solicitudClienteReserva web cuando la doble verificación está activa
Solicitud recibidaClienteSolicitud validada, a la espera de su decisión
Cita confirmadaClienteConfirmación (manual, automática, alta admin, cambio de fecha) — con adjunto ICS
Cita anuladaClienteAnulación por el cliente, rechazo o anulación por el admin — con adjunto ICS de cancelación
RecordatorioClienteAutomáticamente, entre 12 y 36 h antes de la cita
Elija la fecha de su sesiónClienteCreación de una invitación con envío por email
Por confirmar: …AdminNueva solicitud verificada pendiente — con los botones Confirmar / Rechazar
Nueva cita: …AdminNueva cita confirmada (confirmación automática, invitación)
Anulación del cliente: …AdminEl 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:REQUEST en la creación y en cada revisión de horario, METHOD:CANCEL en la anulación;
  • un UID estable 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 SEQUENCE creciente 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) y ATTENDEE (el cliente);
  • DTSTART / DTEND en UTC, STATUS:CONFIRMED o STATUS: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_appointments y {prefix}_owag_customers sobre la columna customer_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):

URLFunció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=1Manifiesto 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étodoRutaParámetrosFunción
GET/public/daysfrom (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/slotsdate (Y-m-d, obligatorio), tokenDevuelve slots: las horas de inicio libres (HH:MM)
POST/public/bookdate, time, name, phone, email, message, consent, website (trampa), ts (marca de tiempo), tokenCrea la solicitud; devuelve status y el mensaje que mostrar
POST/public/verifyuidValida una solicitud unverified
POST/public/canceluidAnula una cita pending o confirmed, dentro del límite del preaviso
POST/public/admin-actionuid, action (confirm|refuse), sigConfirma 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étodoRutaParámetrosFunción
GET/admin/agendafrom, to (Y-m-d)events (el rango solicitado, anuladas excluidas) y pending (todas las solicitudes pendientes futuras)
POST/admin/appointmentkind (manual|block|consult|seance), date, time, duration (5–720 min), label, name, phone, email, notes, force, send_emailCrea 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, silentActualiza, mueve, cambia el estado. silent=1 suprime cualquier envío de email
GET/admin/customersq (búsqueda)El directorio (200 fichas como máximo)
POST/admin/customersname (obligatorio), phone, email, notesCrea una ficha
POST/admin/customers/{id}ídemActualiza una ficha
DELETE/admin/customers/{id}Elimina una ficha
GET/admin/invitesLas invitaciones abiertas (nunca usadas o liberadas, no caducadas)
POST/admin/invitesname (obligatorio), label (obligatorio), duration (15–720), days_valid (1–365, por defecto 60), email, phone, send_emailCrea una invitación; devuelve url y email_sent
DELETE/admin/invites/{id}Elimina una invitación
GET/admin/settingsLos ajustes efectivos
POST/admin/settingscuerpo 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ódigoHTTPSignificado
owag_spam403Trampa para bots rellenada, o marca de tiempo ausente / demasiado reciente
owag_rate429Cuota horaria alcanzada para esta dirección
owag_day_full429Demasiadas solicitudes a la espera de validación en esta fecha
owag_busy503Bloqueo de reserva no obtenido — reintentar en un momento
owag_slot_taken409Franja ocupada entretanto
owag_overlap409Solapamiento detectado (admin) — reenviar con force
owag_invite_invalid404Token de invitación desconocido
owag_invite_used410Invitación ya consumida
owag_invite_expired410Invitación caducada
owag_too_late403Anulación online fuera del plazo de preaviso
owag_bad_sig403Firma HMAC inválida
owag_not_found404Cita o ficha no encontrada
owag_name, owag_phone, owag_email, owag_consent400Campo inválido o consentimiento ausente
owag_bad_input, owag_bad_date400Fecha, hora o entrada inválida
owag_noop400Petición de actualización vacía
owag_insert500Fallo 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

AjusteTipoLímites de la interfazEfecto
hoursarray1 o 2 tramos por díaDos 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_stepentero5 → 120 minSeparación entre las horas ofrecidas. Cuanto más fina, más larga es la lista
consult_durationentero15 → 480 minDuración reservada en la agenda para una consulta web
bufferentero0 → 120 minMargen aplicado a uno y otro lado de la franja candidata
min_notice_hoursentero0 → 168 hNo se ofrece nada antes de ahora + este plazo
max_advance_daysentero7 → 365 díasHorizonte más allá del cual ya no se ofrece ninguna franja
auto_confirm0 / 1Validación manual o confirmación inmediata
email_verify0 / 1Doble opt-in por email (déjelo activo)
cancel_notice_hoursentero0 → 168 hMás allá, la anulación online se rechaza con un mensaje que invita a llamar por teléfono
notify_emailemailDestinatario de las notificaciones admin; sirve también de ORGANIZER en los archivos ICS
retention_monthsentero0 → 120 mesesAnonimización de las citas terminadas; 0 la desactiva
consult_labeltextoTí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

ColumnaTipoNotas
idBIGINTClave primaria
uidCHAR(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
kindVARCHAR(16)consult, seance, manual, block
service_labelVARCHAR(120)Título mostrado
customer_name, customer_phone, customer_emailVARCHARDatos de contacto
customer_msgTEXTMensaje dejado por el cliente
admin_notesTEXTNotas internas, nunca enviadas al cliente
start_dt, end_dtDATETIMELímites de la franja (indexadas sobre start_dt)
statusVARCHAR(16)unverified, pending, confirmed, cancelled, done, noshow
sourceVARCHAR(16)web, invite, admin
invite_idBIGINTInvitación de origen, si procede
reminder_sentTINYINTIndicador de recordatorio (se pone a 0 en caso de cambio de fecha)
created_at, updated_atDATETIMEupdated_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 %s desnudo: 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 a y l 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 usa l 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_manage del 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:

  1. ¿Está introducido el horario del día? Un día vacío es un día cerrado.
  2. La antelación mínima (24 h por defecto) oculta forzosamente el día de hoy y parte de mañana.
  3. ¿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.
  4. El horizonte: los días más allá de max_advance_days nunca se ofrecen.
  5. El margen: un margen amplio en una agenda cargada puede cerrarlo todo.
  6. Un bloqueo quizá cubra la jornada. Abra la página Agenda en el día en cuestión.
  7. 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.

  1. Compruebe que la cita existe realmente en la página Agenda: si está ahí, el plugin ha hecho su trabajo.
  2. 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.
  3. Compruebe el SPF, el DKIM y el DMARC de su dominio.
  4. 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?


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_callback explícitamente en lugar de compartirlo mediante array_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.pot entregada 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 un METHOD:CANCEL que 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.