if ($a == 'newpost' && !empty($s) && !empty($q)) // Проверяем, что действие $a равно 'newpost' (новый пост), а также что заданы переменные $s (раздел форума) и $q (ID темы).
{
cot_shield_protect(); // Защита от спама и флудеров с помощью встроенного механизма Cotonti.
Cot::$db->query("SELECT ft_state FROM $db_forum_topics WHERE ft_id = $q")->fetchColumn() && cot_die();
// Выполняем SQL-запрос к базе данных, чтобы получить состояние (ft_state) темы с ID $q.
// Если запрос вернул ненулевое значение (тема закрыта или удалена), вызываем `cot_die()`, завершив выполнение скрипта.
$sql_forums = Cot::$db->query("SELECT fp_id, fp_text, fp_posterid, fp_creation, fp_updated, fp_updater FROM $db_forum_posts
WHERE fp_topicid = $q ORDER BY fp_creation DESC LIMIT 1");
// Запрашиваем последний пост в теме $q, сортируя их по дате создания (fp_creation) в порядке убывания (DESC),
// чтобы получить самый новый пост (LIMIT 1).
if ($row = $sql_forums->fetch()) { // Если удалось получить данные последнего поста:
if (
Cot::$cfg['forums']['antibumpforums'] // Проверяем, включена ли функция защиты от "бампинга" (искусственного поднятия темы).
&& (
(Cot::$usr['id'] == 0 && $row['fp_posterid'] == 0 && $row['fp_posterip'] == Cot::$usr['ip'])
// Если текущий пользователь не авторизован (гость), а IP последнего поста совпадает с его IP, запрещаем отправку нового поста.
|| ($row['fp_posterid'] > 0 && $row['fp_posterid'] == Cot::$usr['id'])
// Если последний пост принадлежит текущему пользователю (авторизованному), запрещаем отправку нового поста.
)
) {
cot_die(); // Завершаем выполнение, если обнаружена попытка "бампинга".
}
$merge = (!Cot::$cfg['forums']['antibumpforums'] && Cot::$cfg['forums']['mergeforumposts'] && $row['fp_posterid'] == Cot::$usr['id']) ? true : false;
// Проверяем, можно ли объединить новый пост с последним постом.
// Это возможно, если:
// - Отключена защита от бампинга (`antibumpforums = false`).
// - Включена настройка объединения сообщений (`mergeforumposts = true`).
// - Последний пост написан текущим пользователем.
$merge = ($merge && Cot::$cfg['forums']['mergetimeout'] > 0 && (($sys['now'] - $row['fp_updated']) > (Cot::$cfg['forums']['mergetimeout'] * 3600))) ? false : $merge;
// Если объединение сообщений разрешено (`$merge = true`), проверяем, не истек ли лимит времени для объединения (`mergetimeout`).
// Если время между обновлением последнего поста (`fp_updated`) и текущим моментом (`sys['now']`) больше лимита (`mergetimeout` в часах),
// то объединение отменяется (`$merge = false`).
} else {
cot_die(); // Если в теме еще нет постов (запрос `$sql_forums` не вернул строк), выполнение скрипта завершается.
}
Объяснение логики:
-
Проверка входных данных
- Убедимся, что действие
$a— этоnewpost(новый пост). - Проверяем, что заданы идентификаторы
$s(раздел форума) и$q(ID темы).
- Убедимся, что действие
-
Защита от спама
- Функция
cot_shield_protect()предотвращает флуд и возможные атаки.
- Функция
-
Проверка состояния темы
- Выполняется SQL-запрос, чтобы проверить состояние темы (
ft_state). - Если тема закрыта (
ft_stateне пустое), то выполнение прекращается.
- Выполняется SQL-запрос, чтобы проверить состояние темы (
-
Получение последнего поста в теме
- Запрос выбирает последний пост в теме (сортировка
ORDER BY fp_creation DESC).
- Запрос выбирает последний пост в теме (сортировка
-
Проверка на "бампинг"
- Если активирована настройка
antibumpforums, проверяется, чтобы пользователь не "поднимал" тему. - Если последний пост сделан этим же пользователем, то новый пост запрещается.
- Если активирована настройка
-
Объединение постов
- Если активирована настройка
mergeforumposts, новый пост объединяется с последним, если его автор тот же. - Однако, если прошло больше времени, чем указано в
mergetimeout, объединение отменяется.
- Если активирована настройка
-
Если тема пуста — выход
- Если в теме еще нет сообщений, выполнение скрипта завершается (
cot_die()).
- Если в теме еще нет сообщений, выполнение скрипта завершается (
$rmsg = array();
// Инициализация массива $rmsg для хранения данных нового поста.
$rmsg['fp_text'] = cot_import('rmsgtext', 'P', 'HTM');
// Импорт текста нового сообщения (из POST-параметра 'rmsgtext'), который будет сохранен как HTML.
$rmsg['fp_updated'] = (int)$sys['now'];
// Устанавливаем время обновления поста (текущее время из системы).
$rmsg['fp_posterip'] = Cot::$usr['ip'];
// Сохраняем IP-адрес пользователя, который создает новый пост.
if (mb_strlen($rmsg['fp_text']) < Cot::$cfg['forums']['minpostlength']) {
// Проверяем, что длина текста сообщения больше минимальной длины, заданной в конфигурации форума.
cot_error('forums_messagetooshort', 'rmsgtext');
// Если длина текста слишком короткая, вызываем ошибку с сообщением 'forums_messagetooshort' для поля 'rmsgtext'.
cot_redirect(cot_url('forums', "m=posts&q=$q&n=last", '#bottom', true));
// Перенаправляем пользователя обратно на страницу форума, на последний пост (с добавлением якоря '#bottom' для прокрутки).
}
if (!empty(Cot::$extrafields[Cot::$db->forum_posts])) {
// Если существуют дополнительные поля для постов форума (поля, определенные в конфигурации форума).
foreach (Cot::$extrafields[Cot::$db->forum_posts] as $exfld) {
// Перебираем все дополнительные поля.
$rmsg['fp_' . $exfld['field_name']] = cot_import_extrafields('rmsg' . $exfld['field_name'], $exfld, 'P', '', 'forums_post_');
// Импортируем значения этих дополнительных полей через функцию cot_import_extrafields и сохраняем их в массив $rmsg.
}
}
foreach (cot_getextplugins('forums.posts.newpost.first') as $pl) {
// Выполняем хуки, если они есть, для действия 'forums.posts.newpost.first' (перед созданием нового поста).
include $pl;
// Подключаем каждый плагин, который расширяет функционал на этом этапе.
}
Объяснение логики:
-
Инициализация массива
- Создается массив
$rmsg, который будет хранить данные нового поста.
- Создается массив
-
Импорт текста поста
- Текст сообщения (
rmsgtext) импортируется с помощью функцииcot_import(). В данном случае, текст обрабатывается как HTML.
- Текст сообщения (
-
Проверка минимальной длины сообщения
- Если длина текста сообщения меньше минимально установленной длины (
minpostlength), пользователю выводится ошибка, и он перенаправляется обратно на форум.
- Если длина текста сообщения меньше минимально установленной длины (
-
Обработка дополнительных полей
- Если в таблице постов форума определены дополнительные поля (например, кастомные поля), они импортируются и добавляются в массив
$rmsg.
- Если в таблице постов форума определены дополнительные поля (например, кастомные поля), они импортируются и добавляются в массив
-
Выполнение хуков
- Если существуют плагины, подключенные к хуку
forums.posts.newpost.first, они выполняются. Это позволяет расширить функциональность при создании нового поста, например, добавлением дополнительной обработки перед сохранением поста.
- Если существуют плагины, подключенные к хуку
if (!cot_error_found()) {
// Проверяем, если нет ошибок, которые были добавлены ранее с помощью cot_error().
if (!$merge) {
// Если пост не нужно сливать с предыдущим (нет режима слияния сообщений).
$rmsg['fp_topicid'] = (int) $q;
// Устанавливаем ID темы (используем значение переменной $q, которое хранит ID темы).
$rmsg['fp_cat'] = $s;
// Устанавливаем категорию форума (с помощью переменной $s).
$rmsg['fp_posterid'] = (int) Cot::$usr['id'];
// Устанавливаем ID пользователя, который создает пост (из информации о пользователе Cot::$usr).
$rmsg['fp_postername'] = Cot::$usr['name'];
// Устанавливаем имя пользователя, который создает пост.
$rmsg['fp_creation'] = (int) $sys['now'];
// Устанавливаем дату создания поста (текущее время системы).
$rmsg['fp_updater'] = 0;
// Устанавливаем поле обновления в 0, так как это новый пост.
Cot::$db->insert($db_forum_posts, $rmsg);
// Вставляем новый пост в таблицу форумов ($db_forum_posts).
$p = Cot::$db->lastInsertId();
// Получаем ID последнего вставленного поста.
cot_forums_resyncTopic($q, Cot::$usr['id']);
// Пересчитываем статистику темы (например, обновляем время последнего поста, количество сообщений и т. д.).
cot_forums_updateStructureCounters($s);
// Обновляем счетчики категорий форума для текущей категории.
} else {
// Если пост нужно слить с предыдущим (режим слияния сообщений).
$p = (int) $row['fp_id'];
// Получаем ID поста, который будет слит (из строки $row).
$gap_base = empty($row['fp_updated']) ? $row['fp_creation'] : $row['fp_updated'];
// Если у предыдущего поста нет времени обновления, используем время его создания. Иначе, используем время последнего обновления.
$updated = sprintf(Cot::$L['forums_mergetime'], cot_build_timegap($gap_base, Cot::$sys['now']));
// Создаем строку, которая показывает разницу во времени между предыдущим и текущим сообщением.
$rmsg['fp_text'] = $row['fp_text'] . cot_rc('forums_code_update', array('updated' => $updated)) . $rmsg['fp_text'];
// Добавляем в текст нового сообщения информацию о времени обновления, а затем добавляем сам текст нового поста.
$rmsg['fp_updater'] = ($row['fp_posterid'] == Cot::$usr['id'] && ($sys['now'] < $row['fp_updated'] + 300) && empty($row['fp_updater']) ) ? '' : Cot::$usr['name'];
// Устанавливаем поле обновления в имя пользователя, если пост обновляется (если прошло меньше 5 минут с последнего обновления).
Cot::$db->update($db_forum_posts, $rmsg, 'fp_id=' . $row['fp_id']);
// Обновляем пост в таблице форумов ($db_forum_posts) с новыми данными из массива $rmsg.
Cot::$db->update($db_forum_topics, array('ft_updated' => $sys['now']), "ft_id = $q");
// Обновляем время последнего поста в теме (таблица $db_forum_topics).
cot_forums_resyncTopic($q, false);
// Пересчитываем статистику темы без изменения ID пользователя.
cot_forums_updateStructureCounters($s);
// Обновляем счетчики категории форума.
}
}
Объяснение логики:
-
Проверка наличия ошибок
- Код выполняется, если нет ошибок, с помощью проверки
!cot_error_found(). Это позволяет убедиться, что до выполнения не произошло ошибок, связанных с данными, переданными в форму.
- Код выполняется, если нет ошибок, с помощью проверки
-
Создание нового поста (если не используется слияние)
- Если не нужно сливать пост с предыдущим (переменная
$mergeравнаfalse), то:- Вставляется новый пост с данными о теме, категории, авторе, времени создания.
- Также выполняется пересинхронизация статистики темы и обновление счетчиков категории.
- Если не нужно сливать пост с предыдущим (переменная
-
Слияние постов (если используется слияние)
- Если активирован режим слияния сообщений (переменная
$mergeравнаtrue), то:- Получаем ID поста, с которым нужно выполнить слияние.
- Вычисляется разница во времени между текущим и предыдущим сообщением.
- В текст нового сообщения добавляется информация о времени обновления.
- Обновляется информация о посте в базе данных, и тема обновляется с новым временем последнего поста.
- Если активирован режим слияния сообщений (переменная
-
Пересинхронизация и обновление статистики
- После добавления или обновления поста, выполняются два важных действия:
- Пересинхронизация статистики темы (
cot_forums_resyncTopic). - Обновление счетчиков категории форума (
cot_forums_updateStructureCounters).
- Пересинхронизация статистики темы (
- После добавления или обновления поста, выполняются два важных действия:
cot_extrafield_movefiles();
// Перемещает файлы, связанные с дополнительными полями (например, изображения или прикрепленные файлы), если они были загружены при создании нового поста.
/* === Hook === */
foreach (cot_getextplugins('forums.posts.newpost.done') as $pl) {
include $pl;
}
// Запускаются все внешние плагины, связанные с окончанием создания нового поста в форуме. Эти плагины могут добавлять дополнительный функционал.
/* ===== */
if (Cot::$cache) {
// Проверяем, включено ли кэширование в системе.
if (Cot::$cfg['cache_forums']) {
// Если кэширование для форума включено.
// Cot::$cache->page->clearByUri(cot_url('forums', ['m' => 'posts', 'q' => $q]));
// Удаление страницы кэша по URL форума и текущей теме (эта строка закомментирована).
Cot::$cache->static->clearByUri(cot_url('forums'));
// Очистка кэша для всего форума, если обновился какой-либо контент.
}
if (Cot::$cfg['cache_index']) {
// Если включено кэширование главной страницы.
Cot::$cache->static->clear('index');
// Очистка кэша для главной страницы сайта.
}
}
cot_shield_update(30, "New post");
// Обновление защиты от повторных отправок формы (например, защиты от спама). Параметры определяют период (30 секунд) и сообщение.
cot_redirect(
cot_url('forums', ['m' => 'posts', 'q' => $q, 'n' => 'last'], '#bottom', true)
);
// Перенаправление на страницу форума, где отображается последний пост в теме. URL включает параметры: ID темы ($q) и переход к последнему сообщению ('n' => 'last').
// #bottom указывает браузеру прокрутить страницу вниз к последнему сообщению.