300 lines
13 KiB
PHP
300 lines
13 KiB
PHP
<?php
|
||
|
||
/**
|
||
* Motore email del modulo Eventi (PHPMailer, legge le MAIL_* dal .env).
|
||
*
|
||
* Requisito una tantum: composer require phpmailer/phpmailer
|
||
*
|
||
* Builder per i 5 tipi:
|
||
* A) yse_mail_booking() prenotazione (confermata o lista d'attesa)
|
||
* B) yse_mail_user_cancel() disdetta fatta dall'utente
|
||
* C) yse_mail_event_cancel() evento annullato dall'insegnante (a ciascun iscritto)
|
||
* E) yse_mail_promotion() promozione da lista d'attesa (con RICONFERMA)
|
||
* D) yse_mail_reminder() promemoria prima dell'evento
|
||
*
|
||
* Le copie alla scuola (CC) sono decise per-tipo dalla pagina "Opzioni Eventi".
|
||
* L'indirizzo di copia è option.event_notify_email.
|
||
*/
|
||
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
if (!defined('YB_PUBLIC_BASE')) {
|
||
define('YB_PUBLIC_BASE', 'https://yogibook.yogasoul.it/');
|
||
}
|
||
|
||
/* ---------- 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);
|
||
}
|
||
}
|
||
|
||
$mail->isHTML(true);
|
||
$mail->Subject = $subject;
|
||
$mail->Body = $html;
|
||
$mail->AltBody = trim(preg_replace(
|
||
'/\n{3,}/',
|
||
"\n\n",
|
||
strip_tags(str_replace(['</p>', '<br>', '<br/>', '<br />', '</div>'], "\n", $html))
|
||
));
|
||
|
||
return $mail->send();
|
||
} catch (Throwable $e) {
|
||
error_log('[event mail] ' . $e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/* ---------- 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 = '<tr><td style="padding:6px 0;color:#8c837c;width:90px;">Quando</td><td style="padding:6px 0;color:#2b2622;font-weight:600;">' . $when . '</td></tr>';
|
||
if ($addr !== '') {
|
||
$mapsLink = $maps !== '' ? ' <a href="' . yse_h($maps) . '" style="color:' . $accent . ';">apri mappa</a>' : '';
|
||
$rows .= '<tr><td style="padding:6px 0;color:#8c837c;">Dove</td><td style="padding:6px 0;color:#2b2622;font-weight:600;">' . yse_h($addr) . $mapsLink . '</td></tr>';
|
||
}
|
||
|
||
$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 .= '<p style="margin:10px 0 0;"><strong style="color:#2b2622;">' . $f[1] . ':</strong><br>' . nl2br(yse_h($val)) . '</p>';
|
||
}
|
||
}
|
||
|
||
$html = '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="font-size:14px;border-collapse:collapse;margin:10px 0;">' . $rows . '</table>';
|
||
if ($info !== '') {
|
||
$html .= '<div style="background:#faf8f6;border-radius:10px;padding:12px 14px;font-size:14px;color:#4a433d;">' . $info . '</div>';
|
||
}
|
||
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 '
|
||
<div style="background:#f2efec;padding:24px 0;font-family:Arial,Helvetica,sans-serif;">
|
||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td align="center">
|
||
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#fff;border-radius:16px;overflow:hidden;">
|
||
<tr><td style="background:' . $accent . ';height:6px;"></td></tr>
|
||
<tr><td style="padding:28px 30px 6px;">
|
||
<div style="font-size:13px;letter-spacing:.05em;text-transform:uppercase;color:#8c837c;">' . yse_h($from) . '</div>
|
||
<h1 style="margin:6px 0 2px;font-size:22px;color:#2b2622;">' . $heading . '</h1>
|
||
<div style="font-size:17px;color:' . $accent . ';font-weight:600;">' . $title . '</div>
|
||
</td></tr>
|
||
<tr><td style="padding:10px 30px 26px;color:#4a433d;font-size:15px;line-height:1.6;">' . $bodyHtml . '</td></tr>
|
||
<tr><td style="padding:16px 30px;background:#faf8f6;color:#9ca3af;font-size:12px;">Questa email è stata inviata da ' . yse_h($from) . '.</td></tr>
|
||
</table>
|
||
</td></tr></table>
|
||
</div>';
|
||
}
|
||
|
||
function yse_button(string $url, string $label, string $accent): string
|
||
{
|
||
return '<a href="' . yse_h($url) . '" style="display:inline-block;background:' . $accent . ';color:#fff;text-decoration:none;font-weight:600;padding:12px 22px;border-radius:10px;">' . yse_h($label) . '</a>';
|
||
}
|
||
|
||
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 = '<p>Ciao ' . yse_h($name) . ", ti abbiamo messo in lista d'attesa per questo evento. Ti avviseremo appena si libera un posto.</p>";
|
||
} else {
|
||
$heading = 'Prenotazione confermata';
|
||
$intro = '<p>Ciao ' . yse_h($name) . ', la tua prenotazione è confermata. Ecco i dettagli:</p>';
|
||
}
|
||
|
||
$body = $intro;
|
||
$body .= yse_event_info_block($event);
|
||
$body .= '<p style="margin-top:8px;">Posti prenotati: <strong>' . (int) $booking['seats'] . '</strong></p>';
|
||
$body .= '<p style="margin:22px 0 6px;">Se non puoi più partecipare, puoi annullare qui:</p>';
|
||
$body .= '<p>' . yse_button(yse_cancel_url($booking), 'Annulla prenotazione', $accent) . '</p>';
|
||
|
||
$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 = '<p>Ciao ' . yse_h($name) . ', la tua prenotazione per questo evento è stata <strong>annullata</strong>.</p>';
|
||
$body .= yse_event_info_block($event);
|
||
$body .= '<p style="margin-top:14px;">Ci dispiace non vederti questa volta. A presto!</p>';
|
||
|
||
$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 = '<p>Ciao ' . yse_h($name) . ", ci dispiace: l'evento è stato <strong>annullato</strong>.</p>";
|
||
$body .= yse_event_info_block($event);
|
||
$body .= '<p style="margin-top:14px;">La tua prenotazione è stata annullata automaticamente. Per informazioni, rispondi pure a questa email.</p>';
|
||
|
||
$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 = '<p>Ciao ' . yse_h($name) . ", buone notizie: si è liberato un posto e tocca a te!</p>";
|
||
$body .= yse_event_info_block($event);
|
||
$body .= '<p style="margin:22px 0 6px;"><strong>Conferma entro breve</strong> per tenere il posto:</p>';
|
||
$body .= '<p>' . yse_button(yse_confirm_url($booking), 'Confermo, ci sarò', $accent) . '</p>';
|
||
$body .= '<p style="margin-top:16px;font-size:13px;color:#8c837c;">Se non puoi partecipare, <a href="' . yse_h(yse_cancel_url($booking)) . '" style="color:' . $accent . ';">annulla qui</a> così offriamo il posto a un\'altra persona.</p>';
|
||
|
||
$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 = '<p>Ciao ' . yse_h($name) . ',<br>' . $note . '</p>';
|
||
$body .= yse_event_info_block($event);
|
||
if ($meetingLink !== '') {
|
||
$body .= '<p style="margin:16px 0 6px;"><strong>Punto di ritrovo:</strong> <a href="' . yse_h($meetingLink) . '" style="color:' . $accent . ';">apri la posizione sulla mappa</a></p>';
|
||
}
|
||
$body .= '<p style="margin:22px 0 6px;">Se non puoi più partecipare, avvisaci annullando:</p>';
|
||
$body .= '<p>' . yse_button(yse_cancel_url($booking), 'Annulla prenotazione', $accent) . '</p>';
|
||
|
||
$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']);
|
||
}
|