ODSLocal Events & Calendar
PHP ≥ 8.1
WordPress ≥ 6.0
Requiere ODSLocal Municipios
1. Qué hace
Extensión completa de gestión de eventos para ODSLocal Municipios. Permite a colaboradores proponer, gestionar y verificar eventos vinculados a ODS y territorios:
- Propuesta y publicación de eventos con flujo de aprobación.
- Reservas públicas (sin cuenta) o de usuarios logueados.
- Tickets con QR firmados con HMAC.
- Check-in presencial escaneando QR o introduciendo código.
- Certificados de participación emitibles masivamente.
- Integración con el Hub y perfil público de colaboradores.
- Feed iCal subscribible.
- SEO rico con Schema.org
Event.
2. Instalación
- Asegúrate de que ODSLocal Municipios está activo.
- Opcional pero recomendado: ODSLocal Contributor Profiles (añade pestaña «Eventos» al Hub).
- Sube la carpeta
odslocal-events/awp-content/plugins/. - Actívalo. En la activación se crea:
- La tabla
wp_odslocal_event_tickets. - Las páginas:
/crear-evento/,/mis-eventos/,/mis-reservas/,/mi-ticket/,/checkin-evento/,/verificar-certificado/,/calendario-eventos/. - Términos de taxonomía ODS y tipos de evento.
- Un
secret HMACaleatorio para firmar tickets (almacenado enwp_options). - El cron diario
ods_events_auto_issue_certificates.
- La tabla
3. Shortcodes
| Shortcode | Uso típico |
|---|---|
|
Grid mensual de eventos con navegación por meses. |
|
Lista filtrada. Atributos: ods, territory, type, past, columns. |
|
Próximos N eventos. Ideal para widget en home. |
|
Formulario de reserva. Si lo pones en la plantilla single-ods_evento.php, infiere el ID del post actual. |
|
Reservas del usuario logueado. |
|
Formulario público para proponer evento (requiere login). |
|
Eventos submetidos por el usuario. |
|
Página del ticket. URL pública /mi-ticket/?c=<code>&h=<hash>. |
|
UI de escáner QR en directo. URL /checkin-evento/?event=<id>. |
|
Verificación pública de certificado. |
Ejemplos
No hay eventos programados.
No hay eventos programados.
4. Ciclo de vida de un evento
draft → pending → publish ─→ ods_event_cancelled
└→ ods_event_rejected → (re-editable) → pending
| Estado | Significado |
|---|---|
draft |
Borrador del autor. |
pending |
Enviado, esperando aprobación. |
publish |
Publicado, visible en calendario y lista. |
ods_event_cancelled |
Cancelado; todas las reservas quedan cancelled + email. |
ods_event_rejected |
Rechazado con motivo; visible sólo al autor. |
5. Crear un evento (colaborador)
- El colaborador entra en
/crear-evento/(o la pestaña Eventos del Hub). - Rellena título, descripción, ubicación, fechas, capacidad, ODS asociados, territorios.
- Marca si es presencial, online o mixto. En online, añade URL (p. ej. Zoom).
- (Opcional) Sube imagen destacada.
- Guarda como
drafto envía a revisión (pending). - Revisores
ods_platform_managero admins entran en Eventos → Revisión y aprueban/rechazan.
El autor puede editar un evento publicado (dentro de su cap). Si cambia fecha o ubicación, las reservas existentes reciben email de actualización.
6. Reservar entrada (público)
- Visitante o usuario logueado abre la página del evento.
- Pulsa Reservar → aparece un formulario inline.
- Introduce nombre, email, (opcional) número de asistentes si el evento acepta +1.
- Resuelve el CAPTCHA si no está logueado.
- El sistema genera:
ticket_codealeatorio de 16 caracteres hex.hash= primeros 32 chars de HMAC-SHA256 sobrecode|event_idcon el secret del sitio.
- El visitante recibe un email con:
- URL del ticket
/mi-ticket/?c=<code>&h=<hash>. - Adjunto
.icspara añadir al calendario.
- URL del ticket
Restricciones
- 1 reserva por email por evento.
- Honeypot invisible contra bots.
- Rate limit: 10 reservas por IP por 24h (filtrable).
7. El ticket QR
Página del ticket:
- Diseño tipo pase de admisión con foto destacada del evento.
- QR generado client-side con qrcodejs. Codifica el check-in URL con
codeyhash. - Datos del asistente, del evento y ubicación.
- Botón de impresión con layout A4 optimizado.
- Si el asistente hace check-in, aparece sello «Acceso validado».
- Cancelación desde el propio ticket (opcional).
El ticket es válido solo si hash_equals(hash, HMAC_SHA256(code|event_id, SECRET)). Imposible de forjar sin el secret del servidor.
8. Check-in en el evento
El organizador abre /checkin-evento/?event=<id> desde tablet o móvil (requiere capability ods_event_checkin, o ser el autor del evento).
Dos modos de check-in
- Escáner QR: activa la cámara usando html5-qrcode. Selector de cámara en desktop (útil si hay webcam + captura externa). Al detectar un QR válido, muestra el nombre del asistente y lo marca como
checked_in. - Código manual: introduce el
ticket_code(16 hex) si el asistente no tiene acceso al QR.
El check-in guarda timestamp + user_id de quien lo hizo. Si se intenta re-checkin, muestra aviso amarillo («Ya validado hace X min»).
Si la cámara no permite acceso o la CDN del escáner falla, la página cae automáticamente en modo código manual. Siempre se puede completar el check-in.
9. Certificados de participación
Cada evento puede activar la emisión automática de certificados para asistentes que hicieron check-in.
- En el evento, admin/autor marca Certificados activados y personaliza el template (título, logo, firma).
- Tras el evento, clic en Emitir certificados o esperar al cron diario.
- Cada ticket con
checked_inrecibe email con URL pública/verificar-certificado/?c=<code>&h=<hash>. - El asistente puede imprimir el certificado, guardarlo como PDF, compartirlo o añadirlo al CV.
La página del certificado se puede abrir por terceros (empleadores) para verificar que el certificado es auténtico — el mismo HMAC garantiza que no se pueda falsificar.
10. Panel de gestión admin
En Eventos → Gestión (requiere edit_others_ods_eventos):
- Lista de eventos próximos y pasados con estado, aforo y % ocupación.
- Exportar asistentes a CSV.
- Enviar recordatorio a todos los asistentes (rate-limited).
- Actualizar aforo (con validación: no puede ser menor que las reservas confirmadas).
- Notas públicas que se muestran a los asistentes en su ticket.
11. Capabilities adicionales
| Capability | Propósito |
|---|---|
edit_ods_evento, edit_ods_eventos |
CRUD del CPT. |
publish_ods_eventos |
Publicar sin aprobación. |
edit_others_ods_eventos, delete_others_ods_eventos |
Moderación de eventos de terceros. |
ods_event_checkin |
Hacer check-in en cualquier evento. Los autores reciben check-in automático solo para los suyos. |
ods_event_issue_certificate |
Emitir certificados en cualquier evento. |
12. REST API + feed iCal
Namespace público /wp-json/ods-events/v1:
| Ruta | Descripción |
|---|---|
GET /events?from=YYYY-MM-DD&to=YYYY-MM-DD&ods=ods3&territory=42&type=workshop |
Listar eventos filtrados. |
GET /events/<id> |
Detalle de un evento. |
GET /events-by-territory/<code_ine>?limit=5 |
Eventos en un municipio. |
GET /ical?territory=42&ods=ods3 |
Feed iCal subscribible desde Google Calendar, Outlook, etc. |
13. SEO y Google Events
Cada evento publicado emite automáticamente:
- Open Graph + Twitter Card.
- Schema.org
EventJSON-LD constartDate,endDate,eventStatus,eventAttendanceMode,location(Place con coords oVirtualLocation),organizer,offers,keywords. - Meta
descriptionauto-generada si no hay excerpt.
Si ya usas Yoast o RankMath y no quieres duplicar:
add_filter('ods_events_disable_seo', '__return_true');
14. Anti-abuso y rate limiting
- Honeypot invisible en formularios de reserva y submit.
- Rate limit: 10 reservas / IP / 24h. Filtrable:
add_filter('ods_events_rate_limit', fn() => 20); - 1 booking por email y evento.
- Cap estricto en submit y edit.
- Nonce en todos los AJAX.
- HMAC en QR: imposible forjar tickets sin el secret.
- CAPTCHA inline (auto-refresh sin recarga de página).
- set_post_thumbnail valida tipo del attachment.
15. Librerías externas (QR)
Se cargan por defecto desde CDN (ambas con licencia permisiva):
| Librería | Tamaño | Licencia | Función |
|---|---|---|---|
| qrcodejs | ~12 KB | MIT | Generar QR en el ticket. |
| html5-qrcode | ~80 KB | Apache-2.0 | Leer QR vía cámara en check-in. |
Para self-hosting (sin CDN):
add_filter('ods_events_qrcode_lib_url', fn() => plugin_dir_url(__FILE__) . 'vendor/qrcode.min.js');
add_filter('ods_events_scanner_lib_url', fn() => plugin_dir_url(__FILE__) . 'vendor/html5-qrcode.min.js');
16. Solución de problemas
La cámara no se activa en check-in
- Necesita HTTPS (la API MediaDevices no funciona en HTTP).
- Permisos del navegador: algunos dispositivos los resetean tras inactividad.
- En desktop con dos cámaras: usa el selector en la parte superior.
- Si falla, usa el campo de código manual.
El ticket sale en blanco al imprimir
Corregido en 1.0.9. Asegúrate de estar en la última versión. El layout A4 ahora encaja correctamente.
El email de reserva no llega
- Verifica que
wp_mailfunciona. - Revisa spam.
- El header del email incluye
Reply-To: organizer@email; algunos filtros agresivos lo marcan si el dominio no coincide con el del site.
Quiero desactivar los certificados
En el evento, desmarca Certificados activados. El cron automático sólo procesa eventos con esa opción activa.
Cómo exportar todos los asistentes de un evento
Eventos → Gestión → [evento] → Exportar asistentes CSV. Incluye status, timestamp de check-in y correo.