обновление

This commit is contained in:
2025-12-18 09:51:12 +05:00
parent 8076c48b82
commit 927f52fb60
6 changed files with 356 additions and 242 deletions

View File

@@ -338,51 +338,39 @@ function commentListShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$page = $_REQUEST['page'] ?? '';
$ajax = (isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == 1);
$user_group = UGROUP ?? 0;
$secure_code = $_POST['securecode'] ?? '';
$session_captcha = $_SESSION['captcha_keystring'] ?? null;
// Получаем настройки модуля для проверки обязательных полей
$settings = $this->_commentSettingsGet();
// Если запрос пришел не ajax запросом, тогда формируем ссылку для последующего редиректа
if (! $ajax)
{
$link = rewrite_link(base64_decode($page));
}
// --- ПРОВЕРКА ОБЯЗАТЕЛЬНЫХ УНИВЕРСАЛЬНЫХ ПОЛЕЙ ---
// Проверка Поля 1 (хранится в website)
// --- ПРОВЕРКА ОБЯЗАТЕЛЬНЫХ ПОЛЕЙ ---
if ($settings['comment_show_f1'] == 1 && $settings['comment_req_f1'] == 1 && empty($_POST['comment_author_website']))
{
if ($ajax) { echo 'error_req_f1'; exit; }
else { header('Location:' . $link . '#end'); exit; }
}
// Проверка Поля 2 (хранится в city)
if ($settings['comment_show_f2'] == 1 && $settings['comment_req_f2'] == 1 && empty($_POST['comment_author_city']))
{
if ($ajax) { echo 'error_req_f2'; exit; }
else { header('Location:' . $link . '#end'); exit; }
}
// -------------------------------------------------
// Если в настройках модуля включено использование защитного кода, тогда
// --- АНТИСПАМ ---
if ($settings['comment_use_antispam'] == 1)
{
// Если введенный пользователем защитный код неверен, тогда выполняем обновление кода
if (! (isset($session_captcha) && $session_captcha == $secure_code))
{
unset($_SESSION['captcha_keystring']);
if ($ajax)
{
echo 'wrong_securecode';
}
else
{
if ($ajax) { echo 'wrong_securecode'; }
else {
if(isset($GLOBALS['tmpl']))$GLOBALS['tmpl']->assign("wrongSecureCode", 1);
header('Location:' . $link . '#end');
}
@@ -391,47 +379,65 @@ function commentListShow($tpl_dir)
unset($_SESSION['captcha_keystring']);
}
// Определяем флаг модерации комментариев
$comment_status = ($settings['comment_need_approve'] == 1) ? 0 : 1;
// Если комментарии разрешены, тогда получаем все данные, который пользователь указал в форме
if ($settings['comment_active'] == 1
&& !empty($_POST['comment_text'])
&& !empty($_POST['comment_author_name'])
&& in_array($user_group, explode(',', $settings['comment_user_groups'])))
{
// --- !!! НАЧАЛО БЕЗОПАСНОСТИ: Санитаризация и Экранирование !!! ---
// --- ОБРАБОТКА ЗАГРУЗКИ ИЗОБРАЖЕНИЯ ---
$comment_file_name = '';
if ($settings['comment_allow_files'] == 1 && isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK)
{
$upload_path = BASE_DIR . '/uploads/comments/';
// Создаем папку, если её нет
if (!is_dir($upload_path)) {
@mkdir($upload_path, 0775, true);
// Создаем пустой index.html для безопасности (запрет листинга папки)
@file_put_contents($upload_path . 'index.html', '');
}
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif'];
$file_info = getimagesize($_FILES['comment_image']['tmp_name']);
$file_size = $_FILES['comment_image']['size'];
// Проверка: это реально картинка и её размер не более 2Мб
if ($file_info && in_array($file_info['mime'], $allowed_mime) && $file_size <= 2 * 1024 * 1024)
{
$ext = pathinfo($_FILES['comment_image']['name'], PATHINFO_EXTENSION);
$comment_file_name = time() . '_' . rand(100, 999) . '.' . $ext;
if (!move_uploaded_file($_FILES['comment_image']['tmp_name'], $upload_path . $comment_file_name)) {
$comment_file_name = ''; // Сбрасываем, если не удалось переместить
}
}
}
// --- ПОДГОТОВКА ДАННЫХ ДЛЯ БД ---
$new_comment['parent_id'] = (int)($_POST['parent_id'] ?? 0);
$new_comment['document_id'] = (int)($_POST['doc_id'] ?? 0);
$new_comment['comment_author_name'] = addslashes(strip_tags($_POST['comment_author_name'] ?? ''));
$new_comment['comment_author_id'] = empty($_SESSION['user_id']) ? 0 : (int)$_SESSION['user_id'];
$new_comment['comment_author_email'] = addslashes(strip_tags($_POST['comment_author_email'] ?? ''));
// Маппинг универсальных полей в существующие колонки БД
$new_comment['comment_author_city'] = addslashes(strip_tags($_POST['comment_author_city'] ?? '')); // Поле 2
$new_comment['comment_author_website'] = addslashes(strip_tags($_POST['comment_author_website'] ?? '')); // Поле 1
$new_comment['comment_author_city'] = addslashes(strip_tags($_POST['comment_author_city'] ?? ''));
$new_comment['comment_author_website'] = addslashes(strip_tags($_POST['comment_author_website'] ?? ''));
$new_comment['comment_author_ip'] = $_SERVER['REMOTE_ADDR'];
$new_comment['comment_published'] = time();
$new_comment['comment_status'] = $comment_status;
$comment_text_raw = $_POST['comment_text'] ?? '';
$new_comment['comment_file'] = $comment_file_name; // НАША НОВАЯ КОЛОНКА
$comment_text_raw = $_POST['comment_text'] ?? '';
$comment_max_chars = $settings['comment_max_chars'];
$comment_max_chars = (!empty($comment_max_chars) && $comment_max_chars > 10) ? $comment_max_chars : 200;
$comment_text_clean = strip_tags(stripslashes($comment_text_raw));
$comment_text_cut = mb_substr($comment_text_clean, 0, $comment_max_chars);
$comment_text_cut .= (mb_strlen($comment_text_clean) > $comment_max_chars) ? '…' : '';
$new_comment['comment_text'] = addslashes($comment_text_cut);
// --- !!! КОНЕЦ БЕЗОПАСНОСТИ !!! ---
// Выполняем запрос к БД на добавление комментария
// Выполняем запрос
$AVE_DB->Query("
INSERT INTO " . PREFIX . "_module_comment_info
(`" . implode('`,`', array_keys($new_comment)) ."`)
@@ -440,7 +446,7 @@ function commentListShow($tpl_dir)
");
$new_comment['Id'] = $AVE_DB->InsertId();
// Получаем настройки и уведомляем админа (код без изменений...)
// --- УВЕДОМЛЕНИЕ АДМИНА ---
$mail_from = get_settings('mail_from');
$mail_from_name = get_settings('mail_from_name');
$page_link = get_home_link() . urldecode(base64_decode($page)) . '&subaction=showonly&comment_id=' . $new_comment['Id'] . '#' . $new_comment['Id'];
@@ -455,18 +461,9 @@ function commentListShow($tpl_dir)
if ($ajax)
{
if (isset($new_comment['comment_author_id']) && $new_comment['comment_author_id'] > 0)
{
$new_comment['avatar'] = getAvatar($new_comment['comment_author_id'], 48);
}
else
{
$new_comment['avatar'] = '';
}
$new_comment['avatar'] = (isset($new_comment['comment_author_id']) && $new_comment['comment_author_id'] > 0) ? getAvatar($new_comment['comment_author_id'], 48) : '';
$new_comment['comment_changed'] = 0;
$new_comment['comment_published'] = ave_date_format($AVE_Template->get_config_vars('COMMENT_DATE_TIME_FORMAT'), $new_comment['comment_published']);
// Добавляем настройки полей в массив комментария, чтобы шаблон дерева мог их использовать
$new_comment['settings'] = $settings;
$subcomments[] = $new_comment;
@@ -480,99 +477,111 @@ function commentListShow($tpl_dir)
exit;
}
/**
/**
* Метод, предназначенный для редактирования комментария в Публичной части
*
* @param int $comment_id - идентификатор комментария
* с поддержкой удаления и замены изображения
*/
function commentPostEdit($comment_id)
function commentPostEdit($comment_id)
{
global $AVE_DB;
$user_id = $_SESSION['user_id'] ?? null;
$user_group = UGROUP ?? 0;
$post_text = $_POST['text'] ?? '';
$delete_file = (isset($_POST['delete_image']) && $_POST['delete_image'] == 1);
if (empty($user_id)) exit;
$comment_id = intval(preg_replace('/\D/', '', $comment_id));
// Выполняем запрос к БД и получаем всю информацию о комментарии, а также ряд значений из настроек модуля
$row = $AVE_DB->Query("
SELECT
msg.parent_id,
msg.comment_text,
msg.comment_file,
cmnt.comment_user_groups,
cmnt.comment_max_chars,
cmnt.comment_need_approve
FROM
" . PREFIX . "_module_comment_info AS msg,
" . PREFIX . "_module_comments AS cmnt
WHERE comment_active = '1'
WHERE cmnt.comment_active = '1'
AND msg.Id = '" . $comment_id . "'
" . (($user_group != 1) ? "AND comment_author_id = " . $user_id : '') . "
" . (($user_group != 1) ? "AND msg.comment_author_id = " . (int)$user_id : '') . "
")->FetchAssocArray();
// Если данные получены
if ($row !== false)
{
$comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 20;
$comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 200;
// --- ТВОЯ ОРИГИНАЛЬНАЯ ОБРАБОТКА ТЕКСТА ---
$comment_text = $post_text;
// --- !!! НАЧАЛО ИСПРАВЛЕНИЯ УСТАРЕВШИХ ОПЕРАТОРОВ (/e) !!! ---
// Безопасная замена: преобразуем hex-сущности
$comment_text = preg_replace_callback('/&#x([0-9a-f]{1,7});/', function($matches) {
return chr(hexdec($matches[1]));
}, $comment_text);
// Безопасная замена: преобразуем dec-сущности
$comment_text = preg_replace_callback('/&#([0-9]{1,7});/', function($matches) {
return chr($matches[1]);
}, $comment_text);
// --- !!! КОНЕЦ ИСПРАВЛЕНИЯ УСТАРЕВШИХ ОПЕРАТОРОВ !!! ---
$comment_text = preg_replace_callback('/&#x([0-9a-f]{1,7});/', function($matches) { return chr(hexdec($matches[1])); }, $comment_text);
$comment_text = preg_replace_callback('/&#([0-9]{1,7});/', function($matches) { return chr($matches[1]); }, $comment_text);
$comment_text = stripslashes($comment_text);
$comment_text = str_replace(array("<br>\n", "<br />\n", "<br/>\n"), "\n", $comment_text);
// Санитаризация: убираем теги перед обрезкой
$comment_text = strip_tags($comment_text);
$comment_text = mb_substr($comment_text, 0, $comment_max_chars-1);
$message_length = mb_strlen($comment_text);
$comment_text .= ($message_length > $comment_max_chars) ? '…' : '';
$comment_text = strip_tags($comment_text);
$comment_text_cut = mb_substr($comment_text, 0, $comment_max_chars);
$message_length = mb_strlen($comment_text_cut);
if (mb_strlen($comment_text) > $comment_max_chars) $comment_text_cut .= '…';
// Если группа текущего пользователя совпадает с разрешенной группой в настройках модуля, тогда
// выполняем запрос к БД на обновление информации.
if (in_array($user_group, explode(',', $row['comment_user_groups'])) && $message_length > 3)
$is_admin = ($user_group == 1);
$is_allowed_group = in_array($user_group, explode(',', $row['comment_user_groups']));
if ($is_admin || ($is_allowed_group && $message_length > 3))
{
$upload_dir = BASE_DIR . '/uploads/comments/';
$new_file_sql = "";
// 1. ЗАМЕНА ФАЙЛА
if (isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK) {
if (!is_dir($upload_dir)) @mkdir($upload_dir, 0777, true);
// Удаляем старый
if (!empty($row['comment_file'])) {
$old_file = $upload_dir . $row['comment_file'];
if (file_exists($old_file)) @unlink($old_file);
}
$file_ext = strtolower(pathinfo($_FILES['comment_image']['name'], PATHINFO_EXTENSION));
$new_file_name = 'comm_' . time() . '_' . rand(100, 999) . '.' . $file_ext;
if (move_uploaded_file($_FILES['comment_image']['tmp_name'], $upload_dir . $new_file_name)) {
// Используем addslashes вместо Escape_String для надежности
$new_file_sql = ", comment_file = '" . addslashes($new_file_name) . "'";
$delete_file = false;
}
}
// 2. УДАЛЕНИЕ ФАЙЛА
elseif ($delete_file && !empty($row['comment_file'])) {
$old_file = $upload_dir . $row['comment_file'];
if (file_exists($old_file)) @unlink($old_file);
$new_file_sql = ", comment_file = ''";
}
// ОБНОВЛЕНИЕ БАЗЫ
$AVE_DB->Query("
UPDATE " . PREFIX . "_module_comment_info
SET
comment_changed = '" . time() . "',
comment_status = '" . intval(!(bool)$row['comment_need_approve']) . "',
comment_text = '" . addslashes($comment_text) . "'
comment_text = '" . addslashes($comment_text_cut) . "'
$new_file_sql
WHERE
Id = '" . $comment_id . "'
");
// Преобразуем HTML теги в HTML сущности перед выводом (для XSS)
echo htmlspecialchars($comment_text, ENT_QUOTES);
echo htmlspecialchars($comment_text_cut, ENT_QUOTES);
exit;
}
// Если редактирование не прошло, выводим оригинальный текст (экранированный)
echo htmlspecialchars($row['comment_text'], ENT_QUOTES);
}
exit;
}
/**
/**
* Метод, предназначенный для удаления комментария. Если комментарий содержал какие-либо ответы на него,
* то все ответы также будут удалены вместе с родительским комментарием.
*
@@ -582,7 +591,27 @@ function commentListShow($tpl_dir)
{
global $AVE_DB;
$comment_id = (int)$comment_id; // Убедимся, что это целое число
$comment_id = (int)$comment_id;
$upload_dir = BASE_DIR . '/uploads/comments/';
// --- 1. ОЧИСТКА ФАЙЛОВ ПЕРЕД УДАЛЕНИЕМ ИЗ БД ---
// Ищем файлы основного комментария и всех вложенных ответов
$res = $AVE_DB->Query("
SELECT comment_file
FROM " . PREFIX . "_module_comment_info
WHERE (Id = '" . $comment_id . "' OR parent_id = '" . $comment_id . "')
AND comment_file != ''
");
while ($row = $res->FetchAssocArray()) {
$file_path = $upload_dir . $row['comment_file'];
if (file_exists($file_path)) {
@unlink($file_path); // Удаляем файл с сервера
}
}
// --- 2. ТВОЙ ОРИГИНАЛЬНЫЙ КОД УДАЛЕНИЯ ИЗ БД ---
// Выполняем запрос к БД на удаление родительского комментария
$AVE_DB->Query("
@@ -884,6 +913,7 @@ function commentListShow($tpl_dir)
$AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . $this->_admin_edit_link_tpl));
}
/**
/**
/**
* Метод, предназначенный для управления настройками модуля
@@ -894,7 +924,6 @@ function commentListShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$request_sub = $_REQUEST['sub'] ?? '';
$post_max_chars = $_POST['comment_max_chars'] ?? 0;
$post_user_groups = $_POST['comment_user_groups'] ?? array();
@@ -907,7 +936,10 @@ function commentListShow($tpl_dir)
$post_use_page_nav = $_POST['comment_use_page_nav'] ?? 0;
$post_page_nav_count = $_POST['comment_page_nav_count'] ?? 0;
// НОВОЕ: Получение данных для универсальных полей
// НОВОЕ: Настройка разрешения загрузки файлов
$post_allow_files = $_POST['comment_allow_files'] ?? 0;
// Данные для универсальных полей
$post_show_f1 = $_POST['comment_show_f1'] ?? 0;
$post_req_f1 = $_POST['comment_req_f1'] ?? 0;
$post_name_f1 = $_POST['comment_name_f1'] ?? '';
@@ -916,12 +948,10 @@ function commentListShow($tpl_dir)
$post_req_f2 = $_POST['comment_req_f2'] ?? 0;
$post_name_f2 = $_POST['comment_name_f2'] ?? '';
// Если в запросе содержится подзапрос на сохранение данных
if ($request_sub == 'save')
{
$max_chars = (empty($post_max_chars) || $post_max_chars < 50) ? 50 : $post_max_chars;
// Очищаем названия полей вручную, так как EscapeString не поддерживается
$clean_name_f1 = htmlspecialchars(stripslashes($post_name_f1), ENT_QUOTES);
$clean_name_f2 = htmlspecialchars(stripslashes($post_name_f2), ENT_QUOTES);
@@ -936,6 +966,7 @@ function commentListShow($tpl_dir)
comment_use_antispam = '" . (int)$post_use_antispam . "',
comment_use_page_nav = '" . (int)$post_use_page_nav . "',
comment_page_nav_count = '" . (int)$post_page_nav_count . "',
comment_allow_files = '" . (int)$post_allow_files . "',
comment_show_f1 = '" . (int)$post_show_f1 . "',
comment_req_f1 = '" . (int)$post_req_f1 . "',
comment_name_f1 = '" . $clean_name_f1 . "',
@@ -947,15 +978,11 @@ function commentListShow($tpl_dir)
");
}
// Получаем список всех настроек модуля
$row = $this->_commentSettingsGet();
// Преобразуем поля с правами в массивы для удобства отображения в шаблоне
$row['comment_user_groups'] = explode(',', $row['comment_user_groups']);
$row['comment_user_groups_read'] = explode(',', $row['comment_user_groups_read']);
// Передаем данные в шаблон
$AVE_Template->assign($row);
$AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . $this->_admin_settings_tpl));
}

View File

@@ -3,22 +3,13 @@
==================================================================== */
(function waitForJQuery() {
if (typeof jQuery === 'undefined') {
// jQuery еще не загружен. Ждем 10 мс и проверяем снова.
setTimeout(waitForJQuery, 10);
} else {
// JQuery загружен, можно запускать основной код.
(function($){
// ====================================================================
// КОРРЕКЦИЯ: ПРИНУДИТЕЛЬНАЯ ФИКСАЦИЯ HTTPS (если требуется)
// ====================================================================
if (typeof aveabspath !== 'undefined') {
// ПРЕДУПРЕЖДЕНИЕ: Если aveabspath должен быть динамическим,
// удалите эту строку.
// aveabspath = 'https://bag.local/';
}
// ====================================================================
/* Limit symbols */
(function($){
@@ -65,15 +56,12 @@
function cAction(obj, action){
var cid;
// --- Логика для действий с документом (open/close) ---
if (action === 'open' || action === 'close') {
cid = DOC_ID;
} else {
// 1. Пытаемся найти ссылку и взять ID из атрибута rel
var $link = $(obj).closest('a.mod_comment_answer');
cid = $link.attr('rel');
// 2. Если rel не найден, берем ID из родительского блока
if (typeof cid === 'undefined' || cid === false || cid === '') {
cid = $(obj).parents('.mod_comment_box').attr('id');
}
@@ -86,12 +74,10 @@
if (action === 'answer'){
$('#parent_id').val(cid);
// Перемещаем форму и показываем ее
$('#mod_comment_new').insertBefore('#end' + cid).show();
return;
}
// Действия администратора
if (UGROUP == 1){
$.get(aveabspath + 'index.php', {
module: 'comment',
@@ -100,27 +86,21 @@
Id: cid
}, function(){
if (action === 'delete'){
// Удаляем блок комментария
$(obj).parents('.mod_comment_comment').eq(0).remove();
}
if (action === 'open'){
// 1. Меняем ID и текст
var $openButton = $('#mod_comment_open');
$openButton.attr('id', 'mod_comment_close').html('<i class="bi bi-unlock-fill me-1"></i> ' + COMMENT_SITE_CLOSE);
// 2. МЕНЯЕМ КЛАССЫ СТИЛЕЙ
$openButton.removeClass('btn-outline-success').addClass('btn-outline-danger');
}
if (action === 'close'){
// 1. Меняем ID и текст
var $closeButton = $('#mod_comment_close');
$closeButton.attr('id', 'mod_comment_open').html('<i class="bi bi-lock-fill me-1"></i> ' + COMMENT_SITE_OPEN);
// 2. МЕНЯЕМ КЛАССЫ СТИЛЕЙ
$closeButton.removeClass('btn-outline-danger').addClass('btn-outline-success');
}
// Динамическая смена иконки замка для отдельного комментария
if (action === 'unlock'){
$(obj).removeClass('mod_comment_unlock text-success')
.addClass('mod_comment_lock text-dark')
@@ -139,27 +119,22 @@
}
}
function validate(formData, jqForm, options){
function validate(formData, jqForm, options){
$('.alert').remove();
var form = jqForm ? jqForm[0] : $('#mod_comment_new form')[0];
// Проверка имени
if (form.comment_author_name && !form.comment_author_name.value){
alert(COMMENT_ERROR_AUTHOR);
$(form.comment_author_name).focus();
return false;
}
// Проверка email
if (form.comment_author_email && !form.comment_author_email.value){
alert(COMMENT_ERROR_EMAIL);
$(form.comment_author_email).focus();
return false;
}
// --- НОВОЕ: Проверка динамических полей ---
// Проверка Поля №1 (website)
if (typeof REQ_F1 !== 'undefined' && REQ_F1 == '1') {
if (form.comment_author_website && !form.comment_author_website.value) {
alert("Пожалуйста, заполните поле: " + NAME_F1);
@@ -168,7 +143,6 @@ function validate(formData, jqForm, options){
}
}
// Проверка Поля №2 (city)
if (typeof REQ_F2 !== 'undefined' && REQ_F2 == '1') {
if (form.comment_author_city && !form.comment_author_city.value) {
alert("Пожалуйста, заполните поле: " + NAME_F2);
@@ -176,16 +150,13 @@ function validate(formData, jqForm, options){
return false;
}
}
// ------------------------------------------
// Проверка текста
if (!form.comment_text || !form.comment_text.value){
alert(COMMENT_ERROR_TEXT);
if (form.comment_text) $(form.comment_text).focus();
return false;
}
// Проверка капчи (если требуется)
if (IS_IM && form.securecode && !form.securecode.value){
alert(COMMENT_ERROR_CAPTCHA);
$(form.securecode).focus();
@@ -195,35 +166,60 @@ function validate(formData, jqForm, options){
return true;
}
// Функция для создания формы редактирования
function createEditForm(cid, revert, targetElement) {
var currentImg = $('#' + cid).find('.mod_comment_attached_image img').attr('src');
var deletePhotoHtml = '';
if (currentImg) {
deletePhotoHtml = '<div class="form-check mb-2 mt-2">' +
'<input class="form-check-input" type="checkbox" id="del_img_' + cid + '" value="1">' +
'<label class="form-check-label text-danger small" for="del_img_' + cid + '">' +
'<i class="bi bi-trash me-1"></i>Удалить текущее фото</label>' +
'</div>';
}
// Блок для превью нового фото (добавлен класс edit-preview-img)
var editPreviewHtml = '<div id="edit_preview_wrapper_' + cid + '" class="d-none mt-2 mb-2">' +
'<img id="edit_preview_img_' + cid + '" src="#" class="img-thumbnail" style="max-height: 100px; object-fit: contain;">' +
'</div>';
// Поле для загрузки НОВОГО фото (добавлен класс edit-file-input и data-cid)
var uploadNewPhotoHtml = '<div class="mb-3 mt-2">' +
'<label class="form-label small fw-bold mb-1">Заменить или добавить фото:</label>' +
'<input type="file" id="new_file_' + cid + '" class="form-control form-control-sm edit-file-input" data-cid="' + cid + '" accept="image/*">' +
editPreviewHtml +
'</div>';
var textarea = '<p><textarea rows="7" id="ta_' + cid + '" class="editable form-control">' + revert + '</textarea></p>';
var charsLeft = '<p>' + COMMENT_CHARS_LEFT + ' <span class="charsLeft badge bg-secondary" id="charsLeft_' + cid + '"></span></p>';
var buttonSave = '<button type="button" class="btn btn-primary saveButton">' + COMMENT_BUTTON_EDIT + '</button> ';
var buttonReset = '<button type="button" class="btn btn-secondary cancelButton">' + COMMENT_BUTTON_CANCEL + '</button>';
targetElement.after(
'<div class="box"><div class="block" id="forms"><fieldset><legend class="h6">' + COMMENT_EDIT_TITLE
+ '</legend>' + textarea + deletePhotoHtml + uploadNewPhotoHtml + charsLeft + buttonSave + buttonReset + '</fieldset></div></div>'
).remove();
$('.saveButton').off('click').on('click', function(){saveChanges(this, false, cid);});
$('.cancelButton').off('click').on('click', function(){saveChanges(this, revert, cid);});
$('#ta_' + cid).limit(MAX_CHARS, '#charsLeft_' + cid);
}
function setClickable(){
// Отвязываем предыдущие обработчики, чтобы избежать множественных вызовов
$('.editable_text').off('click');
$('.editable_text').click(function(){
var $this = $(this);
var cid = $this.parents('.mod_comment_box').attr('id');
var revert = $this.html();
var textarea = '<p><textarea rows="7" id="ta_' + cid + '" class="editable form-control">' + revert + '</textarea></p>';
var charsLeft = '<p>' + COMMENT_CHARS_LEFT + ' <span class="charsLeft badge bg-secondary" id="charsLeft_' + cid + '"></span></p>';
var buttonSave = '<button type="button" class="btn btn-primary saveButton">' + COMMENT_BUTTON_EDIT + '</button> ';
var buttonReset = '<button type="button" class="btn btn-secondary cancelButton">' + COMMENT_BUTTON_CANCEL + '</button>';
$this.after(
'<div class="box"><div class="block" id="forms"><fieldset><legend class="h6">' + COMMENT_EDIT_TITLE
+ '</legend>' + textarea + charsLeft + buttonSave + buttonReset + '</fieldset></div></div>'
).remove();
$('.saveButton').click(function(){saveChanges(this, false, cid);});
$('.cancelButton').click(function(){saveChanges(this, revert, cid);});
$('#ta_' + cid).limit(MAX_CHARS, '#charsLeft_' + cid);
createEditForm(cid, revert, $this);
})
.attr('title', COMMENT_EDIT_LINK)
.mouseover(function(){$(this).addClass('editable');})
.mouseout(function(){$(this).removeClass('editable');});
// Лимит символов для новой формы
$('#in_message').limit(MAX_CHARS, '#charsLeft_new');
}
@@ -232,26 +228,42 @@ function validate(formData, jqForm, options){
if (!cancel){
var t = $box.find('#ta_' + cid).val();
var deleteImg = $('#del_img_' + cid).is(':checked') ? 1 : 0;
var fd = new FormData();
fd.append('module', 'comment');
fd.append('action', 'edit');
fd.append('Id', cid);
fd.append('text', t);
fd.append('delete_image', deleteImg);
var fileInput = $('#new_file_' + cid)[0];
if (fileInput && fileInput.files[0]) {
// Ключ 'comment_image' должен совпадать с PHP $_FILES['comment_image']
fd.append('comment_image', fileInput.files[0]);
}
$.ajax({
url: aveabspath + 'index.php?ajax=1',
type: 'POST',
data: {
module: 'comment',
action: 'edit',
Id: cid,
text: t
},
data: fd,
processData: false,
contentType: false,
success: function(txt){
$box.after('<div class="mod_comment_text editable_text">' + txt + '</div>').remove();
var now = new Date();
var date = now.toLocaleString();
$('#' + cid).find('.mod_comment_changed').html(' (' + COMMENT_TEXT_CHANGED + ' ' + date + ')');
setClickable();
// Если были операции с файлом, перезагружаем страницу
if (deleteImg == 1 || (fileInput && fileInput.files[0])) {
location.reload();
} else {
$box.after('<div class="mod_comment_text editable_text">' + txt + '</div>').remove();
var now = new Date();
var date = now.toLocaleString();
$('#' + cid).find('.mod_comment_changed').html(' (' + COMMENT_TEXT_CHANGED + ' ' + date + ')');
setClickable();
}
},
error: function(xhr, status, error) {
console.error("AJAX Error (Edit):", status, error);
alert("Ошибка при редактировании комментария.");
alert("Ошибка при сохранении.");
}
});
}
@@ -270,7 +282,9 @@ function validate(formData, jqForm, options){
$('#end' + $('#parent_id').val()).before(data);
$('#parent_id').val('');
$('#mod_comment_new form')[0].reset();
// Перемещаем форму обратно в конец
$('#image_preview_wrapper').addClass('d-none');
$('#image_preview_img').attr('src', '#');
$('#mod_comment_new').insertAfter('#end');
setClickable();
}
@@ -281,109 +295,134 @@ function validate(formData, jqForm, options){
setClickable();
// Смена капчи
// ЛОГИКА ПРЕДПРОСМОТРА ДЛЯ ФОРМ РЕДАКТИРОВАНИЯ (Делегирование)
$(document).on('change', '.edit-file-input', function() {
var cid = $(this).data('cid');
var file = this.files[0];
var previewWrapper = $('#edit_preview_wrapper_' + cid);
var previewImg = $('#edit_preview_img_' + cid);
if (file) {
var reader = new FileReader();
reader.onload = function(e) {
previewImg.attr('src', e.target.result);
previewWrapper.removeClass('d-none');
}
reader.readAsDataURL(file);
} else {
previewWrapper.addClass('d-none');
}
});
// ЛОГИКА ПРЕДПРОСМОТРА ДЛЯ НОВОГО КОММЕНТАРИЯ
$(document).on('change', '#comment_image', function() {
const file = this.files[0];
const previewWrapper = $('#image_preview_wrapper');
const previewImg = $('#image_preview_img');
const errorDiv = $('#file_error');
if (errorDiv.length) errorDiv.addClass('d-none').text('');
if (file) {
if (file.size > 2 * 1024 * 1024) {
if (errorDiv.length) errorDiv.removeClass('d-none').text('Файл слишком большой (макс. 2Мб)');
this.value = '';
previewWrapper.addClass('d-none');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
previewImg.attr('src', e.target.result);
previewWrapper.removeClass('d-none');
}
reader.readAsDataURL(file);
}
});
$(document).on('click', '#remove_image_btn', function() {
$('#comment_image').val('');
$('#image_preview_wrapper').addClass('d-none');
$('#image_preview_img').attr('src', '#');
});
$(document).on('click', '#captcha img', function(){getCaptha();});
// Ответ на комментарий
$(document).on('click', '#reload_captcha', function(e){
e.preventDefault();
getCaptha();
});
$(document).on('click', '.mod_comment_answer', function(e){
e.preventDefault();
cAction(this, 'answer');
});
// Редактирование (для администратора/автора)
$(document).on('click', '.mod_comment_edit', function(e){
e.preventDefault();
var cid = $(this).parents('.mod_comment_box').attr('id');
var commentTextBlock = $('#' + cid).find('.mod_comment_text');
var revert = commentTextBlock.html();
var textarea = '<p><textarea rows="7" id="ta_' + cid + '" class="editable form-control">' + revert + '</textarea></p>';
var charsLeft = '<p>' + COMMENT_CHARS_LEFT + ' <span class="charsLeft badge bg-secondary" id="charsLeft_' + cid + '"></span></p>';
var buttonSave = '<button type="button" class="btn btn-primary saveButton">' + COMMENT_BUTTON_EDIT + '</button> ';
var buttonReset = '<button type="button" class="btn btn-secondary cancelButton">' + COMMENT_BUTTON_CANCEL + '</button>';
commentTextBlock.after(
'<div class="box"><div class="block" id="forms"><fieldset><legend class="h6">' + COMMENT_EDIT_TITLE
+ '</legend>' + textarea + charsLeft + buttonSave + buttonReset + '</fieldset></div></div>'
).remove();
$('.saveButton').click(function(){saveChanges(this, false, cid);});
$('.cancelButton').click(function(){saveChanges(this, revert, cid);});
$('#ta_' + cid).limit(MAX_CHARS, '#charsLeft_' + cid);
createEditForm(cid, revert, commentTextBlock);
});
if (UGROUP == 1) {
// Удаление (с использованием Bootstrap Modal)
$(document).on('click', '.mod_comment_delete', function(e){
e.preventDefault();
var $deleteButton = $(this);
var $modal = $('#deleteCommentModal');
// Привязываем действие к кнопке подтверждения в модальном окне
$('#confirmDeleteButton').off('click').on('click', function() {
cAction($deleteButton[0], 'delete');
// Закрываем модальное окно (совместимость с BS5)
var modalInstance = bootstrap.Modal.getInstance($modal[0]) || new bootstrap.Modal($modal[0]);
modalInstance.hide();
});
// Открываем модальное окно (совместимость с BS5)
var modal = new bootstrap.Modal($modal[0]);
modal.show();
});
// Блокировка/разблокировка
$(document).on('click', '.mod_comment_lock', function(e){e.preventDefault(); cAction(this, 'lock');});
$(document).on('click', '.mod_comment_unlock', function(e){e.preventDefault(); cAction(this, 'unlock');});
// Открыть/Закрыть комментарии для документа
$(document).on('click', '#mod_comment_open', function(e){e.preventDefault(); cAction(this, 'open');});
$(document).on('click', '#mod_comment_close', function(e){e.preventDefault(); cAction(this, 'close');});
}
// Отправка формы (AJAX)
$('#mod_comment_new form').on('submit', function(e){
e.preventDefault();
var form = $(this);
if (!validate(null, form, null)) {
return false;
}
if (!validate(null, form, null)) return false;
var submitButton = form.find('input[type="submit"], button[type="submit"]').first();
var originalButtonText = submitButton.text();
if (!originalButtonText) originalButtonText = submitButton.val();
var formData = form.serialize();
var formData = new FormData(this);
$.ajax({
url: aveabspath + 'index.php?ajax=1',
type: 'POST',
data: formData,
dataType: 'html',
processData: false,
contentType: false,
beforeSend: function() {
submitButton.prop('disabled', true).text('Отправка...');
},
success: function(data) {
displayNewComment(data);
submitButton.prop('disabled', false).text(originalButtonText);
},
timeout: 3000,
error: function(xhr, status, error) {
console.error("AJAX Error (Creation):", status, error);
alert("Ошибка при отправке комментария.");
error: function() {
alert("Ошибка при отправке.");
submitButton.prop('disabled', false).text(originalButtonText);
}
});
return false;
});
// Сброс формы
$('#buttonReset').click(function(){
$('#parent_id').val('');
$('#mod_comment_new form')[0].reset();
$('#image_preview_wrapper').addClass('d-none');
$('#mod_comment_new').insertAfter('#end');
});
});

32
sql.php
View File

@@ -3,7 +3,7 @@
/**
* AVE.cms - Модуль Комментарии
*
* Обновленная структура с поддержкой произвольных полей
* Обновленная структура с поддержкой произвольных полей и загрузки изображений
*/
$module_sql_install = array();
@@ -26,13 +26,16 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comments` (
`comment_use_antispam` enum('1','0') NOT NULL default '1',
`comment_use_page_nav` enum('1','0') NOT NULL default '1',
`comment_page_nav_count` varchar(5) NOT NULL,
/* НОВЫЕ УНИВЕРСАЛЬНЫЕ ПОЛЯ НАСТРОЕК */
/* УНИВЕРСАЛЬНЫЕ ПОЛЯ */
`comment_show_f1` tinyint(1) NOT NULL default '1',
`comment_req_f1` tinyint(1) NOT NULL default '0',
`comment_name_f1` varchar(255) NOT NULL default '',
`comment_show_f2` tinyint(1) NOT NULL default '1',
`comment_req_f2` tinyint(1) NOT NULL default '0',
`comment_name_f2` varchar(255) NOT NULL default '',
/* ПОЛЯ ДЛЯ ЗАГРУЗКИ ФАЙЛОВ */
`comment_allow_files` tinyint(1) NOT NULL default '0',
`comment_file_max_size` int(10) NOT NULL default '2048',
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
@@ -43,22 +46,24 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comment_info` (
`comment_author_name` varchar(255) NOT NULL,
`comment_author_id` int(10) unsigned NOT NULL default '0',
`comment_author_email` varchar(255) NOT NULL,
`comment_author_city` varchar(255) NOT NULL, /* Маппим Field 2 сюда */
`comment_author_website` varchar(255) NOT NULL, /* Маппим Field 1 сюда */
`comment_author_city` varchar(255) NOT NULL,
`comment_author_website` varchar(255) NOT NULL,
`comment_author_ip` varchar(15) NOT NULL,
`comment_published` int(10) unsigned NOT NULL default '0',
`comment_changed` int(10) unsigned NOT NULL default '0',
`comment_text` text NOT NULL,
`comment_status` tinyint(1) unsigned NOT NULL default '1',
`comments_close` tinyint(1) unsigned NOT NULL default '0',
/* ПУТЬ К ПРИКРЕПЛЕННОМУ ФАЙЛУ */
`comment_file` varchar(255) NOT NULL default '',
PRIMARY KEY (`Id`),
KEY `document_id` (`document_id`),
KEY `parent_id` (`parent_id`),
KEY `comment_status` (`comment_status`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PACK_KEYS=0;";
// Начальные данные (добавлены дефолтные значения для новых полей в конце строки)
$module_sql_install[] = "INSERT INTO `%%PRFX%%_module_comments` VALUES (1, 1000, '1,3', '1,2,3,4', '0', '1', '1' , '0', '', 1, 0, '', 1, 0, '');";
// Начальные данные (Добавлено: 0 для allow_files и 2048 для размера в конце)
$module_sql_install[] = "INSERT INTO `%%PRFX%%_module_comments` VALUES (1, 1000, '1,3', '1,2,3,4', '0', '1', '1' , '0', '', 1, 0, '', 1, 0, '', 0, 2048);";
// =================================================================================
// 2. ОБНОВЛЕНИЕ МОДУЛЯ (ALTER TABLE)
@@ -74,16 +79,11 @@ $module_sql_update[] = "
LIMIT 1;
";
// Добавление новых столбцов управления полями
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_show_f1` TINYINT(1) NOT NULL DEFAULT '1';";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_req_f1` TINYINT(1) NOT NULL DEFAULT '0';";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_name_f1` VARCHAR(255) NOT NULL DEFAULT '';";
// Добавление поля для файла в основную таблицу (если модуль уже установлен)
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comment_info` ADD `comment_file` VARCHAR(255) NOT NULL DEFAULT '' AFTER `comments_close`;";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_show_f2` TINYINT(1) NOT NULL DEFAULT '1';";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_req_f2` TINYINT(1) NOT NULL DEFAULT '0';";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_name_f2` VARCHAR(255) NOT NULL DEFAULT '';";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_user_groups_read` TEXT NOT NULL AFTER `comment_user_groups`;";
$module_sql_update[] = "UPDATE `%%PRFX%%_module_comments` SET `comment_user_groups_read` = '1,2,3,4' WHERE `Id` = 1;";
// Добавление настроек загрузки (если модуль уже установлен)
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_allow_files` TINYINT(1) NOT NULL DEFAULT '0';";
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_file_max_size` INT(10) NOT NULL DEFAULT '2048';";
?>

View File

@@ -111,6 +111,22 @@
</div>
</td>
</tr>
<tr>
<td colspan="4" style="background: #f9f9f9; font-weight: bold; text-align: center;">Настройки медиафайлов</td>
</tr>
<tr>
<td>Разрешить загрузку фото:</td>
<td>
<input name="comment_allow_files" type="checkbox" value="1" {if $comment_allow_files=='1'}checked{/if} />
<span style="color: #888; font-size: 11px; margin-left: 10px;">(позволяет прикреплять изображения к комментариям)</span>
</td>
<td>Папка для хранения:</td>
<td>
<code style="color: #555; background: #eee; padding: 2px 5px; border-radius: 3px;">/uploads/comments/</code>
</td>
</tr>
<tr>
<td colspan="4" class="submitArea">
<input type="submit" value="{#COMMENT_BUTTON_SAVE#}" class="basicBtn" style="float: left; margin: 10px;" />

View File

@@ -39,7 +39,7 @@
</div>
{if isset($comments[0])}
<div class="comments-list">
<div class="comments-list" id="comments_list">
{include file="$subtpl" subcomments=$comments[0]}
</div>
{/if}
@@ -61,14 +61,14 @@
</div>
<div class="card-body p-4" id="forms">
<form method="post" action="{$ABS_PATH}">
<form method="post" action="{$ABS_PATH}" enctype="multipart/form-data">
<fieldset>
<legend class="d-none">Форма добавления комментария</legend>
{* Блок для имени и email *}
{if $smarty.session.user_group != '2'}
{* АВТОРИЗОВАННЫЙ ПОЛЬЗОВАТЕЛЬ: Отображаем поля как readonly *}
{* АВТОРИЗОВАННЫЙ ПОЛЬЗОВАТЕЛЬ *}
<div class="row mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<label for="in_author_name_display" class="form-label">{#COMMENT_YOUR_NAME#}</label>
@@ -79,19 +79,16 @@
<input type="email" id="in_author_email_display" class="form-control" value="{$smarty.session.user_email|default:''|escape|stripslashes}" readonly />
</div>
</div>
{* Скрытые поля для отправки *}
<input name="comment_author_name" type="hidden" id="in_author_name" value="{$smarty.session.user_name|default:''|escape|stripslashes}" />
<input name="comment_author_email" type="hidden" id="in_author_email" value="{$smarty.session.user_email|default:''|escape|stripslashes}" />
{else}
{* ГОСТЬ (требуется ввод имени) *}
{* ГОСТЬ *}
<div class="row mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<label for="in_author_name" class="form-label">{#COMMENT_YOUR_NAME#}*</label>
<input name="comment_author_name" type="text" id="in_author_name" class="form-control" placeholder="{#COMMENT_YOUR_NAME#}" value="{$smarty.request.comment_author_name|default:''|escape|stripslashes}" />
</div>
{* ГОСТЬ (email) *}
<div class="col-md-6">
<label for="in_author_email" class="form-label">{#COMMENT_YOUR_EMAIL#}*</label>
<input name="comment_author_email" type="email" id="in_author_email" class="form-control" placeholder="{#COMMENT_YOUR_EMAIL#}" value="{$smarty.request.comment_author_email|default:''|escape|stripslashes}" />
@@ -101,7 +98,6 @@
{* ДИНАМИЧЕСКИЕ ПОЛЯ (Field 1 и Field 2) *}
<div class="row mb-3">
{* Поле №1 *}
{if $comment_show_f1 == 1}
<div class="col-md-6 mb-3 mb-md-0">
<label for="in_author_website" class="form-label">
@@ -111,7 +107,6 @@
</div>
{/if}
{* Поле №2 *}
{if $comment_show_f2 == 1}
<div class="col-md-6">
<label for="in_author_city" class="form-label">
@@ -133,14 +128,40 @@
{#COMMENT_CHARS_LEFT#} <span class="charsLeft fw-bold" id="charsLeft_new"></span>
</p>
{* Блок загрузки изображения *}
{if $comment_allow_files == 1}
<div class="mb-3">
<label for="comment_image" class="form-label text-muted small">
<i class="bi bi-camera me-1"></i> Прикрепить изображение (JPG, PNG, GIF)
</label>
<input type="file" name="comment_image" id="comment_image" class="form-control form-control-sm" accept="image/*">
{* Контейнер для превью *}
<div id="image_preview_wrapper" class="mt-2 d-none position-relative" style="width: fit-content;">
<img id="image_preview_img" src="#" alt="Предпросмотр" class="img-thumbnail" style="max-height: 120px;">
<button type="button" id="remove_image_btn" class="btn btn-danger btn-sm position-absolute top-0 end-0"
style="padding: 0 5px; margin: 2px;" title="Удалить фото">
<i class="bi bi-x-lg"></i>
</button>
</div>
<div id="file_error" class="text-danger small mt-1 d-none"></div>
</div>
{/if}
{* CAPTCHA *}
{if $im}
<div class="row mb-3 align-items-center">
<div class="col-md-4">
<label for="securecode" class="form-label">{#COMMENT_FORM_CODE#}</label>
<span id="captcha" class="d-block mb-2">
<img src="{$ABS_PATH}inc/captcha.php" alt="{#COMMENT_FORM_CODE#}" class="img-fluid border rounded" style="cursor: pointer;" width="120" height="60" border="0" />
</span>
<div id="captcha_block">
<span id="captcha" class="d-block mb-1">
<img src="{$ABS_PATH}inc/captcha.php" alt="{#COMMENT_FORM_CODE#}" class="img-fluid border rounded" style="cursor: pointer;" width="120" height="60" border="0" />
</span>
<a href="javascript:void(0);" id="reload_captcha" class="small text-decoration-none text-muted">
<i class="bi bi-arrow-clockwise"></i> {if #COMMENT_RELOAD_CODE#}{#COMMENT_RELOAD_CODE#}{else}Обновить код{/if}
</a>
</div>
</div>
<div class="col-md-8">
<label for="securecode" class="form-label">{#COMMENT_FORM_CODE_ENTER#}*</label>
@@ -179,24 +200,24 @@
</div>
{/if}
{* Модальное окно удаления ... *}
{* Модальное окно удаления *}
<div class="modal fade" id="deleteCommentModal" tabindex="-1" aria-labelledby="deleteCommentModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteCommentModalLabel">Подтверждение действия</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Закрыть"></button>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteCommentModalLabel">Подтверждение действия</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
Вы уверены, что хотите **удалить этот комментарий**?
Это действие необратимо.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-danger" id="confirmDeleteButton">Удалить комментарий</button>
</div>
</div>
</div>
<div class="modal-body">
Вы уверены, что хотите **удалить этот комментарий**?
Это действие необратимо.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-danger" id="confirmDeleteButton">Удалить комментарий</button>
</div>
</div>
</div>
</div>
{* ПЕРЕМЕННЫЕ JAVASCRIPT *}
@@ -218,7 +239,6 @@
var COMMENT_TEXT_CHANGED = '{#COMMENT_TEXT_CHANGED#}';
var COMMENT_WRONG_CODE = '{#COMMENT_WRONG_CODE#}';
// Передаем настройки обязательности в JS
var REQ_F1 = '{$comment_req_f1}';
var REQ_F2 = '{$comment_req_f2}';
var NAME_F1 = '{$comment_name_f1|default:#COMMENT_YOUR_SITE#}';

View File

@@ -48,7 +48,19 @@
</div>
{* Текст комментария *}
<div class="mod_comment_text{if $smarty.const.UGROUP==1 || $c.comment_author_id==$smarty.session.user_id|default:'*'} editable_text{/if}">{$c.comment_text|escape}</div>
<div class="mod_comment_text{if $smarty.const.UGROUP==1 || $c.comment_author_id==$smarty.session.user_id|default:'*'} editable_text{/if}">{$c.comment_text|escape|nl2br}</div>
{* НОВОЕ: ВЫВОД ПРИКРЕПЛЕННОГО ИЗОБРАЖЕНИЯ *}
{if !empty($c.comment_file)}
<div class="mod_comment_attached_image mt-3">
<a href="{$ABS_PATH}uploads/comments/{$c.comment_file}" target="_blank" class="comment-img-link">
<img src="{$ABS_PATH}uploads/comments/{$c.comment_file}"
class="img-fluid rounded border shadow-sm"
style="max-width: 300px; max-height: 400px; cursor: zoom-in;"
alt="Изображение к комментарию" />
</a>
</div>
{/if}
</div>