' . htmlspecialchars($translations['non_autorise'] ?? 'Доступ запрещён.', ENT_QUOTES, 'UTF-8') . '
'; exit; } $mustReauth = function (int $ttl = 600) { $t = (int) ($_SESSION['admin_reauth_at'] ?? 0); return ($t === 0 || (time() - $t) > $ttl); }; if ($mustReauth()) { $qs = http_build_query([ 'page' => 'reauth', 'redir' => ($_SERVER['REQUEST_URI'] ?? 'index.php?page=maintenance'), ]); header('Location: ?' . $qs); exit; } if (! isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } $CSRF = $_SESSION['csrf_token']; function e($s) { return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8'); } // --------- Template HTML du mail --------- function buildAdminMailHtml(string $subject, string $bodyHtml): string { global $translations; $brand = 'USLUGI.FR'; $brandBas = $translations['mailing_clients_brand_bas'] ?? 'УСЛУГИ.ФР'; $ctaUrl = 'https://uslugi.fr/'; $ctaLabel = $translations['mailing_clients_cta_open_site'] ?? 'Открыть сайт'; $thanks = $translations['mailing_clients_footer_thanks'] ?? 'Спасибо'; return ' ' . e($subject) . '
' . $brand . '
' . $bodyHtml . '
' . e($ctaLabel) . '
© ' . date('Y') . ' ' . $brandBas . ' — ' . e($thanks) . '
'; } function sendClientMail(string $to, string $subject, string $html): bool { [$ok, $err] = uslugi_mail($to, $subject, $html, null, [ // 'from' => ['no-reply@uslugi.fr', 'USLUGI.FR'], // 'reply_to' => ['support@uslugi.fr', 'USLUGI.FR'], // 'headers' => ['X-USLUGI-Source' => 'admin_mailing_clients'], ]); if (! $ok && $err) { error_log('mailing_clients send error to ' . $to . ' : ' . $err); } return $ok; } // --------- Traitement POST --------- $flash = null; if (($_POST['do'] ?? '') === 'send_mailing') { try { if (! hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf'] ?? '')) { throw new Exception('CSRF'); } $subject = trim($_POST['subject'] ?? ''); $body = trim($_POST['body'] ?? ''); $ids = $_POST['recipients'] ?? []; if ($subject === '' || $body === '') { throw new Exception('EMPTY'); } if (! is_array($ids) || count($ids) === 0) { throw new Exception('NO_RECIPIENTS'); } $ids = array_values(array_unique(array_filter(array_map('intval', $ids), fn($v) => $v > 0))); if (! $ids) { throw new Exception('NO_RECIPIENTS'); } // Récupérer les emails clients $in = implode(',', array_fill(0, count($ids), '?')); $sql = "SELECT id, prenom, nom, email FROM client WHERE id IN ($in) AND email IS NOT NULL AND email <> ''"; $st = $pdo->prepare($sql); $st->execute($ids); $rowsMail = $st->fetchAll(PDO::FETCH_ASSOC); if (! $rowsMail) { throw new Exception('NO_EMAILS'); } $sent = 0; $total = count($rowsMail); foreach ($rowsMail as $r) { $email = trim($r['email'] ?? ''); if ($email === '') { continue; } $html = buildAdminMailHtml($subject, $body); if (sendClientMail($email, $subject, $html)) { $sent++; } // usleep(100000); } $flash = [ 'type' => 'success', 'msg' => sprintf( (string) ($translations['mailing_clients_sent_count'] ?? 'Отправлено писем: %d / %d'), $sent, $total ), ]; } catch (Throwable $e) { $code = $e->getMessage(); $msg = $translations['mailing_clients_err_send'] ?? 'Ошибка при отправке.'; if ($code === 'EMPTY') { $msg = $translations['mailing_clients_err_empty'] ?? 'Тема и текст письма обязательны.'; } if ($code === 'NO_RECIPIENTS') { $msg = $translations['mailing_clients_err_no_recipients'] ?? 'Выберите хотя бы одного клиента.'; } if ($code === 'CSRF') { $msg = $translations['mailing_clients_err_csrf'] ?? 'Ошибка безопасности. Обновите страницу.'; } if ($code === 'NO_EMAILS') { $msg = $translations['mailing_clients_err_no_emails'] ?? 'У выбранных клиентов нет email.'; } $flash = ['type' => 'danger', 'msg' => $msg]; } } // --------- Récupération des clients --------- $sql = " SELECT c.id, c.nom, c.prenom, c.email, c.telephone, c.ville, c.created_at, c.updated_at FROM client c ORDER BY c.nom ASC, c.prenom ASC "; $rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC); // Préparer options de filtre par ville $cityOptions = []; foreach ($rows as $r) { $ville = trim((string) ($r['ville'] ?? '')); if ($ville !== '') { $cityOptions[mb_strtolower($ville, 'UTF-8')] = $ville; } } natcasesort($cityOptions); ?>


format('Y-m-d H:i'); } catch (Throwable $e) { $createdFmt = ''; } } $ville = trim((string) ($r['ville'] ?? '')); $hay = mb_strtolower( trim( ($r['prenom'] ?? '') . ' ' . ($r['nom'] ?? '') . ' ' . ($r['email'] ?? '') . ' ' . ($r['telephone'] ?? '') . ' ' . ($r['ville'] ?? '') ), 'UTF-8' ); ?>
>