diff --git a/class/comment.php b/class/comment.php index c9cf524..5ee98e5 100644 --- a/class/comment.php +++ b/class/comment.php @@ -478,108 +478,121 @@ function commentListShow($tpl_dir) } /** - * Метод, предназначенный для редактирования комментария в Публичной части - * с поддержкой удаления и замены изображения - */ + * Метод, предназначенный для редактирования комментария в Публичной части + * с поддержкой удаления и замены изображения + */ 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); +{ + global $AVE_DB; + + // --- 1. ОПРЕДЕЛЯЕМ ПОЛЬЗОВАТЕЛЯ --- + // Используем (int), чтобы гарантированно иметь число для SQL + $user_id = (int)($_SESSION['user_id'] ?? 0); + $user_group = (int)(defined('UGROUP') ? UGROUP : 0); + + // Разрешаем вход всем системным группам (1, 2, 3, 4). + // Если группа не определена (0), значит это "неопознанная сущность" — выходим. + if ($user_group <= 0) exit; - if (empty($user_id)) exit; + $post_text = $_POST['text'] ?? ''; + $delete_file = (isset($_POST['delete_image']) && $_POST['delete_image'] == 1); + + // Очищаем ID комментария + $comment_id = (int)$comment_id; + if ($comment_id <= 0) exit; - $comment_id = intval(preg_replace('/\D/', '', $comment_id)); + // --- 2. ПОЛУЧАЕМ ДАННЫЕ С ПРОВЕРКОЙ ПРАВ --- + // Если не админ (группа 1), то база вернет результат ТОЛЬКО если автор совпадает. + // Для анонимов (группа 2) здесь подставится автор_id = 0, что корректно. + $sql_author_check = ($user_group !== 1) ? "AND msg.comment_author_id = '" . $user_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 cmnt.comment_active = '1' - AND msg.Id = '" . $comment_id . "' - " . (($user_group != 1) ? "AND msg.comment_author_id = " . (int)$user_id : '') . " - ")->FetchAssocArray(); + $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 cmnt.comment_active = '1' + AND msg.Id = '" . $comment_id . "' + " . $sql_author_check . " + ")->FetchAssocArray(); - if ($row !== false) - { - $comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 200; - - // --- ТВОЯ ОРИГИНАЛЬНАЯ ОБРАБОТКА ТЕКСТА --- - $comment_text = $post_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("
\n", "
\n", "
\n"), "\n", $comment_text); - $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 ($row !== false) + { + $comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 200; + + // --- ТВОЯ ОРИГИНАЛЬНАЯ ОБРАБОТКА ТЕКСТА --- + $comment_text = $post_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("
\n", "
\n", "
\n"), "\n", $comment_text); + $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 .= '…'; - $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 = ""; + $is_admin = ($user_group == 1); + // Проверяем, разрешено ли группе (2 или 4) редактирование в настройках модуля + $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); + // 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); - } + // Удаляем старый файл, если он был + 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; + $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 = ''"; - } + if (move_uploaded_file($_FILES['comment_image']['tmp_name'], $upload_dir . $new_file_name)) { + $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_cut) . "' - $new_file_sql - WHERE - Id = '" . $comment_id . "' - "); + // --- ОБНОВЛЕНИЕ БАЗЫ --- + $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_cut) . "' + $new_file_sql + WHERE + Id = '" . $comment_id . "' + "); - echo htmlspecialchars($comment_text_cut, ENT_QUOTES); - exit; - } - echo htmlspecialchars($row['comment_text'], ENT_QUOTES); - } - exit; - } + // Возвращаем текст для обновления на странице + echo htmlspecialchars($comment_text_cut, ENT_QUOTES); + exit; + } + // Если прав на редактирование нет, возвращаем старый текст + echo htmlspecialchars($row['comment_text'], ENT_QUOTES); + } + exit; +} /** * Метод, предназначенный для удаления комментария. Если комментарий содержал какие-либо ответы на него, @@ -587,49 +600,74 @@ function commentPostEdit($comment_id) * * @param int $comment_id - идентификатор комментария */ - function commentPostDelete($comment_id) - { - global $AVE_DB; +function commentPostDelete($comment_id) +{ + global $AVE_DB; - $comment_id = (int)$comment_id; - $upload_dir = BASE_DIR . '/uploads/comments/'; + $comment_id = (int)$comment_id; + if ($comment_id <= 0) exit; - // --- 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 != '' - "); + // --- ШАГ 0. ПРОВЕРКА ПРАВ (БЕЗОПАСНОСТЬ) --- - while ($row = $res->FetchAssocArray()) { - $file_path = $upload_dir . $row['comment_file']; - if (file_exists($file_path)) { - @unlink($file_path); // Удаляем файл с сервера - } - } + // Сначала узнаем, кто автор комментария, который хотят удалить + $comment_data = $AVE_DB->Query(" + SELECT comment_author_id + FROM " . PREFIX . "_module_comment_info + WHERE Id = '" . $comment_id . "' + ")->FetchAssocArray(); - // --- 2. ТВОЙ ОРИГИНАЛЬНЫЙ КОД УДАЛЕНИЯ ИЗ БД --- + if (!$comment_data) exit; // Если комментария нет, и удалять нечего - // Выполняем запрос к БД на удаление родительского комментария - $AVE_DB->Query(" - DELETE - FROM " . PREFIX . "_module_comment_info - WHERE Id = '" . $comment_id . "' - "); + $author_id = (int)$comment_data['comment_author_id']; + $current_user_id = (int)($_SESSION['user_id'] ?? 0); + $user_group = (int)(defined('UGROUP') ? UGROUP : 0); - // Выполняем запрос к БД на удаление дочерних комментариев (ответов) - $AVE_DB->Query(" - DELETE - FROM " . PREFIX . "_module_comment_info - WHERE parent_id = '" . $comment_id . "' - AND parent_id != 0 - "); + $can_delete = false; + + // ПРАВО АДМИНА: Если группа 1 — разрешаем всё сразу + if ($user_group === 1) { + $can_delete = true; + } + // ПРАВО АВТОРА: Если залогинен и ID совпадает с автором — разрешаем + elseif ($current_user_id > 0 && $current_user_id === $author_id) { + $can_delete = true; + } + + // Если ни одно условие не сработало — выкидываем взломщика + if (!$can_delete) { + exit('Доступ запрещен'); + } + + // --- ШАГ 1. ОЧИСТКА ФАЙЛОВ (ТОЛЬКО ЕСЛИ ПРАВА ЕСТЬ) --- + + $upload_dir = BASE_DIR . '/uploads/comments/'; + + $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("DELETE FROM " . PREFIX . "_module_comment_info WHERE Id = '" . $comment_id . "'"); + + // Удаляем все ответы на него + $AVE_DB->Query("DELETE FROM " . PREFIX . "_module_comment_info WHERE parent_id = '" . $comment_id . "' AND parent_id != 0"); + + echo "OK"; // Даем сигнал фронтенду, что всё прошло успешно + exit; +} - exit; - } function commentAdminDelete($comment_id) { global $AVE_DB; diff --git a/js/comment.js b/js/comment.js index 6130a73..2bee965 100644 --- a/js/comment.js +++ b/js/comment.js @@ -78,45 +78,44 @@ return; } - if (UGROUP == 1){ - $.get(aveabspath + 'index.php', { - module: 'comment', - action: action, - docid: DOC_ID, - Id: cid - }, function(){ - if (action === 'delete'){ - $(obj).parents('.mod_comment_comment').eq(0).remove(); - } - - if (action === 'open'){ - var $openButton = $('#mod_comment_open'); - $openButton.attr('id', 'mod_comment_close').html(' ' + COMMENT_SITE_CLOSE); - $openButton.removeClass('btn-outline-success').addClass('btn-outline-danger'); - } - - if (action === 'close'){ - var $closeButton = $('#mod_comment_close'); - $closeButton.attr('id', 'mod_comment_open').html(' ' + COMMENT_SITE_OPEN); - $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') - .attr('title', COMMENT_LOCK_LINK) - .find('i') - .removeClass('bi-unlock-fill').addClass('bi-lock-fill'); - } - if (action === 'lock'){ - $(obj).removeClass('mod_comment_lock text-dark') - .addClass('mod_comment_unlock text-success') - .attr('title', COMMENT_UNLOCK_LINK) - .find('i') - .removeClass('bi-lock-fill').addClass('bi-unlock-fill'); - } - }); - } + // Удаление и модерация (Отправляем запрос, PHP проверит права) + $.get(aveabspath + 'index.php', { + module: 'comment', + action: action, + docid: DOC_ID, + Id: cid + }, function(data){ + if (action === 'delete'){ + $(obj).parents('.mod_comment_comment').eq(0).remove(); + } + + if (action === 'open'){ + var $openButton = $('#mod_comment_open'); + $openButton.attr('id', 'mod_comment_close').html(' ' + COMMENT_SITE_CLOSE); + $openButton.removeClass('btn-outline-success').addClass('btn-outline-danger'); + } + + if (action === 'close'){ + var $closeButton = $('#mod_comment_close'); + $closeButton.attr('id', 'mod_comment_open').html(' ' + COMMENT_SITE_OPEN); + $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') + .attr('title', COMMENT_LOCK_LINK) + .find('i') + .removeClass('bi-unlock-fill').addClass('bi-lock-fill'); + } + if (action === 'lock'){ + $(obj).removeClass('mod_comment_lock text-dark') + .addClass('mod_comment_unlock text-success') + .attr('title', COMMENT_UNLOCK_LINK) + .find('i') + .removeClass('bi-lock-fill').addClass('bi-unlock-fill'); + } + }); } function validate(formData, jqForm, options){ @@ -179,12 +178,12 @@ ''; } - // Блок для превью нового фото (добавлен класс edit-preview-img) + // Блок для превью нового фото var editPreviewHtml = '
' + '' + '
'; - // Поле для загрузки НОВОГО фото (добавлен класс edit-file-input и data-cid) + // Поле для загрузки НОВОГО фото var uploadNewPhotoHtml = '
' + '' + '' + @@ -239,7 +238,6 @@ var fileInput = $('#new_file_' + cid)[0]; if (fileInput && fileInput.files[0]) { - // Ключ 'comment_image' должен совпадать с PHP $_FILES['comment_image'] fd.append('comment_image', fileInput.files[0]); } @@ -250,7 +248,6 @@ processData: false, contentType: false, success: function(txt){ - // Если были операции с файлом, перезагружаем страницу if (deleteImg == 1 || (fileInput && fileInput.files[0])) { location.reload(); } else { @@ -295,7 +292,7 @@ setClickable(); - // ЛОГИКА ПРЕДПРОСМОТРА ДЛЯ ФОРМ РЕДАКТИРОВАНИЯ (Делегирование) + // ЛОГИКА ПРЕДПРОСМОТРА ДЛЯ ФОРМ РЕДАКТИРОВАНИЯ $(document).on('change', '.edit-file-input', function() { var cid = $(this).data('cid'); var file = this.files[0]; @@ -366,22 +363,24 @@ createEditForm(cid, revert, commentTextBlock); }); - if (UGROUP == 1) { - $(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'); - var modalInstance = bootstrap.Modal.getInstance($modal[0]) || new bootstrap.Modal($modal[0]); - modalInstance.hide(); - }); - - var modal = new bootstrap.Modal($modal[0]); - modal.show(); + /* --- УДАЛЕНИЕ: Доступно всем, у кого в шаблоне есть кнопка --- */ + $(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'); + var modalInstance = bootstrap.Modal.getInstance($modal[0]) || new bootstrap.Modal($modal[0]); + modalInstance.hide(); }); + var modal = new bootstrap.Modal($modal[0]); + modal.show(); + }); + + /* --- ТОЛЬКО ДЛЯ АДМИНИСТРАТОРОВ (UGROUP == 1) --- */ + if (typeof UGROUP !== 'undefined' && UGROUP == 1) { $(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');}); diff --git a/module.php b/module.php index 0f0e8bf..3433fe9 100644 --- a/module.php +++ b/module.php @@ -64,13 +64,16 @@ if (!defined('ACP') && isset($_REQUEST['module']) && $_REQUEST['module'] == 'com // Если edit, тогда открываем форму для редактирования текста комментария case 'edit': - $comment->commentPostEdit((int)$_REQUEST['Id']); + if (!empty(UGROUP)) + { + $comment->commentPostEdit((int)$_REQUEST['Id']); + } break; // Если delete, тогда удаляем комментарий case 'delete': - if (UGROUP==1) + if (!empty(UGROUP)) { $comment->commentPostDelete((int)$_REQUEST['Id']); }