init commit

This commit is contained in:
2025-12-15 07:48:07 +05:00
parent 59039fc43f
commit 967c46cd6d
26 changed files with 2659 additions and 0 deletions

49
ReadMe.txt Normal file
View File

@@ -0,0 +1,49 @@
Шаг 1: Назначение перемнных в шаблоне
<script type="text/javascript">
var aveabspath = '[tag:path]';
var UGROUP = <?=UGROUP;?>;
</script>
Шаг 2: Нужен плагин для jQuery - jquery.form
http://malsup.com/jquery/form/
/* ====================================================================
СТИЛИ ДЛЯ ДРЕВОВИДНОГО ОТОБРАЖЕНИЯ КОММЕНТАРИЕВ (Replies)
==================================================================== */
/* 1. Общий контейнер ответа, который находится внутри .mod_comment_comment
и имеет отступ ms-4 */
.mod_comment_comment .mod_comment_comment.ms-4 {
/* Добавляем вертикальную линию слева */
border-left: 3px solid #dee2e6; /* Легкая граница */
/* Уменьшаем margin-left, который дает ms-4, чтобы освободить место
для линии и при этом не уходить слишком далеко вправо на каждом уровне. */
margin-left: 20px !important; /* Принудительно уменьшаем отступ */
padding-left: 15px; /* Отступ между линией и контентом ответа */
/* Опционально: немного отличающийся фон */
background-color: #f8f9fa;
}
/* 2. Отдельно стилизуем рамку для вложенных карточек, чтобы они не выглядели
громоздко внутри родителя */
.mod_comment_comment .mod_comment_comment.ms-4 > .mod_comment_box {
border: none !important; /* Убираем стандартную рамку карточки, чтобы осталась только наша вертикальная линия */
}
/* 3. Убираем рамки у вложенных заголовков, чтобы линия была чистой */
.mod_comment_comment .mod_comment_comment.ms-4 > .mod_comment_box > .card-header {
border-bottom: none !important;
}
/* 4. Более глубокий уровень вложенности (если ответов много) */
/* Если ответ на ответ имеет дополнительный ms-4, стилизуем его дальше */
.mod_comment_comment .mod_comment_comment.ms-4 .mod_comment_comment.ms-4 {
border-color: #adb5bd; /* Чуть темнее линия для следующего уровня */
background-color: #fff; /* Возвращаем белый фон для контраста */
}

954
class/comment.php Normal file
View File

