diff --git a/public/class/SchoolMail.php b/public/class/SchoolMail.php index 26c466d..ee1b07f 100644 --- a/public/class/SchoolMail.php +++ b/public/class/SchoolMail.php @@ -8,6 +8,7 @@ * SchoolMail::toStudent($email, $firstname, 'Oggetto', ['riga 1', 'riga 2']); * SchoolMail::toStudent($email, $firstname, 'Oggetto', $lines, '', $schoolRow, true); // copia alla scuola * SchoolMail::toMany([['email'=>..,'first_name'=>..], ...], 'Oggetto', $lines); // invio di massa + * SchoolMail::sendHtml($email, $nome, 'Oggetto', $htmlCompleto, $schoolRow, $ccEmail); // HTML già pronto * * Le righe sono HTML: i dati variabili vanno passati già con htmlspecialchars(). */ @@ -86,8 +87,48 @@ class SchoolMail return ['sent' => $sent, 'failed' => $failed]; } + /** + * Invio di un'email con HTML già composto (es. layout degli eventi). + * Funziona anche senza sessione se si passa $school (endpoint pubblici, cron). + * $cc: indirizzo in copia (ignorato se non valido o uguale al destinatario). + */ + public static function sendHtml( + string $to, + string $toName, + string $subject, + string $html, + ?array $school = null, + string $cc = '' + ): bool { + if (!filter_var($to, FILTER_VALIDATE_EMAIL)) { + return false; + } + try { + $school = $school ?? SchoolContext::get(); + $mail = self::mailer($school); + $mail->addAddress($to, $toName); + if ($cc !== '' && filter_var($cc, FILTER_VALIDATE_EMAIL) && strcasecmp($cc, $to) !== 0) { + $mail->addCC($cc, (string) ($school['name'] ?? '')); + } + $mail->Subject = $subject; + $mail->Body = $html; + $mail->AltBody = self::htmlToText($html); + $mail->send(); + return true; + } catch (\Throwable $e) { + error_log('SchoolMail sendHtml error (' . $to . '): ' . $e->getMessage()); + return false; + } + } + /* ------------------------------------------------------------------ */ + private static function htmlToText(string $html): string + { + $text = str_replace(['

