Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
itchief committed Mar 12, 2022
1 parent 42bfc7e commit 74007fd
Show file tree
Hide file tree
Showing 74 changed files with 806 additions and 207 deletions.
274 changes: 274 additions & 0 deletions feedback/form-processing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
<?php

/*
* Форма обратной связи (https://itchief.ru/lessons/php/feedback-form-for-website)
* Copyright 2016-2022 Alexander Maltsev
* Licensed under MIT (https://github.com/itchief/feedback-form/blob/master/LICENSE)
*/

header('Content-Type: application/json');

// обработка только ajax запросов (при других запросах завершаем выполнение скрипта)
if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {
exit();
}

// обработка данных, посланных только методом POST (при остальных методах завершаем выполнение скрипта)
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit();
}

// имя файла для хранения логов
define('LOG_FILE', 'logs/' . date('Y-m-d') . '.log');
// писать предупреждения и ошибки в лог
define('HAS_WRITE_LOG', true);
// проверять ли капчу
define('HAS_CHECK_CAPTCHA', true);
// обязательно ли наличие файлов, прикреплённых к форме
define('HAS_ATTACH_REQUIRED', true);
// разрешённые mime типы файлов
define('ALLOWED_MIME_TYPES', ['image/jpeg', 'image/gif', 'image/png']);
// максимально-допустимый размер файла
define('MAX_FILE_SIZE', 512 * 1024);
// директория для хранения файлов
define('UPLOAD_PATH', dirname(__FILE__) . '/uploads/');

// отправлять письмо
define('HAS_SEND_EMAIL', true);
// добавить ли прикреплённые файлы в тело письма в виде ссылок
define('HAS_ATTACH_IN_BODY', true);
define('EMAIL_SETTINGS', [
'addresses' => ['manager@domain.com'], // кому необходимо отправить письмо
'from' => ['no-reply@domain.com', 'Имя сайта'], // от какого email и имени необходимо отправить письмо
'subject' => 'Сообщение с формы обратной связи', // тема письма
'host' => 'ssl://smtp.yandex.ru', // SMTP-хост
'username' => 'name@yandex.ru', // // SMTP-пользователь
'password' => '*********', // SMTP-пароль
'port' => '465' // SMTP-порт
]);
define('HAS_SEND_NOTIFICATION', false);
define('BASE_URL', 'https://domain.com');
define('SUBJECT_FOR_CLIENT', 'Ваше сообщение доставлено');
//
define('HAS_WRITE_TXT', true);

function itc_log($message)
{
if (HAS_WRITE_LOG) {
error_log('Date: ' . date('d.m.Y h:i:s') . ' | ' . $message . PHP_EOL, 3, LOG_FILE);
}
}

$data = [
'errors' => [],
'form' => [],
'logs' => [],
'result' => 'success'
];

$attachs = [];

/* 4 ЭТАП - ВАЛИДАЦИЯ ДАННЫХ (ЗНАЧЕНИЙ ПОЛЕЙ ФОРМЫ) */

// валидация name
if (!empty($_POST['name'])) {
$data['form']['name'] = htmlspecialchars($_POST['name']);
} else {
$data['result'] = 'error';
$data['errors']['name'] = 'Заполните это поле.';
itc_log('Не заполнено поле name.');
}

// валидация email
if (!empty($_POST['email'])) {
$data['form']['email'] = $_POST['email'];
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$data['result'] = 'error';
$data['errors']['email'] = 'Email не корректный.';
itc_log('Email не корректный.');
}
} else {
$data['result'] = 'error';
$data['errors']['email'] = 'Заполните это поле.';
itc_log('Не заполнено поле email.');
}

// валидация message
if (!empty($_POST['message'])) {
$data['form']['message'] = htmlspecialchars($_POST['message']);
if (mb_strlen($data['form']['message'], 'UTF-8') < 20) {
$data['result'] = 'error';
$data['errors']['message'] = 'Это поле должно быть не меньше 20 cимволов.';
itc_log('Поле message должно быть не меньше 20 cимволов.');
}
} else {
$data['result'] = 'error';
$data['errors']['message'] = 'Заполните это поле.';
itc_log('Не заполнено поле message.');
}