@@ -0,0 +1,954 @@
<?php
/**
* Класс, включающий все свойства и методы для управления комментариями как в
* Публичной части сайта, так и в Панели управления.
*
* @package AVE.cms
* @subpackage module_Comment
* @since 1.4
* @filesource
*/
class Comment
{
/**
* Свойства класса
*/
/**
* Идентификатор записи с настройками модуля Комментарии
*
* @var int
*/
private $_config_id = 1;
/**
* Количество комментариев на странице в административной части
*
* @var int
*/
private $_limit = 15;
/**
* Имя файла с шаблоном для вывода блока комментариев
*
* @var string
*/
public $_comments_tree_tpl = 'comments_tree.tpl';
/**
* Имя файла с шаблоном для рекурсивного вывода иерархии комментариев
*
* @var string
*/
public $_comments_tree_sub_tpl = 'comments_tree_sub.tpl';
/**
* Имя файла с шаблоном формы добавления комментария
*
* @var string
*/
public $_comment_form_tpl = 'comment_form.tpl';
/**
* Имя файла с шаблоном нового комментария
*
* @var string
*/
public $_comment_new_tpl = 'comment_new.tpl';
/**
* Имя файлаа с шаблоном уведомляющим о успешном выполнении операции
*
* @var string
*/
public $_comment_thankyou_tpl = 'comment_thankyou.tpl';
/**
* Имя файла с шаблоном редактирования шаблона в административной части
*
* @var string
*/
public $_admin_edit_link_tpl = 'admin_edit.tpl';
/**
* Имя файла с шаблоном списка комментариев в административной части
*
* @var string
*/
public $_admin_comments_tpl = 'admin_comments.tpl';
/**
* Имя файла с шаблоном редактирования настроек модуля в административной части
*
* @var string
*/
public $_admin_settings_tpl = 'admin_settings.tpl';
/**
* Имя файла с шаблоном редактирования шаблона в публичной части
*
* @var string
*/
public $_edit_link_tpl = 'comment_edit.tpl';
/**
* Имя файла с шаблоном для вывода информации об авторе комментария
*
* @var string
*/
public $_postinfo_tpl = 'comment_info.tpl';
/**
* Внутренние методы класса
*/
/**
* Метод, предназначенный для получения основных настроек модуля,которые задаются в Панели управления.
*
* @param string $param название параметра
* @return mixed значение настройки
*/
function _commentSettingsGet($param = '')
{
global $AVE_DB;
// Определяем статическую переменную, которая будет хранить полученные настройки на протяжении всего
// срока жизни объекта.
static $settings = null;
// Если переменная $settings еще не имеет значений, тогда выполняем запрос к БД на получение данных
if ($settings === null)
{
$settings = $AVE_DB->Query("
SELECT *
FROM " . PREFIX . "_module_comments
WHERE Id = '" . $this->_config_id . "'
")->FetchAssocArray();
}
if ($param == '')
return $settings;
// В противном случае возвращаем уже имеющиеся значения
return (isset($settings[$param])
? $settings[$param]
: null);
}
/**
* Метод, предназначенный для получения количества комментариев для определенного документа.
*
* @param int $document_id - идентификатор документа
* @return int - количество комментариев
*/
function _commentPostCountGet($document_id)
{
global $AVE_DB;
// Определяем статический массив, который будет хранить количество комментариев для документов на
// протяжении всего срока жизни объекта.
static $comments = array();
// Если в массиве не найден ключ, который соответствует запрашиваемому документу, тогда выполняем
// запрос к БД на получение количества комментариев
if (! isset($comments[$document_id]))
{
$comments[$document_id] = $AVE_DB->Query("
SELECT COUNT(*)
FROM " . PREFIX . "_module_comment_info
WHERE document_id = '" . (int)$document_id . "'
")->GetCell();
}
// Возвращаем количество комментариев для запрашиваемого документа
return $comments[$document_id];
}
/**
* Внешние методы класса
*/
/**
* Следующие методы описывают работу модуля в Публичной части сайта.
*/
/**
* Метод, предназначенный для получения из БД всех комментариев, относящихся к указанному
* документу с последующим выводом в Публичной части.
*
* @param string $tpl_dir - путь к шаблонам модуля
*
* @todo Вывод информации о авторе комментария
*/
function commentListShow($tpl_dir)
{
global $AVE_DB, $AVE_Template, $AVE_Core;
// Используем оператор объединения с null для PHP 8.4
$document_id = (int)($_REQUEST['id'] ?? 0);
$artpage = $_REQUEST['artpage'] ?? null;
$apage = $_REQUEST['apage'] ?? null;
$user_group = UGROUP ?? 0;
// Проверяем, что в настройках модуля разрешено комментирование документов
if ($this->_commentSettingsGet('comment_active') == 1)
{
// =================================================================================
// НОВОЕ: ПРОВЕРКА ПРАВ НА ПРОСМОТР КОММЕНТАРИЕВ (Read Permission)
// =================================================================================
$read_groups = explode(',', $this->_commentSettingsGet('comment_user_groups_read'));
$assign['no_read_permission'] = 0; // Флаг: 0 = права есть
// Если группа текущего пользователя НЕ в списке разрешенных для ЧТЕНИЯ,
if (!in_array($user_group, $read_groups))
{
// Устанавливаем флаг, что прав на чтение нет
$assign['no_read_permission'] = 1;
}
// =================================================================================
$assign['display_comments'] = 1;
// Если группа пользователя, который в текущий момент просматривает документ попадает в список
// разрешенных (в настройках модуля), тогда создаем флаг, который будет разрешать к показу
// форму для добавления нового комментария
if (in_array($user_group, explode(',', $this->_commentSettingsGet('comment_user_groups'))))
{
$assign['cancomment'] = 1;
}
// ЕСЛИ ЕСТЬ ПРАВА НА ЧТЕНИЕ, ПРОДОЛЖАЕМ ВЫБОРКУ И ВЫВОД
if ($assign['no_read_permission'] == 0)
{
$assign['comment_max_chars'] = $this->_commentSettingsGet('comment_max_chars');
$assign['im'] = $this->_commentSettingsGet('comment_use_antispam');
// Выполняем запрос к БД на получение количества комментариев для текущего документа
$comments = array();
if ($this->_commentSettingsGet('comment_use_page_nav') == 1)
{
$limit = $this->_commentSettingsGet('comment_page_nav_count');
$start = get_current_page() * $limit - $limit;
$num = $AVE_DB->Query("
SELECT COUNT(*)
FROM " . PREFIX . "_module_comment_info
WHERE document_id = '" . $document_id . "'
")->GetCell();
$sql = $AVE_DB->Query("
SELECT *
FROM " . PREFIX . "_module_comment_info
WHERE document_id = '" . $document_id . "'
" . ($user_group == 1 ? '' : "AND comment_status = '1'") . "
ORDER BY comment_published ASC
LIMIT " . $start . "," . $limit . "
");
$pages = @ceil($num / $limit);
if ($num > $limit)
{
$page_nav = '<a class="page_nav" href="index.php?id=' . $AVE_Core->curentdoc->Id
. '&amp;doc=' . (empty($AVE_Core->curentdoc->document_alias) ? prepare_url($AVE_Core->curentdoc->document_title) : $AVE_Core->curentdoc->document_alias)
. ((isset($artpage) && is_numeric($artpage)) ? '&amp;artpage=' . $artpage : '')
. ((isset($apage) && is_numeric($apage)) ? '&amp;apage=' . $apage : '')
. '&amp;page={s}">{t}</a> ';
$page_nav = get_pagination(ceil($num / $limit), 'page', $page_nav, get_settings('navi_box'));
$page_nav = rewrite_link($page_nav);
$GLOBALS['page_id'][$document_id]['page']=($GLOBALS['page_id'][$document_id]['page']>ceil($num / $limit) ? $GLOBALS['page_id'][$document_id]['page'] : ceil($num / $limit));
}
else
{
$page_nav = '';
}
}
else
{
$sql = $AVE_DB->Query("
SELECT *
FROM " . PREFIX . "_module_comment_info
WHERE document_id = '" . $document_id . "'
" . ($user_group == 1 ? '' : "AND comment_status = '1'") . "
ORDER BY comment_published ASC
");
$page_nav = '';
}
// Получаем формат даты, который указан в общих настройках системы и
// приводим дату создания комментария и дату редактирования к этому формату
$date_time_format = $AVE_Template->get_config_vars('COMMENT_DATE_TIME_FORMAT');
while ($row = $sql->FetchAssocArray())
{
$row['comment_published'] = ave_date_format($date_time_format, $row['comment_published']);
$row['comment_changed'] = ave_date_format($date_time_format, $row['comment_changed']);
$comments[$row['parent_id']][] = $row;
}
}
else
{
// Если нет прав, то эти переменные остаются пустыми/неопределенными
$comments = array();
$page_nav = '';
}
// Формируем ряд переменных для использования в шаблоне
$assign['closed'] = @$comments[0][0]['comments_close'];
$assign['comments'] = $comments;
$assign['theme'] = defined('THEME_FOLDER') ? THEME_FOLDER : DEFAULT_THEME_FOLDER;
$assign['doc_id'] = $document_id;
$assign['page'] = base64_encode(get_redirect_link());
$assign['subtpl'] = $tpl_dir . $this->_comments_tree_sub_tpl;
$AVE_Template->assign($assign);
$AVE_Template->assign('page_nav', $page_nav);
// Отображаем шаблон
$AVE_Template->display($tpl_dir . $this->_comments_tree_tpl);
}
}
/**
* Метод, предназначенный для отображения формы при добавлении нового комментария.
*
* @param string $tpl_dir - путь к шаблонам модуля
*/
function commentPostFormShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$docid = (int)($_REQUEST['docid'] ?? 0);
$user_group = UGROUP ?? 0;
// Получаем список комментариев на которые запрещены ответы
$geschlossen = $AVE_DB->Query("
SELECT comments_close
FROM " . PREFIX . "_module_comment_info
WHERE document_id = '" . $docid . "'
LIMIT 1
")->GetCell();
// Формируем ряд переменных для использования в шаблоне
$AVE_Template->assign('closed', $geschlossen);
$AVE_Template->assign('cancomment', ($this->_commentSettingsGet('comment_active') == 1 && in_array($user_group, explode(',', $this->_commentSettingsGet('comment_user_groups')))));
$AVE_Template->assign('comment_max_chars', $this->_commentSettingsGet('comment_max_chars'));
$AVE_Template->assign('theme', defined('THEME_FOLDER') ? THEME_FOLDER : DEFAULT_THEME_FOLDER);
// Отображаем форму для добавления комментария
$AVE_Template->display($tpl_dir . $this->_comment_form_tpl);
}
/**
* Метод, предназначенный для записи в БД нового комментария.
*
* @param string $tpl_dir - путь к шаблонам модуля
*
*/
function commentPostNew($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$page = $_REQUEST['page'] ?? '';
$ajax = (isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == 1);
$user_group = UGROUP ?? 0;
$secure_code = $_POST['securecode'] ?? '';
$session_captcha = $_SESSION['captcha_keystring'] ?? null;
// Если запрос пришел не ajax запросом, тогда формируем ссылку для последующего редиректа
if (! $ajax)
{
$link = rewrite_link(base64_decode($page));
}
// Если в настройках модуля включено использование защитного кода, тогда
if ($this->_commentSettingsGet('comment_use_antispam') == 1)
{
// Если введенный пользователем защитный код неверен, тогда выполняем обновление кода
if (! (isset($session_captcha) && $session_captcha == $secure_code))
{
unset($_SESSION['captcha_keystring']);
if ($ajax)
{
echo 'wrong_securecode';
}
else
{
if(isset($GLOBALS['tmpl']))$GLOBALS['tmpl']->assign("wrongSecureCode", 1);
header('Location:' . $link . '#end');
}
exit;
}
unset($_SESSION['captcha_keystring']);
}
// Определяем флаг модерации комментариев
$comment_status = ($this->_commentSettingsGet('comment_need_approve') == 1) ? 0 : 1;
// Если комментарии разрешены, тогда получаем все данные, который пользователь указал в форме
if ($this->_commentSettingsGet('comment_active') == 1
&& !empty($_POST['comment_text'])
&& !empty($_POST['comment_author_name'])
&& in_array($user_group, explode(',', $this->_commentSettingsGet('comment_user_groups'))))
{
// --- !!! НАЧАЛО БЕЗОПАСНОСТИ: Санитаризация и Экранирование !!! ---
$new_comment['parent_id'] = (int)($_POST['parent_id'] ?? 0);
$new_comment['document_id'] = (int)($_POST['doc_id'] ?? 0);
// Экранирование для предотвращения SQLi:
// Санитаризация для предотвращения XSS в строковых полях:
$new_comment['comment_author_name'] = addslashes(strip_tags($_POST['comment_author_name'] ?? ''));
$new_comment['comment_author_id'] = empty($_SESSION['user_id']) ? 0 : (int)$_SESSION['user_id'];
$new_comment['comment_author_email'] = addslashes(strip_tags($_POST['comment_author_email'] ?? ''));
$new_comment['comment_author_city'] = addslashes(strip_tags($_POST['comment_author_city'] ?? ''));
$new_comment['comment_author_website'] = addslashes(strip_tags($_POST['comment_author_website'] ?? ''));
$new_comment['comment_author_ip'] = $_SERVER['REMOTE_ADDR'];
$new_comment['comment_published'] = time();
$new_comment['comment_status'] = $comment_status;
// Текст комментария до очистки
$comment_text_raw = $_POST['comment_text'] ?? '';
// Определяем максимальную длину символов для комментария
$comment_max_chars = $this->_commentSettingsGet('comment_max_chars');
$comment_max_chars = (!empty($comment_max_chars) && $comment_max_chars > 10) ? $comment_max_chars : 200;
// 1. Убираем HTML-теги для предотвращения XSS
$comment_text_clean = strip_tags(stripslashes($comment_text_raw));
// 2. Обрезка
$comment_text_cut = mb_substr($comment_text_clean, 0, $comment_max_chars);
$comment_text_cut .= (mb_strlen($comment_text_clean) > $comment_max_chars) ? '…' : '';
// 3. Экранирование текста перед вставкой в SQL
$new_comment['comment_text'] = addslashes($comment_text_cut);
// --- !!! КОНЕЦ БЕЗОПАСНОСТИ !!! ---
// Выполняем запрос к БД на добавление комментария
$AVE_DB->Query("
INSERT INTO " . PREFIX . "_module_comment_info
(`" . implode('`,`', array_keys($new_comment)) ."`)
VALUES
('" . implode("','", $new_comment) . "')
");
$new_comment['Id'] = $AVE_DB->InsertId();
// Получаем e-mail адрес из Общих настроек системы и формируем ссылку на комментарий в Публичной части
$mail_from = get_settings('mail_from');
$mail_from_name = get_settings('mail_from_name');
$page_link = get_home_link() . urldecode(base64_decode($page)) . '&subaction=showonly&comment_id=' . $new_comment['Id'] . '#' . $new_comment['Id'];
// Формируем текст уведомления для отправки на e-mail
$mail_text = $AVE_Template->get_config_vars('COMMENT_MESSAGE_ADMIN');
// Используем stripslashes для очистки текста, который будет отправлен по почте
$mail_text = str_replace('%COMMENT%', stripslashes($new_comment['comment_text']), $mail_text);
$mail_text = str_replace('%N%', "\n", $mail_text);
$mail_text = str_replace('%PAGE%', $page_link, $mail_text);
$mail_text = str_replace('&amp;', '&', $mail_text);
// Отправляем уведомление
send_mail(
$mail_from,
$mail_text,
$AVE_Template->get_config_vars('COMMENT_SUBJECT_MAIL'),
$mail_from,
$mail_from_name,
'text'
);
// Если данные были отправлены ajax-запросом, тогда выполняем автоматический показ комментария
// на странице.
if ($ajax)
{
$new_comment['comment_changed'] = 0;
$new_comment['comment_published'] = ave_date_format($AVE_Template->get_config_vars('COMMENT_DATE_TIME_FORMAT'), $new_comment['comment_published']);
$subcomments[] = $new_comment;
$AVE_Template->assign('subcomments', $subcomments);
$AVE_Template->assign('theme', defined('THEME_FOLDER') ? THEME_FOLDER : DEFAULT_THEME_FOLDER);
$AVE_Template->display($tpl_dir . $this->_comments_tree_sub_tpl);
}
}
// Если же данные пришли НЕ ajax-запросом, тогда полностью обновляем страницу.
if (! $ajax) header('Location:' . str_replace("//", "", $link) . '#end');
exit;
}
/**
* Метод, предназначенный для редактирования комментария в Публичной части
*
* @param int $comment_id - идентификатор комментария
*/
function commentPostEdit($comment_id)
{
global $AVE_DB;
$user_id = $_SESSION['user_id'] ?? null;
$user_group = UGROUP ?? 0;
$post_text = $_POST['text'] ?? '';
if (empty($user_id)) exit;
$comment_id = intval(preg_replace('/\D/', '', $comment_id));
// Выполняем запрос к БД и получаем всю информацию о комментарии, а также ряд значений из настроек модуля
$row = $AVE_DB->Query("
SELECT
msg.parent_id,
msg.comment_text,
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 comment_active = '1'
AND msg.Id = '" . $comment_id . "'
" . (($user_group != 1) ? "AND comment_author_id = " . $user_id : '') . "
")->FetchAssocArray();
// Если данные получены
if ($row !== false)
{
$comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 20;
$comment_text = $post_text;
// --- !!! НАЧАЛО ИСПРАВЛЕНИЯ УСТАРЕВШИХ ОПЕРАТОРОВ (/e) !!! ---
// Безопасная замена: преобразуем hex-сущности
$comment_text = preg_replace_callback('/&#x([0-9a-f]{1,7});/', function($matches) {
return chr(hexdec($matches[1]));
}, $comment_text);
// Безопасная замена: преобразуем dec-сущности
$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("<br>\n", "<br />\n", "<br/>\n"), "\n", $comment_text);
// Санитаризация: убираем теги перед обрезкой
$comment_text = strip_tags($comment_text);
$comment_text = mb_substr($comment_text, 0, $comment_max_chars-1);
$message_length = mb_strlen($comment_text);
$comment_text .= ($message_length > $comment_max_chars) ? '…' : '';
// Если группа текущего пользователя совпадает с разрешенной группой в настройках модуля, тогда
// выполняем запрос к БД на обновление информации.
if (in_array($user_group, explode(',', $row['comment_user_groups'])) && $message_length > 3)
{
$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) . "'
WHERE
Id = '" . $comment_id . "'
");
// Преобразуем HTML теги в HTML сущности перед выводом (для XSS)
echo htmlspecialchars($comment_text, ENT_QUOTES);
exit;
}
// Если редактирование не прошло, выводим оригинальный текст (экранированный)
echo htmlspecialchars($row['comment_text'], ENT_QUOTES);
}
exit;
}
/**
* Метод, предназначенный для удаления комментария. Если комментарий содержал какие-либо ответы на него,
* то все ответы также будут удалены вместе с родительским комментарием.
*
* @param int $comment_id - идентификатор комментария
*/
function commentPostDelete($comment_id)
{
global $AVE_DB;
$comment_id = (int)$comment_id; // Убедимся, что это целое число
// Выполняем запрос к БД на удаление родительского комментария
$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
");
exit;
}
function commentAdminDelete($comment_id)
{
global $AVE_DB;
$comment_id = (int)$comment_id; // Убедимся, что это целое число
// Выполняем запрос к БД на удаление родительского комментария
$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
");
// Используем оператор объединения с null для PHP 8.4
$session_id = SESSION ?? '';
header('Location:index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp=' . $session_id);
exit;
}
/**
* Метод, предназначенный для вывода детальной информации об авторе комментария
*
* @param string $tpl_dir - путь к шаблонам модуля
*/
function commentPostInfoShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$comment_id = (int)($_REQUEST['Id'] ?? 0);
// Получаем полную информацию о комментарии
$row = $AVE_DB->Query("
SELECT *
FROM " . PREFIX . "_module_comment_info
WHERE Id = '" . $comment_id . "'
")->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>'
: '';
// Выполняем запрос к БД на получение количества всех комментариев, оставленных данным пользователем
$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();
// Отображаем окно с информацией
$AVE_Template->assign('c', $row);
$AVE_Template->display($tpl_dir . $this->_postinfo_tpl);
}
/**
* Метод, предназначенный для управления запретом или разрешением отвечать на комментарии
*
* @param int $comment_id - идентификатор комментария
* @param string $comment_status - {lock|unlock} признак запрета/разрешения
*/
function commentReplyStatusSet($comment_id, $comment_status = 'lock')
{
global $AVE_DB;
$comment_id = (int)$comment_id;
$AVE_DB->Query("
UPDATE " . PREFIX . "_module_comment_info
SET comment_status = '" . (($comment_status == 'lock') ? 0 : 1) . "'
WHERE Id = '" . $comment_id . "'
");
exit;
}
/**
* Метод, предназначенный для управления запретом или разрешением комментировать документ
*
* @param int $document_id - идентификатор документа
* @param string $comment_status - {close|open} признак запрета/разрешения
*/
function commentStatusSet($document_id, $comment_status = 'open')
{
global $AVE_DB;
$document_id = (int)$document_id;
$AVE_DB->Query("
UPDATE " . PREFIX . "_module_comment_info
SET comments_close = '" . (($comment_status == 'open') ? 0 : 1) . "'
WHERE document_id = '" . $document_id . "'
");
exit;
}
/**
* Следующие методы описывают работу модуля в Административной части сайта.
*/
/**
* Метод, предназначенный для вывода списка всех комментариев в Административной части.
*
* @param string $tpl_dir - путь к шаблонам модуля
*/
function commentAdminListShow($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$request_sort = $_REQUEST['sort'] ?? '';
$session_id = SESSION ?? '';
// Получаем общее количество комментариев
$num = $AVE_DB->Query("SELECT COUNT(*) FROM " . PREFIX . "_module_comment_info")->GetCell();
// Определяем количество страниц, учитывая параметр _limit, который опроеделяет количество
// комментариев отображаемых на одной странице
@$seiten = @ceil($num / $this->_limit);
$start = get_current_page() * $this->_limit - $this->_limit;
$docs = array();
$def_sort = 'ORDER BY doc.Id DESC';
$def_nav = '';
// Определяем условия сортировки комментариев
if (!empty($request_sort))
{
switch ($request_sort)
{
case 'document_desc':
$def_sort = 'ORDER BY doc.Id ASC'; // Предполагаю, что document_desc должен быть ASC
$def_nav = '&sort=document_desc';
break;
case 'document':
$def_sort = 'ORDER BY doc.Id DESC';
$def_nav = '&sort=document';
break;
case 'comment_desc':
$def_sort = 'ORDER BY cmnt.comment_text ASC';
$def_nav = '&sort=comment_desc';
break;
case 'comment':
$def_sort = 'ORDER BY cmnt.comment_text DESC';
$def_nav = '&sort=comment';
break;
case 'created_desc':
$def_sort = 'ORDER BY cmnt.comment_published ASC';
$def_nav = '&sort=created_desc';
break;
case 'created':
$def_sort = 'ORDER BY cmnt.comment_published DESC';
$def_nav = '&sort=created';
break;
}
}
// Выполняем запрос к БД на получение комметариев с учетом параметров сортировки и лимита.
$sql = $AVE_DB->Query("
SELECT
doc.Id,
doc.document_title,
cmnt.Id AS CId,
cmnt.document_id,
cmnt.comment_text,
cmnt.comment_published,
cmnt.comment_status
FROM
" . PREFIX . "_module_comment_info AS cmnt
JOIN
" . PREFIX . "_documents AS doc
ON doc.Id = cmnt.document_id
" . $def_sort . "
LIMIT " . $start . "," . $this->_limit
);
while ($row = $sql->FetchAssocArray())
{
$row['Comments'] = $this->_commentPostCountGet($row['Id']);
array_push($docs, $row);
}
// Если количество комментариев полученных из БД превышает допустимое на странице, тогда формируем
// меню постраницной навигации
if ($num > $this->_limit)
{
$page_nav = ' <a class="pnav" href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp=' . $session_id . '&page={s}' . $def_nav . '">{t}</a> ';
$page_nav = get_pagination($seiten, 'page', $page_nav);
$AVE_Template->assign('page_nav', $page_nav);
}
// Передаем данные в шаблон для вывода и отображаем шаблон
$AVE_Template->assign('docs', $docs);
$AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . $this->_admin_comments_tpl));
}
/**
* Метод, предназначенный для редактирования комментариев в Административной части.
*
* @param string $tpl_dir - путь к шаблонам модуля
*/
function commentAdminPostEdit($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$post_sub = $_POST['sub'] ?? '';
$request_id = (int)($_REQUEST['Id'] ?? 0);
$request_docid = (int)($_REQUEST['docid'] ?? 0);
// Выполняем запрос к БД на получение информации о редактируемом комментарии
$row = $AVE_DB->Query("
SELECT *
FROM " . PREFIX . "_module_comment_info
WHERE Id = '" . $request_id . "'
LIMIT 1
")->FetchAssocArray();
// Если в запросе содержится подзапрос на сохранение данных (пользователь уже отредактировал комментарий
// и нажал кнопку сохранить изменения), тогда выполняем запрос к БД на обновление информации.
if ($post_sub == 'send' && false != $row)
{
// --- !!! БЕЗОПАСНОСТЬ: Экранирование данных перед сохранением !!! ---
$AVE_DB->Query("
UPDATE " . PREFIX . "_module_comment_info
SET
comment_author_name = '" . addslashes(htmlspecialchars($_POST['comment_author_name'] ?? '')) . "',
comment_author_email = '" . addslashes(htmlspecialchars($_POST['comment_author_email'] ?? '')) . "',
comment_author_city = '" . addslashes(htmlspecialchars($_POST['comment_author_city'] ?? '')) . "',
comment_author_website = '" . addslashes(htmlspecialchars($_POST['comment_author_website'] ?? '')) . "',
comment_text = '" . addslashes(htmlspecialchars($_POST['comment_text'] ?? '')) . "',
comment_changed = '" . time() . "'
WHERE
Id = '" . (int)($_POST['Id'] ?? 0) . "'
");
echo '<script>window.opener.location.reload();window.close();</script>';
return;
}
// Если в первой выборке из БД мы получили нулевой результат, тогда генерируем сообщение с ошибкой
if ($row == false)
{
$AVE_Template->assign('editfalse', 1);
}
// в противном случае получаем список комментариев, у которых стоит запрет на ответы
else
{
$closed = $AVE_DB->Query("
SELECT comments_close
FROM " . PREFIX . "_module_comment_info
WHERE document_id = '" . $request_docid . "'
LIMIT 1
")->GetCell();
$AVE_Template->assign('closed', $closed);
$AVE_Template->assign('row', $row);
$AVE_Template->assign('comment_max_chars', $this->_commentSettingsGet('comment_max_chars'));
}
// Отображаем шаблон
$AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . $this->_admin_edit_link_tpl));
}
/**
* Метод, предназначенный для управления настройками модуля
*
* @param string $tpl_dir - путь к шаблонам модуля
*/
function commentAdminSettingsEdit($tpl_dir)
{
global $AVE_DB, $AVE_Template;
// Используем оператор объединения с null для PHP 8.4
$request_sub = $_REQUEST['sub'] ?? '';
$post_max_chars = $_POST['comment_max_chars'] ?? 0;
$post_user_groups = $_POST['comment_user_groups'] ?? array();
// НОВОЕ: Получение данных для групп, которым разрешен просмотр
$post_user_groups_read = $_POST['comment_user_groups_read'] ?? array();
// КОНЕЦ НОВОГО
$post_need_approve = $_POST['comment_need_approve'] ?? 0;
$post_active = $_POST['comment_active'] ?? 0;
$post_use_antispam = $_POST['comment_use_antispam'] ?? 0;
$post_use_page_nav = $_POST['comment_use_page_nav'] ?? 0;
$post_page_nav_count = $_POST['comment_page_nav_count'] ?? 0;
// Если в запросе содержится подзапрос на сохранение данных (пользователь нажал кнопку
// сохранить изменения), тогда выполняем запрос к БД на обновление информации.
if ($request_sub == 'save')
{
$max_chars = (empty($post_max_chars) || $post_max_chars < 50) ? 50 : $post_max_chars;
$AVE_DB->Query("
UPDATE " . PREFIX . "_module_comments
SET
comment_max_chars = '" . (int)$max_chars . "',
comment_user_groups = '" . implode(',', $post_user_groups) . "',
comment_user_groups_read = '" . implode(',', $post_user_groups_read) . "', /* <-- СОХРАНЕНИЕ НОВОГО ПОЛЯ */
comment_need_approve = '" . (int)$post_need_approve . "',
comment_active = '" . (int)$post_active . "',
comment_use_antispam = '" . (int)$post_use_antispam . "',
comment_use_page_nav = '" . (int)$post_use_page_nav . "',
comment_page_nav_count = '" . (int)$post_page_nav_count . "'
WHERE
Id = 1
");
}
// Получаем список всех настроек модуля
$row = $this->_commentSettingsGet();
// Преобразуем поля с правами в массивы для удобства отображения в шаблоне
$row['comment_user_groups'] = explode(',', $row['comment_user_groups']);
$row['comment_user_groups_read'] = explode(',', $row['comment_user_groups_read']); /* <-- ПОЛУЧЕНИЕ НОВОГО ПОЛЯ */
// Передаем данные в шаблон и показываем страницу с настройками модуля
$AVE_Template->assign($row);
$AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . $this->_admin_settings_tpl));
}
}
?>

8
index.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
/**
* Файл-заглушка, предназначенный для запрета показа списка файлов в текущей директории,
* если через адресную строку браузера было прямое общращение к данной директории.
*/
header('Location:/');
exit;
?>

20
info.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
if (! defined('BASE_DIR'))
exit;
$module = array(
'ModuleSysName' => 'comment',
'ModuleVersion' => '1.26.2a',
'ModuleAutor' => 'AVE.cms Team',
'ModuleCopyright' => '&copy; 2007-' . date('Y') . ' AVE.cms',
'ModuleStatus' => 1,
'ModuleIsFunction' => 1,
'ModuleTemplate' => 0,
'ModuleAdminEdit' => 1,
'ModuleFunction' => 'mod_comment',
'ModuleTag' => '[mod_comment]',
'ModuleTagLink' => null,
'ModuleAveTag' => '#\\\[mod_comment]#',
'ModulePHPTag' => '<?php mod_comment(); ?>'
);
?>

297
js/comment.js Normal file
View File

@@ -0,0 +1,297 @@
/* ====================================================================
ОБЕРТКА ДЛЯ ОЖИДАНИЯ JQUERY
==================================================================== */
(function waitForJQuery() {
if (typeof jQuery === 'undefined') {
// jQuery еще не загружен. Ждем 10 мс и проверяем снова.
setTimeout(waitForJQuery, 10);
} else {
// JQuery загружен, можно запускать основной код.
(function($){
// ====================================================================
// КОРРЕКЦИЯ: ПРИНУДИТЕЛЬНАЯ ФИКСАЦИЯ HTTPS
// ====================================================================
if (typeof aveabspath !== 'undefined') {
aveabspath = 'https://bag.local/';
}
// ====================================================================
/*Limit symbols*/
(function($){$.fn.extend({limit:function(limit,element){var interval,f;var self=$(this);$(this).focus(function(){interval=window.setInterval(substring,100)});$(this).blur(function(){clearInterval(interval);substring()});substringFunction="function substring(){ var val = $(self).val();var length = val.length;if(length > limit){$(self).val($(self).val().substring(0,limit));}";if(typeof element!='undefined')substringFunction+="if($(element).html() != limit-length){$(element).html((limit-length<=0)?'0':limit-length);}";substringFunction+="}";eval(substringFunction);substring()}})})(jQuery);
function getCaptha(){
now = new Date();
$('#captcha img').attr('src', aveabspath+'inc/captcha.php?cd=' + now);
};
// ИСПРАВЛЕННАЯ ФУНКЦИЯ cAction
function cAction(obj,action){
var cid;
// --- Логика для действий с документом (open/close) ---
if (action === 'open' || action === 'close') {
cid = DOC_ID;
} else {
// 1. Пытаемся найти ссылку и взять ID из атрибута rel
var $link = $(obj).closest('a.mod_comment_answer');
cid = $link.attr('rel');
// 2. Если rel не найден, берем ID из родительского блока
if (typeof cid === 'undefined' || cid === false || cid === '') {
cid = $(obj).parents('.mod_comment_box').attr('id');
}
}
if (typeof cid === 'undefined' || cid === false || cid === '') {
console.error("Comment ID not found for action: " + action);
return;
}
if (action=='answer'){
$('#parent_id').val(cid);
// --- ИСПРАВЛЕНИЕ: Добавлено .show() для отображения формы после перемещения ---
$('#mod_comment_new').insertBefore('#end'+cid).show();
return;
}
// ... (остальной код для admin actions: delete, lock, open/close)
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'){
$('#mod_comment_open').attr('id', 'mod_comment_close').html(COMMENT_SITE_CLOSE);
}
if (action=='close'){
$('#mod_comment_close').attr('id', 'mod_comment_open').html(COMMENT_SITE_OPEN);
}
// ====================================================================
// ИСПРАВЛЕНИЕ: ДИНАМИЧЕСКАЯ СМЕНА ИКОНКИ ЗАМКА (Font Awesome)
// ====================================================================
if (action=='unlock'){
// Меняем классы: mod_comment_unlock (открыт) -> mod_comment_lock (закрыт)
// Меняем цвет: text-success (зеленый) -> text-dark (темный)
$(obj).removeClass('mod_comment_unlock text-success')
.addClass('mod_comment_lock text-dark')
.attr('title',COMMENT_LOCK_LINK)
.find('i.fa') // Ищем иконку FA
.removeClass('fa-unlock').addClass('fa-lock'); // Меняем иконку
}
if (action=='lock'){
// Меняем классы: mod_comment_lock (закрыт) -> mod_comment_unlock (открыт)
// Меняем цвет: text-dark (темный) -> text-success (зеленый)
$(obj).removeClass('mod_comment_lock text-dark')
.addClass('mod_comment_unlock text-success')
.attr('title',COMMENT_UNLOCK_LINK)
.find('i.fa') // Ищем иконку FA
.removeClass('fa-lock').addClass('fa-unlock'); // Меняем иконку
}
// ====================================================================
});
}
};
// ИСПРАВЛЕННАЯ ФУНКЦИЯ VALIDATE: Устойчива к отсутствию полей
function validate(formData,jqForm,options){
$('.alert').remove();
var form = jqForm ? jqForm[0] : $('#mod_comment_new form')[0];
if (form.comment_author_name && !form.comment_author_name.value){
alert(COMMENT_ERROR_AUTHOR);
$(form.comment_author_name).focus();
return false;
}
if (form.comment_author_email && !form.comment_author_email.value){
alert(COMMENT_ERROR_EMAIL);
$(form.comment_author_email).focus();
return false;
}
if (!form.comment_text || !form.comment_text.value){
alert(COMMENT_ERROR_TEXT);
if (form.comment_text) $(form.comment_text).focus();
return false;
}
if (IS_IM && form.securecode && !form.securecode.value){
alert(COMMENT_ERROR_CAPTCHA);
$(form.securecode).focus();
return false;
}
return true;
};
function setClickable(){
$('.editable_text').click(function(){
var cid = $(this).parents('.mod_comment_box').attr('id');
var revert = $(this).html();
var textarea = '<p><textarea rows="7" id="ta_'+cid+'" class="editable">'+revert+'</textarea></p>';
var charsLeft = '<p>'+COMMENT_CHARS_LEFT+' <span class="charsLeft" id="charsLeft_'+cid+'"></span></p>';
var buttonSave = '<input type="button" value="'+COMMENT_BUTTON_EDIT+'" class="button saveButton" /> ';
var buttonReset = '<input type="button" value="'+COMMENT_BUTTON_CANCEL+'" class="button cancelButton" />';
$(this).after('<div class="box"><div class="block" id="forms"><fieldset><legend>'+COMMENT_EDIT_TITLE
+'</legend>'+textarea+buttonSave+buttonReset+charsLeft+'</fieldset></div></div>').remove();
$('.saveButton').click(function(){saveChanges(this,false,cid);});
$('.cancelButton').click(function(){saveChanges(this,revert,cid);});
$('#ta_'+cid).limit(MAX_CHARS,'#charsLeft_'+cid);
})
.attr('title',COMMENT_EDIT_LINK)
.mouseover(function(){$(this).addClass('editable');})
.mouseout(function(){$(this).removeClass('editable');});
$('#in_message').limit(MAX_CHARS,'#charsLeft_new');
};
// ИСПРАВЛЕННАЯ ФУНКЦИЯ saveChanges
function saveChanges(obj,cancel,cid){
if (!cancel){
var t = $(obj).parent().children().children().val();
$.ajax({
url: aveabspath+'index.php?ajax=1',
type: 'POST',
data: {
module: 'comment',
action: 'edit',
Id: cid,
text: t
},
success: function(txt){
$(obj).parent().parent().parent().after('<div class="mod_comment_text editable_text">'+txt+'</div>').remove();
var now = new Date();
var date = now.toLocaleString();
$('#'+cid).find('.mod_comment_changed').html(' ('+COMMENT_TEXT_CHANGED+' '+date+')');
setClickable();
},
error: function(xhr, status, error) {
console.error("AJAX Error (Edit):", status, error);
alert("Ошибка при редактировании комментария. Пожалуйста, проверьте консоль.");
}
});
}
else {
$(obj).parent().parent().parent().after('<div class="mod_comment_text editable_text">'+cancel+'</div>').remove();
setClickable();
}
};
function displayNewComment(data){
if (data=='wrong_securecode'){
$('#captcha').after('<div class="alert alert-error">'+COMMENT_WRONG_CODE+'</div>');
$('#securecode').focus();
}
else {
$('#end'+$('#parent_id').val()).before(data);
$('#parent_id').val('');
$('#mod_comment_new form')[0].reset();
// --- Перемещаем форму обратно в конец. УБРАНО .hide() для сохранения видимости.
$('#mod_comment_new').insertAfter('#end');
setClickable();
}
getCaptha();
};
$(document).ready(function(){
setClickable();
// Делегирование событий
$(document).on('click', '#captcha img', function(){getCaptha();});
$(document).on('click', '.mod_comment_answer', function(){cAction(this,'answer');});
$(document).on('click', '.mod_comment_edit', function(){
var cid = $(this).parents('.mod_comment_box').attr('id');
var commentTextBlock = $('#'+cid).find('.mod_comment_text');
var revert = commentTextBlock.html();
var textarea = '<p><textarea rows="7" id="ta_'+cid+'" class="editable">'+revert+'</textarea></p>';
var charsLeft = '<p>'+COMMENT_CHARS_LEFT+' <span class="charsLeft" id="charsLeft_'+cid+'"></span></p>';
var buttonSave = '<input type="button" value="'+COMMENT_BUTTON_EDIT+'" class="button saveButton" /> ';
var buttonReset = '<input type="button" value="'+COMMENT_BUTTON_CANCEL+'" class="button cancelButton" />';
commentTextBlock.after(
'<div class="box"><div class="block" id="forms"><fieldset><legend>'+COMMENT_EDIT_TITLE
+'</legend>'+textarea+buttonSave+buttonReset+charsLeft+'</fieldset></div></div>'
).remove();
$('.saveButton').click(function(){saveChanges(this,false,cid);});
$('.cancelButton').click(function(){saveChanges(this,revert,cid);});
$('#ta_'+cid).limit(MAX_CHARS,'#charsLeft_'+cid);
});
if (UGROUP == 1) {
$(document).on('click', '.mod_comment_delete', function(){
if (confirm('Вы уверены, что хотите удалить комментарий?')) {
cAction(this, 'delete');
}
});
$(document).on('click', '.mod_comment_lock', function(){cAction(this, 'lock');});
$(document).on('click', '.mod_comment_unlock', function(){cAction(this, 'unlock');});
$(document).on('click', '#mod_comment_open', function(){cAction(this,'open');});
$(document).on('click', '#mod_comment_close', function(){cAction(this,'close');});
}
// Отправка формы (AJAX) - ИСПРАВЛЕНИЕ ДВОЙНОЙ ОТПРАВКИ
$('#mod_comment_new form').on('submit', function(e){
e.preventDefault();
var form = $(this);
if (!validate(null, form, null)) {
return false;
}
var submitButton = form.find('input[type="submit"], button[type="submit"]').first();
var originalButtonText = submitButton.val();
var formData = form.serialize();
$.ajax({
url: aveabspath+'index.php?ajax=1',
type: 'POST',
data: formData,
dataType: 'html',
// Отключаем кнопку перед отправкой
beforeSend: function() {
submitButton.prop('disabled', true).val('Отправка...');
},
// Включаем кнопку обратно и обрабатываем ответ
success: function(data) {
displayNewComment(data);
submitButton.prop('disabled', false).val(originalButtonText);
},
timeout: 3000,
error: function(xhr, status, error) {
console.error("AJAX Error (Creation):", status, error);
alert("Ошибка при отправке комментария. Пожалуйста, проверьте консоль.");
// Включаем кнопку обратно в случае ошибки
submitButton.prop('disabled', false).val(originalButtonText);
}
});
return false;
});
// Сброс формы
$('#buttonReset').click(function(){
$('#parent_id').val('');
$('#mod_comment_new form')[0].reset();
$('#mod_comment_new').insertAfter('#end');
});
});
})(jQuery);
}
})();

8
lang/index.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
/**
* Файл-заглушка, предназначенный для запрета показа списка файлов в текущей директории,
* если через адресную строку браузера было прямое общращение к данной директории.
*/
header('Location:/');
exit;
?>

92
lang/ru.txt Normal file
View File

@@ -0,0 +1,92 @@
[name]
MODULE_NAME = "Комментарии"
MODULE_DESCRIPTION = "Данный модуль предназначен для организации системы комментариев для документов на сайте. Для того, чтобы использовать данный модуль, разместите системный тег <strong>[mod_comment]</strong> в нужном месте шаблона рубрики."
[module]
COMMENT_EDIT_TITLE = "Редактирование комментария"
COMMENT_IS_CLOSED = "Редактирование не возможно, поскольку обсуждение закрыто."
COMMENT_CLOSE_BUTTON = "Закрыть окно"
COMMENT_EDIT_FALSE = "Извините, но вы не имеете прав для редактирования."
COMMENT_YOUR_NAME = "* Имя:"
COMMENT_YOUR_EMAIL = "* E-mail:"
COMMENT_YOUR_SITE = "Веб-сайт:"
COMMENT_YOUR_FROM = "Город:"
COMMENT_YOUR_TEXT = "* Комментарий:"
COMMENT_BUTTON_EDIT = "Сохранить изменения"
COMMENT_BUTTON_CANCEL = "Отменить"
COMMENT_BUTTON_RESET = "Очистить"
COMMENT_NEW_TITLE = "Написать комментарий"
COMMENT_NEW_CLOSED = "Извините, но Вы не можете добавить комментарий, поскольку обсуждение закрыто."
COMMENT_NEW_FALSE = "Извините, но у Вас не достаточно прав для добавления комментария."
COMMENT_READ_FALSE = "Извините, но у Вас не достаточно прав для просмотра комментариев."
COMMENT_CHARS_LEFT = "Количество оставшихся символов"
COMMENT_BUTTON_ADD = "Добавить комментарий"
COMMENT_INFO = "Информация о пользователе"
COMMENT_USER_NAME = "Имя пользователя:"
COMMENT_DATE_CREATE = "Опубликовано:"
COMMENT_USER_EMAIL = "E-mail пользователя:"
COMMENT_USER_SITE = "Личный сайт:"
COMMENT_USER_FROM = "Откуда:"
COMMENT_USER_COMMENTS = "Оставил комментариев:"
COMMENT_THANKYOU_TITLE = "Спасибо за комментарий"
COMMENT_THANKYOU_TEXT = "Ваш комментарий успешно добавлен."
COMMENT_SITE_TITLE = "Комментарии пользователей"
COMMENT_SITE_CLOSED = "(комментирование временно отключено)"
COMMENT_SITE_ADD = "Добавить комментарий"
COMMENT_LAST_COMMENT = "Последний комментарий"
COMMENT_SITE_CLOSE = "Запретить комментирование"
COMMENT_SITE_OPEN = "Разрешить комментирование"
COMMENT_USER_ADD = "Опубликовано:"
COMMENT_USER_TIME = "в"
COMMENT_ANSWER_LINK = "Ответить на этот комментарий"
COMMENT_EDIT_LINK = "Редактировать комментарий"
COMMENT_LOCK_LINK = "Заблокировать ответы"
COMMENT_DELETE_LINK = "Удалить комментарий"
COMMENT_UNLOCK_LINK = "Разрешить ответы"
COMMENT_TEXT_CHANGED = "Изменено:"
COMMENT_TEXT_ANSWER = "&raquo; Ответил(а):"
COMMENT_AFTER_MODER = "Ваш комментарий добавлен.<br />Прежде чем, он будет опубликован, он должен пройти проверку Администрацией сайта."
COMMENT_MESSAGE_ADMIN = "На Вашем сайте добавлен новый комментарий:%N%%N%%COMMENT% %N%%N%Для просмотра комментария, перейдите по ссылке:%N%%PAGE%"
COMMENT_SUBJECT_MAIL = "Добавлен новый комментарий"
COMMENT_CHARSET_LEFT = "осталось символов"
COMMENT_FORM_CODE = "Защитный код:"
COMMENT_FORM_CODE_ENTER = "Введите код:"
COMMENT_WRONG_CODE = "Указанный защитный код неверен."
COMMENT_DATE_TIME_FORMAT = "%d-%m-%Y %H:%M"
COMMENT_ERROR_AUTHOR = "Укажите Имя!"
COMMENT_ERROR_EMAIL = "Укажите eMail!"
COMMENT_ERROR_TEXT = "Напишите комментарий!"
COMMENT_ERROR_CAPTCHA = "Укажите защитный код!"
[admin]
COMMENT_MODULE_NAME = "Комментарии"
COMMENT_MODULE_COMENTS = "Список комментариев"
COMMENT_MODULE_SETTINGS = "Настройки модуля"
COMMENT_TEXT_COMMENT = "Комментарий"
COMMENT_DATE_CREATE = "Дата создания"
COMMENT_DOC_TITLE = "Документ"
COMMENT_DATE_FORMAT = "%d/%m/%Y %H:%M"
COMMENT_ENABLE_COMMENT = "Разрешить комментарии?"
COMMENT_CHECK_ADMIN = "Опубликовывать только после проверки?"
COMMENT_FOR_GROUPS = "Группы пользователей, которым разрешено оставлять комментарии:"
COMMENT_MAX_CHARS = "Максимальное количество символов:"
COMMENT_BUTTON_SAVE = "Сохранить настройки"
COMMENT_EDIT = "Редактировать комментарий"
COMMENT_SPAMPROTECT = "Защита от спама"
COMMENT_USE_PAGE_NAV = "Использовать постраничную навигацию"
COMMENT_PAGE_NAV_COUNT = "Кол-во комментариев на странице"
COMMENT_EDIT_TITLE = "Редактирование комментария"
COMMENT_IS_CLOSED = "Редактирование не возможно, поскольку обсуждение закрыто."
COMMENT_CLOSE_BUTTON = "Закрыть окно"
COMMENT_EDIT_FALSE = "Извините, но вы не имеете прав для редактирования."
COMMENT_YOUR_NAME = "* Имя:"
COMMENT_YOUR_EMAIL = "* E-mail:"
COMMENT_YOUR_SITE = "Веб-сайт:"
COMMENT_YOUR_FROM = "Город:"
COMMENT_YOUR_TEXT = "* Комментарий:"
COMMENT_BUTTON_EDIT = "Сохранить изменения"
COMMENT_BUTTON_CANCEL = "Отменить"
COMMENT_CHARS_LEFT = "Количество оставшихся символов"
COMMENT_BUTTON_CANCEL = "Отменить"
COMMENT_BUTTON_RESET = "Очистить"
COMMENT_FOR_GROUPS_READ = "Группы пользователей, которым разрешен просмотр комментариев:"

151
module.php Normal file
View File

@@ -0,0 +1,151 @@
<?php
/**
* AVE.cms - Модуль Комментарии
*
* @package AVE.cms
* @subpackage module_Comment
* @filesource
*/
if(!defined('BASE_DIR')) exit;
/**
* Функция, предназначенная для вывода списка комментариев к данному документу.
* Она будет выполнена при парсинге шаблона вместо системного тега [mod_comment].
*/
function mod_comment()
{
global $AVE_Template;
// Подключаем класс и создаем объект дял работы
require_once(BASE_DIR . '/modules/comment/class/comment.php');
$comment = new Comment;
// Подключаем языковые файлы
$tpl_dir = BASE_DIR . '/modules/comment/templates/';
$lang_file = BASE_DIR . '/modules/comment/lang/' . $_SESSION['user_language'] . '.txt';
$AVE_Template->config_load($lang_file, 'module');
// Обращаемся к методу commentListShow() и отображаем список комментариев
$comment->commentListShow($tpl_dir);
}
/**
* Следующий раздел описывает правила поведения модуля и его функциональные возможности
* только при работе в Публичной части сайта.
*/
// Определяем, что мы не находимся в Панели управления и в строке запроса происходит обращение именно к данному модулю
if (!defined('ACP') && isset($_REQUEST['module']) && $_REQUEST['module'] == 'comment' && isset($_REQUEST['action']))
{
// Подключаем основной класс и создаем объект
require_once(BASE_DIR . '/modules/comment/class/comment.php');
$comment = new Comment;
// Определяем директори, где хранятся файлы с шаблонами модуля и подключаем языковые переменные
$tpl_dir = BASE_DIR . '/modules/comment/templates/';
$lang_file = BASE_DIR . '/modules/comment/lang/' . $_SESSION['user_language'] . '.txt';
$AVE_Template->config_load($lang_file, 'module');
// Определяем, какой параметр пришел из строки запроса браузера
switch($_REQUEST['action'])
{
// Если form, тогда отображаем форму для добавления нового комментария
case 'form':
$comment->commentPostFormShow($tpl_dir);
break;
// Если comment, тогда производим запись нового комментария в БД
case 'comment':
$comment->commentPostNew($tpl_dir);
break;
// Если edit, тогда открываем форму для редактирования текста комментария
case 'edit':
$comment->commentPostEdit((int)$_REQUEST['Id']);
break;
// Если delete, тогда удаляем комментарий
case 'delete':
if (UGROUP==1)
{
$comment->commentPostDelete((int)$_REQUEST['Id']);
}
break;
// Если postinfo, тогда отображаем окно с информацией об авторе комментария
case 'postinfo':
$comment->commentPostInfoShow($tpl_dir);
break;
// Если lock или unlock, тогда запрещаем или разрешаем оставлять ответы для имеющихся комментариев
case 'lock':
case 'unlock':
if (UGROUP==1)
{
$comment->commentReplyStatusSet((int)$_REQUEST['Id'], $_REQUEST['action']);
}
break;
// Если open или close, тогда разрешаем или запрещаем полное комментирование документа
case 'open':
case 'close':
if (UGROUP==1)
{
$comment->commentStatusSet((int)$_REQUEST['docid'], $_REQUEST['action']);
}
break;
}
}
/**
* Следующий раздел описывает правила поведения модуля и его функциональные возможности
* только при работе в Административной части сайта.
*/
if (defined('ACP') && !empty($_REQUEST['moduleaction']))
{
// Подключаем основной класс и создаем объект
require_once(BASE_DIR . '/modules/comment/class/comment.php');
$comment = new Comment;
// Определяем директори, где хранятся файлы с шаблонами модуля и подключаем языковые переменные
$tpl_dir = BASE_DIR . '/modules/comment/templates/';
$lang_file = BASE_DIR . '/modules/comment/lang/' . $_SESSION['admin_language'] . '.txt';
$AVE_Template->config_load($lang_file, 'admin');
// Определяем, какой параметр пришел из строки запроса браузера
switch ($_REQUEST['moduleaction'])
{
// Если 1, тогда отображаем список всех комментариев с постраничной навигацией
case '1':
$comment->commentAdminListShow($tpl_dir);
break;
// Если admin_edit, тогда открываем форму для редактирования выбранного комментария
case 'admin_edit':
$comment->commentAdminPostEdit($tpl_dir);
break;
// Если settings, тогда открываем страницу с настройками данного модуля
case 'settings':
// Подключаем файл класса для работы с пользователями, создаем объект и получаем список
// всех групп пользователей, имеющихся в системе.
require_once(BASE_DIR . '/class/class.user.php');
$AVE_User = new AVE_User;
$AVE_Template->assign('groups', $AVE_User->userGroupListGet());
$comment->commentAdminSettingsEdit($tpl_dir);
break;
case 'admin_del':
$comment->commentAdminDelete((int)$_REQUEST['Id']);
break;
}
}
?>

117
sql.php Normal file
View File

@@ -0,0 +1,117 @@
<?php
/**
* AVE.cms - Модуль Комментарии
*
* Данный файл является частью модуля "Комментарии" и содержит mySQL-запросы
* к базе данных при операцих установки, обновления и удаления модуля через Панель управления.
*
* @package AVE.cms
* @subpackage module_Comment
* @since 1.4
* @filesource
*/
$module_sql_install = array();
$module_sql_deinstall = array();
$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`;";
// =================================================================================
// 1. ИЗМЕНЕНИЯ ДЛЯ УСТАНОВКИ МОДУЛЯ
// =================================================================================
$module_sql_install[] = "CREATE TABLE `%%PRFX%%_module_comments` (
`Id` tinyint(1) unsigned NOT NULL auto_increment,
`comment_max_chars` smallint(3) unsigned NOT NULL default '1000',
`comment_user_groups` text NOT NULL,
`comment_user_groups_read` text NOT NULL, /* <-- НОВОЕ ПОЛЕ ДЛЯ ПРАВ НА ПРОСМОТР */
`comment_need_approve` enum('0','1') NOT NULL default '0',
`comment_active` enum('1','0') NOT NULL default '1',
`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,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
$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',
`document_id` int(10) unsigned NOT NULL default '0',
`comment_author_name` varchar(255) NOT NULL,
`comment_author_id` int(10) unsigned NOT NULL default '0',
`comment_author_email` varchar(255) NOT NULL,
`comment_author_city` varchar(255) NOT NULL,
`comment_author_website` varchar(255) NOT NULL,
`comment_author_ip` varchar(15) NOT 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',
PRIMARY KEY (`Id`),
KEY `document_id` (`document_id`),
KEY `parent_id` (`parent_id`),
KEY `comment_status` (`comment_status`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PACK_KEYS=0;";
// ИЗМЕНЕННАЯ СТРОКА: Устанавливаем права на просмотр по умолчанию для всех групп: 1, 2, 3, 4
$module_sql_install[] = "INSERT INTO `%%PRFX%%_module_comments` VALUES (1, 1000, '1,3', '1,2,3,4', '0', '1', '1' , '0', '');";
// ^ max chars, ^ write groups, ^ read groups (ИСПРАВЛЕНО), ^ need_approve ...
// =================================================================================
// 2. ИЗМЕНЕНИЯ ДЛЯ ОБНОВЛЕНИЯ МОДУЛЯ (Добавление нового поля)
// =================================================================================
// Обновление версии модуля (оставлено без изменений)
$module_sql_update[] = "
UPDATE
`%%PRFX%%_module`
SET
ModuleAveTag = '" . $modul['ModuleAveTag'] . "',
ModulePHPTag = '" . $modul['ModulePHPTag'] . "',
ModuleVersion = '" . $modul['ModuleVersion'] . "'
WHERE
ModuleSysName = '" . $modul['ModuleSysName'] . "'
LIMIT 1;
";
// Добавление нового столбца comment_user_groups_read в существующую таблицу
$module_sql_update[] = "
ALTER TABLE
`%%PRFX%%_module_comments`
ADD
`comment_user_groups_read` TEXT NOT NULL
AFTER
`comment_user_groups`;
";
// ИЗМЕНЕННАЯ СТРОКА: Заполнение нового столбца значением по умолчанию для всех групп: 1, 2, 3, 4
$module_sql_update[] = "
UPDATE
`%%PRFX%%_module_comments`
SET
`comment_user_groups_read` = '1,2,3,4'
WHERE
`Id` = 1;
";
$module_sql_update[] = "
ALTER TABLE
`%%PRFX%%_module_comments`
ADD
`comment_use_page_nav` ENUM('0','1') NOT NULL DEFAULT '1'
AFTER
`comment_use_antispam`;
";
$module_sql_update[] = "
ALTER TABLE
`%%PRFX%%_module_comments`
ADD
`comment_page_nav_count` VARCHAR(5) NOT NULL
AFTER
`comment_use_page_nav`;
";
?>

View File

@@ -0,0 +1,89 @@
<script type="text/javascript" language="JavaScript">
$(document).ready(function(){ldelim}
{rdelim});
</script>
<div class="title"><h5>{#COMMENT_MODULE_NAME#}</h5></div>
<div class="widget" style="margin-top: 0px;">
<div class="body">
{#COMMENT_MODULE_COMENTS#}
</div>
</div>
<div class="breadCrumbHolder module">
<div class="breadCrumb module">
<ul>
<li class="firstB"><a href="index.php" title="{#MAIN_PAGE#}">{#MAIN_PAGE#}</a></li>
<li><a href="index.php?do=modules&amp;cp={$sess}">{#MODULES_SUB_TITLE#}</a></li>
<li>{#COMMENT_MODULE_NAME#}</li>
<li><strong class="code">{#COMMENT_MODULE_COMENTS#}</strong></li>
</ul>
</div>
</div>
<div class="widget first">
<div class="head">
<h5 class="iFrames">{#COMMENT_MODULE_COMENTS#}</h5>
<div class="num"><a class="basicNum" href="index.php?do=modules&action=modedit&mod=comment&moduleaction=settings&cp={$sess}">{#COMMENT_MODULE_SETTINGS#}</a></div>
</div>
{if isset($smarty.request.page)}
{assign var="current_page" value=$smarty.request.page|escape}
{else}
{assign var="current_page" value=1}
{/if}
<table cellpadding="0" cellspacing="0" width="100%" class="tableStatic">
<col width="20">
<col>
<col width="120">
<col width="120">
<col width="20">
<col width="20">
<thead>
<tr>
<td></td>
<td><a href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}&page={$current_page}&sort=comment{if $smarty.request.sort|default:'' == 'comment'}_desc{/if}">{#COMMENT_TEXT_COMMENT#}</a> </td>
<td><a href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}&page={$current_page}&sort=created{if $smarty.request.sort|default:'' == 'created'}_desc{/if}">{#COMMENT_DATE_CREATE#}</a> </td>
<td><a href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}&page={$current_page}&sort=document{if $smarty.request.sort|default:'' == 'document'}_desc{/if}">{#COMMENT_DOC_TITLE#}</a> </td>
<td colspan="2">Действия</td>
</tr>
</thead>
<tbody>
{if $docs}
{foreach from=$docs item=doc}
<tr>
<td>{if $doc.comment_status != "0"}<span class="icon_sprite ico_ok"></span>{else}<span class="icon_sprite ico_blanc"></span>{/if}</td>
<td><a class="topDir" title="{$doc.comment_text|escape|truncate:'1000'}" target="_blank" href="../index.php?id={$doc.document_id}&doc=impressum&subaction=showonly&comment_id={$doc.CId}#{$doc.CId}">{$doc.comment_text|escape|truncate:'100'}</a></td>
<td class="date_text dgrey">{$doc.comment_published|date_format:$TIME_FORMAT|pretty_date}</td>
<td><a target="_blank" href="../index.php?id={$doc.document_id}">{$doc.document_title|escape}</a>&nbsp;<span class="date_text dgrey">({$doc.Comments})</span></td>
<td width="20"><a class="topleftDir icon_sprite ico_edit" title="{#COMMENT_EDIT#}" href="javascript:void(0);" onClick="windowOpen('index.php?do=modules&action=modedit&mod=comment&moduleaction=admin_edit&pop=1&docid={$doc.document_id}&Id={$doc.CId}','700','700','1');"></a></td>
<td width="20"><a class="topleftDir icon_sprite ico_delete ConfirmDelete" title="{#COMMENT_DELETE_LINK#}" dir="{#COMMENT_DELETE_LINK#}" name="{#COMMENT_DELETE_LINK#}" href="index.php?do=modules&action=modedit&mod=comment&moduleaction=admin_del&Id={$doc.CId}"></a></td>
</tr>
{/foreach}
{else}
<tr>
<td colspan="6">
<ul class="messages">
<li class="highlight yellow"><strong>Сообщение:</strong><br />Нет комментариев.</li>
</ul>
</td>
</tr>
{/if}
</tbody>
</table>
</div>
{if $page_nav}
<div class="pagination">
<ul class="pages">
{$page_nav}
</ul>
</div>
{/if}

112
templates/admin_edit.tpl Normal file
View File

@@ -0,0 +1,112 @@
<script type="text/javascript" language="JavaScript">
{literal}
$(document).ready(function(){
var left = {/literal}{$comment_max_chars}{literal}
$('#text_counter').text(left);
$('#in_message').keyup(function () {
left = {/literal}{$comment_max_chars}{literal} - $(this).val().length;
if(left < 0){
$('#text_counter').addClass("overlimit");
}
if(left >= 0){
$('#text_counter').removeClass("overlimit");
}
$('#text_counter').text(left);
});
});
{/literal}
</script>
<div class="widget first"></div>
<div class="title"><h5>{#COMMENT_MODULE_NAME#}</h5></div>
<div class="widget" style="margin-top: 0px;">
<div class="body">
{#COMMENT_EDIT_TITLE#}
</div>
</div>
<div class="breadCrumbHolder module">
<div class="breadCrumb module">
<ul>
<li class="firstB"><a href="index.php" title="{#MAIN_PAGE#}">{#MAIN_PAGE#}</a></li>
<li>{#COMMENT_MODULE_NAME#}</li>
<li>{#COMMENT_EDIT_TITLE#}</li>
</ul>
</div>
</div>
<div class="widget first">
{if $closed == 1 && $smarty.const.UGROUP != 1}
{#COMMENT_IS_CLOSED#}
<p><input onclick="window.close();" type="button" class="basicBtn" value="{#COMMENT_CLOSE_BUTTON#}" /></p>
{else}
{if $editfalse==1}
{#COMMENT_EDIT_FALSE#}
{else}
<form method="post" action="index.php" class="mainForm">
<table cellpadding="0" cellspacing="0" width="100%" class="tableStatic">
<col width="150">
{if $smarty.const.UGROUP == 1}
<tr>
<td>{#COMMENT_YOUR_NAME#}</td>
<td><input name="comment_author_name" type="text" id="in_author_name" style="width:250px" value="{$row.comment_author_name|stripslashes|escape}" /></td>
</tr>
<tr>
<td>{#COMMENT_YOUR_EMAIL#}</td>
<td><input name="comment_author_email" type="text" id="in_author_email" style="width:250px" value="{$row.comment_author_email|stripslashes|escape}" /></td>
</tr>
{else}
<input type="hidden" name="comment_author_name" value="{$row.comment_author_name|stripslashes|escape}" />
<input type="hidden" name="comment_author_email" value="{$row.comment_author_email|stripslashes|escape}" />
{/if}
<tr>
<td>{#COMMENT_YOUR_SITE#}</td>
<td><input name="comment_author_website" type="text" id="in_author_website" style="width:250px" value="{$row.comment_author_website|stripslashes|escape}" /></td>
</tr>
<tr>
<td>{#COMMENT_YOUR_FROM#}</td>
<td><input name="comment_author_city" type="text" id="in_author_city" style="width:250px" value="{$row.comment_author_city|stripslashes|escape}" /></td>
</tr>
<tr>
<td>{#COMMENT_YOUR_TEXT#}</td>
<td>
<div class="pr12"><textarea style="width:100%; height:170px" name="comment_text" id="in_message">{$row.comment_text}</textarea></div>
<span id="text_counter"></span>&nbsp;{#COMMENT_CHARS_LEFT#}
</td>
</tr>
<input type="hidden" name="do" value="modules" />
<input type="hidden" name="action" value="modedit" />
<input type="hidden" name="mod" value="comment" />
<input type="hidden" name="moduleaction" value="admin_edit" />
<input type="hidden" name="sub" value="send" />
<input type="hidden" name="Id" value="{$smarty.request.Id|escape}" />
<tr>
<td colspan="3">
<input type="submit" class="basicBtn" value="{#COMMENT_BUTTON_EDIT#}" />&nbsp;
<input type="reset" class="basicBtn" value="{#COMMENT_BUTTON_CANCEL#}" />
</td>
</tr>
</table>
</form>
{/if}
{/if}
</div>

View File

@@ -0,0 +1,105 @@
<div class="title"><h5>{#COMMENT_MODULE_NAME#}</h5></div>
<div class="widget" style="margin-top: 0px;">
<div class="body">
{#COMMENT_MODULE_SETTINGS#}
</div>
</div>
<div class="breadCrumbHolder module">
<div class="breadCrumb module">
<ul>
<li class="firstB"><a href="index.php" title="{#MAIN_PAGE#}">{#MAIN_PAGE#}</a></li>
<li><a href="index.php?do=modules&amp;cp={$sess}">{#MODULES_SUB_TITLE#}</a></li>
<li><a href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}">{#COMMENT_MODULE_NAME#}</a></li>
<li><strong class="code">{#COMMENT_MODULE_SETTINGS#}</strong></li>
</ul>
</div>
</div>
<div class="widget first">
<div class="head">
<h5 class="iFrames">{#COMMENT_MODULE_SETTINGS#}</h5>
<div class="num"><a class="basicNum" href="index.php?do=modules&action=modedit&mod=comment&moduleaction=1&cp={$sess}">{#COMMENT_MODULE_COMENTS#}</a></div>
</div>
<form action="index.php?do=modules&action=modedit&mod=comment&moduleaction=settings&cp={$sess}&sub=save" method="post" class="mainForm">
<table cellpadding="0" cellspacing="0" width="100%" class="tableStatic">
<tr class="noborder">
<td width="240">{#COMMENT_ENABLE_COMMENT#}</td>
<td><input name="comment_active" type="checkbox" value="1" {if $comment_active=='1'}checked{/if} /></td>
</tr>
<tr>
<td width="240">{#COMMENT_CHECK_ADMIN#}</td>
<td>
<input name="comment_need_approve" type="checkbox" value="1" {if $comment_need_approve=='1'}checked{/if} />
</td>
</tr>
<tr>
<td width="240">{#COMMENT_SPAMPROTECT#}</td>
<td>
<input name="comment_use_antispam" type="checkbox" value="1" {if $comment_use_antispam=='1'}checked{/if} />
</td>
</tr>
<tr>
<td width="240">{#COMMENT_USE_PAGE_NAV#}</td>
<td>
<input name="comment_use_page_nav" type="checkbox" value="1" {if $comment_use_page_nav=='1'}checked{/if} />
</td>
</tr>
<tr>
<td width="240">{#COMMENT_PAGE_NAV_COUNT#}</td>
<td>
<input name="comment_page_nav_count" type="text" value="{$comment_page_nav_count}" size="4" style="width: 50px;" />
</td>
</tr>
<tr>
<td width="240">{#COMMENT_FOR_GROUPS#}</td>
<td>
<select name="comment_user_groups[]" multiple="multiple" size="5" style="width:300px">
{foreach from=$groups item=g}
{assign var='sel' value=''}
{if $g->user_group}
{if (in_array($g->user_group,$comment_user_groups)) }
{assign var='sel' value='selected'}
{/if}
{/if}
<option value="{$g->user_group}" {$sel}>{$g->user_group_name|escape}</option>
{/foreach}
</select>
</td>
</tr>
<tr>
<td width="240">{#COMMENT_FOR_GROUPS_READ#}</td>
<td>
<select name="comment_user_groups_read[]" multiple="multiple" size="5" style="width:300px">
{foreach from=$groups item=g}
{assign var='sel_read' value=''}
{if $g->user_group}
{if (in_array($g->user_group,$comment_user_groups_read)) }
{assign var='sel_read' value='selected'}
{/if}
{/if}
<option value="{$g->user_group}" {$sel_read}>{$g->user_group_name|escape}</option>
{/foreach}
</select>
</td>
</tr>
<tr>
<td width="240">{#COMMENT_MAX_CHARS#}</td>
<td><input name="comment_max_chars" type="text" id="comment_max_chars" value="{$comment_max_chars}" size="5" maxlength="5" style="width: 50px;" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="{#COMMENT_BUTTON_SAVE#}" class="basicBtn" /></td>
</tr>
</table>
</form>
</div>

View File

@@ -0,0 +1,71 @@
<!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">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{#COMMENT_EDIT_TITLE#}</title>
<link href="templates/{$theme}/css/style.css" rel="stylesheet" type="text/css" media="screen" />
<script src="templates/{$theme}/js/common.js" type="text/javascript"></script>
</head>
<body id="body_popup">
<div id="module_header"><h2>{#COMMENT_EDIT_TITLE#}</h2></div>
<div id="module_content">
{if $closed == 1 && $smarty.const.UGROUP != 1}
{#COMMENT_IS_CLOSED#}
<p>&nbsp;</p>
<p><input onclick="window.close();" type="button" class="button" value="{#COMMENT_CLOSE_BUTTON#}" /></p>
{else}
{if $editfalse==1}
{#COMMENT_EDIT_FALSE#}
{else}
<form method="post">
{if $smarty.const.UGROUP==1}
<fieldset>
<legend><label for="in_author_name">{#COMMENT_YOUR_NAME#}</label></legend>
<input name="comment_author_name" type="text" id="in_author_name" style="width:250px" value="{$row.comment_author_name|stripslashes|escape}" />
</fieldset>
<fieldset>
<legend><label for="in_author_email">{#COMMENT_YOUR_EMAIL#}</label></legend>
<input name="comment_author_email" type="text" id="in_author_email" style="width:250px" value="{$row.comment_author_email|stripslashes|escape}" />
</fieldset>
{else}
<input name="comment_author_name" type="hidden" id="in_author_name" value="{$row.comment_author_name|stripslashes|escape}" />
<input name="comment_author_email" type="hidden" id="in_author_email" value="{$row.comment_author_email|stripslashes|escape}" />
{/if}
<fieldset>
<legend><label for="in_author_website">{#COMMENT_YOUR_SITE#}</label></legend>
<input name="comment_author_website" type="text" id="in_author_website" style="width:250px" value="{$row.comment_author_website|stripslashes|escape}" />
</fieldset>
<fieldset>
<legend><label for="in_author_city">{#COMMENT_YOUR_FROM#}</label></legend>
<input name="comment_author_city" type="text" id="in_author_city" style="width:250px" value="{$row.comment_author_city|stripslashes|escape}" />
</fieldset>
<fieldset>
<legend><label for="in_message">{#COMMENT_YOUR_TEXT#}</label></legend>
<textarea onkeyup="javascript:textCounter(this.form.comment_text,this.form.charleft,{$row.comment_max_chars});" onkeydown="javascript:textCounter(this.form.comment_text,this.form.charleft,{$row.comment_max_chars});" style="width:98%; height:170px" name="comment_text" id="in_message">{$row.comment_text}</textarea>
<input type="text" size="6" name="charleft" value="{$row.comment_max_chars}" /> {#COMMENT_CHARS_LEFT#}
</fieldset>
<input name="theme" type="hidden" id="theme" value="{$smarty.request.theme|escape}" />
<input name="module" type="hidden" value="comment" />
<input name="action" type="hidden" value="edit" />
<input name="pop" type="hidden" value="1" />
<input name="sub" type="hidden" value="send" />
<input name="Id" type="hidden" value="{$smarty.request.Id|escape}" />
<p>
<input type="submit" class="button" value="{#COMMENT_BUTTON_EDIT#}" />&nbsp;
<input type="reset" class="button" />
</p>
</form>
{/if}
{/if}
</div>
</body>
</html>

View File

@@ -0,0 +1,84 @@
<!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">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<title>{#COMMENT_NEW_TITLE#}</title>
<link href="templates/{$theme}/css/style.css" rel="stylesheet" type="text/css" media="screen" />
<script src="templates/{$theme}/js/common.js" type="text/javascript"></script>
</head>
<body id="body_popup">
<div id="module_header"><h2>{#COMMENT_NEW_TITLE#}</h2></div>
<div id="module_content">
{if $closed==1}
{#COMMENT_NEW_CLOSED#}
<p>&nbsp;</p>
<p><input onclick="window.close();" type="button" class="button" value="{#COMMENT_CLOSE_BUTTON#}" /></p>
{else}
{if !$cancomment}
<p id="module_intro">{#COMMENT_NEW_FALSE#}</p>
<p>&nbsp;</p>
<p><input onclick="window.close();" type="button" class="button" value="{#COMMENT_CLOSE_BUTTON#}" /></p>
{else}
<form method="post">
{if $smarty.session.user_name != ''}
<input name="comment_author_name" type="hidden" value="{$smarty.session.user_name|escape}" />
{else}
<fieldset>
<legend><label for="in_author_name">{#COMMENT_YOUR_NAME#}</label></legend>
<input name="comment_author_name" type="text" id="in_author_name" style="width:250px" value="{$smarty.session.user_name|escape}" />
</fieldset>
<br />
{/if}
{if $smarty.session.user_email != ''}
<input name="comment_author_email" type="hidden" value="{$smarty.session.user_email|escape}" />
{else}
<fieldset>
<legend><label for="in_author_email">{#COMMENT_YOUR_EMAIL#}</label></legend>
<input name="comment_author_email" type="text" id="in_author_email" style="width:250px" value="" />
</fieldset>
<br />
{/if}
<fieldset>
<legend><label for="in_author_website">{#COMMENT_YOUR_SITE#}</label></legend>
<input name="comment_author_website" type="text" id="in_author_website" style="width:250px" />
</fieldset>
<br />
<fieldset>
<legend><label for="in_author_city">{#COMMENT_YOUR_FROM#}</label></legend>
<input name="comment_author_city" type="text" id="in_author_city" style="width:250px" />
</fieldset>
<br />
<fieldset>
<legend><label for="in_message">{#COMMENT_YOUR_TEXT#}</label></legend>
<textarea onkeyup="javascript:textCounter(this.form.comment_text,this.form.charleft,{$comment_max_chars});" onkeydown="javascript:textCounter(this.form.comment_text,this.form.charleft,{$comment_max_chars});" style="width:98%; height:165px" name="comment_text" id="in_message"></textarea>
<input type="text" size="6" name="charleft" value="{$comment_max_chars}" /> {#COMMENT_CHARSET_LEFT#}
</fieldset>
<input name="theme" type="hidden" id="theme" value="{$smarty.request.theme|escape}" />
<input name="module" type="hidden" value="comment" />
<input name="action" type="hidden" value="comment" />
<input name="pop" type="hidden" id="pop" value="1" />
<input name="sub" type="hidden" id="sub" value="send" />
<input name="page" type="hidden" value="{$smarty.request.page|escape}" />
<input name="document_id" type="hidden" value="{$smarty.request.docid|escape}" />
<input name="parent_id" type="hidden" value="{$smarty.request.parent|escape|default:0}" />
<p>
<input type="submit" class="button" value="{#COMMENT_BUTTON_ADD#}" />&nbsp;
<input type="reset" class="button" />
</p>
</form>
{/if}
{/if}
</div>
</body>
</html>

View File

@@ -0,0 +1,54 @@
<!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">
<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>
<body id="body_popup">
<div id="module_header"><h2>{#COMMENT_INFO#}</h2></div>
<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>
<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>

24
templates/comment_new.tpl Normal file
View File

@@ -0,0 +1,24 @@
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="mod_comment_box">
<tr>
<td class="mod_comment_header">
<div class="mod_comment_author">
<a name="{$comment_id}"></a>{#COMMENT_USER_ADD#} <a title="{#COMMENT_INFO#}" href="javascript:void(0);" onclick="popup('index.php?module=comment&action=postinfo&pop=1&Id={$comment_id}&theme={$theme}','comment','500','300','1');">{$comment_author_name}</a> • {$smarty.now|date_format:$TIME_FORMAT|pretty_date}{if $smarty.const.UGROUP==1} • IP:{$comment_author_ip}{/if}
</div>
{if $smarty.const.UGROUP==1}
<div class="mod_comment_icons">
&nbsp;<a class="" title="{#COMMENT_DELETE_LINK#}" href="javascript:void(0);" onclick="cAction(this, 'delete', '{$comment_id}');"><img src="modules/comment/templates/images/trash.gif" alt="" border="0" /></a>
{if $comment_status!=1}
&nbsp;<a class="" title="{#COMMENT_UNLOCK_LINK#}" href="javascript:void(0);" onclick="cAction(this, 'unlock', '{$comment_id}');"><img src="modules/comment/templates/images/unlock.gif" alt="" border="0" /></a>
{else}
&nbsp;<a class="" title="{#COMMENT_LOCK_LINK#}" href="javascript:void(0);" onclick="cAction(this, 'unlock', '{$comment_id}');"><img src="modules/comment/templates/images/lock.gif" alt="" border="0" /></a>
{/if}
</div>
{/if}
</td>
</tr>
<tr>
<td id="id_{$comment_id}" class="mod_comment_text{if $smarty.session.user_id} editable_text{/if}">{$comment_text}</td>
</tr>
</table>

View File

@@ -0,0 +1,15 @@
<!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">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<title>{#COMMENT_THANKYOU_TITLE#}</title>
<link href="templates/{$smarty.request.theme}/css/style.css" rel="stylesheet" type="text/css" media="screen" />
</head>
<body id="body_popup">
<div id="module_header"><h2>{#COMMENT_THANKYOU_TITLE#}</h2></div>
<div id="module_content">
<p id="module_intro">{$JsAfter}</p>
<input onclick="window.close();" type="button" class="button" value="{#COMMENT_CLOSE_BUTTON#}" />
</div>
</body>
</html>

122
templates/comments_show.tpl Normal file
View File

@@ -0,0 +1,122 @@
{if $display_comments==1}<br />
<h6>{#COMMENT_SITE_TITLE#}{if $closed==1} {#COMMENT_SITE_CLOSED#}{/if}</h6>
{if $cancomment==1 && $closed!=1}
<a href="javascript:void(0);" onclick="popup('index.php?docid={$smarty.request.id|escape}&module=comment&action=form&pop=1&theme={$theme}&page={$page}','comment','500','600','1')">{#COMMENT_SITE_ADD#}</a>&nbsp;|&nbsp;
{/if}
<a href="#end">{#COMMENT_LAST_COMMENT#}</a>
{if $smarty.const.UGROUP == 1}
&nbsp;|&nbsp;
{if $closed==1}
<a href="javascript:void(0);" onclick="popup('index.php?document_id={$smarty.request.id|escape}&module=comment&action=open&pop=1','comment','50','50','1');">{#COMMENT_SITE_OPEN#}</a>
{else}
<a href="javascript:void(0);" onclick="popup('index.php?document_id={$smarty.request.id|escape}&module=comment&action=close&pop=1','comment','50','50','1');">{#COMMENT_SITE_CLOSE#}</a>
{/if}
{/if}<br />
<br />
{foreach from=$comments.0 item=c name=co}
{if $smarty.request.subaction=='showonly' && $smarty.request.comment_id==$c.Id}
<div class="mod_comment_highlight">
{/if}
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="mod_comment_box">
<tr>
<td class="mod_comment_header">
<div class="mod_comment_author">
<a name="{$c.Id}"></a>{#COMMENT_USER_ADD#} <a title="{#COMMENT_INFO#}" href="javascript:void(0);" onclick="popup('index.php?module=comment&action=postinfo&pop=1&Id={$c.Id}&theme={$theme}','comment','500','300','1');">{$c.comment_author_name}{*|stripslashes|escape:html*}</a> • {$c.comment_published}{if $smarty.const.UGROUP==1} • IP:{$c.comment_author_ip}{/if}
</div>
<div class="mod_comment_icons">
<a class="popup" href="javascript:void(0);" onclick="popup('index.php?parent={$c.Id}&docid={$smarty.request.id|escape}&module=comment&action=form&pop=1&theme={$theme}&page={$page}','comment','500','520','1');">
<img src="modules/comment/templates/images/reply.gif" alt="" border="0" /><span>{#COMMENT_ANSWER_LINK#}</span>
</a>
{if $smarty.const.UGROUP==1 || $c.comment_author_id==$smarty.session.user_id}
&nbsp;<a class="popup" href="javascript:void(0);" onclick="popup('index.php?parent={$c.Id}&docid={$smarty.request.id|escape}&module=comment&action=edit&pop=1&Id={$c.Id}&theme={$theme}','comment','500','620','1');">
<img src="modules/comment/templates/images/edit.gif" alt="" border="0" /><span>{#COMMENT_EDIT_LINK#}</span></a>
{/if}
{if $smarty.const.UGROUP==1}
&nbsp;<a class="popup" href="javascript:void(0);" onclick="popup('index.php?parent={$c.Id}&docid={$smarty.request.id|escape}&module=comment&action=delete&pop=1&Id={$c.Id}','comment','100','100','1');">
<img src="modules/comment/templates/images/trash.gif" alt="" border="0" /><span>{#COMMENT_DELETE_LINK#}</span>
</a>
{if $c.comment_status!=1}
&nbsp;<a class="popup" href="javascript:void(0);" onclick="popup('index.php?parent={$c.Id}&docid={$smarty.request.id|escape}&module=comment&action=unlock&pop=1&Id={$c.Id}','comment','100','100','1');">
<img src="modules/comment/templates/images/unlock.gif" alt="" border="0" /><span>{#COMMENT_UNLOCK_LINK#}</span>
</a>
{else}
&nbsp;<a class="popup" href="javascript:void(0);" onclick="popup('index.php?parent={$c.Id}&docid={$smarty.request.id|escape}&module=comment&action=lock&pop=1&Id={$c.Id}','comment','100','100','1');">
<img src="modules/comment/templates/images/lock.gif" alt="" border="0" /><span>{#COMMENT_LOCK_LINK#}</span>
</a>
{/if}
{/if}
</div>
</td>
</tr>
<tr>
<td class="mod_comment_text">
{$c.comment_text}
{if $c.comment_changed > 1}<br /><span class="mod_comment_changed">{#COMMENT_TEXT_CHANGED#} {$c.comment_changed}</span>{/if}
</td>
</tr>
</table>
{if $smarty.request.subaction=='showonly' && $smarty.request.comment_id==$c.Id}
</div>
{/if}
{foreach from=$comments[$c.Id] item=sc}
<div class="mod_comment_ans_box">
{if $smarty.request.subaction=='showonly' && $smarty.request.comment_id==$sc.Id}
<div class="mod_comment_highlight">
{/if}
<table width="100%" border="0" cellpadding="0" cellspacing="0" class="mod_comment_box">
<tr>
<td class="mod_comment_header">
<div class="mod_comment_author">
<a name="{$sc.Id}"></a>{#COMMENT_TEXT_ANSWER#} <a title="{#COMMENT_INFO#}" href="javascript:void(0);" onclick="popup('index.php?module=comment&action=postinfo&pop=1&Id={$sc.Id}&theme={$theme}','comment','500','300','1');">{$sc.comment_author_name}{*|stripslashes|escape:html*}</a> ({$sc.comment_published}){if $smarty.const.UGROUP==1} IP:{$sc.comment_author_ip}{/if}
</div>
<div class="mod_comment_icons">
{if $smarty.const.UGROUP==1 || $sc.comment_author_id==$smarty.session.user_id}
<a title="{#COMMENT_EDIT_LINK#}" href="javascript:void(0);" onclick="popup('index.php?parent={$sc.Id}&docid={$smarty.request.id|escape}&module=comment&action=edit&pop=1&Id={$sc.Id}&theme={$theme}','comment','500','620','1');"><img src="modules/comment/templates/images/edit.gif" alt="" border="0" /></a>
{/if}
{if $smarty.const.UGROUP==1}
<a title="{#COMMENT_DELETE_LINK#}" href="javascript:void(0);" onclick="popup('index.php?parent={$sc.Id}&docid={$smarty.request.id|escape}&module=comment&action=delete&pop=1&Id={$sc.Id}','comment','100','100','1');"><img src="modules/comment/templates/images/trash.gif" alt="" border="0" /></a>
{if $sc.comment_status!=1}
<a title="{#COMMENT_UNLOCK_LINK#}" href="javascript:void(0);" onclick="popup('index.php?parent={$sc.Id}&docid={$smarty.request.id|escape}&module=comment&action=unlock&pop=1&Id={$sc.Id}','comment','100','100','1');"><img src="modules/comment/templates/images/unlock.gif" alt="" border="0" /></a>
{else}
<a title="{#COMMENT_LOCK_LINK#}" href="javascript:void(0);" onclick="popup('index.php?parent={$sc.Id}&docid={$smarty.request.id|escape}&module=comment&action=lock&pop=1&Id={$sc.Id}','comment','100','100','1');"><img src="modules/comment/templates/images/lock.gif" alt="" border="0" /></a>
{/if}
{else}
&nbsp;
{/if}
</div>
</td>
</tr>
<tr>
<td class="mod_comment_text">
{$sc.comment_text}
{if $sc.comment_changed > 1}<br /><span class="mod_comment_changed">{#COMMENT_TEXT_CHANGED#} {$sc.comment_changed}</span>{/if}
</td>
</tr>
</table>
{if $smarty.request.subaction=='showonly' && $smarty.request.comment_id==$sc.Id}
</div>
{/if}
</div>
{/foreach}
{/foreach}
{if $smarty.foreach.co.last}<a name="end"></a>{/if}
{/if}

193
templates/comments_tree.tpl Normal file
View File

@@ -0,0 +1,193 @@
{if $display_comments==1}
{* ===================================================================== *}
{* НОВОЕ: ПРОВЕРКА ПРАВ НА ЧТЕНИЕ (самый высокий приоритет) *}
{* ===================================================================== *}
{if $no_read_permission == 1}
<div class="alert alert-warning" role="alert">{#COMMENT_READ_FALSE#}</div>
{else}
{* ===================================================================== *}
{* ВСЁ СУЩЕСТВУЮЩЕЕ СОДЕРЖИМОЕ БЛОКА КОММЕНТАРИЕВ (если права есть) *}
{* ===================================================================== *}
<h3 class="mb-3">
{#COMMENT_SITE_TITLE#}
{if $closed==1 && $smarty.const.UGROUP!=1}
<span class="badge bg-secondary ms-2">{#COMMENT_SITE_CLOSED#}</span>
{/if}
</h3>
<div class="d-flex mb-4 align-items-center">
{* Кнопка "Добавить комментарий" - ведет к якорю формы *}
<a href="#end" class="btn btn-sm btn-outline-primary">
<i class="bi bi-plus-circle me-1"></i> {#COMMENT_SITE_ADD#}
</a>
{if $smarty.const.UGROUP==1}
{* Кнопки управления комментированием для Администратора *}
{if $closed==1}
<a id="mod_comment_open" href="javascript:void(0);" class="btn btn-sm btn-outline-success ms-3">
<i class="bi bi-lock-fill me-1"></i> {#COMMENT_SITE_OPEN#}
</a>
{else}
<a id="mod_comment_close" href="javascript:void(0);" class="btn btn-sm btn-outline-danger ms-3">
<i class="bi bi-unlock-fill me-1"></i> {#COMMENT_SITE_CLOSE#}
</a>
{/if}
{/if}
</div>
{* ИСПРАВЛЕНИЕ 1: Добавлена проверка isset() для безопасного доступа к $comments[0] *}
{if isset($comments[0])}
<div class="comments-list">
{include file="$subtpl" subcomments=$comments[0]}
</div>
{/if}
<a id="end"></a>
{* ===================================================================== *}
{* ФОРМА ДОБАВЛЕНИЯ КОММЕНТАРИЯ (MODAL/BLOCK) *}
{* ===================================================================== *}
<div class="mt-5">
{if $closed==1 && $smarty.const.UGROUP!=1}
<div class="alert alert-info">{#COMMENT_NEW_CLOSED#}</div>
{elseif $cancomment!=1 && $smarty.const.UGROUP!=1}
<div class="alert alert-info">{#COMMENT_NEW_FALSE#}</div>
{else}
<div id="mod_comment_new" class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="mb-0">{#COMMENT_NEW_TITLE#}</h4>
</div>
<div class="card-body p-4" id="forms">
<form method="post" action="{$ABS_PATH}">
<fieldset>
<legend class="d-none">Форма добавления комментария</legend>
{* Блок для имени и email (скрытые поля для авторизованных) *}
{if $smarty.session.user_group != '2'}
{* АВТОРИЗОВАННЫЙ ПОЛЬЗОВАТЕЛЬ *}
<input name="comment_author_name" type="hidden" id="in_author_name" value="{if isset($smarty.session.user_name)}{$smarty.session.user_name|escape|stripslashes}{/if}" />
{* ИСПРАВЛЕНИЕ: Добавлена проверка isset для user_email *}
{if isset($smarty.session.user_email) && $smarty.session.user_email != ''}
<input name="comment_author_email" type="hidden" id="in_author_email" value="{$smarty.session.user_email|escape|stripslashes}" />
{/if}
{else}
{* ГОСТЬ (требуется ввод имени) *}
<div class="row mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<label for="in_author_name" class="form-label">{#COMMENT_YOUR_NAME#}</label>
<input name="comment_author_name" type="text" id="in_author_name" class="form-control" placeholder="{#COMMENT_YOUR_NAME#}" value="{if isset($smarty.request.comment_author_name)}{$smarty.request.comment_author_name|escape|stripslashes}{/if}" />
</div>
{* ГОСТЬ (email) *}
<div class="col-md-6">
<label for="in_author_email" class="form-label">{#COMMENT_YOUR_EMAIL#}</label>
<input name="comment_author_email" type="email" id="in_author_email" class="form-control" placeholder="{#COMMENT_YOUR_EMAIL#}" value="{if isset($smarty.request.comment_author_email)}{$smarty.request.comment_author_email|escape|stripslashes}{/if}" />
</div>
</div>
{/if}
{* Сайт и Город *}
<div class="row mb-3">
<div class="col-md-6 mb-3 mb-md-0">
<label for="in_author_website" class="form-label">{#COMMENT_YOUR_SITE#} (необязательно)</label>
{* ИСПРАВЛЕНИЕ 2: Безопасное чтение comment_author_website через if/else *}
<input name="comment_author_website" type="text" id="in_author_website" class="form-control" placeholder="{#COMMENT_YOUR_SITE#}" value="{if isset($smarty.request.comment_author_website)}{$smarty.request.comment_author_website|escape|stripslashes}{else}{/if}" />
</div>
<div class="col-md-6">
<label for="in_author_city" class="form-label">{#COMMENT_YOUR_FROM#} (необязательно)</label>
{* ИСПРАВЛЕНИЕ 3: Безопасное чтение comment_author_city через if/else *}
<input name="comment_author_city" type="text" id="in_author_city" class="form-control" placeholder="{#COMMENT_YOUR_FROM#}" value="{if isset($smarty.request.comment_author_city)}{$smarty.request.comment_author_city|escape|stripslashes}{else}{/if}" />
</div>
</div>
{* Текст комментария *}
<div class="mb-3">
<label for="in_message" class="form-label">{#COMMENT_YOUR_TEXT#}*</label>
<textarea rows="8" name="comment_text" id="in_message" class="form-control" placeholder="{#COMMENT_YOUR_TEXT#}"></textarea>
</div>
{* Оставшиеся символы *}
<p class="text-end text-muted small">
{#COMMENT_CHARS_LEFT#} <span class="charsLeft fw-bold" id="charsLeft_new"></span>
</p>
{* CAPTCHA *}
{if $im}
<div class="row mb-3 align-items-center">
<div class="col-md-4">
<label for="securecode" class="form-label">{#COMMENT_FORM_CODE#}</label>
<span id="captcha" class="d-block mb-2">
<img src="{$ABS_PATH}inc/captcha.php" alt="{#COMMENT_FORM_CODE#}" class="img-fluid border rounded" style="cursor: pointer;" width="120" height="60" border="0" />
</span>
</div>
<div class="col-md-8">
<label for="securecode" class="form-label">{#COMMENT_FORM_CODE_ENTER#}*</label>
<input name="securecode" type="text" id="securecode" maxlength="10" class="form-control" placeholder="{#COMMENT_FORM_CODE_ENTER#}" />
</div>
</div>
{/if}
{* Кнопки отправки *}
<div class="mt-4">
<button type="submit" class="btn btn-success me-2">
<i class="bi bi-send-fill me-1"></i> {#COMMENT_BUTTON_ADD#}
</button>
<button type="reset" id="buttonReset" class="btn btn-secondary">
<i class="bi bi-x-circle me-1"></i> Сбросить
</button>
</div>
{* Скрытые поля *}
<input name="module" type="hidden" value="comment" />
<input name="action" type="hidden" value="comment" />
<input name="sub" type="hidden" value="send" />
<input name="doc_id" type="hidden" value="{$smarty.request.id|escape}" />
<input name="parent_id" id="parent_id" type="hidden" value="" />
<input name="page" type="hidden" value="{$page}" />
</fieldset>
</form>
</div>
</div>
{/if}
</div> {* <-- Закрывает mt-5 для формы *}
{if $page_nav}
<div class="page_navigation_box mt-4">
<nav aria-label="Comment Page Navigation">{$page_nav}</nav>
</div>
{/if}
<script type="text/javascript">
var COMMENT_SITE_CLOSE = '{#COMMENT_SITE_CLOSE#}';
var COMMENT_SITE_OPEN = '{#COMMENT_SITE_OPEN#}';
var COMMENT_LOCK_LINK = '{#COMMENT_LOCK_LINK#}';
var COMMENT_EDIT_LINK = '{#COMMENT_EDIT_LINK#}';
var COMMENT_EDIT_TITLE = '{#COMMENT_EDIT_TITLE#}';
var COMMENT_UNLOCK_LINK = '{#COMMENT_UNLOCK_LINK#}';
var COMMENT_ERROR_AUTHOR = '{#COMMENT_ERROR_AUTHOR#}';
var COMMENT_ERROR_EMAIL = '{#COMMENT_ERROR_EMAIL#}';
var COMMENT_ERROR_TEXT = '{#COMMENT_ERROR_TEXT#}';
var COMMENT_ERROR_CAPTCHA = '{#COMMENT_ERROR_CAPTCHA#}';
var COMMENT_BUTTON_EDIT = '{#COMMENT_BUTTON_EDIT#}';
var COMMENT_BUTTON_CANCEL = '{#COMMENT_BUTTON_CANCEL#}';
var COMMENT_CHARS_LEFT = '{#COMMENT_CHARS_LEFT#}';
var COMMENT_DATE_TIME_FORMAT = '{#COMMENT_DATE_TIME_FORMAT#}';
var COMMENT_TEXT_CHANGED = '{#COMMENT_TEXT_CHANGED#}';
var COMMENT_WRONG_CODE = '{#COMMENT_WRONG_CODE#}';
var UGROUP = '{$smarty.const.UGROUP}';
var IS_IM = '{$im}';
var DOC_ID = '{$doc_id}';
var MAX_CHARS = '{$comment_max_chars}';
var aveabspath = '{$ABS_PATH}';
</script>
<script src="{$ABS_PATH}modules/comment/js/comment.js" type="text/javascript"></script>
{/if} {* <-- Закрывает if $no_read_permission *}
{/if}

View File

@@ -0,0 +1,90 @@
{foreach from=$subcomments item=c}
{* Контейнер комментария: Используем .card для блока, и ms-4 для вложенности *}
<div class="card mb-3 mod_comment_comment{if $c.parent_id} ms-4{/if}">
{* Подсветка (Highlight) *}
{if isset($smarty.request.subaction) && $smarty.request.subaction=='showonly' && isset($smarty.request.comment_id) && $smarty.request.comment_id==$c.Id}
<div class="border border-warning border-3 rounded p-0">
{/if}
<div id="{$c.Id}" class="mod_comment_box">
{* Заголовок комментария: Используем card-header и d-flex для выравнивания метаданных и иконок *}
<div class="card-header mod_comment_header d-flex justify-content-between align-items-center bg-light clearfix">
{* Информация об авторе и дате *}
<div class="mod_comment_author text-muted small me-2">
{* Автор (Иконка fa-user) *}
<i class="fa fa-user 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>
{* Дата публикации (Иконка fa-clock) *}
<span class="ms-2"><i class="fa fa-clock-o me-1"></i> {$c.comment_published}</span>
{* IP-адрес (для Админа) *}
{if $smarty.const.UGROUP==1}
<span class="ms-2 text-secondary">• IP:{$c.comment_author_ip}</span>
{/if}
{* Метка "Изменено" *}
<span class="mod_comment_changed">{if isset($c.comment_changed) && $c.comment_changed > 1} (<span class="text-secondary">{#COMMENT_TEXT_CHANGED#} {$c.comment_changed}</span>){/if}</span>
</div>
{* Иконки действий - Используем p-2 me-3 для интервала и кликабельности *}
<div class="mod_comment_icons d-flex align-items-center flex-shrink-0">
{* Ссылка "Ответить" (fa-reply) *}
{if $c.comment_author_id!=$smarty.session.user_id|default:'*' && (($cancomment==1 && $closed!=1) || $smarty.const.UGROUP==1)}
<a class="mod_comment_answer p-2 me-3 text-primary" href="javascript:void(0);" rel="{$c.Id}" title="{#COMMENT_ANSWER_LINK#}">
<i class="fa fa-reply"></i>
</a>
{/if}
{* ИКОНКА РЕДАКТИРОВАНИЯ (fa-pencil) *}
{if $smarty.const.UGROUP==1 || $c.comment_author_id==$smarty.session.user_id|default:'*'}
<a class="mod_comment_edit p-2 me-3 text-secondary" href="javascript:void(0);" title="{#COMMENT_EDIT_LINK#}">
<i class="fa fa-pencil"></i>
</a>
{/if}
{if $smarty.const.UGROUP==1}
{* Замок (Разблокировать/Заблокировать) *}
{if $c.comment_status!=1}
<a class="mod_comment_unlock p-2 me-3 text-success" href="javascript:void(0);" title="{#COMMENT_UNLOCK_LINK#}">
<i class="fa fa-unlock"></i>
</a>
{else}
<a class="mod_comment_lock p-2 me-3 text-dark" href="javascript:void(0);" title="{#COMMENT_LOCK_LINK#}">
<i class="fa fa-lock"></i>
</a>
{/if}
{* Корзина (Удалить) *}
<a class="mod_comment_delete p-2 text-danger" href="javascript:void(0);" title="{#COMMENT_DELETE_LINK#}">
<i class="fa fa-trash-o"></i>
</a>
{/if}
</div>
</div>
{* Тело комментария: Используем card-body *}
<div class="card-body mod_comment_text{if $smarty.const.UGROUP==1 || $c.comment_author_id==$smarty.session.user_id|default:'*'} editable_text{/if}">{$c.comment_text|escape}</div>
</div>
{* Закрываем подсветку *}
{if isset($smarty.request.subaction) && $smarty.request.subaction=='showonly' && isset($smarty.request.comment_id) && $smarty.request.comment_id==$c.Id}
</div>
{/if}
{* Точка вставки формы ответа *}
<span id="end{$c.Id}"></span>
{* Рекурсивный вызов дочерних комментариев *}
{if isset($comments) && isset($comments[$c.Id])}
<div class="mt-3">
{include file="$subtpl" subcomments=$comments[$c.Id] sub=1}
</div>
{/if}
</div> {* Закрывает mod_comment_comment *}
{/foreach}

BIN
templates/images/edit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

BIN
templates/images/lock.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

BIN
templates/images/reply.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

BIN
templates/images/trash.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

BIN
templates/images/unlock.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

4
templates/index.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
header('Location:/');
exit;
?>