добавлена массовая загрузка файлов
This commit is contained in:
@@ -478,154 +478,119 @@ function commentListShow($tpl_dir)
|
||||
&& !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)
|
||||
// --- ОБРАБОТКА ЗАГРУЗКИ ИЗОБРАЖЕНИЯ (С ЗАЩИТОЙ ОТ ДУБЛЕЙ) ---
|
||||
$uploaded_files = [];
|
||||
if ($settings['comment_allow_files'] == 1 && isset($_FILES['comment_image']))
|
||||
{
|
||||
$upload_path = BASE_DIR . '/uploads/comments/';
|
||||
|
||||
// Создаем папку, если её нет
|
||||
if (!is_dir($upload_path)) {
|
||||
@mkdir($upload_path, 0775, true);
|
||||
|
||||
// Содержимое для защитного файла index.php
|
||||
$index_content = "<?php
|
||||
/**
|
||||
* Файл-заглушка, предназначенный для запрета показа списка файлов в текущей директории,
|
||||
* если через адресную строку браузера было прямое обращение к данной директории.
|
||||
*/
|
||||
header('Location:/');
|
||||
exit;
|
||||
?>";
|
||||
|
||||
// Создаем index.php для безопасности (запрет листинга папки и редирект)
|
||||
$index_content = "<?php\n/**\n * Файл-заглушка...\n */\nheader('Location:/');\nexit;\n?>";
|
||||
@file_put_contents($upload_path . 'index.php', $index_content);
|
||||
}
|
||||
|
||||
// ---ДИНАМИЧЕСКАЯ ПРОВЕРКА РАСШИРЕНИЯ И РАЗМЕРА ---
|
||||
$ext = strtolower(pathinfo($_FILES['comment_image']['name'], PATHINFO_EXTENSION));
|
||||
$allowed_ext_str = $settings['comment_allowed_extensions'] ?? 'jpg,jpeg,png,gif,webp';
|
||||
$allowed_extensions = array_map('trim', explode(',', strtolower($allowed_ext_str)));
|
||||
$files_to_process = [];
|
||||
if (is_array($_FILES['comment_image']['name'])) {
|
||||
foreach ($_FILES['comment_image']['name'] as $k => $v) {
|
||||
if (!empty($v) && $_FILES['comment_image']['error'][$k] == UPLOAD_ERR_OK) {
|
||||
$files_to_process[] = [
|
||||
'name' => $_FILES['comment_image']['name'][$k],
|
||||
'tmp_name' => $_FILES['comment_image']['tmp_name'][$k],
|
||||
'size' => $_FILES['comment_image']['size'][$k]
|
||||
];
|
||||
}
|
||||
}
|
||||
} elseif (!empty($_FILES['comment_image']['name']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK) {
|
||||
$files_to_process[] = $_FILES['comment_image'];
|
||||
}
|
||||
|
||||
$max_file_size_kb = (int)($settings['comment_max_file_size'] ?? 2048);
|
||||
$max_file_size_bytes = $max_file_size_kb * 1024;
|
||||
$file_size = $_FILES['comment_image']['size'];
|
||||
$allowed_ext_str = $settings['comment_allowed_extensions'] ?? 'jpg,jpeg,png,gif,webp';
|
||||
$allowed_extensions = array_map('trim', explode(',', strtolower($allowed_ext_str)));
|
||||
$max_file_size_bytes = (int)($settings['comment_max_file_size'] ?? 2048) * 1024;
|
||||
|
||||
// Дополнительная проверка: действительно ли это изображение по содержимому
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $_FILES['comment_image']['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
// Защита: не даем загрузить одно и то же содержимое дважды в одном запросе
|
||||
$processed_hashes = [];
|
||||
|
||||
if (in_array($ext, $allowed_extensions)
|
||||
&& $file_size > 0
|
||||
&& $file_size <= $max_file_size_bytes
|
||||
&& strpos($mime, 'image/') === 0) // Проверяем, что MIME начинается с image/
|
||||
{
|
||||
$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 = '';
|
||||
}
|
||||
} else {
|
||||
// Если файл не прошел проверку, можно выдать ошибку (для AJAX)
|
||||
if ($ajax) { echo 'error_file_validation'; exit; }
|
||||
}
|
||||
foreach ($files_to_process as $file) {
|
||||
$file_hash = md5_file($file['tmp_name']);
|
||||
if (in_array($file_hash, $processed_hashes)) continue;
|
||||
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $file['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (in_array($ext, $allowed_extensions)
|
||||
&& $file['size'] > 0
|
||||
&& $file['size'] <= $max_file_size_bytes
|
||||
&& strpos($mime, 'image/') === 0)
|
||||
{
|
||||
$new_name = time() . '_' . rand(100, 999) . '.' . $ext;
|
||||
if (move_uploaded_file($file['tmp_name'], $upload_path . $new_name)) {
|
||||
$uploaded_files[] = $new_name;
|
||||
$processed_hashes[] = $file_hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- ПОДГОТОВКА ДАННЫХ ДЛЯ БД ---
|
||||
|
||||
// ПРОВЕРКА РОДИТЕЛЯ: Если родитель не найден в БД, сбрасываем в 0 (корень)
|
||||
$parent_id = (int)($_POST['parent_id'] ?? 0);
|
||||
if ($parent_id > 0) {
|
||||
$parent_exists = $AVE_DB->Query("SELECT Id FROM " . PREFIX . "_module_comment_info WHERE Id = '" . $parent_id . "'")->GetCell();
|
||||
if (!$parent_exists) $parent_id = 0;
|
||||
}
|
||||
$new_comment['parent_id'] = $parent_id;
|
||||
|
||||
$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['anon_key'] = ($new_comment['comment_author_id'] == 0) ? $anon_key : '';
|
||||
|
||||
$new_comment['comment_author_email'] = addslashes(strip_tags($_POST['comment_author_email'] ?? ''));
|
||||
$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;
|
||||
$new_comment['comment_file'] = $comment_file_name;
|
||||
|
||||
// Пишем список файлов
|
||||
$new_comment['comment_file'] = !empty($uploaded_files) ? implode(',', $uploaded_files) : '';
|
||||
|
||||
// --- ПОЛУЧАЕМ РЕЙТИНГ ИЗ ФОРМЫ ---
|
||||
$user_rating = (int)($_POST['comment_user_rating'] ?? 0);
|
||||
// Если это ответ (parent_id > 0) И в настройках выключено "Использовать в ответах"
|
||||
if ($parent_id > 0 && (!isset($settings['comment_show_user_rating_replies']) || $settings['comment_show_user_rating_replies'] == 0)) {
|
||||
$user_rating = 0; // Принудительно сбрасываем оценку для ответов
|
||||
$user_rating = 0;
|
||||
}
|
||||
if ($user_rating < 0) $user_rating = 0; if ($user_rating > 5) $user_rating = 5;
|
||||
$new_comment['user_rating'] = $user_rating;
|
||||
|
||||
$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);
|
||||
$new_comment['comment_text'] = addslashes($comment_text_clean);
|
||||
|
||||
// Выполняем запрос
|
||||
$AVE_DB->Query("
|
||||
INSERT INTO " . PREFIX . "_module_comment_info
|
||||
(`" . implode('`,`', array_keys($new_comment)) ."`)
|
||||
VALUES
|
||||
('" . implode("','", $new_comment) . "')
|
||||
");
|
||||
$AVE_DB->Query("INSERT INTO " . PREFIX . "_module_comment_info (`" . implode('`,`', array_keys($new_comment)) ."`) VALUES ('" . implode("','", $new_comment) . "')");
|
||||
$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'];
|
||||
|
||||
$mail_text = $AVE_Template->get_config_vars('COMMENT_MESSAGE_ADMIN');
|
||||
$mail_text = str_replace('%COMMENT%', stripslashes($new_comment['comment_text']), $mail_text);
|
||||
$mail_text = str_replace('%N%', "\n", $mail_text);
|
||||
$mail_text = str_replace('%PAGE%', $page_link, $mail_text);
|
||||
$mail_text = str_replace('&', '&', $mail_text);
|
||||
|
||||
send_mail($mail_from, $mail_text, $AVE_Template->get_config_vars('COMMENT_SUBJECT_MAIL'), $mail_from, $mail_from_name, 'text');
|
||||
$page_link = get_home_link() . urldecode(base64_decode($page)) . '&comment_id=' . $new_comment['Id'] . '#' . $new_comment['Id'];
|
||||
$mail_text = "Новый комментарий:\n" . stripslashes($new_comment['comment_text']) . "\n\nСсылка: " . $page_link;
|
||||
send_mail($mail_from, $mail_text, "Новый комментарий на сайте", $mail_from, $mail_from_name, 'text');
|
||||
|
||||
if ($ajax)
|
||||
{
|
||||
// 1. Получаем аватар как обычно
|
||||
$new_comment['avatar'] = (isset($new_comment['comment_author_id']) && $new_comment['comment_author_id'] > 0) ? getAvatar($new_comment['comment_author_id'], 48) : '';
|
||||
|
||||
// 2. Очищаем, если это системный user.png
|
||||
if (!empty($new_comment['avatar']) && strpos($new_comment['avatar'], 'user.png') !== false)
|
||||
{
|
||||
$new_comment['avatar'] = '';
|
||||
}
|
||||
|
||||
// 3. Если аватара нет — генерируем данные для буквы
|
||||
if (empty($new_comment['avatar']))
|
||||
{
|
||||
if (empty($new_comment['avatar']) || strpos($new_comment['avatar'], 'user.png') !== false) {
|
||||
$name = !empty($new_comment['comment_author_name']) ? stripslashes($new_comment['comment_author_name']) : 'Guest';
|
||||
$new_comment['first_letter'] = mb_substr(trim($name), 0, 1, 'UTF-8');
|
||||
$new_comment['avatar_color_index'] = (abs(crc32($name)) % 12) + 1;
|
||||
}
|
||||
$new_comment['comment_changed'] = 0;
|
||||
|
||||
// Передаем флаги для мгновенной активации управления в шаблоне
|
||||
$new_comment['can_edit'] = 1;
|
||||
$new_comment['edit_time_left'] = $this->conf_edit_time;
|
||||
|
||||
$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;
|
||||
$AVE_Template->assign('subcomments', $subcomments);
|
||||
$AVE_Template->assign('theme', defined('THEME_FOLDER') ? THEME_FOLDER : DEFAULT_THEME_FOLDER);
|
||||
$AVE_Template->display($tpl_dir . $this->_comments_tree_sub_tpl);
|
||||
}
|
||||
}
|
||||
@@ -647,7 +612,6 @@ function commentPostEdit($comment_id)
|
||||
if ($comment_id <= 0 || $user_group <= 0) exit('INVALID_ID');
|
||||
|
||||
// 2. Получаем данные комментария и настройки модуля (JOIN)
|
||||
// ДОБАВИЛ: выборку новых полей (allowed_extensions и max_file_size)
|
||||
$row = $AVE_DB->Query("
|
||||
SELECT
|
||||
msg.*,
|
||||
@@ -698,50 +662,69 @@ function commentPostEdit($comment_id)
|
||||
if (mb_strlen($comment_text) > $max) $comment_text_cut .= '…';
|
||||
|
||||
|
||||
// 5. Работа с изображениями (С МАКСИМАЛЬНОЙ ЗАЩИТОЙ)
|
||||
$new_file_sql = "";
|
||||
$upload_dir = BASE_DIR . '/uploads/comments/';
|
||||
// 5. Работа с изображениями (МУЛЬТИЗАГРУЗКА И УДАЛЕНИЕ)
|
||||
$upload_dir = BASE_DIR . '/uploads/comments/';
|
||||
// Получаем текущие файлы из БД в массив
|
||||
$current_files = !empty($row['comment_file']) ? explode(',', $row['comment_file']) : [];
|
||||
|
||||
if (isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK) {
|
||||
|
||||
// Настройки из БД
|
||||
$file_ext = strtolower(pathinfo($_FILES['comment_image']['name'], PATHINFO_EXTENSION));
|
||||
$allowed_ext_str = $row['comment_allowed_extensions'] ?? 'jpg,jpeg,png,gif,webp';
|
||||
$allowed_extensions = array_map('trim', explode(',', strtolower($allowed_ext_str)));
|
||||
|
||||
$max_kb = (int)($row['comment_max_file_size'] ?? 2048);
|
||||
$file_size = $_FILES['comment_image']['size'];
|
||||
// --- А. Обработка выборочного удаления ---
|
||||
if (!empty($_POST['delete_files'])) {
|
||||
$files_to_remove = explode(',', $_POST['delete_files']);
|
||||
foreach ($files_to_remove as $rem_file) {
|
||||
$rem_file = trim($rem_file);
|
||||
if (empty($rem_file)) continue;
|
||||
|
||||
// --- НОВОЕ: Проверка MIME-типа (защита от переименованных скриптов) ---
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $_FILES['comment_image']['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
// ПРОВЕРКА: расширение + размер + MIME-тип
|
||||
if (in_array($file_ext, $allowed_extensions)
|
||||
&& $file_size > 0
|
||||
&& $file_size <= ($max_kb * 1024)
|
||||
&& strpos($mime, 'image/') === 0) // Файл действительно картинка
|
||||
{
|
||||
if (!is_dir($upload_dir)) @mkdir($upload_dir, 0775, true);
|
||||
|
||||
// Удаляем старый файл, если он был
|
||||
if (!empty($row['comment_file']) && file_exists($upload_dir . $row['comment_file'])) {
|
||||
@unlink($upload_dir . $row['comment_file']);
|
||||
// Удаляем физически
|
||||
if (file_exists($upload_dir . $rem_file)) {
|
||||
@unlink($upload_dir . $rem_file);
|
||||
}
|
||||
// Удаляем из нашего массива для БД
|
||||
$current_files = array_filter($current_files, function($v) use ($rem_file) {
|
||||
return trim($v) !== $rem_file;
|
||||
});
|
||||
}
|
||||
|
||||
// Генерируем новое имя
|
||||
$new_file_name = 'comm_' . time() . '_' . rand(100, 999) . '.' . $file_ext;
|
||||
|
||||
if (move_uploaded_file($_FILES['comment_image']['tmp_name'], $upload_dir . $new_file_name)) {
|
||||
$new_file_sql = ", comment_file = '" . addslashes($new_file_name) . "'";
|
||||
}
|
||||
} else {
|
||||
// Если файл не прошел серверную проверку
|
||||
echo "FILE_ERROR";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Б. Загрузка новых файлов (если есть) ---
|
||||
// Проверяем, что пришел массив файлов
|
||||
if (isset($_FILES['comment_image']) && is_array($_FILES['comment_image']['name'])) {
|
||||
|
||||
$allowed_ext_str = $row['comment_allowed_extensions'] ?? 'jpg,jpeg,png,gif,webp';
|
||||
$allowed_extensions = array_map('trim', explode(',', strtolower($allowed_ext_str)));
|
||||
$max_kb = (int)($row['comment_max_file_size'] ?? 2048);
|
||||
|
||||
// Перебираем загруженные файлы
|
||||
foreach ($_FILES['comment_image']['name'] as $i => $fname) {
|
||||
if ($_FILES['comment_image']['error'][$i] == UPLOAD_ERR_OK) {
|
||||
|
||||
$tmp_name = $_FILES['comment_image']['tmp_name'][$i];
|
||||
$file_ext = strtolower(pathinfo($fname, PATHINFO_EXTENSION));
|
||||
$file_size = $_FILES['comment_image']['size'][$i];
|
||||
|
||||
// Проверка MIME-типа
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $tmp_name);
|
||||
finfo_close($finfo);
|
||||
|
||||
if (in_array($file_ext, $allowed_extensions)
|
||||
&& $file_size > 0
|
||||
&& $file_size <= ($max_kb * 1024)
|
||||
&& strpos($mime, 'image/') === 0)
|
||||
{
|
||||
if (!is_dir($upload_dir)) @mkdir($upload_dir, 0775, true);
|
||||
|
||||
$new_file_name = 'comm_' . time() . '_' . rand(1000, 9999) . '.' . $file_ext;
|
||||
|
||||
if (move_uploaded_file($tmp_name, $upload_dir . $new_file_name)) {
|
||||
$current_files[] = $new_file_name; // Добавляем новый файл к списку
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Собираем итоговую строку файлов для БД через запятую
|
||||
$final_files_str = implode(',', array_unique(array_filter($current_files)));
|
||||
|
||||
$user_rating = isset($_POST['user_rating']) ? (int)$_POST['user_rating'] : (int)$row['user_rating'];
|
||||
|
||||
@@ -754,8 +737,8 @@ if (isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLO
|
||||
comment_changed = '" . time() . "',
|
||||
comment_text = '" . addslashes($comment_text_cut) . "',
|
||||
comment_status = '" . $new_status . "',
|
||||
user_rating = '" . $user_rating . "'
|
||||
$new_file_sql
|
||||
user_rating = '" . $user_rating . "',
|
||||
comment_file = '" . addslashes($final_files_str) . "'
|
||||
WHERE
|
||||
Id = '" . $comment_id . "'
|
||||
");
|
||||
@@ -774,8 +757,6 @@ function commentPostDelete($comment_id)
|
||||
if ($comment_id <= 0) die('Ошибка: Неверный ID');
|
||||
|
||||
// --- ШАГ 0. ПОЛУЧЕНИЕ ДАННЫХ И ПРОВЕРКА ПРАВ ---
|
||||
|
||||
// ГАРАНТИРУЕМ, что настройки загружены из БД в свойства класса
|
||||
$this->_commentSettingsGet();
|
||||
|
||||
$comment_data = $AVE_DB->Query("
|
||||
@@ -798,7 +779,6 @@ function commentPostDelete($comment_id)
|
||||
$is_author = ($current_user_id > 0 && $current_user_id == $comment_data['comment_author_id']) ||
|
||||
($comment_data['comment_author_id'] == 0 && !empty($comment_data['anon_key']) && $comment_data['anon_key'] == $anon_key);
|
||||
|
||||
// Теперь $this->conf_edit_time точно содержит значение из БД (благодаря вызову выше)
|
||||
$is_time_ok = (time() - (int)$comment_data['comment_published'] < (int)$this->conf_edit_time);
|
||||
|
||||
if ($is_author && $is_time_ok) {
|
||||
@@ -811,29 +791,36 @@ function commentPostDelete($comment_id)
|
||||
die('Доступ запрещен');
|
||||
}
|
||||
|
||||
// --- ШАГ 1. ПРОВЕРКА НАЛИЧИЯ ОТВЕТОВ (без изменений) ---
|
||||
$has_children = $AVE_DB->Query("SELECT COUNT(*) FROM " . PREFIX . "_module_comment_info WHERE parent_id = '" . $comment_id . "'")->GetCell();
|
||||
$upload_dir = BASE_DIR . '/uploads/comments/';
|
||||
|
||||
// Мягкое удаление ТОЛЬКО для авторов, если есть ответы
|
||||
// --- ВАРИАНТ А: МЯГКОЕ УДАЛЕНИЕ (если есть ответы и удаляет не админ) ---
|
||||
if ($has_children > 0 && $user_group != 1) {
|
||||
// Удаляем файлы физически, если они есть
|
||||
if (!empty($comment_data['comment_file'])) {
|
||||
if (file_exists($upload_dir . $comment_data['comment_file'])) @unlink($upload_dir . $comment_data['comment_file']);
|
||||
$files_to_del = explode(',', $comment_data['comment_file']);
|
||||
foreach ($files_to_del as $f_name) {
|
||||
$f_path = $upload_dir . trim($f_name);
|
||||
if (!empty($f_name) && file_exists($f_path)) @unlink($f_path);
|
||||
}
|
||||
}
|
||||
|
||||
$del_text = "<span class='comment-deleted'>Комментарий удален автором.</span>";
|
||||
$AVE_DB->Query("
|
||||
UPDATE " . PREFIX . "_module_comment_info
|
||||
SET comment_text = '" . addslashes($del_text) . "', comment_file = '', anon_key = '', comment_changed = '" . time() . "'
|
||||
SET comment_text = '" . addslashes($del_text) . "',
|
||||
comment_file = '',
|
||||
anon_key = '',
|
||||
comment_changed = '" . time() . "'
|
||||
WHERE Id = '" . $comment_id . "'
|
||||
");
|
||||
echo "OK_SOFT";
|
||||
}
|
||||
else {
|
||||
// --- ВАРИАНТ Б: ПОЛНОЕ УДАЛЕНИЕ (Для админа или если нет детей) ---
|
||||
|
||||
// --- ВАРИАНТ Б: ПОЛНОЕ УДАЛЕНИЕ (Админ или нет вложенных ответов) ---
|
||||
$ids_to_delete = [$comment_id];
|
||||
|
||||
// Если админ — собираем всю ветку детей для удаления
|
||||
if ($user_group == 1) {
|
||||
$all_child_ids = [];
|
||||
$parent_ids = [$comment_id];
|
||||
@@ -852,14 +839,20 @@ function commentPostDelete($comment_id)
|
||||
}
|
||||
|
||||
$final_ids_str = implode(',', $ids_to_delete);
|
||||
|
||||
// Массовое удаление всех файлов во всех удаляемых комментариях
|
||||
$files_res = $AVE_DB->Query("SELECT comment_file FROM " . PREFIX . "_module_comment_info WHERE Id IN ($final_ids_str)");
|
||||
while ($f = $files_res->FetchAssocArray()) {
|
||||
if (!empty($f['comment_file'])) {
|
||||
$f_path = $upload_dir . $f['comment_file'];
|
||||
if (file_exists($f_path)) @unlink($f_path);
|
||||
$files_to_del = explode(',', $f['comment_file']);
|
||||
foreach ($files_to_del as $f_name) {
|
||||
$f_path = $upload_dir . trim($f_name);
|
||||
if (!empty($f_name) && file_exists($f_path)) @unlink($f_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем записи из БД
|
||||
$AVE_DB->Query("DELETE FROM " . PREFIX . "_module_comment_info WHERE Id IN ($final_ids_str)");
|
||||
|
||||
echo "OK";
|
||||
|
||||
402
js/comment.js
402
js/comment.js
@@ -155,13 +155,16 @@ function initCommentTimers() {
|
||||
// Проверка Текста
|
||||
setInvalid($(form.comment_text), !form.comment_text.value.trim(), "Введите текст комментария");
|
||||
|
||||
// Проверка файла в основной форме
|
||||
// Проверка файлов в основной форме (Мультизагрузка)
|
||||
var $fileInput = $(form).find('#comment_image');
|
||||
if ($fileInput.length && $fileInput[0].files[0]) {
|
||||
var isFileOk = checkFileConsistency($fileInput[0].files[0], $('#file_error'));
|
||||
if (!isFileOk) {
|
||||
$fileInput.addClass('is-invalid');
|
||||
isValid = false;
|
||||
if ($fileInput.length && $fileInput[0].files.length > 0) {
|
||||
var files = $fileInput[0].files;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (!checkFileConsistency(files[i], $('#file_error'))) {
|
||||
$fileInput.addClass('is-invalid');
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,31 +233,56 @@ if ($fileInput.length && $fileInput[0].files[0]) {
|
||||
$doc.on('change', '#comment_image', function() {
|
||||
var input = this;
|
||||
var $errorBox = $('#file_error');
|
||||
var $previewWrapper = $('#image_preview_wrapper');
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
// Сначала проверяем файл
|
||||
if (!checkFileConsistency(input.files[0], $errorBox)) {
|
||||
$(input).val('').addClass('is-invalid');
|
||||
$('#image_preview_wrapper').addClass('d-none');
|
||||
return;
|
||||
}
|
||||
$previewWrapper.empty().addClass('d-none');
|
||||
$errorBox.addClass('d-none');
|
||||
$(input).removeClass('is-invalid');
|
||||
|
||||
// Если файл прошел проверку — показываем превью
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
$('#image_preview_img').attr('src', e.target.result);
|
||||
$('#image_preview_wrapper').removeClass('d-none');
|
||||
$(input).removeClass('is-invalid');
|
||||
}
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
if (input.files && input.files.length > 0) {
|
||||
var files = Array.from(input.files);
|
||||
$previewWrapper.removeClass('d-none').addClass('d-flex flex-wrap gap-2');
|
||||
|
||||
files.forEach(function(file) {
|
||||
if (checkFileConsistency(file, $errorBox)) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var imgHtml = `
|
||||
<div class="position-relative preview-item">
|
||||
<img src="${e.target.result}" class="img-thumbnail" style="max-height: 100px; min-width: 100px; object-fit: cover;">
|
||||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-new-img"
|
||||
style="padding: 0 5px; margin: 2px;" title="Удалить">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>`;
|
||||
$previewWrapper.append(imgHtml);
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
$(input).val('').addClass('is-invalid');
|
||||
$previewWrapper.addClass('d-none');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$doc.on('click', '#remove_image_btn', function() {
|
||||
$('#comment_image').val('');
|
||||
$('#image_preview_wrapper').addClass('d-none');
|
||||
$('#image_preview_img').attr('src', '#');
|
||||
});
|
||||
// старый обработчик, доработанный под мультизагрузку
|
||||
$doc.on('click', '#remove_image_btn', function() {
|
||||
$('#comment_image').val(''); // Очищаем выбор файлов
|
||||
$('#image_preview_wrapper').addClass('d-none').empty(); // Скрываем и полностью очищаем блок превью
|
||||
// Строка с #image_preview_img больше не нужна, так как картинок теперь много
|
||||
});
|
||||
|
||||
// удаление картинок по одной (крестиком на самой картинке)
|
||||
$doc.on('click', '.remove-new-img', function() {
|
||||
$(this).closest('.preview-item').remove(); // Удаляем только этот контейнер с картинкой
|
||||
|
||||
// Если после удаления картинок внутри не осталось — скрываем обертку и чистим инпут
|
||||
if ($('#image_preview_wrapper').children().length === 0) {
|
||||
$('#image_preview_wrapper').addClass('d-none');
|
||||
$('#comment_image').val('');
|
||||
}
|
||||
});
|
||||
|
||||
// --- ЛОГИКА ВЫБОРА РЕЙТИНГА В ФОРМЕ ---
|
||||
$doc.on('mouseenter', '.star-choice', function() {
|
||||
@@ -404,155 +432,186 @@ $doc.on('mouseleave', '.rating-edit-block, #rating_wrapper', function() {
|
||||
cAction(this, $(this).hasClass('mod_comment_lock') ? 'lock' : 'unlock');
|
||||
});
|
||||
|
||||
// --- РЕДАКТИРОВАНИЕ (С ПРЕДПРОСМОТРОМ ТЕКУЩЕГО ФОТО) ---
|
||||
$doc.off('click', '.mod_comment_edit').on('click', '.mod_comment_edit', function(e) {
|
||||
e.preventDefault();
|
||||
var cid = $(this).data('id');
|
||||
var $wrapper = $('#comment_wrapper_' + cid);
|
||||
var $textBlock = $wrapper.find('.mod_comment_text').first();
|
||||
|
||||
if ($wrapper.find('.edit-form-container').length > 0) return;
|
||||
|
||||
var isMyOwn = $wrapper.attr('data-is-own') == '1';
|
||||
var currentRating = parseInt($wrapper.attr('data-user-rating')) || 0;
|
||||
|
||||
$('.edit-form-container').remove();
|
||||
$('.mod_comment_text').show();
|
||||
|
||||
var cleanText = $textBlock.html().replace(/<br\s*\/?>/mg, "\n").trim();
|
||||
var currentImg = $wrapper.find('.mod_comment_attached_image img').first().attr('src');
|
||||
|
||||
var starsEditBlock = '';
|
||||
|
||||
// --- ИЗМЕНЕНИЕ 2: Проверка запрета звезд для ответов при редактировании ---
|
||||
// Добавлена более строгая проверка data-parent
|
||||
var parentIdVal = parseInt($wrapper.attr('data-parent')) || 0;
|
||||
var isReply = parentIdVal > 0;
|
||||
var ratingForbiddenForReply = (isReply && typeof SHOW_USER_RATING_REPLIES !== 'undefined' && SHOW_USER_RATING_REPLIES == '0');
|
||||
|
||||
if (typeof SHOW_USER_RATING !== 'undefined' && SHOW_USER_RATING == '1' && isMyOwn && !ratingForbiddenForReply) {
|
||||
if (typeof UGROUP !== 'undefined' && (UGROUP != '2' || RATING_ANON_SET == '1')) {
|
||||
var starsHtml = '';
|
||||
for(var i=1; i<=5; i++) {
|
||||
var starClass = (i <= currentRating) ? 'bi-star-fill' : 'bi-star';
|
||||
starsHtml += `<i class="star-choice ${starClass}" data-value="${i}" style="cursor: pointer; color: #ffc107; font-size: 1.2rem; margin-right: 2px;"></i>`;
|
||||
}
|
||||
starsEditBlock = `
|
||||
<div class="mb-2 rating-edit-block">
|
||||
<label class="small text-muted d-block mb-1">Ваша оценка:</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<div id="user_rating_stars_${cid}">${starsHtml}</div>
|
||||
<a href="javascript:void(0);" class="reset-edit-stars ms-3 text-decoration-none small text-muted" data-id="${cid}">
|
||||
<i class="bi bi-x-circle"></i> Сбросить
|
||||
</a>
|
||||
</div>
|
||||
<input type="hidden" name="comment_user_rating" id="rating_input_${cid}" value="${currentRating}" />
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
// --- КОНЕЦ ИЗМЕНЕНИЯ 2 ---
|
||||
|
||||
// Обертка для ТЕКУЩЕГО изображения с крестиком (как при загрузке)
|
||||
var currentImgPreview = '';
|
||||
if (currentImg) {
|
||||
currentImgPreview = `
|
||||
<div id="existing_preview_wrapper_${cid}" class="mt-2 mb-2 position-relative" style="width: fit-content;">
|
||||
<img src="${currentImg}" class="img-thumbnail" style="max-height: 100px;">
|
||||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-existing-img"
|
||||
data-id="${cid}" style="padding: 0 5px; margin: 2px;" title="Удалить текущее фото">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
<input class="form-check-input d-none" type="checkbox" id="del_img_${cid}">
|
||||
</div>`;
|
||||
}
|
||||
|
||||
var fileInputHtml = '';
|
||||
if (typeof UGROUP !== 'undefined' && (UGROUP != '2' || (typeof ALLOW_FILES_ANON !== 'undefined' && ALLOW_FILES_ANON == '1'))) {
|
||||
// Получаем значения для подсказки (те же, что и в основной форме)
|
||||
var allowedText = (typeof ALLOWED_EXTENSIONS !== 'undefined') ? ALLOWED_EXTENSIONS : 'jpg,jpeg,png,gif,webp';
|
||||
var sizeText = (typeof MAX_FILE_SIZE_KB !== 'undefined') ? MAX_FILE_SIZE_KB : '2048';
|
||||
|
||||
fileInputHtml = `
|
||||
<div class="mb-2">
|
||||
<label class="small text-muted mb-1"><i class="bi bi-paperclip me-1"></i> Заменить/Добавить файл:</label>
|
||||
|
||||
// НАЧАЛО $doc.off('click', '.mod_comment_edit')
|
||||
|
||||
$doc.off('click', '.mod_comment_edit').on('click', '.mod_comment_edit', function(e) {
|
||||
e.preventDefault();
|
||||
var cid = $(this).data('id');
|
||||
var $wrapper = $('#comment_wrapper_' + cid);
|
||||
|
||||
if ($wrapper.length === 0) {
|
||||
console.error("Обертка #comment_wrapper_" + cid + " не найдена!");
|
||||
return;
|
||||
}
|
||||
|
||||
var $textBlock = $wrapper.find('.mod_comment_text').first();
|
||||
|
||||
// Пытаемся найти блок с картинками
|
||||
var $attachedImagesBlock = $wrapper.find('.mod_comment_attached_image');
|
||||
if ($attachedImagesBlock.length === 0) {
|
||||
$attachedImagesBlock = $wrapper.find('.comment-files, .attached-images');
|
||||
}
|
||||
|
||||
if ($wrapper.find('.edit-form-container').length > 0) return;
|
||||
|
||||
var isMyOwn = $wrapper.attr('data-is-own') == '1';
|
||||
var currentRating = parseInt($wrapper.attr('data-user-rating')) || 0;
|
||||
|
||||
$('.edit-form-container').remove();
|
||||
$('.mod_comment_text').show();
|
||||
$('.mod_comment_attached_image, .comment-files').show();
|
||||
|
||||
var cleanText = $textBlock.html().replace(/<br\s*\/?>/mg, "\n").trim();
|
||||
|
||||
// --- СБОР ТЕКУЩИХ КАРТИНОК (ТОТ САМЫЙ РАБОЧИЙ КОД) ---
|
||||
var existingImagesHtml = '';
|
||||
var $foundImgs = $wrapper.find('img').filter(function() {
|
||||
return !$(this).hasClass('star-choice') && !$(this).hasClass('avatar') && !$(this).closest('.stars').length;
|
||||
});
|
||||
|
||||
if ($foundImgs.length > 0) {
|
||||
existingImagesHtml = '<div class="d-flex flex-wrap gap-2 mb-3" style="border: 1px dashed #ccc; padding: 10px; background: #fff;">';
|
||||
$foundImgs.each(function(index) {
|
||||
var imgSrc = $(this).attr('src');
|
||||
var fileName = imgSrc.split('/').pop();
|
||||
|
||||
<div class="small text-muted mb-2">
|
||||
Разрешены: <span class="fw-bold">${allowedText}</span>.
|
||||
Макс. размер: <span class="fw-bold">${sizeText} Кб</span>.
|
||||
existingImagesHtml += `
|
||||
<div id="existing_wrapper_${cid}_${index}" class="position-relative">
|
||||
<img src="${imgSrc}" class="img-thumbnail" style="width: 80px; height: 80px; object-fit: cover;">
|
||||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-existing-img"
|
||||
data-id="${cid}"
|
||||
data-img-path="${fileName}"
|
||||
data-target="existing_wrapper_${cid}_${index}"
|
||||
style="padding: 0 5px; margin: 2px; line-height: 1; z-index: 10;">
|
||||
<i class="bi bi-x-lg" style="font-size: 0.8rem;"></i>
|
||||
</button>
|
||||
</div>`;
|
||||
});
|
||||
existingImagesHtml += '</div>';
|
||||
}
|
||||
|
||||
// --- РЕЙТИНГ (ВОЗВРАЩЕНА ПОЛНАЯ ОРИГИНАЛЬНАЯ ЛОГИКА) ---
|
||||
var starsEditBlock = '';
|
||||
var parentIdVal = parseInt($wrapper.attr('data-parent')) || 0;
|
||||
var isReply = parentIdVal > 0;
|
||||
var ratingForbiddenForReply = (isReply && typeof SHOW_USER_RATING_REPLIES !== 'undefined' && SHOW_USER_RATING_REPLIES == '0');
|
||||
|
||||
if (typeof SHOW_USER_RATING !== 'undefined' && SHOW_USER_RATING == '1' && isMyOwn && !ratingForbiddenForReply) {
|
||||
if (typeof UGROUP !== 'undefined' && (UGROUP != '2' || RATING_ANON_SET == '1')) {
|
||||
var starsHtml = '';
|
||||
for(var i=1; i<=5; i++) {
|
||||
var starClass = (i <= currentRating) ? 'bi-star-fill' : 'bi-star';
|
||||
starsHtml += `<i class="star-choice ${starClass}" data-value="${i}" style="cursor: pointer; color: #ffc107; font-size: 1.2rem; margin-right: 2px;"></i>`;
|
||||
}
|
||||
starsEditBlock = `
|
||||
<div class="mb-2 rating-edit-block">
|
||||
<label class="small text-muted d-block mb-1">Ваша оценка:</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<div id="user_rating_stars_${cid}">${starsHtml}</div>
|
||||
<a href="javascript:void(0);" class="reset-edit-stars ms-3 text-decoration-none small text-muted" data-id="${cid}">
|
||||
<i class="bi bi-x-circle"></i> Сбросить
|
||||
</a>
|
||||
</div>
|
||||
<input type="hidden" name="comment_user_rating" id="rating_input_${cid}" value="${currentRating}" />
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// --- СБОРКА ФОРМЫ ---
|
||||
var editHtml = `
|
||||
<div class="edit-form-container border rounded p-3 bg-light mt-2 mb-2">
|
||||
<textarea rows="5" id="ta_${cid}" class="form-control mb-3">${cleanText}</textarea>
|
||||
${starsEditBlock}
|
||||
|
||||
<p class="small text-muted mb-1">Загруженные файлы:</p>
|
||||
${existingImagesHtml}
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="small text-muted mb-1">Добавить новые:</label>
|
||||
<input type="file" name="comment_image[]" id="new_file_${cid}" class="form-control form-control-sm" accept="image/*" multiple>
|
||||
</div>
|
||||
<input type="hidden" name="delete_files" id="delete_files_${cid}" value="">
|
||||
|
||||
<div class="d-flex gap-2 mt-3 align-items-center">
|
||||
<button type="button" class="btn btn-sm btn-primary saveButton" data-id="${cid}">Сохранить</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary cancelButton">Отмена</button>
|
||||
<small class="ms-auto text-muted">Осталось: <span id="charsLeft_${cid}"></span></small>
|
||||
</div>
|
||||
|
||||
<input type="file" id="new_file_${cid}" class="form-control form-control-sm" accept="image/*">
|
||||
<div id="file_error_${cid}" class="text-danger small mt-1 d-none"></div>
|
||||
</div>
|
||||
<div id="edit_preview_wrapper_${cid}" class="mt-2 mb-2 d-none position-relative" style="width: fit-content;">
|
||||
<img id="edit_preview_img_${cid}" src="#" alt="Превью" class="img-thumbnail" style="max-height: 100px;">
|
||||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-edit-new-img"
|
||||
data-id="${cid}" style="padding: 0 5px; margin: 2px;">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="edit_progress_container_${cid}" class="mt-2 d-none">
|
||||
<div class="progress" style="height: 5px;"><div id="edit_progress_bar_${cid}" class="progress-bar bg-success" style="width: 0%"></div></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
$textBlock.hide();
|
||||
$attachedImagesBlock.hide();
|
||||
$foundImgs.hide();
|
||||
|
||||
$textBlock.after(editHtml);
|
||||
|
||||
// Добавил поддержку лимита символов
|
||||
if ($.fn.limit) {
|
||||
$('#ta_' + cid).limit(1000, '#charsLeft_' + cid);
|
||||
}
|
||||
$('#ta_' + cid).focus();
|
||||
});
|
||||
|
||||
var editHtml = `
|
||||
<div class="edit-form-container border rounded p-3 bg-light mt-2 mb-2">
|
||||
<textarea rows="5" id="ta_${cid}" class="form-control mb-2">${cleanText}</textarea>
|
||||
${starsEditBlock}
|
||||
${currentImgPreview}
|
||||
${fileInputHtml}
|
||||
<div class="d-flex gap-2 align-items-center mt-3">
|
||||
<button type="button" class="btn btn-sm btn-primary saveButton" data-id="${cid}">Сохранить</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary cancelButton">Отмена</button>
|
||||
<small class="ms-auto text-muted">Осталось: <span id="charsLeft_${cid}"></span></small>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
$textBlock.hide().after(editHtml);
|
||||
$('#ta_' + cid).limit(1000, '#charsLeft_' + cid).focus();
|
||||
});
|
||||
|
||||
// Удаление ТЕКУЩЕГО изображения при редактировании
|
||||
$doc.on('click', '.remove-existing-img', function() {
|
||||
var cid = $(this).data('id');
|
||||
$('#existing_preview_wrapper_' + cid).addClass('d-none');
|
||||
$('#del_img_' + cid).prop('checked', true); // Помечаем скрытый чекбокс на удаление
|
||||
});
|
||||
// КОНЕЦ $doc.off('click', '.mod_comment_edit')
|
||||
|
||||
|
||||
$doc.on('click', '.remove-existing-img', function() {
|
||||
var cid = $(this).data('id');
|
||||
var imgPath = $(this).data('img-path'); // Имя файла
|
||||
var targetId = $(this).data('target');
|
||||
|
||||
// Добавляем имя файла в скрытый инпут для сервера
|
||||
var $deleteInput = $('#delete_files_' + cid);
|
||||
var currentDeleteList = $deleteInput.val() ? $deleteInput.val().split(',') : [];
|
||||
|
||||
if (currentDeleteList.indexOf(imgPath) === -1) {
|
||||
currentDeleteList.push(imgPath);
|
||||
$deleteInput.val(currentDeleteList.join(','));
|
||||
}
|
||||
|
||||
// Скрываем блок с картинкой
|
||||
$('#' + targetId).fadeOut(300);
|
||||
});
|
||||
|
||||
// Обработка выбора НОВОГО файла при редактировании
|
||||
$doc.on('change', 'input[id^="new_file_"]', function() {
|
||||
var cid = $(this).attr('id').replace('new_file_', '');
|
||||
var input = this;
|
||||
|
||||
// Создаем или находим блок для ошибки
|
||||
if ($('#file_error_' + cid).length === 0) {
|
||||
$(input).after('<div id="file_error_'+cid+'" class="text-danger small mt-1 d-none"></div>');
|
||||
}
|
||||
var $errorBox = $('#file_error_' + cid);
|
||||
var $previewWrapper = $('#edit_preview_wrapper_' + cid);
|
||||
|
||||
// Сбрасываем старые ошибки
|
||||
// Очищаем превью перед новым выбором
|
||||
$previewWrapper.find('.preview-item-new').remove();
|
||||
$errorBox.addClass('d-none').text('');
|
||||
$(input).removeClass('is-invalid');
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
// 1. Проверяем файл функцией checkFileConsistency
|
||||
if (!checkFileConsistency(input.files[0], $errorBox)) {
|
||||
$(input).val('').addClass('is-invalid');
|
||||
$('#edit_preview_wrapper_' + cid).addClass('d-none');
|
||||
$errorBox.removeClass('d-none');
|
||||
return;
|
||||
}
|
||||
if (input.files && input.files.length > 0) {
|
||||
$previewWrapper.removeClass('d-none');
|
||||
|
||||
Array.from(input.files).forEach(function(file) {
|
||||
if (checkFileConsistency(file, $errorBox)) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
// Создаем новое превью для каждого файла
|
||||
var item = `
|
||||
<div class="position-relative preview-item-new">
|
||||
<img src="${e.target.result}" class="img-thumbnail" style="max-height: 100px;">
|
||||
</div>`;
|
||||
$previewWrapper.append(item);
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Если всё ОК — показываем превью
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
$('#edit_preview_img_' + cid).attr('src', e.target.result);
|
||||
$('#edit_preview_wrapper_' + cid).removeClass('d-none');
|
||||
$('#existing_preview_wrapper_' + cid).addClass('d-none');
|
||||
$('#del_img_' + cid).prop('checked', true);
|
||||
}
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
// Прячем старое фото и помечаем на удаление
|
||||
$('#existing_preview_wrapper_' + cid).addClass('d-none');
|
||||
$('#del_img_' + cid).prop('checked', true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -569,23 +628,28 @@ $doc.on('change', 'input[id^="new_file_"]', function() {
|
||||
$container.remove();
|
||||
});
|
||||
|
||||
$doc.on('click', '.saveButton', function() {
|
||||
$doc.on('click', '.saveButton', function() {
|
||||
var $btn = $(this);
|
||||
var cid = $btn.data('id');
|
||||
var fd = new FormData();
|
||||
fd.append('module', 'comment'); fd.append('action', 'edit'); fd.append('Id', cid);
|
||||
fd.append('text', $('#ta_' + cid).val());
|
||||
|
||||
|
||||
var rInput = $('#rating_input_' + cid);
|
||||
if (rInput.length) fd.append('user_rating', rInput.val());
|
||||
|
||||
fd.append('delete_image', $('#del_img_' + cid).is(':checked') ? 1 : 0);
|
||||
fd.append('delete_files', $('#delete_files_' + cid).val());
|
||||
|
||||
// --- ИЗМЕНЕННЫЙ БЛОК ДЛЯ MULTIPLE ---
|
||||
var fileInput = $('#new_file_' + cid);
|
||||
if (fileInput.length) {
|
||||
var file = fileInput[0].files[0];
|
||||
if (file) fd.append('comment_image', file);
|
||||
if (fileInput.length && fileInput[0].files.length > 0) {
|
||||
for (var i = 0; i < fileInput[0].files.length; i++) {
|
||||
// Добавляем каждый файл в массив comment_image[]
|
||||
fd.append('comment_image[]', fileInput[0].files[i]);
|
||||
}
|
||||
}
|
||||
// ------------------------------------
|
||||
|
||||
$.ajax({
|
||||
url: aveabspath + 'index.php?ajax=1',
|
||||
@@ -606,7 +670,7 @@ $doc.on('change', 'input[id^="new_file_"]', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Отправка новой формы (или ответа)
|
||||
// Отправка новой формы (или ответа)
|
||||
$doc.on('submit', '#mod_comment_new form', function(e) {
|
||||
e.preventDefault();
|
||||
if (!validate(this)) return false;
|
||||
@@ -615,10 +679,24 @@ $doc.on('change', 'input[id^="new_file_"]', function() {
|
||||
var $btn = $form.find('[type="submit"]');
|
||||
var originalBtnText = $btn.text();
|
||||
|
||||
// --- ДОБАВЛЕНА ЛОГИКА ДЛЯ МНОЖЕСТВЕННЫХ ФАЙЛОВ ---
|
||||
var formData = new FormData(this);
|
||||
var fileInput = $form.find('#comment_image')[0];
|
||||
|
||||
if (fileInput && fileInput.files.length > 0) {
|
||||
// Удаляем одиночное поле, чтобы заменить его массивом
|
||||
formData.delete('comment_image');
|
||||
// Добавляем каждый файл в массив comment_image[]
|
||||
for (var i = 0; i < fileInput.files.length; i++) {
|
||||
formData.append('comment_image[]', fileInput.files[i]);
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------
|
||||
|
||||
$.ajax({
|
||||
url: aveabspath + 'index.php?ajax=1',
|
||||
type: 'POST',
|
||||
data: new FormData(this),
|
||||
data: formData, // Используем formData с поддержкой нескольких файлов
|
||||
processData: false,
|
||||
contentType: false,
|
||||
xhr: function() {
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
{if $smarty.session.user_group != '2' || $comment_allow_files_anon == 1}
|
||||
<div class="mb-3">
|
||||
<label for="comment_image" class="form-label mb-1">
|
||||
<span class="text-muted small"><i class="bi bi-paperclip me-1"></i> Прикрепить файл</span>
|
||||
<span class="text-muted small"><i class="bi bi-paperclip me-1"></i> Прикрепить файлы</span>
|
||||
</label>
|
||||
|
||||
<div class="small text-muted mb-2">
|
||||
@@ -153,11 +153,12 @@
|
||||
Макс. размер: <span class="fw-bold">{$comment_max_file_size|default:'2048'} Кб</span>.
|
||||
</div>
|
||||
|
||||
<input type="file" name="comment_image" id="comment_image" class="form-control form-control-sm">
|
||||
{* Ключевое изменение: name="comment_image[]" и атрибут multiple *}
|
||||
<input type="file" name="comment_image[]" id="comment_image" class="form-control form-control-sm" multiple>
|
||||
|
||||
<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;"><i class="bi bi-x-lg"></i></button>
|
||||
{* Контейнер для нескольких превью *}
|
||||
<div id="image_preview_wrapper" class="mt-2 d-flex flex-wrap gap-2 d-none">
|
||||
{* Сюда JS будет добавлять превью *}
|
||||
</div>
|
||||
|
||||
<div id="upload_progress_container" class="mt-2 d-none">
|
||||
|
||||
@@ -54,12 +54,22 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{* Вывод изображения *}
|
||||
{* Вывод изображения (ИСПРАВЛЕНО ДЛЯ MULTIPLE) *}
|
||||
{if !empty($c.comment_file)}
|
||||
<div class="mod_comment_attached_image mt-2" id="image_container_{$c.Id}">
|
||||
<a href="{$ABS_PATH}uploads/comments/{$c.comment_file}" target="_blank">
|
||||
<img src="{$ABS_PATH}uploads/comments/{$c.comment_file}" class="img-fluid rounded border shadow-sm" alt="Изображение" />
|
||||
</a>
|
||||
<div class="mod_comment_attached_images mt-2 d-flex flex-wrap gap-2" id="image_container_{$c.Id}">
|
||||
{assign var="photos" value=","|explode:$c.comment_file}
|
||||
{foreach from=$photos item=photo}
|
||||
{if $photo|trim}
|
||||
<div class="comment-image-item">
|
||||
<a href="{$ABS_PATH}uploads/comments/{$photo|trim}" target="_blank" class="d-block">
|
||||
<img src="{$ABS_PATH}uploads/comments/{$photo|trim}"
|
||||
class="img-fluid rounded border shadow-sm"
|
||||
style="max-height: 150px; width: auto; object-fit: cover;"
|
||||
alt="Изображение" />
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
{/foreach}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user