добален рейтинг со звездами
This commit is contained in:
@@ -777,6 +777,102 @@ function commentPostDelete($comment_id)
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод для обработки голосования за комментарий
|
||||
*/
|
||||
function commentVote()
|
||||
{
|
||||
global $AVE_DB;
|
||||
|
||||
$comment_id = (int)($_POST['comment_id'] ?? 0);
|
||||
$vote_value = (int)($_POST['vote'] ?? 0);
|
||||
$ajax = (isset($_POST['ajax']) && $_POST['ajax'] == 1);
|
||||
|
||||
// Базовая проверка значения (от 1 до 5 звезд)
|
||||
if ($comment_id <= 0 || $vote_value < 1 || $vote_value > 5) {
|
||||
if ($ajax) {
|
||||
if (ob_get_length()) ob_end_clean();
|
||||
echo 'error';
|
||||
exit;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Идентификация пользователя
|
||||
$user_id = empty($_SESSION['user_id']) ? 0 : (int)$_SESSION['user_id'];
|
||||
$anon_key = $this->_getAnonKey();
|
||||
|
||||
// 2. Получаем данные о комментарии (FetchRow исправлен)
|
||||
$comment_data = $AVE_DB->Query("
|
||||
SELECT comment_author_id, anon_key
|
||||
FROM " . PREFIX . "_module_comment_info
|
||||
WHERE Id = '" . $comment_id . "'
|
||||
")->FetchRow();
|
||||
|
||||
if (!$comment_data) {
|
||||
if ($ajax) {
|
||||
if (ob_get_length()) ob_end_clean();
|
||||
echo 'error';
|
||||
exit;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Запрет голосовать за свой же комментарий
|
||||
$is_author = false;
|
||||
if ($user_id > 0 && $user_id == $comment_data->comment_author_id) $is_author = true;
|
||||
if ($user_id == 0 && $anon_key == $comment_data->anon_key) $is_author = true;
|
||||
|
||||
if ($is_author) {
|
||||
if ($ajax) {
|
||||
if (ob_get_length()) ob_end_clean();
|
||||
echo 'own_comment';
|
||||
exit;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Проверка на повторное голосование
|
||||
$sql_check = "SELECT id FROM " . PREFIX . "_module_comment_votes WHERE comment_id = '" . $comment_id . "' AND ";
|
||||
if ($user_id > 0) {
|
||||
$sql_check .= "user_id = '" . $user_id . "'";
|
||||
} else {
|
||||
$sql_check .= "anon_key = '" . $anon_key . "'";
|
||||
}
|
||||
|
||||
if ($AVE_DB->Query($sql_check)->GetCell()) {
|
||||
if ($ajax) {
|
||||
if (ob_get_length()) ob_end_clean();
|
||||
echo 'already_voted';
|
||||
exit;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Записываем голос в лог
|
||||
$AVE_DB->Query("
|
||||
INSERT INTO " . PREFIX . "_module_comment_votes
|
||||
(comment_id, user_id, anon_key, vote_value, date_voted)
|
||||
VALUES
|
||||
('" . $comment_id . "', '" . $user_id . "', '" . $anon_key . "', '" . $vote_value . "', '" . time() . "')
|
||||
");
|
||||
|
||||
// 6. Обновляем агрегированные данные
|
||||
$AVE_DB->Query("
|
||||
UPDATE " . PREFIX . "_module_comment_info
|
||||
SET rating_sum = rating_sum + " . $vote_value . ",
|
||||
rating_count = rating_count + 1
|
||||
WHERE Id = '" . $comment_id . "'
|
||||
");
|
||||
|
||||
// 7. ФИНАЛ: Очищаем мусор и отдаем чистый ответ
|
||||
if ($ajax) {
|
||||
if (ob_get_length()) ob_end_clean(); // Выбрасываем все Warning-и и HTML
|
||||
echo 'success';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function commentAdminDelete($comment_id)
|
||||
{
|
||||
global $AVE_DB;
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --- ДЕЙСТВИЯ (DELETE, LOCK, OPEN/CLOSE) --- */
|
||||
/* --- ДЕЙСТВИЯ (DELETE, LOCK, OPEN/CLOSE, VOTE) --- */
|
||||
function cAction(obj, action) {
|
||||
var $btn = $(obj);
|
||||
var cid = $btn.data('id');
|
||||
@@ -119,6 +119,54 @@
|
||||
initCommentTimers();
|
||||
var $doc = $(document);
|
||||
|
||||
// --- ЛОГИКА ГОЛОСОВАНИЯ (РЕЙТИНГ) ---
|
||||
$doc.on('mouseenter', '.star-item', function() {
|
||||
$(this).prevAll().addBack().removeClass('bi-star').addClass('bi-star-fill');
|
||||
$(this).nextAll().removeClass('bi-star-fill').addClass('bi-star');
|
||||
});
|
||||
|
||||
$doc.on('mouseleave', '.comment-stars', function() {
|
||||
// При уходе мыши визуально ничего не меняем до клика или рефреша
|
||||
});
|
||||
|
||||
$doc.on('click', '.star-item', function() {
|
||||
var $container = $(this).closest('.comment-rating-container');
|
||||
var commentId = $container.data('id');
|
||||
var voteValue = $(this).data('value');
|
||||
|
||||
$.ajax({
|
||||
// Используем относительный путь для избежания проблем с протоколом HTTPS/HTTP
|
||||
url: 'index.php?module=comment&action=vote&ajax=1',
|
||||
type: 'POST',
|
||||
data: {
|
||||
comment_id: commentId,
|
||||
vote: voteValue,
|
||||
ajax: 1 // Дублируем для надежности
|
||||
},
|
||||
success: function(response) {
|
||||
var res = response.toString().trim();
|
||||
|
||||
// Проверяем наличие ключевого слова в ответе (защита от варнингов PHP)
|
||||
if (res.indexOf('success') !== -1) {
|
||||
$container.html('<span class="text-success small fw-bold">Спасибо!</span>');
|
||||
setTimeout(function(){ location.reload(); }, 1000);
|
||||
} else if (res.indexOf('already_voted') !== -1) {
|
||||
alert('Вы уже голосовали за этот комментарий.');
|
||||
} else if (res.indexOf('own_comment') !== -1) {
|
||||
alert('Нельзя голосовать за свой собственный комментарий.');
|
||||
} else {
|
||||
console.warn("Server response:", response);
|
||||
alert('Ошибка при обработке голоса сервером.');
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error("XHR Status:", xhr.status);
|
||||
alert('Ошибка связи с сервером. Статус: ' + xhr.status);
|
||||
}
|
||||
});
|
||||
});
|
||||
// ------------------------------------
|
||||
|
||||
// Глобальные кнопки управления
|
||||
$doc.on('click', '#mod_comment_close', function(e) {
|
||||
e.preventDefault(); cAction(this, 'close');
|
||||
@@ -127,17 +175,15 @@
|
||||
e.preventDefault(); cAction(this, 'open');
|
||||
});
|
||||
|
||||
// КНОПКА ОТВЕТА (ИСПРАВЛЕНО: перемещает форму, а не шлет AJAX)
|
||||
// КНОПКА ОТВЕТА
|
||||
$doc.off('click', '.mod_comment_answer').on('click', '.mod_comment_answer', function(e) {
|
||||
e.preventDefault();
|
||||
var cid = $(this).data('id');
|
||||
var $form = $('#mod_comment_new');
|
||||
|
||||
// Переносим форму и ставим parent_id
|
||||
$form.insertAfter('#comment_' + cid).show();
|
||||
$('#parent_id').val(cid);
|
||||
|
||||
// Скролл к форме
|
||||
$('html, body').animate({
|
||||
scrollTop: $form.offset().top - 150
|
||||
}, 500);
|
||||
|
||||
@@ -62,6 +62,11 @@ if (!defined('ACP') && isset($_REQUEST['module']) && $_REQUEST['module'] == 'com
|
||||
$comment->commentPostNew($tpl_dir);
|
||||
break;
|
||||
|
||||
// --- НОВОЕ: ОБРАБОТКА ГОЛОСОВАНИЯ (РЕЙТИНГ) ---
|
||||
case 'vote':
|
||||
$comment->commentVote();
|
||||
break;
|
||||
|
||||
// Если edit, тогда открываем форму для редактирования текста комментария
|
||||
case 'edit':
|
||||
if (!empty(UGROUP))
|
||||
|
||||
50
sql.php
50
sql.php
@@ -3,8 +3,8 @@
|
||||
/**
|
||||
* AVE.cms - Модуль Комментарии
|
||||
*
|
||||
* Обновленная структура с поддержкой произвольных полей, загрузки изображений
|
||||
* и идентификации анонимных пользователей (anon_key)
|
||||
* Обновленная структура с поддержкой рейтинга (звезд),
|
||||
* идентификации анонимных пользователей и загрузки файлов.
|
||||
*/
|
||||
|
||||
$module_sql_install = array();
|
||||
@@ -13,6 +13,7 @@ $module_sql_update = array();
|
||||
|
||||
$module_sql_deinstall[] = "DROP TABLE IF EXISTS `%%PRFX%%_module_comments`;";
|
||||
$module_sql_deinstall[] = "DROP TABLE IF EXISTS `%%PRFX%%_module_comment_info`;";
|
||||
$module_sql_deinstall[] = "DROP TABLE IF EXISTS `%%PRFX%%_module_comment_votes`;";
|
||||
|
||||
// =================================================================================
|
||||
// 1. УСТАНОВКА МОДУЛЯ (CREATE TABLE)
|
||||
@@ -27,14 +28,12 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comments` (
|
||||
`comment_use_antispam` enum('1','0') NOT NULL default '1',
|
||||
`comment_use_page_nav` enum('1','0') NOT NULL default '1',
|
||||
`comment_page_nav_count` varchar(5) NOT NULL,
|
||||
/* УНИВЕРСАЛЬНЫЕ ПОЛЯ */
|
||||
`comment_show_f1` tinyint(1) NOT NULL default '1',
|
||||
`comment_req_f1` tinyint(1) NOT NULL default '0',
|
||||
`comment_name_f1` varchar(255) NOT NULL default '',
|
||||
`comment_show_f2` tinyint(1) NOT NULL default '1',
|
||||
`comment_req_f2` tinyint(1) NOT NULL default '0',
|
||||
`comment_name_f2` varchar(255) NOT NULL default '',
|
||||
/* ПОЛЯ ДЛЯ ЗАГРУЗКИ ФАЙЛОВ */
|
||||
`comment_allow_files` tinyint(1) NOT NULL default '0',
|
||||
`comment_file_max_size` int(10) NOT NULL default '2048',
|
||||
PRIMARY KEY (`Id`)
|
||||
@@ -50,15 +49,16 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comment_info` (
|
||||
`comment_author_city` varchar(255) NOT NULL,
|
||||
`comment_author_website` varchar(255) NOT NULL,
|
||||
`comment_author_ip` varchar(15) NOT NULL,
|
||||
/* НОВОЕ ПОЛЕ: КЛЮЧ АНОНИМА (MD5 = 32 символа) */
|
||||
`anon_key` varchar(32) DEFAULT NULL,
|
||||
`comment_published` int(10) unsigned NOT NULL default '0',
|
||||
`comment_changed` int(10) unsigned NOT NULL default '0',
|
||||
`comment_text` text NOT NULL,
|
||||
`comment_status` tinyint(1) unsigned NOT NULL default '1',
|
||||
`comments_close` tinyint(1) unsigned NOT NULL default '0',
|
||||
/* ПУТЬ К ПРИКРЕПЛЕННОМУ ФАЙЛУ */
|
||||
`comment_file` varchar(255) NOT NULL default '',
|
||||
/* ПОЛЯ РЕЙТИНГА */
|
||||
`rating_sum` int(10) NOT NULL default '0',
|
||||
`rating_count` int(10) NOT NULL default '0',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `document_id` (`document_id`),
|
||||
KEY `parent_id` (`parent_id`),
|
||||
@@ -66,7 +66,20 @@ $module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comment_info` (
|
||||
KEY `anon_key` (`anon_key`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PACK_KEYS=0;";
|
||||
|
||||
// Начальные данные
|
||||
// Таблица для хранения истории голосов (защита от повторов)
|
||||
$module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comment_votes` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`comment_id` int(10) unsigned NOT NULL,
|
||||
`user_id` int(10) unsigned DEFAULT '0',
|
||||
`anon_key` varchar(32) DEFAULT '',
|
||||
`vote_value` tinyint(1) NOT NULL,
|
||||
`date_voted` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `comment_id` (`comment_id`),
|
||||
KEY `anon_key` (`anon_key`),
|
||||
KEY `user_id` (`user_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
|
||||
|
||||
$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);";
|
||||
|
||||
// =================================================================================
|
||||
@@ -83,13 +96,22 @@ $module_sql_update[] = "
|
||||
LIMIT 1;
|
||||
";
|
||||
|
||||
// Добавление поля для файла (если модуль обновляется со старой версии)
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comment_info` ADD `comment_file` VARCHAR(255) NOT NULL DEFAULT '' AFTER `comments_close`;";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_allow_files` TINYINT(1) NOT NULL DEFAULT '0';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comments` ADD `comment_file_max_size` INT(10) NOT NULL DEFAULT '2048';";
|
||||
// Добавляем поля рейтинга в существующую таблицу
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comment_info` ADD `rating_sum` INT(10) NOT NULL DEFAULT '0';";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comment_info` ADD `rating_count` INT(10) NOT NULL DEFAULT '0';";
|
||||
|
||||
// НОВОЕ: Добавляем поле anon_key в существующую таблицу
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comment_info` ADD `anon_key` VARCHAR(32) DEFAULT NULL AFTER `comment_author_ip`;";
|
||||
$module_sql_update[] = "ALTER TABLE `%%PRFX%%_module_comment_info` ADD INDEX `anon_key` (`anon_key`);";
|
||||
// Создаем таблицу голосов, если её еще нет
|
||||
$module_sql_update[] = "CREATE TABLE IF NOT EXISTS `%%PRFX%%_module_comment_votes` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`comment_id` int(10) unsigned NOT NULL,
|
||||
`user_id` int(10) unsigned DEFAULT '0',
|
||||
`anon_key` varchar(32) DEFAULT '',
|
||||
`vote_value` tinyint(1) NOT NULL,
|
||||
`date_voted` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `comment_id` (`comment_id`),
|
||||
KEY `anon_key` (`anon_key`),
|
||||
KEY `user_id` (`user_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
|
||||
|
||||
?>
|
||||
@@ -22,13 +22,32 @@
|
||||
|
||||
<div class="flex-grow-1">
|
||||
{* Информация об авторе и дате *}
|
||||
<div class="mod_comment_meta mb-2 text-muted small">
|
||||
<div class="mod_comment_meta mb-2 text-muted small d-flex align-items-center flex-wrap">
|
||||
|
||||
<i class="bi bi-person me-1"></i> {#COMMENT_USER_ADD#} <a title="{#COMMENT_INFO#}" href="javascript:void(0);" onclick="popup('{$ABS_PATH}index.php?module=comment&action=postinfo&pop=1&Id={$c.Id}&theme={$theme}','comment','500','300','1');" class="fw-bold link-dark">{$c.comment_author_name|stripslashes|escape}</a>
|
||||
<span class="me-2"><i class="bi bi-person me-1"></i> {#COMMENT_USER_ADD#} <a title="{#COMMENT_INFO#}" href="javascript:void(0);" onclick="popup('{$ABS_PATH}index.php?module=comment&action=postinfo&pop=1&Id={$c.Id}&theme={$theme}','comment','500','300','1');" class="fw-bold link-dark">{$c.comment_author_name|stripslashes|escape}</a></span>
|
||||
|
||||
<span class="ms-2"><i class="bi bi-clock me-1"></i> {$c.comment_published}</span>
|
||||
<span class="me-2"><i class="bi bi-clock me-1"></i> {$c.comment_published}</span>
|
||||
|
||||
{* ----- БЛОК РЕЙТИНГА (ЗВЕЗДЫ) ----- *}
|
||||
<div class="comment-rating-container d-inline-flex align-items-center ms-2 py-1 px-2 bg-light rounded border" data-id="{$c.Id}" style="line-height: 1;">
|
||||
<div class="comment-stars d-inline-flex" style="cursor: pointer; color: #ffc107; font-size: 0.9rem;">
|
||||
{assign var="avg_rating" value=0}
|
||||
{if isset($c.rating_count) && $c.rating_count > 0}
|
||||
{math equation="round(x / y)" x=$c.rating_sum y=$c.rating_count assign="avg_rating"}
|
||||
{/if}
|
||||
|
||||
{section name=star start=1 loop=6}
|
||||
<i class="star-item bi {if $smarty.section.star.index <= $avg_rating}bi-star-fill{else}bi-star{/if} {if !$smarty.section.star.last}me-1{/if}"
|
||||
data-value="{$smarty.section.star.index}"
|
||||
title="Оценить на {$smarty.section.star.index}"></i>
|
||||
{/section}
|
||||
</div>
|
||||
{if isset($c.rating_count)}
|
||||
<span class="ms-2 text-muted fw-bold" style="font-size: 0.8rem;">({$c.rating_count})</span>
|
||||
{/if}
|
||||
</div>
|
||||
{* --------------------------------- *}
|
||||
|
||||
{* Динамические поля в мета-данных комментария *}
|
||||
{if $comment_show_f1 == 1 && $c.comment_author_website}
|
||||
<span class="ms-2 d-inline-block">
|
||||
<i class="bi bi-link-45deg"></i> <strong>{$comment_name_f1|default:#COMMENT_YOUR_SITE#}:</strong> {$c.comment_author_website|stripslashes|escape}
|
||||
@@ -44,7 +63,7 @@
|
||||
<span class="ms-2 text-secondary">• IP:{$c.comment_author_ip}</span>
|
||||
{/if}
|
||||
|
||||
<span class="mod_comment_changed" id="changed_{$c.Id}">
|
||||
<span class="mod_comment_changed ms-2" id="changed_{$c.Id}">
|
||||
{if isset($c.comment_changed) && $c.comment_changed > 1}
|
||||
(<span class="text-secondary">{#COMMENT_TEXT_CHANGED#} {$c.comment_changed}</span>)
|
||||
{/if}
|
||||
@@ -87,15 +106,13 @@
|
||||
{* ПРАВАЯ ЧАСТЬ: Кнопки действий *}
|
||||
<div class="actions-buttons">
|
||||
{* Кнопка ответа *}
|
||||
{* ПРАВА НА ОТВЕТ *}
|
||||
{if ($cancomment==1 && $closed!=1) || $smarty.const.UGROUP==1}
|
||||
{* Показываем кнопку только если это НЕ мой собственный комментарий *}
|
||||
{if !$c.is_my_own}
|
||||
<a class="mod_comment_answer p-2 text-primary text-decoration-none" href="javascript:void(0);" data-id="{$c.Id}" title="{#COMMENT_ANSWER_LINK#}">
|
||||
<i class="bi bi-reply-fill me-1"></i> {#COMMENT_ANSWER_LINK#}
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
||||
{if ($cancomment==1 && $closed!=1) || $smarty.const.UGROUP==1}
|
||||
{if !(isset($c.is_my_own) && $c.is_my_own)}
|
||||
<a class="mod_comment_answer p-2 text-primary text-decoration-none" href="javascript:void(0);" data-id="{$c.Id}" title="{#COMMENT_ANSWER_LINK#}">
|
||||
<i class="bi bi-reply-fill me-1"></i> {#COMMENT_ANSWER_LINK#}
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{* ПРАВА НА РЕДАКТИРОВАНИЕ *}
|
||||
{if $c.can_edit}
|
||||
|
||||
Reference in New Issue
Block a user