', '
', '
', '
', '', ''], "\n", $html); + return trim(preg_replace('/\n{3,}/', "\n\n", html_entity_decode(strip_tags($text), ENT_QUOTES, 'UTF-8'))); + } + private static function mailer(array $school): PHPMailer { $schoolName = (string) ($school['name'] ?? 'YogiBook'); diff --git a/public/event_form.php b/public/event_form.php index 46ba911..c8d5fa2 100644 --- a/public/event_form.php +++ b/public/event_form.php @@ -1,17 +1,17 @@ nuovo evento - * - event_form.php?id=123 -> modifica evento esistente + * - event_form.php?id=123 -> modifica evento esistente (solo se della scuola) * Invia a event_save.php (insert/update + redirect). * - * Extra rispetto alla v1: * - flatpickr per data/ora (fine = inizio + 1h automatica) - * - sede da event_locations (default scuola) o indirizzo personalizzato + * - sede da event_locations della scuola (default) o indirizzo personalizzato * - "Calcola coordinate" da indirizzo (geocoding via event_geocode.php) - * - copertina drag&drop -> event_upload_cover.php -> assets/uploads/events/ + * - copertina drag&drop -> event_upload_cover.php */ $pdo = DBHandlerSelect::getInstance()->getConnection(); @@ -24,8 +24,8 @@ $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; $ev = null; if ($id > 0) { - $stmt = $pdo->prepare("SELECT * FROM events WHERE id = ?"); - $stmt->execute([$id]); + $stmt = $pdo->prepare("SELECT * FROM events WHERE id = ? AND idschool = ?"); + $stmt->execute([$id, $idschool]); $ev = $stmt->fetch(PDO::FETCH_ASSOC); if (!$ev) { header('Location: events.php?message=error'); @@ -33,19 +33,21 @@ if ($id > 0) { } } -// Default dal record unico di `option` -$opt = $pdo->query("SELECT * FROM `option` LIMIT 1")->fetch(PDO::FETCH_ASSOC); -$defaultColor = $opt['event_default_color'] ?? '#4CAF50'; -$defaultMaxPerBooking = (int) ($opt['event_default_max_per_booking'] ?? 1); -$defReminderEnabled = ($opt['event_reminder_enabled'] ?? 'N') === 'Y'; -$defReminderHours = (int) ($opt['event_reminder_hours'] ?? 24); -$defOfferHours = (int) ($opt['event_offer_expiry_hours'] ?? 24); +// Default dalle impostazioni della scuola +$defaultColor = (string) SchoolSettings::get('event_default_color', '#4CAF50'); +$defaultMaxPerBooking = (int) SchoolSettings::get('event_default_max_per_booking', 1); +$defReminderEnabled = SchoolSettings::get('event_reminder_enabled', 'N') === 'Y'; +$defReminderHours = (int) SchoolSettings::get('event_reminder_hours', 24); +$defOfferHours = (int) SchoolSettings::get('event_offer_expiry_hours', 24); -// Sedi salvate (se la tabella non esiste ancora, si degrada su indirizzo libero) +// Sedi salvate della scuola (se la query fallisce si degrada su indirizzo libero) $locations = []; try { - $locations = $pdo->query("SELECT * FROM event_locations ORDER BY is_default DESC, name ASC")->fetchAll(PDO::FETCH_ASSOC); -} catch (Throwable $e) { + $st = $pdo->prepare("SELECT * FROM event_locations WHERE idschool = ? ORDER BY is_default DESC, name ASC"); + $st->execute([$idschool]); + $locations = $st->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $ex) { + error_log('[event_form] sedi: ' . $ex->getMessage()); $locations = []; } diff --git a/public/event_save.php b/public/event_save.php index 7015048..23b653d 100644 --- a/public/event_save.php +++ b/public/event_save.php @@ -1,9 +1,11 @@ getConnection(); -// --- ID insegnante loggato ------------------------------------------------- -// headscript.php popola $iduserlogin da Auth::user()->present()->id -$currentUserId = $iduserlogin ?? ($_SESSION['iduserlogin'] ?? null); - // --- Helper ---------------------------------------------------------------- function slugify(string $text): string { @@ -32,18 +30,14 @@ function slugify(string $text): string return $text !== '' ? $text : 'evento'; } -function uniqueSlug(PDO $pdo, string $base, int $excludeId): string +/** Slug unico all'interno della scuola (esclude l'evento in modifica) */ +function uniqueSlug(PDO $pdo, string $base, int $excludeId, int $idschool): string { + $st = $pdo->prepare("SELECT COUNT(*) FROM events WHERE slug = ? AND idschool = ? AND id <> ?"); $slug = $base; $i = 2; while (true) { - if ($excludeId > 0) { - $st = $pdo->prepare("SELECT COUNT(*) FROM events WHERE slug = ? AND id <> ?"); - $st->execute([$slug, $excludeId]); - } else { - $st = $pdo->prepare("SELECT COUNT(*) FROM events WHERE slug = ?"); - $st->execute([$slug]); - } + $st->execute([$slug, $idschool, $excludeId]); if ((int) $st->fetchColumn() === 0) { return $slug; } @@ -63,9 +57,15 @@ function toDateTime(?string $v): ?string } // --- Lettura e validazione input ------------------------------------------- -$id = isset($_POST['id']) ? (int) $_POST['id'] : 0; -$title = trim($_POST['title'] ?? ''); +$id = isset($_POST['id']) ? (int) $_POST['id'] : 0; +// In modifica l'evento deve appartenere alla scuola corrente +if ($id > 0 && !SchoolScope::belongs('events', $id)) { + header('Location: events.php?message=error'); + exit(); +} + +$title = trim($_POST['title'] ?? ''); $startsAt = toDateTime($_POST['starts_at'] ?? null); $maxSeats = (int) ($_POST['max_seats'] ?? 0); @@ -77,7 +77,7 @@ if ($title === '' || $startsAt === null || $maxSeats < 1) { $slugInput = trim($_POST['slug'] ?? ''); $slugBase = slugify($slugInput !== '' ? $slugInput : $title); -$slug = uniqueSlug($pdo, $slugBase, $id); +$slug = uniqueSlug($pdo, $slugBase, $id, $idschool); $endsAt = toDateTime($_POST['ends_at'] ?? null); $address = trim($_POST['address'] ?? ''); @@ -121,7 +121,7 @@ $suitableFor = trim($_POST['suitable_for'] ?? ''); $waitlistEnabled = (isset($_POST['waitlist_enabled']) && $_POST['waitlist_enabled'] === 'Y') ? 'Y' : 'N'; -// Override per-evento (NULL = usa default scuola) +// Override per evento (NULL = usa default scuola) $reminderEnabled = (isset($_POST['reminder_enabled']) && in_array($_POST['reminder_enabled'], ['Y', 'N'], true)) ? $_POST['reminder_enabled'] : null; $reminderHours = (($_POST['reminder_hours'] ?? '') !== '') ? max(1, min(168, (int) $_POST['reminder_hours'])) : null; $offerHours = (($_POST['offer_expiry_hours'] ?? '') !== '') ? max(1, min(336, (int) $_POST['offer_expiry_hours'])) : null; @@ -154,52 +154,52 @@ try { waitlist_enabled = :waitlist_enabled, reminder_enabled = :reminder_enabled, reminder_hours = :reminder_hours, offer_expiry_hours = :offer_expiry_hours, cover_image = :cover_image, gallery = :gallery, status = :status, publish_at = :publish_at, updated_at = NOW() - WHERE id = :id"; - $params = [':id' => $id]; + WHERE id = :id AND idschool = :idschool"; + $params = [':id' => $id, ':idschool' => $idschool]; } else { $sql = "INSERT INTO events - (user_id, title, slug, description, instructions, what_to_bring, suitable_for, + (idschool, user_id, title, slug, description, instructions, what_to_bring, suitable_for, organizer_name, starts_at, ends_at, address, lat, lng, cost, currency, max_seats, max_per_booking, color, waitlist_enabled, reminder_enabled, reminder_hours, offer_expiry_hours, cover_image, gallery, status, publish_at, created_at, updated_at) VALUES - (:user_id, :title, :slug, :description, :instructions, :what_to_bring, :suitable_for, + (:idschool, :user_id, :title, :slug, :description, :instructions, :what_to_bring, :suitable_for, :organizer_name, :starts_at, :ends_at, :address, :lat, :lng, :cost, :currency, :max_seats, :max_per_booking, :color, :waitlist_enabled, :reminder_enabled, :reminder_hours, :offer_expiry_hours, :cover_image, :gallery, :status, :publish_at, NOW(), NOW())"; - $params = [':user_id' => $currentUserId]; + $params = [':idschool' => $idschool, ':user_id' => (int) $iduserlogin]; } $params += [ - ':title' => $title, - ':slug' => $slug, - ':description' => $description !== '' ? $description : null, - ':instructions' => $instructions !== '' ? $instructions : null, - ':what_to_bring' => $whatToBring !== '' ? $whatToBring : null, - ':suitable_for' => $suitableFor !== '' ? $suitableFor : null, - ':organizer_name' => $organizer !== '' ? $organizer : null, - ':starts_at' => $startsAt, - ':ends_at' => $endsAt, - ':address' => $address !== '' ? $address : null, - ':lat' => $lat, - ':lng' => $lng, - ':cost' => $cost, - ':currency' => $currency, - ':max_seats' => $maxSeats, - ':max_per_booking' => $maxPerBooking, - ':waitlist_enabled' => $waitlistEnabled, - ':reminder_enabled' => $reminderEnabled, - ':reminder_hours' => $reminderHours, + ':title' => $title, + ':slug' => $slug, + ':description' => $description !== '' ? $description : null, + ':instructions' => $instructions !== '' ? $instructions : null, + ':what_to_bring' => $whatToBring !== '' ? $whatToBring : null, + ':suitable_for' => $suitableFor !== '' ? $suitableFor : null, + ':organizer_name' => $organizer !== '' ? $organizer : null, + ':starts_at' => $startsAt, + ':ends_at' => $endsAt, + ':address' => $address !== '' ? $address : null, + ':lat' => $lat, + ':lng' => $lng, + ':cost' => $cost, + ':currency' => $currency, + ':max_seats' => $maxSeats, + ':max_per_booking' => $maxPerBooking, + ':waitlist_enabled' => $waitlistEnabled, + ':reminder_enabled' => $reminderEnabled, + ':reminder_hours' => $reminderHours, ':offer_expiry_hours' => $offerHours, - ':color' => $color, - ':cover_image' => $coverImage !== '' ? $coverImage : null, - ':gallery' => $gallery, - ':publish_at' => $publishAt, - ':status' => $status, + ':color' => $color, + ':cover_image' => $coverImage !== '' ? $coverImage : null, + ':gallery' => $gallery, + ':publish_at' => $publishAt, + ':status' => $status, ]; $stmt = $pdo->prepare($sql); @@ -207,7 +207,7 @@ try { header('Location: events.php?message=success'); exit(); -} catch (Throwable $ex) { +} catch (PDOException $ex) { error_log('[event_save] ' . $ex->getMessage()); header("Location: {$redirectOnError}"); exit(); diff --git a/public/events.php b/public/events.php index a3a23d5..4b76f8b 100644 --- a/public/events.php +++ b/public/events.php @@ -1,8 +1,9 @@ event_participants.php * - Modifica -> event_form.php?id= @@ -31,18 +32,23 @@ $sql = "SELECT e.*, SUM(seats) AS booked, COUNT(*) AS bookings_count FROM event_bookings - WHERE status IN ('confirmed','pending','offered') + WHERE idschool = ? + AND status IN ('confirmed','pending','offered') GROUP BY event_id ) b ON b.event_id = e.id LEFT JOIN ( SELECT event_id, SUM(seats) AS waiting FROM event_bookings - WHERE status = 'waiting' + WHERE idschool = ? + AND status = 'waiting' GROUP BY event_id ) w ON w.event_id = e.id LEFT JOIN auth_users u ON u.id = e.user_id + WHERE e.idschool = ? ORDER BY e.starts_at ASC"; -$events = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC); +$st = $pdo->prepare($sql); +$st->execute([$idschool, $idschool, $idschool]); +$events = $st->fetchAll(PDO::FETCH_ASSOC); $mesi = [ 1 => 'gennaio', @@ -87,7 +93,7 @@ function statusBadge(string $status): array YogiBook - Eventi - + diff --git a/public/include/event_mailer.php b/public/include/event_mailer.php index 40d3368..ba589bd 100644 --- a/public/include/event_mailer.php +++ b/public/include/event_mailer.php @@ -1,299 +1,71 @@ le prenotazioni promosse (per inviare le email) */ +if (!function_exists('promoteFromWaitlist')) { + function promoteFromWaitlist(PDO $pdo, int $eventId): array + { + $ev = $pdo->prepare("SELECT max_seats, idschool FROM events WHERE id = ?"); + $ev->execute([$eventId]); + $event = $ev->fetch(PDO::FETCH_ASSOC); + if (!$event) { + return []; + } + $maxSeats = (int) $event['max_seats']; + $idschool = (int) $event['idschool']; + if ($maxSeats <= 0 || $idschool <= 0) { + return []; + } -// Assicura l'autoload (di norma già caricato da db-functions.php) -if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - $__al = dirname(__DIR__, 2) . '/vendor/autoload.php'; - if (is_file($__al)) { - require_once $__al; - } -} + $os = $pdo->prepare("SELECT COALESCE(SUM(seats),0) FROM event_bookings + WHERE event_id = ? AND idschool = ? + AND status IN ('confirmed','pending','offered')"); + $os->execute([$eventId, $idschool]); + $free = $maxSeats - (int) $os->fetchColumn(); + if ($free <= 0) { + return []; + } -if (!defined('YB_PUBLIC_BASE')) { - define('YB_PUBLIC_BASE', 'https://yogibook.yogasoul.it/'); -} + $ws = $pdo->prepare("SELECT * FROM event_bookings + WHERE event_id = ? AND idschool = ? AND status = 'waiting' + ORDER BY created_at ASC, id ASC"); + $ws->execute([$eventId, $idschool]); + $waiting = $ws->fetchAll(PDO::FETCH_ASSOC); -/* ---------- Opzioni ---------- */ -function yse_mail_options(): array -{ - static $opt = null; - if ($opt !== null) { - return $opt; - } - $row = []; - try { - $pdo = DBHandlerSelect::getInstance()->getConnection(); - $row = $pdo->query("SELECT * FROM `option` LIMIT 1")->fetch(PDO::FETCH_ASSOC) ?: []; - } catch (Throwable $e) { - $row = []; - } - $opt = [ - 'cc_email' => trim((string) ($row['event_notify_email'] ?? '')), - 'cc' => [ - 'booking' => ($row['event_cc_booking'] ?? 'N') === 'Y', - 'user_cancel' => ($row['event_cc_user_cancel'] ?? 'N') === 'Y', - 'event_cancel' => ($row['event_cc_event_cancel'] ?? 'N') === 'Y', - 'reminder' => ($row['event_cc_reminder'] ?? 'N') === 'Y', - 'promotion' => ($row['event_cc_promotion'] ?? 'N') === 'Y', - ], - ]; - return $opt; -} - -/* ---------- Invio base ---------- */ -function yse_send(string $to, string $toName, string $subject, string $html, bool $ccSchool): bool -{ - $env = $_ENV; - try { - $mail = new \PHPMailer\PHPMailer\PHPMailer(true); - $mail->isSMTP(); - $mail->Host = $env['MAIL_HOST'] ?? ''; - $mail->SMTPAuth = true; - $mail->Username = $env['MAIL_USERNAME'] ?? ''; - $mail->Password = $env['MAIL_PASSWORD'] ?? ''; - $enc = strtolower($env['MAIL_ENCRYPTION'] ?? 'ssl'); - $mail->SMTPSecure = ($enc === 'tls') - ? \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS - : \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS; - $mail->Port = (int) ($env['MAIL_PORT'] ?? 465); - $mail->CharSet = 'UTF-8'; - - $mail->setFrom($env['MAIL_FROM_ADDRESS'] ?? 'info@yogasoul.it', $env['MAIL_FROM_NAME'] ?? 'YogiBook'); - $mail->addAddress($to, $toName); - - if ($ccSchool) { - $ccEmail = yse_mail_options()['cc_email']; - if ($ccEmail !== '' && strcasecmp($ccEmail, $to) !== 0) { - $mail->addCC($ccEmail); + $promoted = []; + // Promozione = "offerta": tiene il posto ma richiede riconferma (confirm.php) + $upd = $pdo->prepare("UPDATE event_bookings + SET status = 'offered', offered_at = NOW(), updated_at = NOW() + WHERE id = ? AND idschool = ?"); + foreach ($waiting as $w) { + $need = (int) $w['seats']; + if ($need <= $free) { + $upd->execute([$w['id'], $idschool]); + $free -= $need; + $w['status'] = 'offered'; + $promoted[] = $w; + if ($free <= 0) { + break; + } + } else { + break; // rispetta la coda } } - - $mail->isHTML(true); - $mail->Subject = $subject; - $mail->Body = $html; - $mail->AltBody = trim(preg_replace( - '/\n{3,}/', - "\n\n", - strip_tags(str_replace(['

