добавлен метод вывода информации об Авторе комментария

This commit is contained in:
2026-01-31 14:38:34 +05:00
parent 034e71eed0
commit 8300c4b693
3 changed files with 244 additions and 78 deletions

View File

@@ -1501,37 +1501,92 @@ function commentAdminDelete($comment_id)
*
* @param string $tpl_dir - путь к шаблонам модуля
*/
function commentPostInfoShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
$comment_id = (int)($_REQUEST['Id'] ?? 0);
function commentPostInfoShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
$comment_id = (int)($_REQUEST['Id'] ?? 0);
// Получаем полную информацию о комментарии
$row = $AVE_DB->Query("
SELECT *
FROM " . PREFIX . "_module_comment_info
WHERE Id = '" . $comment_id . "'
")->FetchAssocArray();
// 1. Получаем данные комментария и настройки заголовков
$row = $AVE_DB->Query("
SELECT c.*, m.comment_name_f1, m.comment_name_f2
FROM " . PREFIX . "_module_comment_info AS c
LEFT JOIN " . PREFIX . "_module_comments AS m ON 1=1
WHERE c.Id = '$comment_id'
LIMIT 1
")->FetchAssocArray();
// Преобразуем адрес сайта к формату ссылки
$row['comment_author_website'] = str_replace('http://', '', $row['comment_author_website']);
$row['comment_author_website'] = ($row['comment_author_website'] != '')
? '<a target="_blank" href="http://' . $row['comment_author_website'] . '">' . $row['comment_author_website'] .'</a>'
: '';
if (!$row) exit('Данные не найдены');
// Выполняем запрос к БД на получение количества всех комментариев, оставленных данным пользователем
$row['num'] = $AVE_DB->Query("
SELECT COUNT(*)
FROM " . PREFIX . "_module_comment_info
WHERE comment_author_id = '" . $row['comment_author_id'] . "'
AND comment_author_id != 0
")->GetCell();
$author_id = (int)($row['comment_author_id'] ?? 0);
$a_key = $row['anon_key'] ?? '';
// Отображаем окно с информацией
$AVE_Template->assign('c', $row);
$AVE_Template->display($tpl_dir . $this->_postinfo_tpl);
}
// --- ЛОГИКА ДАТ ---
if ($author_id > 0) {
$user_data = $AVE_DB->Query("SELECT reg_time, last_visit FROM " . PREFIX . "_users WHERE Id = '$author_id' LIMIT 1")->FetchAssocArray();
$row['date_label'] = 'Дата регистрации';
$row['date_value'] = $user_data['reg_time'] ?? 0;
$row['last_visit'] = $user_data['last_visit'] ?? 0;
} else {
$first_time = ($a_key != '') ? $AVE_DB->Query("SELECT MIN(comment_published) FROM " . PREFIX . "_module_comment_info WHERE anon_key = '" . addslashes($a_key) . "'")->GetCell() : 0;
$row['date_label'] = 'Дата первого комментария';
$row['date_value'] = $first_time;
$row['last_visit'] = 0;
}
// --- ПОДСЧЕТ КОММЕНТАРИЕВ И СУММАРНОГО РЕЙТИНГА ---
if ($author_id > 0) {
$where = "comment_author_id = '$author_id'";
} elseif ($a_key != '') {
$where = "anon_key = '" . addslashes($a_key) . "'";
} else {
$where = "Id = '$comment_id'"; // Крайний случай
}
// Считаем всё одним запросом: количество, сумму звезд и сумму голосов
$stats = $AVE_DB->Query("
SELECT
COUNT(*) as total_cnt,
SUM(rating_sum) as sum_stars,
SUM(rating_count) as sum_votes
FROM " . PREFIX . "_module_comment_info
WHERE $where
")->FetchAssocArray();
$row['num'] = $stats['total_cnt'] ?? 1;
// Вычисляем средний рейтинг (от 1 до 5)
$avg_rating = 0;
if (!empty($stats['sum_votes'])) {
$avg_rating = round($stats['sum_stars'] / $stats['sum_votes']);
}
$row['avg_rating'] = $avg_rating;
$row['total_votes'] = $stats['sum_votes'];
// --- ОБРАБОТКА ДОП. ПОЛЕЙ (F1, F2) ---
$custom_fields = [];
$fields_to_check = [
['val' => $row['comment_author_website'], 'title' => $row['comment_name_f1'], 'default' => 'Личный сайт'],
['val' => $row['comment_author_city'], 'title' => $row['comment_name_f2'], 'default' => 'Откуда']
];
foreach ($fields_to_check as $f) {
$val = trim($f['val'] ?? '');
if ($val != '') {
$title = (!empty($f['title'])) ? $f['title'] : $f['default'];
$value = (preg_match('/^(http|https|www\.)/i', $val))
? '<a target="_blank" href="http://' . str_replace(['http://', 'https://'], '', $val) . '">' . str_replace(['http://', 'https://'], '', $val) . '</a>'
: htmlspecialchars($val);
$custom_fields[] = ['title' => $title, 'value' => $value];
}
}
$AVE_Template->assign('c', $row);
$AVE_Template->assign('custom_fields', $custom_fields);
header('Content-Type: text/html; charset=utf-8');
$AVE_Template->display($tpl_dir . $this->_postinfo_tpl);
exit;
}
/**
* Метод, предназначенный для управления запретом или разрешением отвечать на комментарии

View File

@@ -1,54 +1,47 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<div class="author-info-popover">
<ul class="list-unstyled mb-0 small">
<li class="mb-1">
<strong>{#COMMENT_USER_NAME#}:</strong>
{$c.comment_author_name|stripslashes|escape}
</li>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<title>{#COMMENT_INFO#}</title>
<link href="templates/{$smarty.request.theme|escape}/css/style.css" rel="stylesheet" type="text/css" media="screen" />
</head>
{if $c.date_value}
<li class="mb-1">
<strong>{$c.date_label}:</strong>
{$c.date_value|date_format:$TIME_FORMAT|pretty_date}
</li>
{/if}
<body id="body_popup">
{if $c.last_visit}
<li class="mb-1">
<strong>Последний визит:</strong>
{$c.last_visit|date_format:$TIME_FORMAT|pretty_date}
</li>
{/if}
<div id="module_header"><h2>{#COMMENT_INFO#}</h2></div>
{foreach from=$custom_fields item=field}
<li class="mb-1">
<strong>{$field.title}:</strong> {$field.value}
</li>
{/foreach}
<div id="module_content">
<table width="100%" border="0" cellspacing="1" cellpadding="4">
<tr>
<td width="160">{#COMMENT_USER_NAME#}</td>
<td>{$c.comment_author_name|stripslashes|escape}</td>
</tr>
<li class="mb-1">
<strong>{#COMMENT_USER_COMMENTS#}:</strong>
{$c.num}
</li>
<tr>
<td width="160">{#COMMENT_DATE_CREATE#}</td>
<td>{$c.comment_published|date_format:$TIME_FORMAT|pretty_date}</td>
</tr>
<tr>
<td width="160">{#COMMENT_USER_EMAIL#}</td>
<td>
{assign var=comment_author_email value=$c.comment_author_email}
{mailto address="$comment_author_email" encode="javascript_charcode"}
</td>
</tr>
<tr>
<td width="160">{#COMMENT_USER_SITE#}</td>
<td>{$c.comment_author_website|default:'-'}</td>
</tr>
<tr>
<td width="160">{#COMMENT_USER_FROM#}</td>
<td>{$c.comment_author_city|stripslashes|escape|default:'-'}</td>
</tr>
<tr>
<td width="160">{#COMMENT_USER_COMMENTS#}</td>
<td>{$c.num|default:'-'}</td>
</tr>
</table>
<p><input onclick="window.close();" type="button" class="button" value="{#COMMENT_CLOSE_BUTTON#}" /></p>
</div>
</body>
</html>
<li class="mt-2 pt-2 border-top">
<strong>Рейтинг автора:</strong>
<span class="text-warning shadow-sm-text">
{section name=star start=1 loop=6}
{if $smarty.section.star.index <= $c.avg_rating}
<i class="bi-star-fill"></i>
{else}
<i class="bi-star text-muted"></i>
{/if}
{/section}
</span>
<small class="text-secondary">({$c.total_votes|default:0} гол.)</small>
</li>
</ul>
</div>

View File

@@ -1,3 +1,41 @@
{literal}
<style>
/* Стили для стандартной шапки поповера */
.popover-header {
background-color: #f0f0f0 !important; /* Тот самый серый цвет */
border-bottom: 1px solid #dee2e6 !important;
padding: 8px 12px !important;
font-size: 0.9rem !important;
font-weight: 600 !important;
}
/* Кнопка закрытия в шапке */
.pop-close {
background: none !important;
border: none !important;
font-size: 22px !important;
line-height: 1 !important;
color: #999 !important;
cursor: pointer !important;
padding: 0 !important;
margin-left: 10px !important;
}
.pop-close:hover {
color: #f00 !important;
}
.popover-body {
padding: 12px !important;
min-width: 230px !important;
}
.author-popover, .author-popover:focus, .author-popover:active {
outline: none !important;
box-shadow: none !important;
text-decoration: none !important;
}
</style>
{/literal}
{foreach from=$subcomments item=c name=sub_loop}
{* ОПРЕДЕЛЯЕМ СТАТУС УДАЛЕНИЯ *}
{assign var="is_deleted" value=false}
@@ -60,7 +98,13 @@
{if !$is_deleted}
<span class="d-flex align-items-center">
<i class="bi bi-person me-1"></i>
<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 text-decoration-none">{$c.comment_author_name|stripslashes|escape}</a>
<a href="javascript:void(0);"
class="author-popover fw-bold link-dark text-decoration-none"
data-id="{$c.Id}"
role="button"
tabindex="0">
{$c.comment_author_name|stripslashes|escape}
</a>
{* ПРОВЕРКА ИСТОРИИ ИМЕН АНОНИМА *}
{if !empty($c.past_names)}
<span class="badge rounded-pill bg-light text-danger border border-danger ms-1"
@@ -284,4 +328,78 @@
</div>
{/if}
{/foreach}
{/foreach}
{literal}
<script type="text/javascript">
(function() {
// Глобальный обработчик для крестика
document.addEventListener('click', function(e) {
if (e.target.closest('.pop-close')) {
const pid = e.target.closest('.pop-close').getAttribute('data-id');
const trigger = document.querySelector(`.author-popover[data-id="${pid}"]`);
if (trigger) {
const ins = bootstrap.Popover.getInstance(trigger);
if (ins) ins.hide();
trigger.blur();
}
}
});
const startInitialization = () => {
if (typeof bootstrap === 'undefined') return false;
document.querySelectorAll('.author-popover').forEach(el => {
if (el.getAttribute('data-popover-initialized')) return;
new bootstrap.Popover(el, {
html: true,
sanitize: false,
trigger: 'focus',
placement: 'top',
// ВОТ ЗДЕСЬ ВОЗВРАЩАЕМ ШАПКУ
title: function() {
const cid = el.getAttribute('data-id');
return `
<span>Информация об авторе</span>
<button type="button" class="pop-close" data-id="${cid}">&times;</button>
`;
},
content: function() {
const commentId = el.getAttribute('data-id');
const popoverId = 'popover-content-' + commentId;
setTimeout(() => {
const popContent = document.getElementById(popoverId);
if (popContent && popContent.innerHTML === '...') {
if (el.getAttribute('data-loading') === 'true') return;
el.setAttribute('data-loading', 'true');
fetch('index.php?module=comment&action=postinfo&pop=1&Id=' + commentId)
.then(res => res.text())
.then(html => {
popContent.innerHTML = html;
el.setAttribute('data-loading', 'false');
})
.catch(() => { popContent.innerHTML = 'Ошибка'; el.setAttribute('data-loading', 'false'); });
}
}, 30);
return `<div id="${popoverId}">...</div>`;
},
// ПЕРЕОПРЕДЕЛЯЕМ ШАБЛОН (чтобы шапка была флексовой для крестика)
template: `
<div class="popover" role="tooltip">
<div class="popover-arrow"></div>
<h3 class="popover-header d-flex justify-content-between align-items-center"></h3>
<div class="popover-body"></div>
</div>`
});
el.setAttribute('data-popover-initialized', 'true');
});
return true;
};
let timer = setInterval(() => { if (startInitialization()) clearInterval(timer); }, 300);
})();
</script>
{/literal}