Mailing_client, indes/tools, les deux fichiers lang
This commit is contained in:
1 parent
f852c0516a
commit
865de8a976
5 files changed
+513
-7
No files matched your search
@@ -49,6 +49,7 @@
|
||||
'afficher_messages' => __DIR__ . '/tools/afficher_messages.php',
|
||||
'new_inscrits' => __DIR__ . '/tools/new_clients.php',
|
||||
'mail_praticiens' => __DIR__ . '/tools/mailing_praticiens.php',
|
||||
'mailing_client' => __DIR__ . '/tools/mailing_client.php',
|
||||
'connecter_en_admin' => __DIR__ . '/tools/connecter_en_admin.php',
|
||||
'clean_uploads' => __DIR__ . '/tools/clean_uploads.php',
|
||||
];
|
||||
@@ -125,6 +126,9 @@
|
||||
<li onclick="location.href='<?php echo h(admin_url('mail_praticiens')) ?>'">
|
||||
<?php echo h($translations['mail_praticiens'] ?? 'Рассылка практикующим') ?>
|
||||
</li>
|
||||
<li onclick="location.href='<?php echo h(admin_url('mailing_client')) ?>'">
|
||||
<?php echo h($translations['mail_clients'] ?? 'Рассылка клиентам') ?>
|
||||
</li>
|
||||
|
||||
<li onclick="location.href='<?php echo h(admin_url('connecter_en_admin')) ?>'">
|
||||
<?php echo h($translations['connecter_en_admin'] ?? 'Войти от имени пользователя') ?>
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../public/includes/init_admin.php';
|
||||
|
||||
const ADMIN_PRACTICIEN_ID = 88888888;
|
||||
$me = (int) ($_SESSION['reference_id'] ?? 0);
|
||||
if ($me !== ADMIN_PRACTICIEN_ID) {
|
||||
http_response_code(403);
|
||||
echo '<div class="container py-5"><div class="alert alert-danger">'
|
||||
. htmlspecialchars($translations['non_autorise'] ?? 'Доступ запрещён.', ENT_QUOTES, 'UTF-8')
|
||||
. '</div></div>';
|
||||
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 = '<img src="https://uslugi.fr/public/uploads/img/logo_mail.png" width="150" alt="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 '<!DOCTYPE html><html lang="ru"><head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>' . e($subject) . '</title>
|
||||
</head>
|
||||
<body style="margin:0;padding:0;background:#f4f6f8;font-family:Arial,Helvetica,sans-serif;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f4f6f8;padding:24px 0;">
|
||||
<tr><td align="center">
|
||||
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="background:#fff;border-radius:8px;overflow:hidden;">
|
||||
<tr>
|
||||
<td style="background:#38378B;color:#fff;padding:20px 24px;font-size:18px;font-weight:600;">' . $brand . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:24px 24px 8px 24px;color:#111;font-size:16px;line-height:1.6;">' . $bodyHtml . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="padding:8px 24px 24px 24px;">
|
||||
<a href="' . e($ctaUrl) . '" target="_blank"
|
||||
style="display:inline-block;text-decoration:none;padding:12px 20px;border-radius:999px;background:#E03C3F;color:#fff;font-weight:600;">
|
||||
' . e($ctaLabel) . '
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="margin-top:12px;">
|
||||
<tr>
|
||||
<td style="text-align:center;color:#9ca3af;font-size:12px;padding:8px 0;">© ' . date('Y') . ' ' . $brandBas . ' — ' . e($thanks) . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
</body></html>';
|
||||
}
|
||||
|
||||
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);
|
||||
?>
|
||||
|
||||
<div class="shadow p-4 rounded bg-light">
|
||||
<h2 class="mb-3"><?php echo e($translations['mailing_clients_title'] ?? 'Массовая рассылка клиентам') ?></h2>
|
||||
|
||||
<?php if ($flash): ?>
|
||||
<div class="alert alert-<?php echo e($flash['type']) ?>"><?php echo e($flash['msg']) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" id="mailingForm">
|
||||
<input type="hidden" name="csrf" value="<?php echo e($CSRF) ?>">
|
||||
<input type="hidden" name="do" value="send_mailing">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?php echo e($translations['mailing_clients_subject_label'] ?? 'Тема письма') ?></label>
|
||||
<input type="text" name="subject" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label"><?php echo e($translations['mailing_clients_body_label'] ?? 'Текст письма') ?></label>
|
||||
<textarea id="mailBody" name="body" class="form-control" rows="8"></textarea>
|
||||
<div class="form-text">
|
||||
<?php echo e($translations['mailing_clients_body_help'] ?? 'Текст будет отправлен как HTML-письмо с шапкой USLUGI.FR и красной кнопкой «Открыть сайт».') ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<h4 class="mb-3"><?php echo e($translations['mailing_clients_list_title'] ?? 'Список клиентов') ?></h4>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label"><?php echo e($translations['mailing_clients_filter_email'] ?? 'Фильтр по email') ?></label>
|
||||
<select id="filter_email" class="form-select">
|
||||
<option value="all"><?php echo e($translations['mailing_clients_email_all'] ?? 'Все клиенты') ?></option>
|
||||
<option value="with_email"><?php echo e($translations['mailing_clients_email_with'] ?? 'С email') ?></option>
|
||||
<option value="without_email"><?php echo e($translations['mailing_clients_email_without'] ?? 'Без email') ?></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label"><?php echo e($translations['mailing_clients_filter_city'] ?? 'Фильтр по городу') ?></label>
|
||||
<select id="filter_city" class="form-select">
|
||||
<option value=""><?php echo e($translations['mailing_clients_city_all'] ?? 'Все города') ?></option>
|
||||
<?php foreach ($cityOptions as $city): ?>
|
||||
<option value="<?php echo e($city) ?>"><?php echo e($city) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label"><?php echo e($translations['mailing_clients_search_label'] ?? 'Поиск') ?></label>
|
||||
<input
|
||||
type="text"
|
||||
id="filter_search"
|
||||
class="form-control"
|
||||
placeholder="<?php echo e($translations['mailing_clients_search_ph'] ?? 'Имя, фамилия, email, телефон...') ?>"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="chk_all" class="form-check-input">
|
||||
<label for="chk_all" class="form-check-label">
|
||||
<?php echo e($translations['mailing_clients_select_all_visible'] ?? 'Выбрать всех в текущем списке') ?>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-danger" style="border-radius:999px;">
|
||||
<?php echo e($translations['mailing_clients_send_btn'] ?? 'Отправить письмо выбранным') ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover align-middle" id="tabClients">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width:40px;"></th>
|
||||
<th><?php echo e($translations['mailing_clients_th_prenom'] ?? 'Имя') ?></th>
|
||||
<th><?php echo e($translations['mailing_clients_th_nom'] ?? 'Фамилия') ?></th>
|
||||
<th><?php echo e($translations['mailing_clients_th_email'] ?? 'Email') ?></th>
|
||||
<th><?php echo e($translations['mailing_clients_th_phone'] ?? 'Телефон') ?></th>
|
||||
<th><?php echo e($translations['mailing_clients_th_city'] ?? 'Город') ?></th>
|
||||
<th><?php echo e($translations['mailing_clients_th_created'] ?? 'Дата регистрации') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($rows as $r):
|
||||
$email = trim((string) ($r['email'] ?? ''));
|
||||
$emailStatus = ($email !== '') ? 'with_email' : 'without_email';
|
||||
|
||||
$createdFmt = '';
|
||||
if (! empty($r['created_at'])) {
|
||||
try {
|
||||
$created = new DateTimeImmutable($r['created_at']);
|
||||
$createdFmt = $created->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'
|
||||
);
|
||||
?>
|
||||
<tr
|
||||
data-email-status="<?php echo e($emailStatus) ?>"
|
||||
data-city="<?php echo e($ville) ?>"
|
||||
data-hay="<?php echo e($hay) ?>"
|
||||
>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input row-chk"
|
||||
name="recipients[]"
|
||||
value="<?php echo (int)$r['id'] ?>"
|
||||
<?php echo $email === '' ? 'disabled' : '' ?>
|
||||
>
|
||||
</td>
|
||||
<td><?php echo e($r['prenom']) ?></td>
|
||||
<td><?php echo e($r['nom']) ?></td>
|
||||
<td><?php echo e($r['email']) ?></td>
|
||||
<td><?php echo e($r['telephone']) ?></td>
|
||||
<td><?php echo e($r['ville']) ?></td>
|
||||
<td><?php echo e($createdFmt) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (empty($rows)): ?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-muted text-center">
|
||||
<?php echo e($translations['mailing_clients_none'] ?? 'Нет клиентов.') ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- TinyMCE -->
|
||||
<script src="https://cdn.tiny.cloud/1/l9t0aozxqrwrd6cv3gbopqgt7bi299cjtwwxvyhylwa1kjey/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>
|
||||
<script>
|
||||
tinymce.init({
|
||||
selector: '#mailBody',
|
||||
menubar: false,
|
||||
height: 260,
|
||||
language: 'ru',
|
||||
plugins: 'link lists',
|
||||
toolbar: 'undo redo | bold italic underline | bullist numlist | link removeformat',
|
||||
branding: false
|
||||
});
|
||||
|
||||
// Filtres JS
|
||||
(function () {
|
||||
const emailSel = document.getElementById('filter_email');
|
||||
const citySel = document.getElementById('filter_city');
|
||||
const searchInp = document.getElementById('filter_search');
|
||||
const rows = Array.from(document.querySelectorAll('#tabClients tbody tr'));
|
||||
const chkAll = document.getElementById('chk_all');
|
||||
|
||||
function applyFilters() {
|
||||
const emailFilter = emailSel.value;
|
||||
const cityFilter = (citySel.value || '').trim().toLowerCase();
|
||||
const q = (searchInp.value || '').trim().toLowerCase();
|
||||
|
||||
rows.forEach(tr => {
|
||||
let ok = true;
|
||||
|
||||
const trEmailStatus = tr.dataset.emailStatus || '';
|
||||
const trCity = (tr.dataset.city || '').trim().toLowerCase();
|
||||
const hay = (tr.dataset.hay || '').toLowerCase();
|
||||
|
||||
if (emailFilter !== 'all' && trEmailStatus !== emailFilter) ok = false;
|
||||
if (cityFilter && trCity !== cityFilter) ok = false;
|
||||
if (q && !hay.includes(q)) ok = false;
|
||||
|
||||
tr.style.display = ok ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
emailSel.addEventListener('change', applyFilters);
|
||||
citySel.addEventListener('change', applyFilters);
|
||||
searchInp.addEventListener('input', applyFilters);
|
||||
|
||||
if (chkAll) {
|
||||
chkAll.addEventListener('change', function () {
|
||||
const checked = this.checked;
|
||||
|
||||
rows.forEach(tr => {
|
||||
if (tr.style.display === 'none') return;
|
||||
|
||||
const cb = tr.querySelector('.row-chk');
|
||||
if (cb && !cb.disabled) {
|
||||
cb.checked = checked;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@@ -2030,4 +2030,47 @@ Cliquez simplement sur le lien pour vous connecter.<br>',
|
||||
'tvalidat_success_msg' => 'Un nouveau lien de validation vient d’être envoyé à votre adresse email.',
|
||||
'tvalidat_err_lien_invalide' => 'Lien invalide.',
|
||||
'tvalidat_err_lien_expire' => 'Le lien de validation a expiré.',
|
||||
|
||||
'mailing_clients_brand_bas' => 'USLUGI.FR',
|
||||
'mailing_clients_cta_open_site' => 'Ouvrir le site',
|
||||
'mailing_clients_footer_thanks' => 'Merci',
|
||||
|
||||
'mailing_clients_title' => 'Mailing clients',
|
||||
'mailing_clients_subject_label' => 'Sujet du message',
|
||||
'mailing_clients_body_label' => 'Contenu du message',
|
||||
'mailing_clients_body_help' => 'Le texte sera envoyé comme email HTML avec l’en-tête USLUGI.FR et un bouton rouge « Ouvrir le site ».',
|
||||
|
||||
'mailing_clients_list_title' => 'Liste des clients',
|
||||
|
||||
'mailing_clients_filter_email' => 'Filtre par email',
|
||||
'mailing_clients_email_all' => 'Tous les clients',
|
||||
'mailing_clients_email_with' => 'Avec email',
|
||||
'mailing_clients_email_without' => 'Sans email',
|
||||
|
||||
'mailing_clients_filter_city' => 'Filtre par ville',
|
||||
'mailing_clients_city_all' => 'Toutes les villes',
|
||||
|
||||
'mailing_clients_search_label' => 'Recherche',
|
||||
'mailing_clients_search_ph' => 'Prénom, nom, email, téléphone...',
|
||||
|
||||
'mailing_clients_select_all_visible' => 'Sélectionner tous les clients visibles',
|
||||
'mailing_clients_send_btn' => 'Envoyer un message aux clients sélectionnés',
|
||||
|
||||
'mailing_clients_th_prenom' => 'Prénom',
|
||||
'mailing_clients_th_nom' => 'Nom',
|
||||
'mailing_clients_th_email' => 'Email',
|
||||
'mailing_clients_th_phone' => 'Téléphone',
|
||||
'mailing_clients_th_city' => 'Ville',
|
||||
'mailing_clients_th_created' => 'Date d’inscription',
|
||||
|
||||
'mailing_clients_none' => 'Aucun client.',
|
||||
|
||||
'mailing_clients_sent_count' => 'Messages envoyés : %d / %d',
|
||||
|
||||
'mailing_clients_err_send' => 'Erreur lors de l’envoi.',
|
||||
'mailing_clients_err_empty' => 'Le sujet et le contenu du message sont obligatoires.',
|
||||
'mailing_clients_err_no_recipients' => 'Sélectionnez au moins un client.',
|
||||
'mailing_clients_err_csrf' => 'Erreur de sécurité. Rechargez la page.',
|
||||
'mailing_clients_err_no_emails' => 'Les clients sélectionnés n’ont pas d’adresse email.',
|
||||
'mail_clients' => 'Mailing clients',
|
||||
];
|
||||
@@ -2040,4 +2040,47 @@ return [
|
||||
'tvalidat_success_msg' => 'Новая ссылка для подтверждения была отправлена на ваш email.',
|
||||
'tvalidat_err_lien_invalide' => 'Недействительная ссылка.',
|
||||
'tvalidat_err_lien_expire' => 'Срок действия ссылки подтверждения истёк.',
|
||||
|
||||
'mailing_clients_brand_bas' => 'USLUGI.FR',
|
||||
'mailing_clients_cta_open_site' => 'Открыть сайт',
|
||||
'mailing_clients_footer_thanks' => 'Спасибо',
|
||||
|
||||
'mailing_clients_title' => 'Маилинг клиентам',
|
||||
'mailing_clients_subject_label' => 'Тема письма',
|
||||
'mailing_clients_body_label' => 'Текст письма',
|
||||
'mailing_clients_body_help' => 'Текст будет отправлен как HTML-письмо с шапкой USLUGI.FR и красной кнопкой «Открыть сайт».',
|
||||
|
||||
'mailing_clients_list_title' => 'Список клиентов',
|
||||
|
||||
'mailing_clients_filter_email' => 'Фильтр по email',
|
||||
'mailing_clients_email_all' => 'Все клиенты',
|
||||
'mailing_clients_email_with' => 'С email',
|
||||
'mailing_clients_email_without' => 'Без email',
|
||||
|
||||
'mailing_clients_filter_city' => 'Фильтр по городу',
|
||||
'mailing_clients_city_all' => 'Все города',
|
||||
|
||||
'mailing_clients_search_label' => 'Поиск',
|
||||
'mailing_clients_search_ph' => 'Имя, фамилия, email, телефон...',
|
||||
|
||||
'mailing_clients_select_all_visible' => 'Выбрать всех в текущем списке',
|
||||
'mailing_clients_send_btn' => 'Отправить письмо выбранным клиентам',
|
||||
|
||||
'mailing_clients_th_prenom' => 'Имя',
|
||||
'mailing_clients_th_nom' => 'Фамилия',
|
||||
'mailing_clients_th_email' => 'Email',
|
||||
'mailing_clients_th_phone' => 'Телефон',
|
||||
'mailing_clients_th_city' => 'Город',
|
||||
'mailing_clients_th_created' => 'Дата регистрации',
|
||||
|
||||
'mailing_clients_none' => 'Нет клиентов.',
|
||||
|
||||
'mailing_clients_sent_count' => 'Отправлено писем: %d / %d',
|
||||
|
||||
'mailing_clients_err_send' => 'Ошибка при отправке.',
|
||||
'mailing_clients_err_empty' => 'Тема и текст письма обязательны.',
|
||||
'mailing_clients_err_no_recipients' => 'Выберите хотя бы одного клиента.',
|
||||
'mailing_clients_err_csrf' => 'Ошибка безопасности. Обновите страницу.',
|
||||
'mailing_clients_err_no_emails' => 'У выбранных клиентов нет email.',
|
||||
'mail_clients' => 'Маилинг клиентам',
|
||||
];
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
declare (strict_types = 1);
|
||||
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
@@ -12,23 +12,23 @@ ini_set('error_log', __DIR__ . '/webcron_error.log');
|
||||
// ===== sécurité =====
|
||||
const CRON_TOKEN = 'e3e6ba5785a447c3f763a4ca4b8c36480df63a19';
|
||||
|
||||
if (!hash_equals(CRON_TOKEN, $_GET['token'] ?? '')) {
|
||||
if (! hash_equals(CRON_TOKEN, $_GET['token'] ?? '')) {
|
||||
http_response_code(403);
|
||||
exit("forbidden\n");
|
||||
}
|
||||
|
||||
// ===== anti double exécution =====
|
||||
$lockFile = __DIR__ . '/cron.lock';
|
||||
$lock = fopen($lockFile, 'c');
|
||||
if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) {
|
||||
$lock = fopen($lockFile, 'c');
|
||||
if (! $lock || ! flock($lock, LOCK_EX | LOCK_NB)) {
|
||||
exit("already running\n");
|
||||
}
|
||||
|
||||
// ===== log minimal =====
|
||||
file_put_contents(__DIR__ . '/webcron.log', date('Y-m-d H:i:s') . " run\n", FILE_APPEND);
|
||||
file_put_contents(__DIR__ . '/webcron.log', date('Y-m-d H:i:s') . " before worker\n", FILE_APPEND);
|
||||
|
||||
// ===== lance ton worker =====
|
||||
require __DIR__ . '/worker.php';
|
||||
|
||||
echo "OK\n";
|
||||
file_put_contents(__DIR__ . '/webcron.log', date('Y-m-d H:i:s') . " after worker\n", FILE_APPEND);
|
||||
|
||||
echo "OK\n";
|
||||
Reference in new issue
Block a user