в админку вынесено управление расширениями и размером файлов
This commit is contained in:
@@ -255,7 +255,8 @@ function commentListShow($tpl_dir)
|
||||
{
|
||||
$assign['comment_max_chars'] = $settings['comment_max_chars'];
|
||||
$assign['im'] = $settings['comment_use_antispam'];
|
||||
|
||||
$assign['comment_allowed_extensions'] = $settings['comment_allowed_extensions'] ?? 'jpg,jpeg,png,gif';
|
||||
$assign['comment_max_file_size'] = $settings['comment_max_file_size'] ?? 2048;
|
||||
$comments = array();
|
||||
|
||||
if ($settings['comment_use_page_nav'] == 1)
|
||||
@@ -483,12 +484,12 @@ function commentListShow($tpl_dir)
|
||||
{
|
||||
$upload_path = BASE_DIR . '/uploads/comments/';
|
||||
|
||||
// Создаем папку, если её нет
|
||||
if (!is_dir($upload_path)) {
|
||||
@mkdir($upload_path, 0775, true);
|
||||
// Создаем папку, если её нет
|
||||
if (!is_dir($upload_path)) {
|
||||
@mkdir($upload_path, 0775, true);
|
||||
|
||||
// Содержимое для защитного файла index.php
|
||||
$index_content = "<?php
|
||||
// Содержимое для защитного файла index.php
|
||||
$index_content = "<?php
|
||||
/**
|
||||
* Файл-заглушка, предназначенный для запрета показа списка файлов в текущей директории,
|
||||
* если через адресную строку браузера было прямое обращение к данной директории.
|
||||
@@ -497,24 +498,37 @@ header('Location:/');
|
||||
exit;
|
||||
?>";
|
||||
|
||||
// Создаем index.php для безопасности (запрет листинга папки и редирект)
|
||||
@file_put_contents($upload_path . 'index.php', $index_content);
|
||||
}
|
||||
|
||||
$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 = ''; // Сбрасываем, если не удалось переместить
|
||||
}
|
||||
// Создаем index.php для безопасности (запрет листинга папки и редирект)
|
||||
@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)));
|
||||
|
||||
$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'];
|
||||
|
||||
// Дополнительная проверка: действительно ли это изображение по содержимому
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $_FILES['comment_image']['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
||||
// --- ПОДГОТОВКА ДАННЫХ ДЛЯ БД ---
|
||||
@@ -546,7 +560,7 @@ exit;
|
||||
$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;
|
||||
@@ -585,21 +599,21 @@ exit;
|
||||
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) : '';
|
||||
$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'] = '';
|
||||
}
|
||||
// 2. Очищаем, если это системный user.png
|
||||
if (!empty($new_comment['avatar']) && strpos($new_comment['avatar'], 'user.png') !== false)
|
||||
{
|
||||
$new_comment['avatar'] = '';
|
||||
}
|
||||
|
||||
// 3. Если аватара нет — генерируем данные для буквы
|
||||
if (empty($new_comment['avatar']))
|
||||
{
|
||||
$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;
|
||||
}
|
||||
// 3. Если аватара нет — генерируем данные для буквы
|
||||
if (empty($new_comment['avatar']))
|
||||
{
|
||||
$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;
|
||||
|
||||
// Передаем флаги для мгновенной активации управления в шаблоне
|
||||
@@ -633,14 +647,16 @@ function commentPostEdit($comment_id)
|
||||
if ($comment_id <= 0 || $user_group <= 0) exit('INVALID_ID');
|
||||
|
||||
// 2. Получаем данные комментария и настройки модуля (JOIN)
|
||||
// ДОБАВИЛ: cmnt.comment_edit_time в выборку SQL
|
||||
// ДОБАВИЛ: выборку новых полей (allowed_extensions и max_file_size)
|
||||
$row = $AVE_DB->Query("
|
||||
SELECT
|
||||
msg.*,
|
||||
cmnt.comment_max_chars,
|
||||
cmnt.comment_need_approve,
|
||||
cmnt.comment_user_groups,
|
||||
cmnt.comment_edit_time
|
||||
cmnt.comment_edit_time,
|
||||
cmnt.comment_allowed_extensions,
|
||||
cmnt.comment_max_file_size
|
||||
FROM " . PREFIX . "_module_comment_info AS msg
|
||||
JOIN " . PREFIX . "_module_comments AS cmnt ON cmnt.Id = 1
|
||||
WHERE msg.Id = '" . $comment_id . "'
|
||||
@@ -655,12 +671,10 @@ function commentPostEdit($comment_id)
|
||||
$is_author = ($user_id > 0 && $user_id == $row['comment_author_id']) ||
|
||||
($row['comment_author_id'] == 0 && !empty($row['anon_key']) && $row['anon_key'] == $anon_key);
|
||||
|
||||
// ИСПРАВЛЕНО: Теперь берем лимит времени ПРЯМО из БД ($row), а не из переменной класса
|
||||
$time_limit = (isset($row['comment_edit_time'])) ? (int)$row['comment_edit_time'] : 60;
|
||||
$time_passed = time() - (int)$row['comment_published'];
|
||||
$is_time_ok = ($time_passed < $time_limit);
|
||||
|
||||
// Если не админ, проверяем: автор ли это и не вышло ли время
|
||||
if (!$is_admin) {
|
||||
if (!$is_author) exit('NOT_AUTHOR');
|
||||
if (!$is_time_ok) {
|
||||
@@ -669,7 +683,7 @@ function commentPostEdit($comment_id)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Обработка текста (без изменений...)
|
||||
// 4. Обработка текста
|
||||
$comment_text = $_POST['text'] ?? '';
|
||||
|
||||
$comment_text = preg_replace_callback('/&#x([0-9a-f]{1,7});/i', function($matches) { return chr(hexdec($matches[1])); }, $comment_text);
|
||||
@@ -683,30 +697,51 @@ function commentPostEdit($comment_id)
|
||||
$comment_text_cut = mb_substr($comment_text, 0, $max);
|
||||
if (mb_strlen($comment_text) > $max) $comment_text_cut .= '…';
|
||||
|
||||
// 5. Работа с изображениями (без изменений...)
|
||||
$new_file_sql = "";
|
||||
$upload_dir = BASE_DIR . '/uploads/comments/';
|
||||
|
||||
if (isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK) {
|
||||
// 5. Работа с изображениями (С МАКСИМАЛЬНОЙ ЗАЩИТОЙ)
|
||||
$new_file_sql = "";
|
||||
$upload_dir = BASE_DIR . '/uploads/comments/';
|
||||
|
||||
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'];
|
||||
|
||||
// --- НОВОЕ: Проверка 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']);
|
||||
}
|
||||
|
||||
$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)) {
|
||||
$new_file_sql = ", comment_file = '" . addslashes($new_file_name) . "'";
|
||||
}
|
||||
}
|
||||
elseif (isset($_POST['delete_image']) && $_POST['delete_image'] == 1) {
|
||||
if (!empty($row['comment_file']) && file_exists($upload_dir . $row['comment_file'])) {
|
||||
@unlink($upload_dir . $row['comment_file']);
|
||||
}
|
||||
$new_file_sql = ", comment_file = ''";
|
||||
} else {
|
||||
// Если файл не прошел серверную проверку
|
||||
echo "FILE_ERROR";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$user_rating = isset($_POST['user_rating']) ? (int)$_POST['user_rating'] : (int)$row['user_rating'];
|
||||
|
||||
@@ -1281,6 +1316,11 @@ function commentAdminSettingsEdit($tpl_dir)
|
||||
// Настройки рейтинга и файлов
|
||||
$post_allow_files = $_POST['comment_allow_files'] ?? 0;
|
||||
$post_allow_files_anon = $_POST['comment_allow_files_anon'] ?? 0;
|
||||
|
||||
/* НОВОЕ: Настройки расширений и максимального размера файла */
|
||||
$post_allowed_extensions = $_POST['comment_allowed_extensions'] ?? 'jpg,jpeg,png,gif';
|
||||
$post_max_file_size = $_POST['comment_max_file_size'] ?? 2048;
|
||||
|
||||
$post_rating_type = $_POST['comment_rating_type'] ?? 0;
|
||||
$post_show_user_rating = $_POST['comment_show_user_rating'] ?? 0;
|
||||
|
||||
@@ -1308,9 +1348,12 @@ function commentAdminSettingsEdit($tpl_dir)
|
||||
{
|
||||
$max_chars = (empty($post_max_chars) || $post_max_chars < 50) ? 50 : $post_max_chars;
|
||||
|
||||
// Подготовка имен полей
|
||||
// Подготовка имен полей и настроек файлов
|
||||
$clean_name_f1 = htmlspecialchars(stripslashes($post_name_f1), ENT_QUOTES);
|
||||
$clean_name_f2 = htmlspecialchars(stripslashes($post_name_f2), ENT_QUOTES);
|
||||
|
||||
// Очищаем строку расширений от пробелов и лишних запятых
|
||||
$clean_extensions = implode(',', array_filter(array_map('trim', explode(',', $post_allowed_extensions))));
|
||||
|
||||
$AVE_DB->Query("
|
||||
UPDATE " . PREFIX . "_module_comments
|
||||
@@ -1325,6 +1368,11 @@ function commentAdminSettingsEdit($tpl_dir)
|
||||
comment_page_nav_count = '" . (int)$post_page_nav_count . "',
|
||||
comment_allow_files = '" . (int)$post_allow_files . "',
|
||||
comment_allow_files_anon = '" . (int)$post_allow_files_anon . "',
|
||||
|
||||
/* СОХРАНЕНИЕ НОВЫХ ПОЛЕЙ */
|
||||
comment_allowed_extensions = '" . addslashes($clean_extensions) . "',
|
||||
comment_max_file_size = '" . (int)$post_max_file_size . "',
|
||||
|
||||
comment_rating_type = '" . (int)$post_rating_type . "',
|
||||
comment_show_user_rating = '" . (int)$post_show_user_rating . "',
|
||||
comment_show_user_rating_replies = '" . (int)$post_show_user_rating_replies . "',
|
||||
|
||||
170
js/comment.js
170
js/comment.js
@@ -89,6 +89,34 @@ function initCommentTimers() {
|
||||
|
||||
// ВАЛИДАЦИЯ ПОЛЕЙ
|
||||
|
||||
// Функция для проверки файла (расширение и размер)
|
||||
function checkFileConsistency(file, $errorDisplay) {
|
||||
if (!file) return true;
|
||||
|
||||
// Получаем настройки из шаблона (те переменные, что мы добавили в тег script)
|
||||
var allowedExts = (typeof ALLOWED_EXTENSIONS !== 'undefined') ? ALLOWED_EXTENSIONS.toLowerCase().split(',') : ['jpg', 'jpeg', 'png', 'gif'];
|
||||
var maxSizeKb = (typeof MAX_FILE_SIZE_KB !== 'undefined') ? parseInt(MAX_FILE_SIZE_KB) : 2048;
|
||||
|
||||
var fileName = file.name.toLowerCase();
|
||||
var fileExt = fileName.split('.').pop();
|
||||
var fileSizeKb = file.size / 1024;
|
||||
|
||||
// Проверка расширения
|
||||
if ($.inArray(fileExt, allowedExts) === -1) {
|
||||
$errorDisplay.text('Тип файла .' + fileExt + ' не разрешен.').removeClass('d-none');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Проверка размера
|
||||
if (fileSizeKb > maxSizeKb) {
|
||||
$errorDisplay.text('Файл слишком большой (' + Math.round(fileSizeKb) + ' Кб). Максимум: ' + maxSizeKb + ' Кб').removeClass('d-none');
|
||||
return false;
|
||||
}
|
||||
|
||||
$errorDisplay.addClass('d-none').text('');
|
||||
return true;
|
||||
}
|
||||
|
||||
function validate(form) {
|
||||
var isValid = true;
|
||||
|
||||
@@ -127,6 +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 (!isValid) {
|
||||
// Скроллим к первой ошибке
|
||||
$('html, body').animate({
|
||||
@@ -189,18 +227,28 @@ function initCommentTimers() {
|
||||
// Счетчик: Количество оставшихся символов 1000 для формы создания комментария
|
||||
$('#in_message').limit(1000, '#charsLeft_new');
|
||||
|
||||
// --- ЛОГИКА ПРЕДПРОСМОТРА ИЗОБРАЖЕНИЯ (НОВАЯ ФОРМА) ---
|
||||
$doc.on('change', '#comment_image', function() {
|
||||
var input = this;
|
||||
if (input.files && input.files[0]) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
$('#image_preview_img').attr('src', e.target.result);
|
||||
$('#image_preview_wrapper').removeClass('d-none');
|
||||
}
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
});
|
||||
$doc.on('change', '#comment_image', function() {
|
||||
var input = this;
|
||||
var $errorBox = $('#file_error');
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
// Сначала проверяем файл
|
||||
if (!checkFileConsistency(input.files[0], $errorBox)) {
|
||||
$(input).val('').addClass('is-invalid');
|
||||
$('#image_preview_wrapper').addClass('d-none');
|
||||
return;
|
||||
}
|
||||
|
||||
// Если файл прошел проверку — показываем превью
|
||||
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]);
|
||||
}
|
||||
});
|
||||
|
||||
$doc.on('click', '#remove_image_btn', function() {
|
||||
$('#comment_image').val('');
|
||||
@@ -418,24 +466,35 @@ $doc.on('mouseleave', '.rating-edit-block, #rating_wrapper', function() {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
var fileInputHtml = '';
|
||||
if (typeof UGROUP !== 'undefined' && (UGROUP != '2' || (typeof ALLOW_FILES_ANON !== 'undefined' && ALLOW_FILES_ANON == '1'))) {
|
||||
fileInputHtml = `
|
||||
<div class="mb-2">
|
||||
<label class="small text-muted mb-1">Заменить/Добавить фото:</label>
|
||||
<input type="file" id="new_file_${cid}" class="form-control form-control-sm" accept="image/*">
|
||||
</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>`;
|
||||
}
|
||||
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>
|
||||
|
||||
<div class="small text-muted mb-2">
|
||||
Разрешены: <span class="fw-bold">${allowedText}</span>.
|
||||
Макс. размер: <span class="fw-bold">${sizeText} Кб</span>.
|
||||
</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>`;
|
||||
}
|
||||
|
||||
var editHtml = `
|
||||
<div class="edit-form-container border rounded p-3 bg-light mt-2 mb-2">
|
||||
@@ -461,22 +520,41 @@ $doc.on('mouseleave', '.rating-edit-block, #rating_wrapper', function() {
|
||||
$('#del_img_' + cid).prop('checked', true); // Помечаем скрытый чекбокс на удаление
|
||||
});
|
||||
|
||||
// Обработка выбора НОВОГО файла при редактировании
|
||||
$doc.on('change', 'input[id^="new_file_"]', function() {
|
||||
var cid = $(this).attr('id').replace('new_file_', '');
|
||||
var input = this;
|
||||
if (input.files && input.files[0]) {
|
||||
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]);
|
||||
}
|
||||
});
|
||||
// Обработка выбора НОВОГО файла при редактировании
|
||||
$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);
|
||||
|
||||
// Сбрасываем старые ошибки
|
||||
$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;
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
});
|
||||
|
||||
// Удаление НОВОГО выбранного файла в режиме редактирования
|
||||
$doc.on('click', '.remove-edit-new-img', function() {
|
||||
|
||||
20
sql.php
20
sql.php
@@ -5,7 +5,8 @@
|
||||
*
|
||||
* Обновленная структура с поддержкой выбора типа рейтинга,
|
||||
* идентификации анонимов, загрузки файлов и защиты по IP.
|
||||
* ДОБАВЛЕНО: Настройка отображения авторской оценки в ответах.
|
||||
* ДОБАВЛЕНО: Управление расширениями и максимальным размером файлов.
|
||||
* СИНХРОНИЗАЦИЯ: Добавлен формат webp по умолчанию.
|
||||
*/
|
||||
|
||||
$module_sql_install = array();
|
||||
@@ -36,10 +37,10 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comments` (
|
||||
`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',
|
||||
`comment_allowed_extensions` varchar(255) NOT NULL default 'jpg,jpeg,png,gif,webp',
|
||||
`comment_max_file_size` int(10) NOT NULL default '2048',
|
||||
`comment_rating_type` tinyint(1) NOT NULL default '0',
|
||||
`comment_show_user_rating` tinyint(1) NOT NULL default '0',
|
||||
/* НОВОЕ ПОЛЕ: Показывать оценку автора в ответах */
|
||||
`comment_show_user_rating_replies` tinyint(1) NOT NULL default '0',
|
||||
`comment_rating_anon_vote` tinyint(1) NOT NULL default '0',
|
||||
`comment_rating_anon_set` tinyint(1) NOT NULL default '0',
|
||||
@@ -49,7 +50,7 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comments` (
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
|
||||
|
||||
// Таблица info (без изменений авторской оценки, она уже есть: user_rating)
|
||||
// Таблица info
|
||||
$module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comment_info` (
|
||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
`parent_id` int(10) unsigned NOT NULL default '0',
|
||||
@@ -92,8 +93,8 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comment_votes` (
|
||||
KEY `remote_addr` (`remote_addr`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
|
||||
|
||||
/* Настройки по умолчанию (добавлено 0 перед 0, 0, 0, 60, 30) */
|
||||
$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, 0, 1, 0, 0, 0, 0, 60, 30);";
|
||||
/* Настройки по умолчанию */
|
||||
$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, 'jpg,jpeg,png,gif,webp', 2048, 0, 1, 0, 0, 0, 0, 60, 30);";
|
||||
|
||||
// =================================================================================
|
||||
// 2. ОБНОВЛЕНИЕ МОДУЛЯ (ALTER TABLE)
|
||||
@@ -103,12 +104,13 @@ $module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_cookie_life` INT(11) NOT NULL DEFAULT '30';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_rating_type` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_show_user_rating` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
|
||||
/* Добавляем настройку отображения рейтинга автора в ответах */
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_show_user_rating_replies` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_rating_anon_vote` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_rating_anon_set` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_allow_files_anon` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
|
||||
/* Новые поля для файлов при обновлении */
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_allowed_extensions` VARCHAR(255) NOT NULL DEFAULT 'jpg,jpeg,png,gif,webp';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD COLUMN IF NOT EXISTS `comment_max_file_size` INT(10) NOT NULL DEFAULT '2048';";
|
||||
|
||||
?>
|
||||
@@ -166,7 +166,6 @@
|
||||
Включить "Авторские звезды"
|
||||
</label>
|
||||
</td>
|
||||
{* Новая позиция для управления в ответах *}
|
||||
<td style="border-right: 1px solid #ddd;">
|
||||
<label style="cursor: pointer; display: flex; align-items: center; font-weight: normal;">
|
||||
<input name="comment_show_user_rating_replies" type="checkbox" value="1" {if $comment_show_user_rating_replies=='1'}checked{/if} style="margin-right: 10px;" />
|
||||
@@ -200,6 +199,17 @@
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Допустимые расширения:</td>
|
||||
<td style="border-right: 1px solid #ddd;">
|
||||
<input name="comment_allowed_extensions" type="text" value="{$comment_allowed_extensions|default:'jpg,jpeg,png,gif'}" style="width: 90%;" placeholder="Пример: jpg, png, gif" />
|
||||
</td>
|
||||
<td>Макс. размер файла (Кб):</td>
|
||||
<td>
|
||||
<input name="comment_max_file_size" type="text" value="{$comment_max_file_size|default:'2048'}" size="6" style="width: 80px;" />
|
||||
<span style="color: #888; font-size: 11px; margin-left: 5px;">KB</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="4" class="submitArea">
|
||||
|
||||
@@ -141,26 +141,34 @@
|
||||
{#COMMENT_CHARS_LEFT#} <span class="charsLeft fw-bold" id="charsLeft_new"></span>
|
||||
</p>
|
||||
|
||||
{if $comment_allow_files == 1}
|
||||
{if $smarty.session.user_group != '2' || $comment_allow_files_anon == 1}
|
||||
<div class="mb-3">
|
||||
<label for="comment_image" class="form-label text-muted small">
|
||||
<i class="bi bi-camera me-1"></i> Прикрепить изображение
|
||||
</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;"><i class="bi bi-x-lg"></i></button>
|
||||
</div>
|
||||
<div id="upload_progress_container" class="mt-2 d-none">
|
||||
<div class="progress" style="height: 8px;">
|
||||
<div id="upload_progress_bar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="file_error" class="text-danger small mt-1 d-none"></div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{if $comment_allow_files == 1}
|
||||
{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>
|
||||
</label>
|
||||
|
||||
<div class="small text-muted mb-2">
|
||||
Разрешены: <span class="fw-bold">{$comment_allowed_extensions|default:'jpg, png, gif'}</span>.
|
||||
Макс. размер: <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">
|
||||
|
||||
<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>
|
||||
|
||||
<div id="upload_progress_container" class="mt-2 d-none">
|
||||
<div class="progress" style="height: 8px;">
|
||||
<div id="upload_progress_bar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="file_error" class="text-danger small mt-1 d-none"></div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{if $im}
|
||||
<div class="row mb-3 align-items-center">
|
||||
@@ -259,6 +267,9 @@
|
||||
var SHOW_USER_RATING_REPLIES = '{$comment_show_user_rating_replies|default:0}';
|
||||
var RATING_ANON_SET = '{$comment_rating_anon_set|default:0}';
|
||||
var ALLOW_FILES_ANON = '{$comment_allow_files_anon|default:0}';
|
||||
// --- ПЕРЕМЕННЫЕ ДЛЯ ВАЛИДАЦИИ ФАЙЛОВ ---
|
||||
var ALLOWED_EXTENSIONS = '{$comment_allowed_extensions|default:"jpg,jpeg,png,gif"}';
|
||||
var MAX_FILE_SIZE_KB = '{$comment_max_file_size|default:2048}';
|
||||
</script>
|
||||
<script src="{$ABS_PATH}modules/comment/js/comment.js" type="text/javascript"></script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user