From 81a75f1f728df11084d6ae35d2a0552c150515a3 Mon Sep 17 00:00:00 2001 From: solocla Date: Mon, 5 Oct 2026 15:06:57 +0200 Subject: [PATCH] email certificati --- public/send_email.php | 98 +++++++++----- public/situationusers.php | 122 ++++++++++++++++-- .../schools/1/logo-3de387c60ffae02f.png | Bin 0 -> 132161 bytes 3 files changed, 178 insertions(+), 42 deletions(-) create mode 100644 public/uploads/schools/1/logo-3de387c60ffae02f.png diff --git a/public/send_email.php b/public/send_email.php index accb177..300c7e4 100644 --- a/public/send_email.php +++ b/public/send_email.php @@ -8,6 +8,7 @@ use PHPMailer\PHPMailer\Exception; // Imposta l'header per JSON header('Content-Type: application/json'); +ini_set('display_errors', '0'); // gli avvisi vanno nel log, non nella risposta // Funzione per generare il template HTML dell'email function generateEmailTemplate($message) @@ -40,7 +41,7 @@ function generateEmailTemplate($message) text-align: center; } .header img { - max-width: 50px; /* Ridotto da 150px a 100px per rimpicciolire il logo */ + max-width: 100px; /* Ridotto da 150px a 100px per rimpicciolire il logo */ } .content { padding: 30px; @@ -77,8 +78,8 @@ function generateEmailTemplate($message)
YogaSoul - My WordPress Blog + width="120" + style="display: block; width: 120px; max-width: 120px; height: auto; margin: 0 auto;">
@@ -95,6 +96,16 @@ function generateEmailTemplate($message) HTML; } +// Trasforma il testo del modulo in HTML: +// **testo** -> grassetto, link cliccabili, a capo ->
+function formatEmailBody(string $text): string +{ + $html = htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); + $html = preg_replace('/\*\*([^*\n]+?)\*\*/', '$1', $html); + $html = preg_replace('~(https?://[^\s<]+)~', '$1', $html); + return nl2br($html); +} + // Gestione della richiesta try { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { @@ -105,26 +116,15 @@ try { $body = isset($_POST['body']) ? trim($_POST['body']) : ''; $recipients = isset($_POST['recipients']) ? json_decode($_POST['recipients'], true) : []; - if (empty($subject) || empty($body) || empty($recipients)) { + if (empty($subject) || empty($body) || empty($recipients) || !is_array($recipients)) { throw new Exception('Dati mancanti.'); } - // Prepara tutti gli indirizzi email ricevuti - $to = array_column($recipients, 'email'); - - if (empty($to)) { - throw new Exception('Nessun destinatario valido trovato.'); - } - - - // Sostituisci i newline con
per l'HTML - $htmlBody = nl2br($body); - $emailContent = generateEmailTemplate($htmlBody); + // Invii multipli: più tempo a disposizione dello script + @set_time_limit(300); // Configura PHPMailer $mail = new PHPMailer(true); - - // Configurazione server SMTP con dati da .env $mail->isSMTP(); $mail->Host = $_ENV['MAIL_HOST']; $mail->SMTPAuth = true; @@ -132,29 +132,59 @@ try { $mail->Password = $_ENV['MAIL_PASSWORD']; $mail->SMTPSecure = $_ENV['MAIL_ENCRYPTION']; $mail->Port = (int) $_ENV['MAIL_PORT']; - $mail->Timeout = 5; // Timeout di 5 secondi - $mail->SMTPKeepAlive = false; // Disabilita la connessione persistente + $mail->Timeout = 5; + $mail->SMTPKeepAlive = true; // una sola connessione per tutti gli invii + $mail->CharSet = 'UTF-8'; // accenti ed emoji corretti - // Mittente $mail->setFrom($_ENV['MAIL_FROM_ADDRESS'], $_ENV['MAIL_FROM_NAME']); - - // Destinatari principali - foreach ($to as $recipient) { - $mail->addAddress($recipient); - } - - // Contenuto dell'email $mail->isHTML(true); $mail->Subject = $subject; - $mail->Body = $emailContent; - $mail->AltBody = strip_tags($emailContent); // Versione testuale dell'email - // Invia l'email - $mail->send(); + $sent = 0; + $failed = []; - echo json_encode(['success' => true, 'message' => 'Email inviata con successo.']); -} catch (Exception $e) { - // Log dell'errore completo + // Un'email per ogni destinatario: nessuno vede gli indirizzi degli altri + foreach ($recipients as $r) { + $email = trim($r['email'] ?? ''); + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $failed[] = $email !== '' ? $email : '(email vuota)'; + continue; + } + + $nome = trim((string) ($r['nome'] ?? '')); + if ($nome === '') { + $nome = 'allieva/o'; + } + + $personalText = str_replace('{nome}', $nome, $body); + + try { + $mail->clearAddresses(); + $mail->addAddress($email, $nome); + $mail->Body = generateEmailTemplate(formatEmailBody($personalText)); + $mail->AltBody = str_replace('**', '', $personalText); + $mail->send(); + $sent++; + } catch (Exception $e) { + $failed[] = $email; + error_log('Errore invio a ' . $email . ': ' . $mail->ErrorInfo); + $mail->getSMTPInstance()->reset(); + } + } + + $mail->smtpClose(); + + if ($sent === 0) { + throw new Exception('Nessuna email inviata.'); + } + + $message = 'Email inviate: ' . $sent; + if (!empty($failed)) { + $message .= '. Non inviate (' . count($failed) . '): ' . implode(', ', $failed); + } + + echo json_encode(['success' => true, 'message' => $message]); +} catch (\Throwable $e) { error_log('Errore in send_email.php: ' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine()); echo json_encode(['success' => false, 'message' => "Errore nell'invio dell'email: " . $e->getMessage()]); } diff --git a/public/situationusers.php b/public/situationusers.php index 415819b..cc86f03 100644 --- a/public/situationusers.php +++ b/public/situationusers.php @@ -21,6 +21,9 @@ SELECT auth_users.last_name AS cognome_utente, auth_users.email AS email_utente, COALESCE(orderbook_lessons.lezioni_acquistate, 0) AS lezioni_acquistate, + MAX(CASE WHEN active_orders.iduser IS NOT NULL THEN 1 ELSE 0 END) AS ha_ordine_attivo, + MAX(cert.ultima_scadenza) AS ultima_scadenza_cert, + MAX(CASE WHEN cert.ultima_scadenza IS NULL OR cert.ultima_scadenza < CURDATE() THEN 1 ELSE 0 END) AS certificato_scaduto, COUNT(CASE WHEN bookingclass.bookingstart <= CURDATE() AND bookingclass.lostlesson = 'N' THEN 1 END) AS lezioni_praticate, COUNT(CASE WHEN bookingclass.bookingstart > CURDATE() THEN 1 END) AS lezioni_programmate, COUNT(CASE WHEN bookingclass.lostlesson = 'Y' THEN 1 END) AS lezioni_perse, @@ -34,11 +37,23 @@ LEFT JOIN ( FROM orderbook GROUP BY iduser ) AS orderbook_lessons ON auth_users.id = orderbook_lessons.iduser +LEFT JOIN ( + SELECT DISTINCT iduser + FROM orderbook + WHERE expireon >= CURDATE() +) AS active_orders ON auth_users.id = active_orders.iduser +LEFT JOIN ( + SELECT iduser, MAX(expirydatedocument) AS ultima_scadenza + FROM certificateuserprofile + GROUP BY iduser +) AS cert ON auth_users.id = cert.iduser LEFT JOIN bookingclass ON auth_users.id = bookingclass.iduser GROUP BY auth_users.id, auth_users.last_name ORDER BY auth_users.last_name"; $users = $pdo->query($sql)->fetchAll(); +$activeUsersCount = count(array_filter($users, fn($u) => (int) $u['ha_ordine_attivo'] === 1)); +$expiredCertCount = count(array_filter($users, fn($u) => (int) $u['ha_ordine_attivo'] === 1 && (int) $u['certificato_scaduto'] === 1)); ?> @@ -147,6 +162,43 @@ $users = $pdo->query($sql)->fetchAll(); filter: brightness(0.93); } + .active-counter { + background: #cdfbc4; + color: #1a7f2e; + border-radius: 8px; + padding: 8px 14px; + font-size: 14px; + font-weight: 600; + } + + .btn-email-cert { + background: #dc3545; + } + + .cert-counter { + background: #fbc7c4; + color: #a12820; + } + + .cert-badge { + display: inline-block; + border-radius: 6px; + padding: 2px 8px; + font-size: 12px; + font-weight: 600; + white-space: nowrap; + } + + .cert-ok { + background: #cdfbc4; + color: #1a7f2e; + } + + .cert-scaduto { + background: #fbc7c4; + color: #a12820; + } + .lost-lesson { background-color: #ffcc80 !important; } @@ -271,7 +323,12 @@ $users = $pdo->query($sql)->fetchAll();
Statistiche utenti

Ciao , clicca sul nome di un utente per il dettaglio delle lezioni.

- +
+ + Utenti con abbonamento attivo: su + + Abbonati con certificato scaduto: +
@@ -285,6 +342,7 @@ $users = $pdo->query($sql)->fetchAll(); + @@ -305,11 +363,25 @@ $users = $pdo->query($sql)->fetchAll(); + @@ -378,6 +450,7 @@ $users = $pdo->query($sql)->fetchAll(); +
Scrivi {nome} per il nome del destinatario e **testo** per il grassetto.
@@ -428,6 +501,29 @@ $users = $pdo->query($sql)->fetchAll();
Program. Perse Da progr.Certificato Email
+ + + Mancante + + Data non valida + + Scaduto + + Valido fino al + +