Make drone serial number field mandatory in trip form

- Added required validation for bvs_number field in send_plan.php
- Updated form label to show asterisk (*) indicating required field
- Implemented file attachment support in email notifications (multipart MIME)
- Files are now temporarily saved and automatically deleted after email is sent
This commit is contained in:
Kirik
2025-11-20 00:41:48 +01:00
parent c6f909a042
commit ac334495b9
3 changed files with 151 additions and 9 deletions
+2 -2
View File
@@ -192,8 +192,8 @@ disableComments = true
</div>
<div class="form-group">
<label for="bvs_number"><strong>Учётный номер для БВС от 0,15 кг или серийный номер для БВС до 0,15 кг</strong></label>
<textarea id="bvs_number" name="bvs_number" placeholder="Укажите учётный номер для БВС от 0,15 кг или серийный номер для БВС до 0,15 кг"></textarea>
<label for="bvs_number"><strong>Учётный номер для БВС от 0,15 кг или серийный номер для БВС до 0,15 кг *</strong></label>
<textarea id="bvs_number" name="bvs_number" placeholder="Укажите учётный номер для БВС от 0,15 кг или серийный номер для БВС до 0,15 кг" required></textarea>
</div>
<div class="form-group">
+134 -4
View File
@@ -59,22 +59,32 @@ function get_forms_settings() {
/**
* Простое шифрование данных для безопасного хранения
* Совместимо с Python Crypto.Cipher.AES + unpad
*/
function encrypt_data($data, $key) {
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
$iv = random_bytes(16);
$encrypted = openssl_encrypt($json, 'AES-256-CBC', hash('sha256', $key), 0, $iv);
return base64_encode($iv . $encrypted);
// openssl_encrypt с параметром 0 сам добавляет PKCS7 padding и возвращает base64
$encrypted_b64 = openssl_encrypt($json, 'AES-256-CBC', hash('sha256', $key), 0, $iv);
// Декодируем base64 чтобы получить бинарные данные
$encrypted_raw = base64_decode($encrypted_b64);
// Добавляем IV в начало и кодируем все вместе
return base64_encode($iv . $encrypted_raw);
}
/**
* Расшифровка данных
* Совместимо с Python Crypto.Cipher.AES + unpad
*/
function decrypt_data($encrypted_data, $key) {
$data = base64_decode($encrypted_data);
$iv = substr($data, 0, 16);
$encrypted = substr($data, 16);
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', hash('sha256', $key), 0, $iv);
$encrypted_raw = substr($data, 16);
// Кодируем обратно в base64 для openssl_decrypt
$encrypted_b64 = base64_encode($encrypted_raw);
// openssl_decrypt с параметром 0 сам убирает PKCS7 padding
$decrypted = openssl_decrypt($encrypted_b64, 'AES-256-CBC', hash('sha256', $key), 0, $iv);
return json_decode($decrypted, true);
}
@@ -208,6 +218,126 @@ function send_telegram_notification($form_data, $form_type) {
}
}
/**
* Отправка уведомления на Email через msmtp с поддержкой файлов
*/
function send_email_notification($form_data, $form_type) {
$settings = get_forms_settings();
if (!$settings['send_email']) {
return true; // Отправка email отключена
}
// Получаем admin email из .env
$admin_email = getenv('ADMIN_EMAIL') ?: 'admin@example.com';
$from_email = getenv('SMTP_FROM_EMAIL') ?: getenv('SMTP_USERNAME') ?: 'noreply@yourdomain.com';
$from_name = getenv('SMTP_FROM_NAME') ?: 'PTP Robot';
// Формируем тему письма
$subject = ($form_type === 'plan')
? '🎫 Новая заявка на поездку - ' . $form_data['name']
: '❓ Новый вопрос - ' . ($form_data['subject'] ?? 'Без темы');
// Формируем тело письма
$message = "Новая заявка с сайта\n\n";
$message .= "Тип: " . ($form_type === 'plan' ? 'Заявка на поездку' : 'Вопрос') . "\n";
$message .= "Время: " . date('Y-m-d H:i:s') . "\n\n";
$message .= "=== ДАННЫЕ ЗАЯВИТЕЛЯ ===\n";
$message .= "Имя: " . $form_data['name'] . "\n";
$message .= "Email: " . $form_data['email'] . "\n";
if ($form_type === 'plan') {
$message .= "Телефон: " . ($form_data['phone'] ?: 'не указан') . "\n";
$message .= "Telegram: " . ($form_data['telegram'] ?: 'не указан') . "\n";
$message .= "Поездка: " . ($form_data['trip_period'] ?: 'не выбрана') . "\n";
$message .= "БВС/Направление: " . ($form_data['bvs_number'] ?: 'не указано') . "\n";
} else {
if (!empty($form_data['telegram'])) {
$message .= "Telegram: " . $form_data['telegram'] . "\n";
}
if (!empty($form_data['phone'])) {
$message .= "Телефон: " . $form_data['phone'] . "\n";
}
$message .= "Тема: " . $form_data['subject'] . "\n";
$message .= "\n=== СООБЩЕНИЕ ===\n";
$message .= $form_data['message'] . "\n";
}
// Проверяем наличие файла для отправки
$attachment_file = $form_data['bvs_file_path'] ?? null;
$has_attachment = $attachment_file && file_exists($attachment_file);
// Создаем boundary для multipart письма если есть файл
$boundary = "----=_Part_" . md5(time() . rand());
if ($has_attachment) {
// Заголовки письма с файлом (multipart)
$headers = "From: $from_name <$from_email>\r\n";
$headers .= "Reply-To: " . $form_data['email'] . "\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
// Формируем multipart тело письма
$multipart_message = "--$boundary\r\n";
$multipart_message .= "Content-Type: text/plain; charset=UTF-8\r\n";
$multipart_message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
$multipart_message .= $message . "\r\n";
// Добавляем файл
$file_content = file_get_contents($attachment_file);
$file_name = basename($attachment_file);
// Извлекаем оригинальное имя файла (после bvs_)
if (preg_match('/bvs_\w+_(.+)$/', $file_name, $matches)) {
$file_name = $matches[1];
}
$multipart_message .= "--$boundary\r\n";
$multipart_message .= "Content-Type: application/pdf; name=\"$file_name\"\r\n";
$multipart_message .= "Content-Transfer-Encoding: base64\r\n";
$multipart_message .= "Content-Disposition: attachment; filename=\"$file_name\"\r\n\r\n";
$multipart_message .= chunk_split(base64_encode($file_content)) . "\r\n";
$multipart_message .= "--$boundary--\r\n";
$email_message = $multipart_message;
} else {
// Заголовки письма без файла
$headers = "From: $from_name <$from_email>\r\n";
$headers .= "Reply-To: " . $form_data['email'] . "\r\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
$email_message = $message;
}
// Отправка письма
try {
error_log("=== Attempting mail() ===");
error_log("To: $admin_email | From: $from_email | Subject: $subject | Has attachment: " . ($has_attachment ? 'YES' : 'NO'));
$result = mail($admin_email, $subject, $email_message, $headers);
error_log("mail() returned: " . ($result ? 'TRUE' : 'FALSE'));
if ($result) {
error_log("Email отправлен успешно на: $admin_email");
// Удаляем временный файл после отправки
if ($has_attachment && file_exists($attachment_file)) {
unlink($attachment_file);
error_log("Временный файл удален: $attachment_file");
}
return true;
} else {
error_log("Ошибка отправки email на: $admin_email");
return false;
}
} catch (Exception $e) {
error_log("Исключение при отправке email: " . $e->getMessage());
return false;
}
}
/**
* Получение списка всех заявок (для Telegram бота)
*/
+15 -3
View File
@@ -16,8 +16,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$bvs_file = $_FILES['bvs_file'] ?? null;
// Валидация
if (empty($name) || empty($consent)) {
$error = "Обязательные поля: Фамилия, имя, Согласие на обработку данных.";
if (empty($name) || empty($consent) || empty($bvs_number)) {
$error = "Обязательные поля: Фамилия, имя, Учётный номер дрона, Согласие на обработку данных.";
} elseif (empty($phone)) {
$error = "Обязательное поле: Телефон.";
} elseif (empty($email) && empty($telegram)) {
@@ -38,6 +38,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if (!isset($error)) {
// Сохраняем загруженный файл во временную папку
$uploaded_file_path = null;
if ($bvs_file && $bvs_file['error'] === UPLOAD_ERR_OK) {
$upload_dir = sys_get_temp_dir() . '/ptp_forms_uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0700, true);
}
$uploaded_file_path = $upload_dir . uniqid('bvs_') . '_' . basename($bvs_file['name']);
move_uploaded_file($bvs_file['tmp_name'], $uploaded_file_path);
}
// Подготавливаем данные заявки
$form_data = [
'name' => $name,
@@ -47,7 +58,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'bvs_number' => $bvs_number,
'trip_period' => $trip_period,
'consent' => $consent,
'bvs_file' => $bvs_file ? $bvs_file['name'] : ''
'bvs_file' => $bvs_file ? $bvs_file['name'] : '',
'bvs_file_path' => $uploaded_file_path
];
$success_messages = [];