финальный пуш админки , надеюсь на это. языковые сделаны, почищен код комментарии в коде и все такое.
This commit is contained in:
43
README.md
43
README.md
@@ -1,53 +1,58 @@
|
||||
## Модуль Комментарии.
|
||||
|
||||
### Функционал:
|
||||
* Возможность создавать комментарии как зарегистрированным Пользователям так и Анонимам
|
||||
* Разрешения задаются в Админпанели
|
||||
* Возможность создавать комментарии как зарегистрированным Пользователям, так и Анонимам
|
||||
* Разрешения задаются в Админ панели
|
||||
* Аватар Пользователя
|
||||
* если есть в системе - выведет его изображение, если нет выведет аватар с первой буквой имени пользователя или анонима
|
||||
* Авторская оценка
|
||||
* При создании комментария , Автор может поставить оценку материалу (документу) - звезды 1...5 баллов
|
||||
* Права для Анонимов (разрешено/нет)настраиваются в Админпанели
|
||||
* Разрешать Авторскую оценку в ответах настраивается в Админпанели
|
||||
* При создании комментария, Автор может поставить оценку материалу (документу) - звезды 1...5 баллов
|
||||
* Права для Анонимов (разрешено/нет), настраиваются в Админ панели
|
||||
* Разрешать Авторскую оценку в ответах настраивается в Админ панели
|
||||
* Редактирование Авторской оценки Администратором в публичной заблокировано. Однако, если в порыве гнева, вы все таки решили изменить Авторскую оценку, несмотря на возможную потерю после такого вашего действия желания у Авторов в дальнейшем ставить хоть какие то оценки вашим супер интересным статьям, то сделать это возможно из Админ панели, но при условии, что ваше самолюбие заставит вас открыть файл /class/comment.php и сменить значение в переменной $_edit_avtor_rating = 0; на $_edit_avtor_rating = 1; (находится в самом начале файла).
|
||||
* Рейтинг комментариев
|
||||
* Пользователи и Анонимы могут ставить оценки комментариям других Авторов
|
||||
* На выбор два варианта или пять звезд или лайк (сердечко)
|
||||
* Права для Анонимов (разрешено участвовать/нет)настраиваются в Админпанели
|
||||
* Права для Анонимов (разрешено участвовать/нет), настраиваются в Админ панели
|
||||
* Таймер времени, при котором доступно редактирование или удаление комментария Автором
|
||||
* Время задается в Админпанели, после истечении времени редактирование или удаление комментария будет сделать невозможно, за исключением Администратора
|
||||
* Время задается в Админ панели, после истечении времени редактирование или удаление комментария будет сделать невозможно, за исключением Администратора
|
||||
* Время жизни куки для Анонимов
|
||||
* Задается в Админпанели, в течении этого времени Аноним будет считаться Автором своих комментариев
|
||||
* Задается в Админ панели, в течении этого времени Аноним будет считаться Автором своих комментариев
|
||||
* Файлы
|
||||
* Авторы могут загружать в свои комментарии разрешенные в Админпанели файлы.
|
||||
* Размер и тип файла задаются в Админпанели. Отдельные права для Анонимов. Для изображений выводятся их превью, для остальных превью с именем расширения этого файла.
|
||||
* Авторы могут загружать в свои комментарии разрешенные в Админ панели файлы.
|
||||
* Размер и тип файла задаются в Админ панели. Отдельные права для Анонимов. Для изображений выводятся их превью, для остальных превью с именем расширения этого файла.
|
||||
|
||||
* Дополнительные поля
|
||||
* Два дополнительных поля, их названия и обязательные или нет, настраивааются в Админпанели
|
||||
* Два дополнительных поля, их названия и обязательные или нет, настраиваются в Админ панели
|
||||
* Валидация обязательных полей на заполненность + проверка на разрешения для файлов
|
||||
* Комментарий - родитель (ветка комментариев)
|
||||
* Если на комментарий Автора другие Пользователи сделали ответ, то удалить такой комментарий Автору или Администратору в публичной части сайта будет невозможно, вместо этого будет произведено мягкое удаление - удалится все , что относится к данному комментарию (Рейтинги, файлы , текст) и вместо этого будет выводится специальный аватар и текст: Комментарий удален автором либо Комментарий удален администратором. Восстановить данные такого комментария невозможно.
|
||||
* Если на комментарий Автора другие Пользователи сделали ответ, то удалить такой комментарий Автору или Администратору в публичной части сайта будет невозможно, вместо этого будет произведено мягкое удаление, - удалится все, что относится к данному комментарию (Рейтинги, файлы, текст) и вместо этого будет выводится специальный аватар и текст: Комментарий удален автором либо Комментарий удален администратором. Восстановить данные такого комментария невозможно.
|
||||
* Удаление комментариев в Админке
|
||||
* Одиночные комментарии (не имеющие ответов) удаляются сразу и навсегда.
|
||||
* Комментарии родители (имеющие ответы/ветку ответов) получают два варианта одиночного удаления:
|
||||
* Мягкое удаление (удалится все , что относится к данному комментарию (Рейтинги, файлы , текст) и вместо этого будет выводится специальный аватар и текст: Комментарий удален администратором. Восстановить данные такого комментария невозможно,
|
||||
* Мягкое удаление (удалится все, что относится к данному комментарию (Рейтинги, файлы, текст) и вместо этого будет выводится специальный аватар и текст: Комментарий удален администратором. Восстановить данные такого комментария невозможно,
|
||||
* Жесткое удаление самого комментария и всей ветки ответов на этот комментарий. Администратор выбирает сам какой вариант ему необходим.
|
||||
* Массовое удаление - одиночные комментарии удаляются сразу, комментарии-родители удаляются Мягким вариантом (с подстановкой текста Комментарий удален Администратором).
|
||||
* Статус Скрыт/Опубликован (иконка глаз)
|
||||
* Если комментарий родитель и на него есть ответы:
|
||||
* При клике по иконке Скрыть (как в публичной части сайта так и в Админке) - автоматически будет скрыт как сам комментарий так и все ответы на него (ветка комментариев), при этом у ответов возможность скрытия/публикации будет заблокирована до тех пор, пока родительский комментарий не будет разблокирован (опубликован). Восстановление в ветке статусов - опубликован, происходит персонально, кликом Опубликовать по каждому комментарию в ветке.
|
||||
* При клике по иконке Скрыть, как в публичной части сайта, так и в Админке, - автоматически будет скрыт как сам комментарий и все ответы на него (ветка комментариев), при этом у ответов возможность скрытия/публикации будет заблокирована до тех пор, пока родительский комментарий не будет разблокирован (опубликован). Восстановление в ветке статусов - опубликован, происходит персонально кликом - Опубликовать по каждому комментарию в ветке.
|
||||
* Если комментарий одиночный (на него нет ответов):
|
||||
* При клике по иконке Скрыть - комментарий будет скрыт. При клике опубликовать - будет опубликован.
|
||||
* Видимость скрытых комментариев:
|
||||
* Если комментарий родитель и на него есть ответы и он скрыт - будут также скрыты все ответы на него. Такой комментарий будет видеть только Администратор и Автор комментария с пометкой "На модерации", однако если этот комментарий будет в свою очередь потомком вышестояшего в иерархии комментария-родителя другого Автора и тот в свою очередь будет скрыт - то тогда комментарий будет виден только Администратору.
|
||||
* Если комментарий одиночный и на него нет ответов и он, в свою очередь, сам не является ответом: если статус Скрыт - то его видит только Администратор и сам Автор. Другие посетители его не видят.
|
||||
* Если комментарий родитель, и на него есть ответы, но Администратор решит его скрыть - будут также скрыты все ответы на него, включая ответы на ответы (все потомки). Такой комментарий (скрытые комментарии в ветке) будут видеть только Администратор и Автор комментария с пометкой "На модерации", однако если этот комментарий будет в свою очередь потомком вышестоящего в иерархии комментария-родителя другого Автора и тот в свою очередь будет скрыт - такой комментарий будет виден только Администратору.
|
||||
* Если комментарий одиночный и на него нет ответов и он, в свою очередь, сам не является ответом: если статус Скрыт - то его видит только Администратор и сам Автор. Другие посетители его не видят.
|
||||
* Порядок разблокировки скрытых комментариев в ветке. В публичной части у всех потомков будут отключены кнопки "Показать комментарий" до тех пор, пока не будет разблокирован родительский комментарий, т.е. сначала нужно разблокировать (клик по иконке "перечеркнутый красный глаз - Показать комментарий") главного родителя (если в ветке есть ответы на ответы), после этого станут доступными иконки ("перечеркнутый красный глаз - Показать комментарий") у следующих родителей (если они есть) и так по иерархии - спускаться нужно сверху вниз и до последнего потомка. В админ панели проще работать с скрытыми ветками. В ней есть два типа иконок-замочков - родительские и для потомков. При наведении мыши на иконку потомков - будет выведена подсказка - Id комментария, который является родителям для данного комментария. Действия аналогичны публичной части - разблокируйте сначала родителей, затем потомков. Используйте фильтр в Админке - вывести скрытые комментарии и далее работайте с ними, так будет удобнее.
|
||||
* Контроль Имен Анонима
|
||||
* Если Анонимный пользователь, в течении жизни куки, сменит имя под которым он опубликовал свой первый комментарий, рядом с именем появится плашка с тултипом в котором будут перечислены все его имена.
|
||||
* Если Анонимный пользователь, в течении жизни куки, сменит имя, под которым он опубликовал свой первый комментарий, рядом с именем появится плашка с тултипом в котором будут перечислены все его имена.
|
||||
* Пагинация
|
||||
* Пагинация работает только для родительских комментариев, количество ответов (дети и внуки) на родительский комментарий - задается в Админпанели отдельно. Если общее количество ответов превышает лимит ответов установленный из Админки, будет выведена кнопка Показать еще N ответов, при клике по которой AJAX подгрузит и выведет оставшиеся комментарии на страницу.
|
||||
* Пагинация работает только для родительских комментариев, количество ответов (дети и внуки) на родительский комментарий - задается в Админ панели отдельно. Если общее количество ответов превышает лимит ответов, установленный в Админке, будет выведена кнопка Показать еще N ответов, при клике по которой AJAX подгрузит и выведет оставшиеся комментарии на страницу.
|
||||
* Сортировка
|
||||
* В админ панели доступен поиск комментария по следующим параметрам: по Имени, IP адресу, по тексту комментария. Сортировка: сначала новые, сначала старые, популярные (согласно Рейтингу пользователей), обсуждаемые - по количеству ответов на комментарий-родитель, оценки Автора (у кого больше всех звезд). Фильтрация: выводить все сообщения, только с файлами, только скрытые. Вывод комментариев по умолчанию - плоский список, но для удобства есть и древовидный список, что позволяет видеть сразу всю ветку. Количество на странице: выводит желаемое количество комментариев на страницу. Период: вам доступен календарь, в котором вы сами выбираете нужный вам период для просмотра - от и до.
|
||||
* Система тегов:
|
||||
* Тег [mod_comment] (Без параметров) - Это основной системный тег для вывода полноценного функционала на странице документа, выводит форму создания нового комментария + сами комментарии.
|
||||
* Тег [mod_comment] (Без параметров), - Это основной системный тег для вывода полноценного функционала на странице документа, выводит форму создания нового комментария + сами комментарии.
|
||||
* Тег [mod_comment:X] (Один параметр) - Выводит виджет последних комментариев, где X - количество выводимых комментариев, количество символов текста комментария равно 150 по умолчанию.
|
||||
* Тег [mod_comment:X:Y] (Два параметра) - Выводит виджет последних комментариев, где X - количество выводимых комментариев, а Y - количество символов в тексте комментария.
|
||||
|
||||
#### Сводная таблица параметров по умолчанию
|
||||
| Тег | Лимит (X) | Обрезка текста (Y) | Режим вывода |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @author Александр Сальников (Repellent)
|
||||
* @copyright 2026
|
||||
* @subpackage module_Comment
|
||||
* @since 3.31
|
||||
* @since 3.33
|
||||
* @filesource
|
||||
*/
|
||||
class Comment
|
||||
@@ -347,10 +347,10 @@ function commentListShow($tpl_dir)
|
||||
{
|
||||
$start = get_current_page() * $limit - $limit;
|
||||
|
||||
// 1. Считаем только РОДИТЕЛЕЙ для пагинации
|
||||
// Считаем только РОДИТЕЛЕЙ для пагинации
|
||||
$num = $AVE_DB->Query("SELECT COUNT(*) FROM " . PREFIX . "_module_comment_info WHERE document_id = '" . $document_id . "' AND parent_id = '0' " . $where_visibility)->GetCell();
|
||||
|
||||
// 2. Основной запрос (сортировка применяется к родителям в подзапросе)
|
||||
// Основной запрос (сортировка применяется к родителям в подзапросе)
|
||||
$sql = $AVE_DB->Query("
|
||||
SELECT * FROM " . PREFIX . "_module_comment_info
|
||||
WHERE document_id = '" . $document_id . "'
|
||||
@@ -755,7 +755,7 @@ function commentPostEdit($comment_id)
|
||||
{
|
||||
global $AVE_DB;
|
||||
|
||||
// 1. Инициализация данных
|
||||
// Инициализация данных
|
||||
$comment_id = (int)$comment_id;
|
||||
$user_id = (int)($_SESSION['user_id'] ?? 0);
|
||||
$user_group = (int)(defined('UGROUP') ? UGROUP : 0);
|
||||
@@ -763,7 +763,7 @@ function commentPostEdit($comment_id)
|
||||
|
||||
if ($comment_id <= 0 || $user_group <= 0) exit('INVALID_ID');
|
||||
|
||||
// 2. Получаем данные комментария и настройки модуля (JOIN)
|
||||
// Получаем данные комментария и настройки модуля (JOIN)
|
||||
$row = $AVE_DB->Query("
|
||||
SELECT
|
||||
msg.*,
|
||||
@@ -784,7 +784,7 @@ function commentPostEdit($comment_id)
|
||||
|
||||
if (!$row) exit('NOT_FOUND');
|
||||
|
||||
// 3. Проверка прав
|
||||
// Проверка прав
|
||||
$is_admin = ($user_group == 1);
|
||||
|
||||
$is_author = ($user_id > 0 && $user_id == $row['comment_author_id']) ||
|
||||
@@ -802,7 +802,7 @@ function commentPostEdit($comment_id)
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
@@ -817,7 +817,7 @@ function commentPostEdit($comment_id)
|
||||
if (mb_strlen($comment_text) > $max) $comment_text_cut .= '…';
|
||||
|
||||
|
||||
// 5. Работа с изображениями (МУЛЬТИЗАГРУЗКА И УДАЛЕНИЕ)
|
||||
// Работа с изображениями (МУЛЬТИЗАГРУЗКА И УДАЛЕНИЕ)
|
||||
|
||||
// ПРОВЕРКА ПРАВ НА ЗАГРУЗКУ (Защита от взлома через POST)
|
||||
$allow_files = (int)($row['comment_allow_files'] ?? 0);
|
||||
@@ -842,7 +842,7 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
// Получаем текущие файлы из БД в массив
|
||||
$current_files = !empty($row['comment_file']) ? explode(',', $row['comment_file']) : [];
|
||||
|
||||
// --- А. Обработка выборочного удаления ---
|
||||
// --- Обработка выборочного удаления ---
|
||||
if (!empty($_POST['delete_files'])) {
|
||||
$files_to_remove = explode(',', $_POST['delete_files']);
|
||||
foreach ($files_to_remove as $rem_file) {
|
||||
@@ -860,10 +860,10 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Б. Загрузка новых файлов (с проверкой лимита) ---
|
||||
// --- Загрузка новых файлов (с проверкой лимита) ---
|
||||
if (isset($_FILES['comment_image']) && is_array($_FILES['comment_image']['name'])) {
|
||||
|
||||
// 1. Считаем, сколько реально новых файлов пытаются загрузить
|
||||
// Считаем, сколько реально новых файлов пытаются загрузить
|
||||
$new_files_to_upload_count = 0;
|
||||
foreach ($_FILES['comment_image']['name'] as $k => $fname) {
|
||||
if (!empty($fname) && $_FILES['comment_image']['error'][$k] == UPLOAD_ERR_OK) {
|
||||
@@ -871,7 +871,7 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Проверка лимита: (Оставшиеся старые + Новые) не должно быть больше MAX
|
||||
// Проверка лимита: (Оставшиеся старые + Новые) не должно быть больше MAX
|
||||
$max_limit = (int)($row['comment_max_files'] ?? 5);
|
||||
if ((count($current_files) + $new_files_to_upload_count) > $max_limit) {
|
||||
echo "MAX_FILES_LIMIT_EXCEEDED";
|
||||
@@ -904,18 +904,18 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
if (!is_dir($upload_dir)) @mkdir($upload_dir, 0775, true);
|
||||
|
||||
|
||||
// 1. Получаем оригинальное имя файла без расширения
|
||||
// Получаем оригинальное имя файла без расширения
|
||||
$original_basename = pathinfo($fname, PATHINFO_FILENAME);
|
||||
|
||||
// 2. Очищаем имя
|
||||
// Очищаем имя
|
||||
$clean_name = prepare_fname($original_basename);
|
||||
|
||||
// 3. Защита от пустого имени
|
||||
// Защита от пустого имени
|
||||
if (empty($clean_name)) {
|
||||
$clean_name = 'file';
|
||||
}
|
||||
|
||||
// 4. Формируем новое имя: чистое_имя + время + расширение
|
||||
// Формируем новое имя: чистое_имя + время + расширение
|
||||
$new_file_name = $clean_name . '_' . time() . '.' . $file_ext;
|
||||
|
||||
if (move_uploaded_file($tmp_name, $upload_dir . $new_file_name)) {
|
||||
@@ -937,7 +937,7 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
|
||||
if ($can_edit_rating && isset($_POST['user_rating'])) {
|
||||
$user_rating = (int)$_POST['user_rating'];
|
||||
// Ограничиваем диапазон от 0 до 5 на всякий случай
|
||||
// Ограничиваем диапазон от 0 до 5
|
||||
if ($user_rating < 0) $user_rating = 0;
|
||||
if ($user_rating > 5) $user_rating = 5;
|
||||
} else {
|
||||
@@ -1229,7 +1229,7 @@ function commentAdminDelete($comment_id)
|
||||
{
|
||||
global $AVE_DB;
|
||||
|
||||
// 1. Собираем ID в массив (поддерживаем и одиночное, и массовое удаление)
|
||||
// Собираем ID в массив (поддерживаем и одиночное, и массовое удаление)
|
||||
if (is_array($comment_id)) {
|
||||
$ids = array_map('intval', $comment_id);
|
||||
} else {
|
||||
@@ -1312,7 +1312,6 @@ function commentAdminDelete($comment_id)
|
||||
{
|
||||
global $AVE_DB, $AVE_Template;
|
||||
|
||||
// Используем оператор объединения с null для PHP 8.4
|
||||
$comment_id = (int)($_REQUEST['Id'] ?? 0);
|
||||
|
||||
// Получаем полную информацию о комментарии
|
||||
@@ -1356,7 +1355,7 @@ function commentReplyStatusSet($comment_id, $comment_status = 'lock')
|
||||
$status_numeric = ($comment_status == 'lock') ? 0 : 1;
|
||||
|
||||
if ($status_numeric == 0) {
|
||||
// --- ТУДА (Скрываем всю ветку) ---
|
||||
// --- Скрываем всю ветку ---
|
||||
$all_ids = [$comment_id];
|
||||
$stack = [$comment_id];
|
||||
while (!empty($stack)) {
|
||||
@@ -1372,9 +1371,9 @@ function commentReplyStatusSet($comment_id, $comment_status = 'lock')
|
||||
echo "OK_LOCKED";
|
||||
}
|
||||
else {
|
||||
// --- ОБРАТНО (Открываем ТОЛЬКО один коммент) ---
|
||||
// --- Открываем ТОЛЬКО один коммент ---
|
||||
|
||||
// Магия SQL: Обновляем только если (родителя нет) ИЛИ (у родителя статус не 0)
|
||||
// Обновляем только если (родителя нет) ИЛИ (у родителя статус не 0)
|
||||
$AVE_DB->Query("
|
||||
UPDATE " . PREFIX . "_module_comment_info AS child
|
||||
LEFT JOIN " . PREFIX . "_module_comment_info AS parent ON child.parent_id = parent.Id
|
||||
@@ -1522,10 +1521,6 @@ function commentAdminListShow($tpl_dir)
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// --- ПАРАМЕТРЫ ФИЛЬТРАЦИИ ---
|
||||
$search = $_GET['search_query'] ?? '';
|
||||
$sort = $_GET['sort_order'] ?? 'new';
|
||||
@@ -1581,9 +1576,6 @@ function commentAdminListShow($tpl_dir)
|
||||
$all_items = array();
|
||||
$format = "%d %B %Y, %H:%M";
|
||||
|
||||
|
||||
|
||||
|
||||
// --- ПЕРЕД ЦИКЛОМ: Получаем список всех скрытых ID в системе ---
|
||||
$locked_ids = [];
|
||||
$res_locked = $AVE_DB->Query("SELECT Id FROM " . PREFIX . "_module_comment_info WHERE comment_status = '0'");
|
||||
@@ -1656,11 +1648,6 @@ if ($res_locked) {
|
||||
$all_items[$row['Id']] = $row;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// --- ПОСТРОЕНИЕ ДЕРЕВА ---
|
||||
$child_map = [];
|
||||
foreach ($all_items as $id => $item) {
|
||||
@@ -1680,13 +1667,13 @@ $docs = [];
|
||||
if ($view_mode == 'flat') {
|
||||
foreach ($all_items as $item) {
|
||||
$item['depth_level'] = 0;
|
||||
// Теперь мы берем parent_locked, который вычислили выше
|
||||
// берем parent_locked, который вычислили выше
|
||||
$docs[] = $item;
|
||||
}
|
||||
}
|
||||
// Если вид древовидный — используем твою отлаженную логику
|
||||
// Если вид древовидный
|
||||
else {
|
||||
// Универсальная функция с наследованием блокировки parent_locked
|
||||
// функция с наследованием блокировки parent_locked
|
||||
$buildTree = function($parent_id, $level, $is_parent_hidden) use (&$buildTree, &$docs, &$processed, &$all_items, &$child_map) {
|
||||
if (isset($child_map[$parent_id])) {
|
||||
foreach ($child_map[$parent_id] as $child_id) {
|
||||
@@ -1763,7 +1750,7 @@ function commentAdminPostEdit($tpl_dir)
|
||||
$request_id = (int)($_REQUEST['Id'] ?? 0);
|
||||
$is_ajax = isset($_REQUEST['ajax']);
|
||||
|
||||
// 1. Получаем данные и настройки одним запросом
|
||||
// Получаем данные и настройки одним запросом
|
||||
$row = $AVE_DB->Query("
|
||||
SELECT
|
||||
msg.*,
|
||||
@@ -1788,11 +1775,11 @@ $row = $AVE_DB->Query("
|
||||
// Получаем массив текущих файлов (разбиваем по запятой)
|
||||
$current_files = !empty($row['comment_file']) ? explode(',', $row['comment_file']) : [];
|
||||
|
||||
// --- А. Удаление файлов ---
|
||||
// Удаление файлов ---
|
||||
if (!empty($_POST['delete_files'])) {
|
||||
$files_to_remove = (array)$_POST['delete_files'];
|
||||
foreach ($files_to_remove as $rem_file) {
|
||||
$rem_file = basename(trim($rem_file)); // Очистка имени для безопасности
|
||||
$rem_file = basename(trim($rem_file)); // Очистка имени
|
||||
if (empty($rem_file)) continue;
|
||||
|
||||
if (file_exists($upload_dir . $rem_file)) {
|
||||
@@ -1914,7 +1901,7 @@ function commentAdminSettingsEdit($tpl_dir)
|
||||
$post_user_groups_read = $_POST['comment_user_groups_read'] ?? array();
|
||||
$post_allow_self_answer = $_POST['comment_allow_self_answer'] ?? 0;
|
||||
|
||||
// НОВАЯ НАСТРОЙКА: Порядок сортировки
|
||||
// Порядок сортировки
|
||||
$post_sort_order = $_POST['comment_sort_order'] ?? 'ASC';
|
||||
|
||||
$post_need_approve = $_POST['comment_need_approve'] ?? 0;
|
||||
|
||||
34
lang/ru.txt
34
lang/ru.txt
@@ -121,6 +121,7 @@ COMMENT_JS_SEC_CODE_WRONG = "Код капчи введен неверно!"
|
||||
COMMENT_JS_ERR_SRV = "Ошибка связи с сервером"
|
||||
COMMENT_WAITING_MODERATION = "На модерации"
|
||||
COMMENT_LAST_TITEL = "Последние комментарии"
|
||||
COMMENT_LAST_TITEL_ANSWER = "ответил(а)"
|
||||
COMMENT_HIDDEN_BY_MODERATOR = "Комментарий скрыт модератором и находится на проверке."
|
||||
COMMENT_TEXT_DEL_BY_ADMIN = "Комментарий удален администратором."
|
||||
COMMENT_TEXT_DEL_BY_AUTHOR = "Комментарий удален автором."
|
||||
@@ -256,3 +257,36 @@ COMMENT_TABLE_ACTIONS = "Действия"
|
||||
COMMENT_RATING_ADMIN = "Рейтинг комментариев"
|
||||
COMMENT_RATING_TITEL = "Авторская оценка"
|
||||
COMMENT_RATING_RESET = "Сбросить"
|
||||
COMMENT_ADM_SEARCH_TITEL = "Поиск:"
|
||||
COMMENT_ADM_SEARCH_FIELD = "Имя, IP или текст..."
|
||||
COMMENT_ADM_SORT_TITEL = "Сортировка:"
|
||||
COMMENT_ADM_SORT_DROP_NEW = "Сначала новые"
|
||||
COMMENT_ADM_SORT_DROP_OLD = "Сначала старые"
|
||||
COMMENT_ADM_SORT_DROP_POP = "Популярные (Рейтинг)"
|
||||
COMMENT_ADM_SORT_DROP_ANS = "Обсуждаемые (Ответы)"
|
||||
COMMENT_ADM_SORT_FILTR_TITEL = "Фильтр:"
|
||||
COMMENT_ADM_SORT_FILTR_ALL = "Все сообщения"
|
||||
COMMENT_ADM_SORT_FILTR_FILE = "Только с файлами"
|
||||
COMMENT_ADM_SORT_FILTR_HID = "Скрытые"
|
||||
COMMENT_ADM_SORT_VIEW_TITEL = "Вид / Лимит:"
|
||||
COMMENT_ADM_SORT_VIEW_LIN = "Линейный"
|
||||
COMMENT_ADM_SORT_VIEW_TREE = "Древовидный"
|
||||
COMMENT_ADM_SORT_DATE_TITEL = "Период:"
|
||||
COMMENT_ADM_SORT_BTN_SEARCH = "Найти"
|
||||
COMMENT_ADM_SORT_BTN_RESET = "Сброс"
|
||||
COMMENT_ADM_AVA_DEL_USER = "Пользователь удален"
|
||||
COMMENT_ADM_DATE_EDIT_COMM = "ред."
|
||||
COMMENT_ADM_DOC_LINK = "Документ #"
|
||||
COMMENT_ADM_UNLOCK_PARENT_COM = "Сначала опубликуйте родителя"
|
||||
|
||||
COMMENT_ADM_FILE_MAX_LIM = "Превышен лимит: максимум "
|
||||
COMMENT_ADM_FILE_MAX_LIM_A = " файлов."
|
||||
COMMENT_ADM_FILE_MAX_LIM_B = "Формат "
|
||||
COMMENT_ADM_FILE_MAX_LIM_C = " не разрешен!"
|
||||
COMMENT_ADM_FILE_MAX_LIM_D = "Файл слишком большой: "
|
||||
|
||||
COMMENT_ADD_FILES_ALLOW = "Разрешены:"
|
||||
COMMENT_ADD_FILES_MAX_SIZE = "Макс. размер:"
|
||||
COMMENT_ADD_FILES_SIZE = "KB"
|
||||
COMMENT_ADD_FILES_MAX_COUNT = "Макс. количество:"
|
||||
COMMENT_ADD_FILES_COUNT_PIC = "шт."
|
||||
@@ -269,42 +269,43 @@
|
||||
|
||||
<div class="grid-container">
|
||||
<div class="form-group">
|
||||
<label style="font-weight:bold;">Поиск:</label>
|
||||
<input type="text" name="search_query" value="{$smarty.get.search_query|default:''|escape}" placeholder="Имя, IP или текст..." style="width:160px; padding:5px; border:1px solid #ccc; border-radius:3px;">
|
||||
<label style="font-weight:bold;">{#COMMENT_ADM_SEARCH_TITEL#}</label>
|
||||
<input type="text" name="search_query" value="{$smarty.get.search_query|default:''|escape}" placeholder="{#COMMENT_ADM_SEARCH_FIELD#}" style="width:160px; padding:5px; border:1px solid #ccc; border-radius:3px;">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label style="font-weight:bold;">Сортировка:</label>
|
||||
<label style="font-weight:bold;">{#COMMENT_ADM_SORT_TITEL#}</label>
|
||||
<select name="sort_order" style="width:180px; padding:5px;">
|
||||
{assign var="get_sort" value=$smarty.get.sort_order|default:'new'}
|
||||
<option value="new" {if $get_sort == 'new'}selected{/if}>Сначала новые</option>
|
||||
<option value="old" {if $get_sort == 'old'}selected{/if}>Сначала старые</option>
|
||||
<option value="popular" {if $get_sort == 'popular'}selected{/if}>Популярные (Рейтинг)</option>
|
||||
<option value="discussed" {if $get_sort == 'discussed'}selected{/if}>Обсуждаемые (Ответы)</option>
|
||||
<option value="new" {if $get_sort == 'new'}selected{/if}>{#COMMENT_ADM_SORT_DROP_NEW#}</option>
|
||||
<option value="old" {if $get_sort == 'old'}selected{/if}>{#COMMENT_ADM_SORT_DROP_OLD#}</option>
|
||||
<option value="popular" {if $get_sort == 'popular'}selected{/if}>{#COMMENT_ADM_SORT_DROP_POP#}</option>
|
||||
<option value="discussed" {if $get_sort == 'discussed'}selected{/if}>{#COMMENT_ADM_SORT_DROP_ANS#}</option>
|
||||
<option value="user_rating" {if $get_sort == 'user_rating'}selected{/if}>Оценка автора</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label style="font-weight:bold;">Фильтр:</label>
|
||||
<label style="font-weight:bold;">{#COMMENT_ADM_SORT_FILTR_TITEL#}</label>
|
||||
<select name="filter_type" style="width:150px; padding:5px;">
|
||||
{assign var="get_filter" value=$smarty.get.filter_type|default:''}
|
||||
<option value="" {if $get_filter == ''}selected{/if}>Все сообщения</option>
|
||||
<option value="with_files" {if $get_filter == 'with_files'}selected{/if}>Только с файлами</option>
|
||||
<option value="hidden" {if $get_filter == 'hidden'}selected{/if}>Скрытые</option>
|
||||
<option value="" {if $get_filter == ''}selected{/if}>{#COMMENT_ADM_SORT_FILTR_ALL#}</option>
|
||||
<option value="with_files" {if $get_filter == 'with_files'}selected{/if}>{#COMMENT_ADM_SORT_FILTR_FILE#}</option>
|
||||
<option value="hidden" {if $get_filter == 'hidden'}selected{/if}>{#COMMENT_ADM_SORT_FILTR_HID#}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label style="font-weight:bold;">Вид / Лимит:</label>
|
||||
<label style="font-weight:bold;">{#COMMENT_ADM_SORT_VIEW_TITEL#}</label>
|
||||
<div style="display:flex; gap:5px; align-items: center;">
|
||||
{assign var="get_view" value=$smarty.get.view_mode|default:'flat'}
|
||||
{assign var="get_limit" value=$smarty.get.per_page|default:'15'}
|
||||
<select name="view_mode" style="width:100px; padding:5px;">
|
||||
<option value="flat" {if $get_view == 'flat'}selected{/if}>Линейный</option>
|
||||
<option value="tree" {if $get_view != 'flat'}selected{/if}>Древовидный</option>
|
||||
<option value="flat" {if $get_view == 'flat'}selected{/if}>{#COMMENT_ADM_SORT_VIEW_LIN#}</option>
|
||||
<option value="tree" {if $get_view != 'flat'}selected{/if}>{#COMMENT_ADM_SORT_VIEW_TREE#}</option>
|
||||
</select>
|
||||
<select name="per_page" style="width:60px; padding:5px;">
|
||||
<option value="2" {if $get_limit == '5'}selected{/if}>2</option>
|
||||
<option value="5" {if $get_limit == '5'}selected{/if}>5</option>
|
||||
<option value="15" {if $get_limit == '15'}selected{/if}>15</option>
|
||||
<option value="50" {if $get_limit == '50'}selected{/if}>50</option>
|
||||
@@ -314,7 +315,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label style="font-weight:bold;">Период:</label>
|
||||
<label style="font-weight:bold;">{#COMMENT_ADM_SORT_DATE_TITEL#}</label>
|
||||
<div style="display:flex; align-items:center; gap:5px;">
|
||||
<input type="date" name="date_from" value="{$smarty.get.date_from|default:''}" style="padding:4px; width: 135px;">
|
||||
<span>—</span>
|
||||
@@ -323,8 +324,8 @@
|
||||
</div>
|
||||
|
||||
<div class="buttons-container">
|
||||
<input type="submit" value="Найти" class="blueBtn" style="height: 28px; line-height: 1;">
|
||||
<input type="button" value="Сброс" class="redBtn" style="height: 28px; line-height: 1;" onclick="window.location.href='index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}'">
|
||||
<input type="submit" value="{#COMMENT_ADM_SORT_BTN_SEARCH#}" class="blueBtn" style="height: 28px; line-height: 1;">
|
||||
<input type="button" value="{#COMMENT_ADM_SORT_BTN_RESET#}" class="redBtn" style="height: 28px; line-height: 1;" onclick="window.location.href='index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}'">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -375,7 +376,7 @@
|
||||
</div>
|
||||
<div style="white-space: nowrap;">
|
||||
<span style="font-weight:bold; color:{if $is_deleted}#95a5a6{else}#2c3e50{/if};">
|
||||
{if $is_deleted}Пользователь удален{else}{$row.comment_author_name|escape}{/if}
|
||||
{if $is_deleted}{#COMMENT_ADM_AVA_DEL_USER#}{else}{$row.comment_author_name|escape}{/if}
|
||||
</span>
|
||||
{if !$is_deleted}
|
||||
<span class="author-stars">
|
||||
@@ -392,7 +393,7 @@
|
||||
<div style="font-size:11px; line-height:1.2;">
|
||||
<div>{$row.date_pub}</div>
|
||||
{if $row.date_edit}
|
||||
<div style="color:#d35400;">ред. {$row.date_edit}</div>
|
||||
<div style="color:#d35400;">{#COMMENT_ADM_DATE_EDIT_COMM#} {$row.date_edit}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
@@ -403,7 +404,11 @@
|
||||
<div style="margin-bottom: 2px;">
|
||||
{* Ссылка на документ оставляем без отступа или с минимальным, чтобы не «ломать» таблицу *}
|
||||
<a href="{$row.document_link}" target="_blank" style="color:#2980b9; font-weight:bold; text-decoration:none; border-bottom: 1px solid #d1d1d1; font-size: 11px; opacity: 0.7;">
|
||||
{$row.document_title|default:"Документ #`$row.document_id`"}
|
||||
{if $row.document_title}
|
||||
{$row.document_title|stripslashes|escape}
|
||||
{else}
|
||||
{#COMMENT_ADM_DOC_LINK#}{$row.document_id}
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -504,35 +509,30 @@
|
||||
{* --- ИКОНКИ СТАТУСА (ЗАМОЧКИ)*}
|
||||
{if $row.parent_locked}
|
||||
{* Если родитель скрыт, показываем серый замок *}
|
||||
<span class="icon_sprite ico_lock_no" style="opacity: 0.5; filter: grayscale(1); cursor: help;" title="Сначала опубликуйте родителя (ID: {$row.parent_id})"></span>
|
||||
<span class="icon_sprite ico_lock_no" style="opacity: 0.5; filter: grayscale(1); cursor: help;" title="{#COMMENT_ADM_UNLOCK_PARENT_COM#} (ID: {$row.parent_id})"></span>
|
||||
{else}
|
||||
{* Обычные кнопки, если родитель активен *}
|
||||
{if $row.comment_status == '1'}
|
||||
{* ИЗМЕНЕНО: Добавлены параметры в href *}
|
||||
<a class="icon_sprite ico_unlock_no" title="{#COMMENT_ICON_HIDE_ADM#}" href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&admin_action=set_status_0&id={$row.CId}&cp={$sess}&sort_order={$smarty.get.sort_order|default:'new'}&view_mode={$smarty.get.view_mode|default:'flat'}&filter_type={$smarty.get.filter_type|default:''}&per_page={$smarty.get.per_page|default:'15'}"></a>
|
||||
{else}
|
||||
{* ИЗМЕНЕНО: Добавлены параметры в href *}
|
||||
<a class="icon_sprite ico_unlock" title="{#COMMENT_ICON_SHOW_ADM#}" href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&admin_action=set_status_1&id={$row.CId}&cp={$sess}&sort_order={$smarty.get.sort_order|default:'new'}&view_mode={$smarty.get.view_mode|default:'flat'}&filter_type={$smarty.get.filter_type|default:''}&per_page={$smarty.get.per_page|default:'15'}"></a>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{* --- ОСТАЛЬНЫЕ КНОПКИ ОСТАЮТСЯ ВНУТРИ ПРОВЕРКИ --- *}
|
||||
{if !$is_deleted}
|
||||
{* ИЗМЕНЕНО: Добавлены параметры в href *}
|
||||
<a data-dialog="edit-comment-{$row.CId}"
|
||||
href="index.php?do=modules&action=modedit&mod=comment&moduleaction=admin_edit&Id={$row.CId}&cp={$sess}&pop=1&onlycontent=1&sort_order={$smarty.get.sort_order|default:'new'}&view_mode={$smarty.get.view_mode|default:'flat'}&filter_type={$smarty.get.filter_type|default:''}&per_page={$smarty.get.per_page|default:'15'}"
|
||||
data-width="" data-height="700" data-modal="true" data-title="{#COMMENT_ICON_EDIT_ADM#}"
|
||||
class="openDialog icon_sprite ico_edit" title="{#COMMENT_ICON_EDIT_ADM#}"></a>
|
||||
|
||||
{if $row.has_children}
|
||||
{* ИЗМЕНЕНО: Добавлены параметры в href *}
|
||||
<a class="icon_sprite ico_delete ConfirmDelete" title="{#COMMENT_ICON_DEL_SOFT#}" dir="{#COMMENT_CONFIRM_ADM_DIR_SOFT#}" name="{#COMMENT_CONFIRM_ADM_NAME_SOFT#}"
|
||||
href="index.php?do=modules&action=modedit&mod=comment&moduleaction=admin_del&admin_action_type=soft&Id={$row.CId}&cp={$sess}&sort_order={$smarty.get.sort_order|default:'new'}&view_mode={$smarty.get.view_mode|default:'flat'}&filter_type={$smarty.get.filter_type|default:''}&per_page={$smarty.get.per_page|default:'15'}"></a>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{* Полное удаление всегда снаружи *}
|
||||
{* ИЗМЕНЕНО: Добавлены параметры в href *}
|
||||
<a class="icon_sprite ico_delete ico_delete_all ConfirmDelete" title="{#COMMENT_ICON_DEL_HARD#}" dir="{#COMMENT_CONFIRM_ADM_DIR#}" name="{#COMMENT_CONFIRM_ADM_NAME#}"
|
||||
href="index.php?do=modules&action=modedit&mod=comment&moduleaction=admin_del&admin_action_type=full&Id={$row.CId}&cp={$sess}&sort_order={$smarty.get.sort_order|default:'new'}&view_mode={$smarty.get.view_mode|default:'flat'}&filter_type={$smarty.get.filter_type|default:''}&per_page={$smarty.get.per_page|default:'15'}"></a>
|
||||
</div>
|
||||
@@ -575,7 +575,7 @@ function refreshStyles() {
|
||||
// Стилизуем селект массовых действий
|
||||
$('#mass_action_select').styler('destroy').styler();
|
||||
|
||||
// Если решили стилизовать и дату (многие версии FormStyler это умеют вручную)
|
||||
// стилизовать дату
|
||||
$('.searchLine input[type="date"]').styler('destroy').styler();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
var COMMENT_ADM_FILE_MAX_LIM = '{#COMMENT_ADM_FILE_MAX_LIM#}';
|
||||
var COMMENT_ADM_FILE_MAX_LIM_A = '{#COMMENT_ADM_FILE_MAX_LIM_A#}';
|
||||
var COMMENT_ADM_FILE_MAX_LIM_B = '{#COMMENT_ADM_FILE_MAX_LIM_B#}';
|
||||
var COMMENT_ADM_FILE_MAX_LIM_C = '{#COMMENT_ADM_FILE_MAX_LIM_C#}';
|
||||
var COMMENT_ADM_FILE_MAX_LIM_D = '{#COMMENT_ADM_FILE_MAX_LIM_D#}';
|
||||
|
||||
{literal}
|
||||
$(document).ready(function(){
|
||||
// Инициализация параметров из Smarty
|
||||
var maxFiles = {/literal}{$row.comment_max_files|default:5}{literal};
|
||||
var maxFileSize = {/literal}{$row.comment_max_file_size|default:2048}{literal} * 1024;
|
||||
var allowedExts = {/literal}'{$row.comment_allowed_extensions|default:"jpg,jpeg,png,gif,webp,zip,txt"}'{literal}.split(',');
|
||||
var allowedExts = {/literal}'{$row.comment_allowed_extensions|default:"jpg,jpeg,png,gif,webp"}'{literal}.split(',');
|
||||
var existingFilesCount = {/literal}{$row.file_list|@count|default:0}{literal};
|
||||
|
||||
var left = {/literal}{$comment_max_chars|default:1000}{literal};
|
||||
@@ -49,7 +55,7 @@ $(document).ready(function(){
|
||||
|
||||
// Проверка общего лимита количества
|
||||
if ((currentTotal + files.length) > maxFiles) {
|
||||
showFileAlert("Превышен лимит: максимум " + maxFiles + " файлов.", $uploadBox);
|
||||
showFileAlert(COMMENT_ADM_FILE_MAX_LIM + maxFiles + COMMENT_ADM_FILE_MAX_LIM_A, $uploadBox);
|
||||
$input.val('');
|
||||
return false;
|
||||
}
|
||||
@@ -60,15 +66,15 @@ $(document).ready(function(){
|
||||
var ext = file.name.split('.').pop().toLowerCase();
|
||||
|
||||
if ($.inArray(ext, allowedExts) == -1) {
|
||||
showFileAlert("Формат <b>." + ext + "</b> не разрешен!", $uploadBox);
|
||||
showFileAlert(COMMENT_ADM_FILE_MAX_LIM_B + "<b>." + ext + "</b>" + COMMENT_ADM_FILE_MAX_LIM_C, $uploadBox);
|
||||
hasError = true;
|
||||
return false; // break loop
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size > maxFileSize) {
|
||||
showFileAlert("Файл слишком большой: " + (file.size/1024).toFixed(1) + " KB", $uploadBox);
|
||||
showFileAlert(COMMENT_ADM_FILE_MAX_LIM_D + (file.size/1024).toFixed(1) + " KB", $uploadBox);
|
||||
hasError = true;
|
||||
return false; // break loop
|
||||
return false;
|
||||
}
|
||||
|
||||
// Если ошибок нет, читаем превью (FileReader)
|
||||
@@ -341,7 +347,7 @@ $(document).ready(function(){
|
||||
<div class="upload-info-box">
|
||||
<input type="file" name="comment_image[]" multiple />
|
||||
<div style="font-size:10px; color:#888; margin-top:5px;">
|
||||
{#COMMENT_ADD_FILES_ALLOW#}: {$row.comment_allowed_extensions} (max: {$row.comment_max_files})
|
||||
{#COMMENT_ADD_FILES_ALLOW#} {$row.comment_allowed_extensions} ({#COMMENT_ADD_FILES_MAX_COUNT#} {$row.comment_max_files} {#COMMENT_ADD_FILES_COUNT_PIC#} | {#COMMENT_ADD_FILES_MAX_SIZE#} {$row.comment_max_file_size} {#COMMENT_ADD_FILES_SIZE#})
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
</span>
|
||||
|
||||
{if $smarty.const.UGROUP==1}
|
||||
{* Если родитель НЕ скрыт — показываем кнопку управления ребенком *}
|
||||
{* Если родитель НЕ скрыт — показываем кнопку управления потомком *}
|
||||
<a class="btn btn-sm btn-link {if $c.comment_status==1}text-success{else}text-danger{/if} mod_comment_toggle px-2"
|
||||
href="javascript:void(0);"
|
||||
data-id="{$c.Id}"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
{*Виджет Последние *}
|
||||
|
||||
<div class="last-comments-widget" style="max-width: 400px;">
|
||||
<h4 class="widget-title mb-4" style="font-weight: 700; border-left: 4px solid #0d6efd; padding-left: 15px; line-height: 1.2;">{#COMMENT_LAST_TITEL#}</h4>
|
||||
|
||||
@@ -23,7 +25,7 @@
|
||||
{$c.comment_author_name}
|
||||
{if $c.parent_id > 0}
|
||||
<span class="lc-reply-label">
|
||||
<i class="bi bi-reply-all-fill text-primary"></i> ответил(а)
|
||||
<i class="bi bi-reply-all-fill text-primary"></i> {#COMMENT_LAST_TITEL_ANSWER#}
|
||||
</span>
|
||||
{/if}
|
||||
</strong>
|
||||
|
||||
Reference in New Issue
Block a user