// проверка капчи
if (HAS_CHECK_CAPTCHA) {
session_start();
if ($_POST['captcha'] === $_SESSION['captcha']) {
$data['form']['captcha'] = $_POST['captcha'];
} else {
$data['result'] = 'error';
$data['errors']['captcha'] = 'Код не соответствует изображению.';
itc_log('Не пройдена капча. Указанный код ' . $captcha . ' не соответствует ' . $_SESSION['captcha']);
}
}

// валидация agree
if ($_POST['agree'] == 'true') {
$data['form']['agree'] = true;
} else {
$data['result'] = 'error';
$data['errors']['agree'] = 'Необходимо установить этот флажок.';
itc_log('Не установлен флажок для поля agree.');
}

// валидация прикреплённых файлов
if (empty($_FILES['attach'])) {
if (HAS_ATTACH_REQUIRED) {
$data['result'] = 'error';
$data['errors']['attach'] = 'Заполните это поле.';
itc_log('Не прикреплены файлы к форме.');
}
} else {
foreach ($_FILES['attach']['error'] as $key => $error) {
if ($error == UPLOAD_ERR_OK) {
$name = basename($_FILES['attach']['name'][$key]);
$size = $_FILES['attach']['size'][$key];
$mtype = mime_content_type($_FILES['attach']['tmp_name'][$key]);
if (!in_array($mtype, ALLOWED_MIME_TYPES)) {
$data['result'] = 'error';
$data['errors']['attach'][$key] = 'Файл имеет не разрешённый тип.';
itc_log('Прикреплённый файл ' . $name . ' имеет не разрешённый тип.');
} else if ($size > MAX_FILE_SIZE) {
$data['result'] = 'error';
$data['errors']['attach'][$key] = 'Размер файла превышает допустимый.';
itc_log('Размер файла ' . $name . ' превышает допустимый.');
}
}
}
if ($data['result'] === 'success') {
// перемещаем файлы в папку UPLOAD_PATH
foreach ($_FILES['attach']['name'] as $key => $attach) {
$ext = mb_strtolower(pathinfo($_FILES['attach']['name'][$key], PATHINFO_EXTENSION));
$name = basename($_FILES['attach']['name'][$key], $ext);
$tmp = $_FILES['attach']['tmp_name'][$key];
$newName = rtrim($name, '.') . '_' . uniqid() . '.' . $ext;
if (!move_uploaded_file($tmp, UPLOAD_PATH . $newName)) {
$data['result'] = 'error';
$data['errors']['attach'][$key] = 'Ошибка при загрузке файла.';
itc_log('Ошибка при перемещении файла ' . $name . '.');
} else {
$attachs[] = UPLOAD_PATH . $newName;
}
}
}
}

use PHPMailer\PHPMailer\PHPMailer;
//use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

require 'vendor/phpmailer/phpmailer/src/Exception.php';
require 'vendor/phpmailer/phpmailer/src/PHPMailer.php';
require 'vendor/phpmailer/phpmailer/src/SMTP.php';

