Mi cuenta

ODSLocal Events & Calendar

Calendario, reservas, tickets QR, check-in, certificados de participación
Versión 1.0.10
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

  1. Asegúrate de que ODSLocal Municipios está activo.
  2. Opcional pero recomendado: ODSLocal Contributor Profiles (añade pestaña «Eventos» al Hub).
  3. Sube la carpeta odslocal-events/ a wp-content/plugins/.
  4. 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 HMAC aleatorio para firmar tickets (almacenado en wp_options).
    • El cron diario ods_events_auto_issue_certificates.

3. Shortcodes

Shortcode Uso típico
‹

Octubre 2026

›
Lun Mar Mié Jue Vie Sáb Dom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Grid mensual de eventos con navegación por meses.
📅

No hay eventos programados.

Lista filtrada. Atributos: ods, territory, type, past, columns.
📅

No hay eventos programados.

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.
Debes iniciar sesión para ver tus reservas.
Reservas del usuario logueado.
¿Tienes una cuenta? Inicia sesión para publicar eventos directamente. Si no, rellena este formulario y nuestro equipo revisará tu propuesta.
Formulario público para proponer evento (requiere login).
Debes iniciar sesión.
Eventos submetidos por el usuario.
Falta el código del ticket.
Página del ticket. URL pública /mi-ticket/?c=<code>&h=<hash>.
Debes iniciar sesión.
UI de escáner QR en directo. URL /checkin-evento/?event=<id>.
Faltan parámetros de verificación.
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)

  1. El colaborador entra en /crear-evento/ (o la pestaña Eventos del Hub).
  2. Rellena título, descripción, ubicación, fechas, capacidad, ODS asociados, territorios.
  3. Marca si es presencial, online o mixto. En online, añade URL (p. ej. Zoom).
  4. (Opcional) Sube imagen destacada.
  5. Guarda como draft o envía a revisión (pending).
  6. Revisores ods_platform_manager o admins entran en Eventos → Revisión y aprueban/rechazan.
ℹ Editar tras publicar
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)

  1. Visitante o usuario logueado abre la página del evento.
  2. Pulsa Reservar → aparece un formulario inline.
  3. Introduce nombre, email, (opcional) número de asistentes si el evento acepta +1.
  4. Resuelve el CAPTCHA si no está logueado.
  5. El sistema genera:
    • ticket_code aleatorio de 16 caracteres hex.
    • hash = primeros 32 chars de HMAC-SHA256 sobre code|event_id con el secret del sitio.
  6. El visitante recibe un email con:
    • URL del ticket /mi-ticket/?c=<code>&h=<hash>.
    • Adjunto .ics para añadir al calendario.

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 code y hash.
  • 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

  1. 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.
  2. 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»).

⚠ Fallback si el escáner no carga
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.

  1. En el evento, admin/autor marca Certificados activados y personaliza el template (título, logo, firma).
  2. Tras el evento, clic en Emitir certificados o esperar al cron diario.
  3. Cada ticket con checked_in recibe email con URL pública /verificar-certificado/?c=<code>&h=<hash>.
  4. 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 Event JSON-LD con startDate, endDate, eventStatus, eventAttendanceMode, location (Place con coords o VirtualLocation), organizer, offers, keywords.
  • Meta description auto-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_mail funciona.
  • 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.

Saltar al contenido principal