', '
', '
', '
', ''], "\n", $html)) - )); - - return $mail->send(); - } catch (Throwable $e) { - error_log('[event mail] ' . $e->getMessage()); - return false; + return $promoted; } } - -/* ---------- Helper contenuto ---------- */ -function yse_h($v): string -{ - return htmlspecialchars((string) $v, ENT_QUOTES, 'UTF-8'); -} - -function yse_fmt_datetime(array $event): string -{ - $mesi = [ - 1 => 'gennaio', - 2 => 'febbraio', - 3 => 'marzo', - 4 => 'aprile', - 5 => 'maggio', - 6 => 'giugno', - 7 => 'luglio', - 8 => 'agosto', - 9 => 'settembre', - 10 => 'ottobre', - 11 => 'novembre', - 12 => 'dicembre' - ]; - $t = strtotime($event['starts_at']); - $s = (int) date('j', $t) . ' ' . $mesi[(int) date('n', $t)] . ' ' . date('Y', $t) . ' · ' . date('H:i', $t); - if (!empty($event['ends_at'])) { - $s .= '–' . date('H:i', strtotime($event['ends_at'])); - } - return $s; -} - -function yse_maps_link(array $event): string -{ - $q = trim((string) ($event['address'] ?? '')); - if ($q === '' && !empty($event['lat']) && !empty($event['lng'])) { - $q = $event['lat'] . ',' . $event['lng']; - } - return $q !== '' ? 'https://www.google.com/maps/search/?api=1&query=' . rawurlencode($q) : ''; -} - -function yse_event_info_block(array $event): string -{ - $accent = (isset($event['color']) && preg_match('/^#[0-9a-fA-F]{6}$/', $event['color'])) ? $event['color'] : '#4CAF50'; - $when = yse_h(yse_fmt_datetime($event)); - $addr = trim((string) ($event['address'] ?? '')); - $maps = yse_maps_link($event); - - $rows = 'Quando' . $when . ''; - if ($addr !== '') { - $mapsLink = $maps !== '' ? '  apri mappa' : ''; - $rows .= 'Dove' . yse_h($addr) . $mapsLink . ''; - } - - $info = ''; - foreach ([['suitable_for', 'Per chi è adatto'], ['what_to_bring', 'Cosa portare'], ['instructions', 'Istruzioni']] as $f) { - $val = trim((string) ($event[$f[0]] ?? '')); - if ($val !== '') { - $info .= '

