'
. 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 = '
';
$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) . '
| © ' . 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);
?>