diff --git a/class/comment.php b/class/comment.php
index e1d7b48..c9cf524 100644
--- a/class/comment.php
+++ b/class/comment.php
@@ -338,51 +338,39 @@ function commentListShow($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;
- // Получаем настройки модуля для проверки обязательных полей
$settings = $this->_commentSettingsGet();
- // Если запрос пришел не ajax запросом, тогда формируем ссылку для последующего редиректа
if (! $ajax)
{
$link = rewrite_link(base64_decode($page));
}
- // --- ПРОВЕРКА ОБЯЗАТЕЛЬНЫХ УНИВЕРСАЛЬНЫХ ПОЛЕЙ ---
- // Проверка Поля 1 (хранится в website)
+ // --- ПРОВЕРКА ОБЯЗАТЕЛЬНЫХ ПОЛЕЙ ---
if ($settings['comment_show_f1'] == 1 && $settings['comment_req_f1'] == 1 && empty($_POST['comment_author_website']))
{
if ($ajax) { echo 'error_req_f1'; exit; }
else { header('Location:' . $link . '#end'); exit; }
}
- // Проверка Поля 2 (хранится в city)
if ($settings['comment_show_f2'] == 1 && $settings['comment_req_f2'] == 1 && empty($_POST['comment_author_city']))
{
if ($ajax) { echo 'error_req_f2'; exit; }
else { header('Location:' . $link . '#end'); exit; }
}
- // -------------------------------------------------
- // Если в настройках модуля включено использование защитного кода, тогда
+ // --- АНТИСПАМ ---
if ($settings['comment_use_antispam'] == 1)
{
- // Если введенный пользователем защитный код неверен, тогда выполняем обновление кода
if (! (isset($session_captcha) && $session_captcha == $secure_code))
{
unset($_SESSION['captcha_keystring']);
-
- if ($ajax)
- {
- echo 'wrong_securecode';
- }
- else
- {
+ if ($ajax) { echo 'wrong_securecode'; }
+ else {
if(isset($GLOBALS['tmpl']))$GLOBALS['tmpl']->assign("wrongSecureCode", 1);
header('Location:' . $link . '#end');
}
@@ -391,47 +379,65 @@ function commentListShow($tpl_dir)
unset($_SESSION['captcha_keystring']);
}
- // Определяем флаг модерации комментариев
$comment_status = ($settings['comment_need_approve'] == 1) ? 0 : 1;
- // Если комментарии разрешены, тогда получаем все данные, который пользователь указал в форме
if ($settings['comment_active'] == 1
&& !empty($_POST['comment_text'])
&& !empty($_POST['comment_author_name'])
&& in_array($user_group, explode(',', $settings['comment_user_groups'])))
{
- // --- !!! НАЧАЛО БЕЗОПАСНОСТИ: Санитаризация и Экранирование !!! ---
-
+ // --- ОБРАБОТКА ЗАГРУЗКИ ИЗОБРАЖЕНИЯ ---
+ $comment_file_name = '';
+ if ($settings['comment_allow_files'] == 1 && isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK)
+ {
+ $upload_path = BASE_DIR . '/uploads/comments/';
+
+ // Создаем папку, если её нет
+ if (!is_dir($upload_path)) {
+ @mkdir($upload_path, 0775, true);
+ // Создаем пустой index.html для безопасности (запрет листинга папки)
+ @file_put_contents($upload_path . 'index.html', '');
+ }
+
+ $allowed_mime = ['image/jpeg', 'image/png', 'image/gif'];
+ $file_info = getimagesize($_FILES['comment_image']['tmp_name']);
+ $file_size = $_FILES['comment_image']['size'];
+
+ // Проверка: это реально картинка и её размер не более 2Мб
+ if ($file_info && in_array($file_info['mime'], $allowed_mime) && $file_size <= 2 * 1024 * 1024)
+ {
+ $ext = pathinfo($_FILES['comment_image']['name'], PATHINFO_EXTENSION);
+ $comment_file_name = time() . '_' . rand(100, 999) . '.' . $ext;
+
+ if (!move_uploaded_file($_FILES['comment_image']['tmp_name'], $upload_path . $comment_file_name)) {
+ $comment_file_name = ''; // Сбрасываем, если не удалось переместить
+ }
+ }
+ }
+
+ // --- ПОДГОТОВКА ДАННЫХ ДЛЯ БД ---
$new_comment['parent_id'] = (int)($_POST['parent_id'] ?? 0);
$new_comment['document_id'] = (int)($_POST['doc_id'] ?? 0);
-
$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'] ?? '')); // Поле 2
- $new_comment['comment_author_website'] = addslashes(strip_tags($_POST['comment_author_website'] ?? '')); // Поле 1
-
+ $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'] ?? '';
+ $new_comment['comment_file'] = $comment_file_name; // НАША НОВАЯ КОЛОНКА
+ $comment_text_raw = $_POST['comment_text'] ?? '';
$comment_max_chars = $settings['comment_max_chars'];
$comment_max_chars = (!empty($comment_max_chars) && $comment_max_chars > 10) ? $comment_max_chars : 200;
$comment_text_clean = strip_tags(stripslashes($comment_text_raw));
-
$comment_text_cut = mb_substr($comment_text_clean, 0, $comment_max_chars);
$comment_text_cut .= (mb_strlen($comment_text_clean) > $comment_max_chars) ? '…' : '';
-
$new_comment['comment_text'] = addslashes($comment_text_cut);
- // --- !!! КОНЕЦ БЕЗОПАСНОСТИ !!! ---
-
- // Выполняем запрос к БД на добавление комментария
+ // Выполняем запрос
$AVE_DB->Query("
INSERT INTO " . PREFIX . "_module_comment_info
(`" . implode('`,`', array_keys($new_comment)) ."`)
@@ -440,7 +446,7 @@ function commentListShow($tpl_dir)
");
$new_comment['Id'] = $AVE_DB->InsertId();
- // Получаем настройки и уведомляем админа (код без изменений...)
+ // --- УВЕДОМЛЕНИЕ АДМИНА ---
$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'];
@@ -455,18 +461,9 @@ function commentListShow($tpl_dir)
if ($ajax)
{
- if (isset($new_comment['comment_author_id']) && $new_comment['comment_author_id'] > 0)
- {
- $new_comment['avatar'] = getAvatar($new_comment['comment_author_id'], 48);
- }
- else
- {
- $new_comment['avatar'] = '';
- }
+ $new_comment['avatar'] = (isset($new_comment['comment_author_id']) && $new_comment['comment_author_id'] > 0) ? getAvatar($new_comment['comment_author_id'], 48) : '';
$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']);
-
- // Добавляем настройки полей в массив комментария, чтобы шаблон дерева мог их использовать
$new_comment['settings'] = $settings;
$subcomments[] = $new_comment;
@@ -480,99 +477,111 @@ function commentListShow($tpl_dir)
exit;
}
- /**
+/**
* Метод, предназначенный для редактирования комментария в Публичной части
- *
- * @param int $comment_id - идентификатор комментария
+ * с поддержкой удаления и замены изображения
*/
- function commentPostEdit($comment_id)
+function commentPostEdit($comment_id)
{
global $AVE_DB;
$user_id = $_SESSION['user_id'] ?? null;
$user_group = UGROUP ?? 0;
$post_text = $_POST['text'] ?? '';
-
+
+ $delete_file = (isset($_POST['delete_image']) && $_POST['delete_image'] == 1);
if (empty($user_id)) exit;
$comment_id = intval(preg_replace('/\D/', '', $comment_id));
- // Выполняем запрос к БД и получаем всю информацию о комментарии, а также ряд значений из настроек модуля
$row = $AVE_DB->Query("
SELECT
msg.parent_id,
msg.comment_text,
+ msg.comment_file,
cmnt.comment_user_groups,
cmnt.comment_max_chars,
cmnt.comment_need_approve
FROM
" . PREFIX . "_module_comment_info AS msg,
" . PREFIX . "_module_comments AS cmnt
- WHERE comment_active = '1'
+ WHERE cmnt.comment_active = '1'
AND msg.Id = '" . $comment_id . "'
- " . (($user_group != 1) ? "AND comment_author_id = " . $user_id : '') . "
+ " . (($user_group != 1) ? "AND msg.comment_author_id = " . (int)$user_id : '') . "
")->FetchAssocArray();
- // Если данные получены
if ($row !== false)
{
-
- $comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 20;
-
+ $comment_max_chars = ($row['comment_max_chars'] != '' && $row['comment_max_chars'] > 10) ? $row['comment_max_chars'] : 200;
+
+ // --- ТВОЯ ОРИГИНАЛЬНАЯ ОБРАБОТКА ТЕКСТА ---
$comment_text = $post_text;
-
- // --- !!! НАЧАЛО ИСПРАВЛЕНИЯ УСТАРЕВШИХ ОПЕРАТОРОВ (/e) !!! ---
-
- // Безопасная замена: преобразуем hex-сущности
- $comment_text = preg_replace_callback('/([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 = preg_replace_callback('/([0-9a-f]{1,7});/', function($matches) { return chr(hexdec($matches[1])); }, $comment_text);
+ $comment_text = preg_replace_callback('/([0-9]{1,7});/', function($matches) { return chr($matches[1]); }, $comment_text);
$comment_text = stripslashes($comment_text);
$comment_text = str_replace(array("
\n", "
\n", "
\n"), "\n", $comment_text);
-
- // Санитаризация: убираем теги перед обрезкой
- $comment_text = strip_tags($comment_text);
-
- $comment_text = mb_substr($comment_text, 0, $comment_max_chars-1);
- $message_length = mb_strlen($comment_text);
- $comment_text .= ($message_length > $comment_max_chars) ? '…' : '';
+ $comment_text = strip_tags($comment_text);
+
+ $comment_text_cut = mb_substr($comment_text, 0, $comment_max_chars);
+ $message_length = mb_strlen($comment_text_cut);
+ if (mb_strlen($comment_text) > $comment_max_chars) $comment_text_cut .= '…';
-
- // Если группа текущего пользователя совпадает с разрешенной группой в настройках модуля, тогда
- // выполняем запрос к БД на обновление информации.
- if (in_array($user_group, explode(',', $row['comment_user_groups'])) && $message_length > 3)
+ $is_admin = ($user_group == 1);
+ $is_allowed_group = in_array($user_group, explode(',', $row['comment_user_groups']));
+
+ if ($is_admin || ($is_allowed_group && $message_length > 3))
{
+ $upload_dir = BASE_DIR . '/uploads/comments/';
+ $new_file_sql = "";
+
+ // 1. ЗАМЕНА ФАЙЛА
+ if (isset($_FILES['comment_image']) && $_FILES['comment_image']['error'] == UPLOAD_ERR_OK) {
+ if (!is_dir($upload_dir)) @mkdir($upload_dir, 0777, true);
+
+ // Удаляем старый
+ if (!empty($row['comment_file'])) {
+ $old_file = $upload_dir . $row['comment_file'];
+ if (file_exists($old_file)) @unlink($old_file);
+ }
+
+ $file_ext = strtolower(pathinfo($_FILES['comment_image']['name'], PATHINFO_EXTENSION));
+ $new_file_name = 'comm_' . time() . '_' . rand(100, 999) . '.' . $file_ext;
+
+ if (move_uploaded_file($_FILES['comment_image']['tmp_name'], $upload_dir . $new_file_name)) {
+ // Используем addslashes вместо Escape_String для надежности
+ $new_file_sql = ", comment_file = '" . addslashes($new_file_name) . "'";
+ $delete_file = false;
+ }
+ }
+ // 2. УДАЛЕНИЕ ФАЙЛА
+ elseif ($delete_file && !empty($row['comment_file'])) {
+ $old_file = $upload_dir . $row['comment_file'];
+ if (file_exists($old_file)) @unlink($old_file);
+ $new_file_sql = ", comment_file = ''";
+ }
+
+ // ОБНОВЛЕНИЕ БАЗЫ
$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) . "'
+ comment_text = '" . addslashes($comment_text_cut) . "'
+ $new_file_sql
WHERE
Id = '" . $comment_id . "'
");
- // Преобразуем HTML теги в HTML сущности перед выводом (для XSS)
- echo htmlspecialchars($comment_text, ENT_QUOTES);
+ echo htmlspecialchars($comment_text_cut, ENT_QUOTES);
exit;
}
-
- // Если редактирование не прошло, выводим оригинальный текст (экранированный)
echo htmlspecialchars($row['comment_text'], ENT_QUOTES);
}
exit;
}
- /**
+/**
* Метод, предназначенный для удаления комментария. Если комментарий содержал какие-либо ответы на него,
* то все ответы также будут удалены вместе с родительским комментарием.
*
@@ -582,7 +591,27 @@ function commentListShow($tpl_dir)
{
global $AVE_DB;
- $comment_id = (int)$comment_id; // Убедимся, что это целое число
+ $comment_id = (int)$comment_id;
+ $upload_dir = BASE_DIR . '/uploads/comments/';
+
+ // --- 1. ОЧИСТКА ФАЙЛОВ ПЕРЕД УДАЛЕНИЕМ ИЗ БД ---
+
+ // Ищем файлы основного комментария и всех вложенных ответов
+ $res = $AVE_DB->Query("
+ SELECT comment_file
+ FROM " . PREFIX . "_module_comment_info
+ WHERE (Id = '" . $comment_id . "' OR parent_id = '" . $comment_id . "')
+ AND comment_file != ''
+ ");
+
+ while ($row = $res->FetchAssocArray()) {
+ $file_path = $upload_dir . $row['comment_file'];
+ if (file_exists($file_path)) {
+ @unlink($file_path); // Удаляем файл с сервера
+ }
+ }
+
+ // --- 2. ТВОЙ ОРИГИНАЛЬНЫЙ КОД УДАЛЕНИЯ ИЗ БД ---
// Выполняем запрос к БД на удаление родительского комментария
$AVE_DB->Query("
@@ -884,6 +913,7 @@ function commentListShow($tpl_dir)
$AVE_Template->assign('content', $AVE_Template->fetch($tpl_dir . $this->_admin_edit_link_tpl));
}
+/**
/**
/**
* Метод, предназначенный для управления настройками модуля
@@ -894,7 +924,6 @@ function commentListShow($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();
@@ -907,7 +936,10 @@ function commentListShow($tpl_dir)
$post_use_page_nav = $_POST['comment_use_page_nav'] ?? 0;
$post_page_nav_count = $_POST['comment_page_nav_count'] ?? 0;
- // НОВОЕ: Получение данных для универсальных полей
+ // НОВОЕ: Настройка разрешения загрузки файлов
+ $post_allow_files = $_POST['comment_allow_files'] ?? 0;
+
+ // Данные для универсальных полей
$post_show_f1 = $_POST['comment_show_f1'] ?? 0;
$post_req_f1 = $_POST['comment_req_f1'] ?? 0;
$post_name_f1 = $_POST['comment_name_f1'] ?? '';
@@ -916,12 +948,10 @@ function commentListShow($tpl_dir)
$post_req_f2 = $_POST['comment_req_f2'] ?? 0;
$post_name_f2 = $_POST['comment_name_f2'] ?? '';
- // Если в запросе содержится подзапрос на сохранение данных
if ($request_sub == 'save')
{
$max_chars = (empty($post_max_chars) || $post_max_chars < 50) ? 50 : $post_max_chars;
- // Очищаем названия полей вручную, так как EscapeString не поддерживается
$clean_name_f1 = htmlspecialchars(stripslashes($post_name_f1), ENT_QUOTES);
$clean_name_f2 = htmlspecialchars(stripslashes($post_name_f2), ENT_QUOTES);
@@ -936,6 +966,7 @@ function commentListShow($tpl_dir)
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 . "',
+ comment_allow_files = '" . (int)$post_allow_files . "',
comment_show_f1 = '" . (int)$post_show_f1 . "',
comment_req_f1 = '" . (int)$post_req_f1 . "',
comment_name_f1 = '" . $clean_name_f1 . "',
@@ -947,15 +978,11 @@ function commentListShow($tpl_dir)
");
}
- // Получаем список всех настроек модуля
$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));
}
diff --git a/js/comment.js b/js/comment.js
index 0386803..6130a73 100644
--- a/js/comment.js
+++ b/js/comment.js
@@ -3,22 +3,13 @@
==================================================================== */
(function waitForJQuery() {
if (typeof jQuery === 'undefined') {
- // jQuery еще не загружен. Ждем 10 мс и проверяем снова.
setTimeout(waitForJQuery, 10);
} else {
- // JQuery загружен, можно запускать основной код.
-
(function($){
- // ====================================================================
- // КОРРЕКЦИЯ: ПРИНУДИТЕЛЬНАЯ ФИКСАЦИЯ HTTPS (если требуется)
- // ====================================================================
if (typeof aveabspath !== 'undefined') {
- // ПРЕДУПРЕЖДЕНИЕ: Если aveabspath должен быть динамическим,
- // удалите эту строку.
// aveabspath = 'https://bag.local/';
}
- // ====================================================================
/* Limit symbols */
(function($){
@@ -65,15 +56,12 @@
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');
}
@@ -86,12 +74,10 @@
if (action === 'answer'){
$('#parent_id').val(cid);
- // Перемещаем форму и показываем ее
$('#mod_comment_new').insertBefore('#end' + cid).show();
return;
}
- // Действия администратора
if (UGROUP == 1){
$.get(aveabspath + 'index.php', {
module: 'comment',
@@ -100,27 +86,21 @@
Id: cid
}, function(){
if (action === 'delete'){
- // Удаляем блок комментария
$(obj).parents('.mod_comment_comment').eq(0).remove();
}
if (action === 'open'){
- // 1. Меняем ID и текст
var $openButton = $('#mod_comment_open');
$openButton.attr('id', 'mod_comment_close').html(' ' + COMMENT_SITE_CLOSE);
- // 2. МЕНЯЕМ КЛАССЫ СТИЛЕЙ
$openButton.removeClass('btn-outline-success').addClass('btn-outline-danger');
}
if (action === 'close'){
- // 1. Меняем ID и текст
var $closeButton = $('#mod_comment_close');
$closeButton.attr('id', 'mod_comment_open').html(' ' + COMMENT_SITE_OPEN);
- // 2. МЕНЯЕМ КЛАССЫ СТИЛЕЙ
$closeButton.removeClass('btn-outline-danger').addClass('btn-outline-success');
}
- // Динамическая смена иконки замка для отдельного комментария
if (action === 'unlock'){
$(obj).removeClass('mod_comment_unlock text-success')
.addClass('mod_comment_lock text-dark')
@@ -139,27 +119,22 @@
}
}
-function validate(formData, jqForm, options){
+ 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;
}
- // Проверка email
if (form.comment_author_email && !form.comment_author_email.value){
alert(COMMENT_ERROR_EMAIL);
$(form.comment_author_email).focus();
return false;
}
- // --- НОВОЕ: Проверка динамических полей ---
-
- // Проверка Поля №1 (website)
if (typeof REQ_F1 !== 'undefined' && REQ_F1 == '1') {
if (form.comment_author_website && !form.comment_author_website.value) {
alert("Пожалуйста, заполните поле: " + NAME_F1);
@@ -168,7 +143,6 @@ function validate(formData, jqForm, options){
}
}
- // Проверка Поля №2 (city)
if (typeof REQ_F2 !== 'undefined' && REQ_F2 == '1') {
if (form.comment_author_city && !form.comment_author_city.value) {
alert("Пожалуйста, заполните поле: " + NAME_F2);
@@ -176,16 +150,13 @@ function validate(formData, jqForm, options){
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();
@@ -195,35 +166,60 @@ function validate(formData, jqForm, options){
return true;
}
+ // Функция для создания формы редактирования
+ function createEditForm(cid, revert, targetElement) {
+ var currentImg = $('#' + cid).find('.mod_comment_attached_image img').attr('src');
+ var deletePhotoHtml = '';
+
+ if (currentImg) {
+ deletePhotoHtml = '
' + COMMENT_CHARS_LEFT + '
'; + var buttonSave = ' '; + var buttonReset = ''; + + targetElement.after( + '' + COMMENT_CHARS_LEFT + '
'; - var buttonSave = ' '; - var buttonReset = ''; - - $this.after( - '' + COMMENT_CHARS_LEFT + '
'; - var buttonSave = ' '; - var buttonReset = ''; - - commentTextBlock.after( - '/uploads/comments/
+