' . $f[1] . ':
' . nl2br(yse_h($val)) . '

'; - } - } - - $html = '' . $rows . '
'; - if ($info !== '') { - $html .= '
' . $info . '
'; - } - return $html; -} - -function yse_email_layout(array $event, string $heading, string $bodyHtml): string -{ - $accent = (isset($event['color']) && preg_match('/^#[0-9a-fA-F]{6}$/', $event['color'])) ? $event['color'] : '#4CAF50'; - $from = $_ENV['MAIL_FROM_NAME'] ?? 'YogaSoul'; - $title = yse_h($event['title'] ?? ''); - return ' -
-
- - - - - -
-
' . yse_h($from) . '
-

' . $heading . '

-
' . $title . '
-
' . $bodyHtml . '
Questa email è stata inviata da ' . yse_h($from) . '.
-
-
'; -} - -function yse_button(string $url, string $label, string $accent): string -{ - return '' . yse_h($label) . ''; -} - -function yse_cancel_url(array $booking): string -{ - return YB_PUBLIC_BASE . 'apiwp/cancel.php?token=' . rawurlencode($booking['token']); -} - -function yse_confirm_url(array $booking): string -{ - return YB_PUBLIC_BASE . 'apiwp/confirm.php?token=' . rawurlencode($booking['token']); -} - -/* ---------- Builder per tipo ---------- */ - -/** A) Prenotazione (confermata o lista d'attesa) */ -function yse_mail_booking(array $booking, array $event): bool -{ - $accent = $event['color'] ?? '#4CAF50'; - $waiting = ($booking['status'] ?? '') === 'waiting'; - $parts = explode(' ', trim($booking['name'])); - $name = $parts[0] ?? ''; - - if ($waiting) { - $heading = "Sei in lista d'attesa"; - $intro = '

Ciao ' . yse_h($name) . ", ti abbiamo messo in lista d'attesa per questo evento. Ti avviseremo appena si libera un posto.

"; - } else { - $heading = 'Prenotazione confermata'; - $intro = '

Ciao ' . yse_h($name) . ', la tua prenotazione è confermata. Ecco i dettagli:

'; - } - - $body = $intro; - $body .= yse_event_info_block($event); - $body .= '

Posti prenotati: ' . (int) $booking['seats'] . '

'; - $body .= '

Se non puoi più partecipare, puoi annullare qui:

'; - $body .= '

' . yse_button(yse_cancel_url($booking), 'Annulla prenotazione', $accent) . '

'; - - $subject = ($waiting ? "Lista d'attesa: " : 'Prenotazione confermata: ') . $event['title']; - $html = yse_email_layout($event, $heading, $body); - return yse_send($booking['email'], $booking['name'], $subject, $html, yse_mail_options()['cc']['booking']); -} - -/** B) Disdetta fatta dall'utente */ -function yse_mail_user_cancel(array $booking, array $event): bool -{ - $parts = explode(' ', trim($booking['name'])); - $name = $parts[0] ?? ''; - $body = '

Ciao ' . yse_h($name) . ', la tua prenotazione per questo evento è stata annullata.

'; - $body .= yse_event_info_block($event); - $body .= '

Ci dispiace non vederti questa volta. A presto!

'; - - $subject = 'Prenotazione annullata: ' . $event['title']; - $html = yse_email_layout($event, 'Prenotazione annullata', $body); - return yse_send($booking['email'], $booking['name'], $subject, $html, yse_mail_options()['cc']['user_cancel']); -} - -/** C) Evento annullato dall'insegnante (a ciascun iscritto) */ -function yse_mail_event_cancel(array $booking, array $event): bool -{ - $parts = explode(' ', trim($booking['name'])); - $name = $parts[0] ?? ''; - $body = '

Ciao ' . yse_h($name) . ", ci dispiace: l'evento è stato annullato.

"; - $body .= yse_event_info_block($event); - $body .= '

La tua prenotazione è stata annullata automaticamente. Per informazioni, rispondi pure a questa email.

'; - - $subject = 'Evento annullato: ' . $event['title']; - $html = yse_email_layout($event, 'Evento annullato', $body); - return yse_send($booking['email'], $booking['name'], $subject, $html, yse_mail_options()['cc']['event_cancel']); -} - -/** E) Promozione da lista d'attesa — con RICONFERMA */ -function yse_mail_promotion(array $booking, array $event): bool -{ - $accent = $event['color'] ?? '#4CAF50'; - $parts = explode(' ', trim($booking['name'])); - $name = $parts[0] ?? ''; - $body = '

Ciao ' . yse_h($name) . ", buone notizie: si è liberato un posto e tocca a te!

"; - $body .= yse_event_info_block($event); - $body .= '

Conferma entro breve per tenere il posto:

'; - $body .= '

' . yse_button(yse_confirm_url($booking), 'Confermo, ci sarò', $accent) . '

'; - $body .= '

Se non puoi partecipare, annulla qui così offriamo il posto a un\'altra persona.

'; - - $subject = 'Si è liberato un posto: ' . $event['title']; - $html = yse_email_layout($event, 'Un posto per te', $body); - return yse_send($booking['email'], $booking['name'], $subject, $html, yse_mail_options()['cc']['promotion']); -} - -/** D) Promemoria prima dell'evento (testo e punto di ritrovo opzionali) */ -function yse_mail_reminder(array $booking, array $event, string $message = '', string $meetingLink = ''): bool -{ - $accent = $event['color'] ?? '#4CAF50'; - $parts = explode(' ', trim($booking['name'])); - $name = $parts[0] ?? ''; - $note = $message !== '' ? nl2br(yse_h($message)) : 'Ti ricordiamo il tuo prossimo appuntamento.'; - - $body = '

Ciao ' . yse_h($name) . ',
' . $note . '

'; - $body .= yse_event_info_block($event); - if ($meetingLink !== '') { - $body .= '

Punto di ritrovo: apri la posizione sulla mappa

'; - } - $body .= '

Se non puoi più partecipare, avvisaci annullando:

'; - $body .= '

' . yse_button(yse_cancel_url($booking), 'Annulla prenotazione', $accent) . '

'; - - $subject = 'Promemoria: ' . $event['title']; - $html = yse_email_layout($event, 'Ci vediamo presto', $body); - return yse_send($booking['email'], $booking['name'], $subject, $html, yse_mail_options()['cc']['reminder']); -}