if ($data['result'] == 'success' && HAS_SEND_EMAIL == true) {
// получаем содержимое email шаблона и заменяем в нём
$template = file_get_contents(dirname(__FILE__) . '/template/email.tpl');
$search = ['%subject%', '%name%', '%email%', '%message%', '%date%'];
$replace = [EMAIL_SETTINGS['subject'], $data['form']['name'], $data['form']['email'], $data['form']['message'], date('d.m.Y H:i')];
$body = str_replace($search, $replace, $template);
// добавление файлов в виде ссылок
if (HAS_ATTACH_IN_BODY && count($attachs)) {
$ul = 'Файлы, прикреплённые к форме:<ul>';
foreach ($attachs as $attach) {
$href = str_replace($_SERVER['DOCUMENT_ROOT'], '', $attach);
$name = basename($href);
$ul .= '<li><a href="' . BASE_URL . $href . '">' . $name . '</a></li>';

$data['href'][] = BASE_URL . $href;
}
$ul .= '</ul>';
$body = str_replace('%attachs%', $ul, $body);
} else {
$body = str_replace('%attachs%', '', $body);
}
$mail = new PHPMailer();
try {
//Server settings
$mail->isSMTP();
$mail->Host = EMAIL_SETTINGS['host'];
$mail->SMTPAuth = true;
$mail->Username = EMAIL_SETTINGS['username'];
$mail->Password = EMAIL_SETTINGS['password'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = EMAIL_SETTINGS['port'];
//Recipients
$mail->setFrom(EMAIL_SETTINGS['from'][0], EMAIL_SETTINGS['from'][1]);
foreach (EMAIL_SETTINGS['addresses'] as $address) {
$mail->addAddress(trim($address));
}
//Attachments
if (!HAS_ATTACH_IN_BODY && count($attachs)) {
foreach ($attachs as $attach) {
$mail->addAttachment($attach);
}
}
//Content
$mail->CharSet = 'UTF-8';
$mail->Encoding = 'base64';
$mail->isHTML(true);
$mail->Subject = EMAIL_SETTINGS['subject'];
$mail->Body = $body;
$mail->send();
itc_log('Форма успешно отправлена.');
} catch (Exception $e) {
$data['result'] = 'error';
itc_log('Ошибка при отправке письма: ' . $mail->ErrorInfo);
}
}

if ($data['result'] == 'success' && HAS_SEND_NOTIFICATION) {
// очистка адресов и прикреплёных файлов
$mail->clearAllRecipients();
$mail->clearAttachments();
// получаем содержимое email шаблона и заменяем в нём плейсхолдеры на соответствующие им значения
$template = file_get_contents(dirname(__FILE__) . '/template/email_client.tpl');
$search = ['%subject%', '%name%', '%date%'];
$replace = [SUBJECT_FOR_CLIENT, $data['form']['name'], date('d.m.Y H:i')];
$body = str_replace($search, $replace, $template);
try {
// устанавливаем параметры
$mail->Subject = SUBJECT_FOR_CLIENT;
$mail->Body = $body;
$mail->addAddress($data['form']['email']);
$mail->send();
itc_log('Успешно отправлено уведомление пользователю.');
} catch (Exception $e) {
itc_log('Ошибка при отправке уведомления пользователю: ' . $mail->ErrorInfo);
}
}

if ($data['result'] == 'success' && HAS_WRITE_TXT) {
$output = '=======' . date('d.m.Y H:i') . '=======';
$output .= 'Имя: ' . $data['form']['name'] . PHP_EOL;
$output .= 'Email: ' . $data['form']['email'] . PHP_EOL;
$output .= 'Сообщение: ' . $data['form']['message'] . PHP_EOL;
if (count($attachs)) {
$output .= 'Файлы:' . PHP_EOL;
foreach ($attachs as $attach) {
$output .= $attach . PHP_EOL;
}
}
$output = '=====================';
error_log($output, 3, 'logs/forms.log');
}

echo json_encode($data);
exit();
23 changes: 7 additions & 16 deletions feedback/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ <h1>Форма обратной связи</h1>

<div class="form-container form__wrapper">
<!-- Форма обратной связи -->
<form id="feedback-form" action="/feedback/process/process.php" enctype="multipart/form-data" novalidate>
<form id="feedback-form" action="/feedback/form-processing.php" enctype="multipart/form-data" novalidate>
<div class="form-row">
<!-- Имя пользователя -->
<div class="form-group">
Expand Down Expand Up @@ -75,7 +75,7 @@ <h1>Форма обратной связи</h1>

<!-- Пользовательское солашение -->
<div class="form-group form-agree form-check">
<input class="form-check-input" type="checkbox" name="agree" id="agree" required>
<input class="form-check-input" type="checkbox" name="agree" id="agree" required value="true">
<label class="form-check-label" for="agree">Нажимая кнопку, я принимаю условия <a href="#">Пользовательского
соглашения</a> и даю своё согласие на обработку моих персональных данных</label>
<div class="invalid-feedback"></div>
Expand Down Expand Up @@ -107,24 +107,15 @@ <h1>Форма обратной связи</h1>

</div>

<script src="/feedback/js/process-forms.js"></script>
<script src="/feedback/js/form-processing.js"></script>
<script>
/*
Параметры указываются в виде:
{
ключ: значение;
ключ: значение;
...
}
Основные параметры
attachMaxItems: 3,
attachMaxFileSize: 128,
attachExt: ['png', 'jpg']
attachMaxItems: 3,
attachMaxFileSize: 128,
attachExt: ['png', 'jpg']
*/

const form = new ItcSubmitForm('form', {
isCheckValidationOnClient: true
});
const form = new ItcSubmitForm('form');

// при получении ответа result="success" от сервера
document.querySelector('form').addEventListener('success', (e) => {
Expand Down
Loading

0 comments on commit 74007fd

Please sign in to comment.