<?php
namespace App\Controllers\Forum;
use App\Controllers\BaseController;
use App\Models\File;
use App\Models\Polling;
use App\Models\Post;
use App\Models\Bookmark;
use App\Models\Topic;
use App\Models\User;
use App\Models\Vote;
use App\Models\Flood;
use App\Classes\Request;
use App\Classes\Validator;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Database\Query\JoinClause;
class TopicController extends BaseController
{
/**
* Главная страница
*/
public function index($id)
{
$topic = Topic::query()
->select('topics.*', 'bookmarks.count_posts as bookmark_posts')
->where('topics.id', $id)
->leftJoin('bookmarks', function (JoinClause $join) {
$join->on('topics.id', '=', 'bookmarks.topic_id')
->where('bookmarks.user_id', '=', getUser('id'));
})
->with('forum.parent')
->first();
if (! $topic) {
abort(404, 'Данной темы не существует!');
}
$total = Post::query()->where('topic_id', $topic->id)->count();
$page = paginate(setting('forumpost'), $total);
$posts = Post::query()
->select('posts.*', 'pollings.vote')
->where('topic_id', $topic->id)
->leftJoin('pollings', function (JoinClause $join) {
$join->on('posts.id', 'pollings.relate_id')
->where('pollings.relate_type', Post::class)
->where('pollings.user_id', getUser('id'));
})
->with('files', 'user', 'editUser')
->offset($page->offset)
->limit($page->limit)
->orderBy('created_at')
->get();
if (getUser()) {
if ($topic->count_posts > $topic->bookmark_posts) {
Bookmark::query()
->where('topic_id', $topic->id)
->where('user_id', getUser('id'))
->update(['count_posts' => $topic->count_posts]);
}
}
// Кураторы
if ($topic->moderators) {
$topic->curators = User::query()->whereIn('id', explode(',', $topic->moderators))->get();
$topic->isModer = $topic->curators->where('id', getUser('id'))->isNotEmpty();
}
// Голосование
$vote = Vote::query()->where('topic_id', $topic->id)->first();
if ($vote) {
$vote->poll = $vote->pollings()
->where('user_id', getUser('id'))
->first();
if ($vote->answers->isNotEmpty()) {
$results = array_pluck($vote->answers, 'result', 'answer');
$max = max($results);
arsort($results);
$vote->voted = $results;
$vote->sum = ($vote->count > 0) ? $vote->count : 1;
$vote->max = ($max > 0) ? $max : 1;
}
}
return view('forum/topic', compact('topic', 'posts', 'page', 'vote'));
}
/**
* Создание сообщения
*/
public function create($id)
{
$msg = check(Request::input('msg'));
$token = check(Request::input('token'));
if (! $user = getUser()) {
abort(403, 'Авторизуйтесь для добавления сообщения!');
}
$topic = Topic::query()
->select('topics.*', 'forums.parent_id')
->where('topics.id', $id)
->leftJoin('forums', 'topics.forum_id', '=', 'forums.id')
->first();
if (! $topic) {
abort(404, 'Выбранная вами тема не существует, возможно она была удалена!');
}
$validator = new Validator();
$validator->equal($token, $_SESSION['token'], ['msg' => 'Неверный идентификатор сессии, повторите действие!'])
->empty($topic->closed, ['msg' => 'Запрещено писать в закрытую тему!'])
->equal(Flood::isFlood(), true, ['msg' => 'Антифлуд! Разрешается отправлять сообщения раз в ' . Flood::getPeriod() . ' сек!'])
->length($msg, 5, setting('forumtextlength'), ['msg' => 'Слишком длинное или короткое сообщение!']);
// Проверка сообщения на схожесть
$post = Post::query()->where('topic_id', $topic->id)->orderBy('id', 'desc')->first();
$validator->notEqual($msg, $post['text'], ['msg' => 'Ваше сообщение повторяет предыдущий пост!']);
if ($validator->isValid()) {
$msg = antimat($msg);
if (
$post &&
$post->created_at + 600 > SITETIME &&
getUser('id') === $post->user_id &&
(utfStrlen($msg) + utfStrlen($post->text) <= setting('forumtextlength'))
) {
$newpost = $post->text . "\n\n" . '[i][size=1]Добавлено через ' . makeTime(SITETIME - $post->created_at) . ' сек.[/size][/i]' . "\n" . $msg;
$post->update([
'text' => $newpost,
]);
} else {
$post = Post::query()->create([
'topic_id' => $topic->id,
'user_id' => getUser('id'),
'text' => $msg,
'created_at' => SITETIME,
'ip' => getIp(),
'brow' => getBrowser(),
]);
$user->update([
'allforum' => DB::raw('allforum + 1'),
'point' => DB::raw('point + 1'),
'money' => DB::raw('money + 5'),
]);
$topic->update([
'count_posts' => DB::raw('count_posts + 1'),
'last_post_id' => $post->id,
'updated_at' => SITETIME,
]);
$topic->forum->update([
'count_posts' => DB::raw('count_posts + 1'),
'last_topic_id' => $topic->id,
]);
// Обновление родительского форума
if ($topic->forum->parent_id) {
$topic->forum->parent->update([
'last_topic_id' => $topic->id,
]);
}
}
// Рассылка уведомлений в приват
$parseText = preg_replace('|\[quote(.*?)\](.*?)\[/quote\]|s', '', $msg);
preg_match_all('|\[b\](.*?)\[\/b\]|s', $parseText, $matches);
if (isset($matches[1])) {
$usersAnswer = array_unique($matches[1]);
foreach ($usersAnswer as $login) {
if ($login == getUser('login')) {
continue;
}
$user = User::query()->where('login', $login)->first();
if ($user && $user->notify) {
sendPrivate($user, getUser(), 'Пользователь ' . getUser('login') . ' ответил вам в теме [url=' . siteUrl() . '/topic/' . $topic->id . '/' . $post->id . ']' . $topic->title . '[/url]' . PHP_EOL . 'Текст сообщения: ' . $msg);
}
}
}
// Загрузка файла
if (! empty($_FILES['file']['name'])) {
if (getUser('point') >= setting('forumloadpoints')) {
if (is_uploaded_file($_FILES['file']['tmp_name'])) {
$filename = check($_FILES['file']['name']);
$filename = (!isUtf($filename)) ? utfLower(winToUtf($filename)) : utfLower($filename);
$filesize = $_FILES['file']['size'];
if ($filesize > 0 && $filesize <= setting('forumloadsize')) {
$arrext = explode(',', setting('forumextload'));
$ext = getExtension($filename);
if (in_array($ext, $arrext, true)) {
if (utfStrlen($filename) > 50) {
$filename = utfSubstr($filename, 0, 45) . '.' . $ext;
}
if (! file_exists(HOME . '/uploads/forum/' . $topic->id)) {
$old = umask(0);
mkdir(HOME . '/uploads/forum/' . $topic->id, 0777, true);
umask($old);
}
$num = 0;
$hash = $post->id . '.' . $ext;
while (file_exists(HOME . '/uploads/forum/' . $topic->id . '/' . $hash)) {
$num++;
$hash = $post->id . '_' . $num . '.' . $ext;
}
move_uploaded_file($_FILES['file']['tmp_name'], HOME . '/uploads/forum/' . $topic->id . '/' . $hash);
File::query()->create([
'relate_type' => Post::class,
'relate_id' => $post->id,
'hash' => $hash,
'name' => $filename,
'size' => $filesize,
'user_id' => getUser('id'),
'created_at' => SITETIME,
]);
} else {
$fileError = 'Файл не загружен! Недопустимое расширение!';
}
} else {
$fileError = 'Файл не загружен! Максимальный размер ' . formatSize(setting('forumloadsize')) . '!';
}
} else {
$fileError = 'Ошибка! Не удалось загрузить файл!';
}
} else {
$fileError = 'Ошибка! У вас недостаточно актива для загрузки файлов!';
}
if (isset($fileError)) {
setFlash('danger', $fileError);
}
}
setFlash('success', 'Сообщение успешно добавлено!');
} else {
setInput(Request::all());
setFlash('danger', $validator->getErrors());
}
redirect('/topic/end/' . $topic->id);
}
/**
* Удаление сообщений
*/
public function delete($id)
{
$token = check(Request::input('token'));
$del = intar(Request::input('del'));
$page = int(Request::input('page'));
$topic = Topic::query()->find($id);
if (! $topic) {
abort(404, 'Данной темы не существует!');
}
$isModer = in_array(getUser('id'), explode(',', $topic->moderators)) ? true : false;
$validator = new Validator();
$validator->equal($token, $_SESSION['token'], 'Неверный идентификатор сессии, повторите действие!')
->true(getUser(), 'Для закрытия тем необходимо авторизоваться')
->notEmpty($del, 'Отстутствуют выбранные сообщения для удаления!')
->empty($topic->closed, 'Редактирование невозможно. Данная тема закрыта!')
->equal($isModer, true, 'Удалять сообщения могут только кураторы темы!');
if ($validator->isValid()) {
// ------ Удаление загруженных файлов -------//
$files = File::query()
->where('relate_type', Post::class)
->whereIn('relate_id', $del)
->get();
if ($files->isNotEmpty()) {
foreach ($files as $file) {
deleteFile(UPLOADS . '/forum/' . $topic->id . '/' . $file->hash);
$file->delete();
}
}
$delPosts = Post::query()->whereIn('id', $del)->delete();
$topic->decrement('count_posts', $delPosts);
$topic->forum->decrement('count_posts', $delPosts);
setFlash('success', 'Выбранные сообщения успешно удалены!');
} else {
setFlash('danger', $validator->getErrors());
}
redirect('/topic/' . $topic->id . '?page=' . $page);
}
/**
* Закрытие темы
*/
public function close($id)
{
$token = check(Request::input('token'));
$topic = Topic::query()->find($id);
$validator = new Validator();
$validator->equal($token, $_SESSION['token'], 'Неверный идентификатор сессии, повторите действие!')
->true(getUser(), 'Для закрытия тем необходимо авторизоваться')
->gte(getUser('point'), setting('editforumpoint'), 'Для закрытия тем вам необходимо набрать ' . plural(setting('editforumpoint'), setting('scorename')) . '!')
->notEmpty($topic, 'Выбранная вами тема не существует, возможно она была удалена!')
->equal($topic['user_id'], getUser('id'), 'Вы не автор данной темы!')
->empty($topic->closed, 'Данная тема уже закрыта!');
if ($validator->isValid()) {
$topic->update(['closed' => 1]);
$vote = Vote::query()->where('topic_id', $topic->id)->first();
if ($vote) {
$vote->closed = 1;
$vote->save();
$vote->pollings()->delete();
}
setFlash('success', 'Тема успешно закрыта!');
} else {
setFlash('danger', $validator->getErrors());
}
redirect('/topic/' . $topic->id);
}
/**
* Редактирование темы
*/
public function edit($id)
{
if (! getUser()) {
abort(403, 'Авторизуйтесь для изменения темы!');
}
if (getUser('point') < setting('editforumpoint')) {
abort('default', 'У вас недостаточно актива для изменения темы!');
}
$topic = Topic::query()->find($id);
if (! $topic) {
abort(404, 'Выбранная вами тема не существует, возможно она была удалена!');
}
if ($topic->user_id !== getUser('id')) {
abort('default', 'Изменение невозможно, вы не автор данной темы!');
}
if ($topic->closed) {
abort('default', ' Изменение невозможно, данная тема закрыта!');
}
$post = Post::query()->where('topic_id', $topic->id)
->orderBy('id')
->first();
if (Request::isMethod('post')) {
$token = check(Request::input('token'));
$title = check(Request::input('title'));
$msg = check(Request::input('msg'));
$validator = new Validator();
$validator->equal($token, $_SESSION['token'], 'Неверный идентификатор сессии, повторите действие!')
->length($title, 5, 50, ['title' => 'Слишком длинное или короткое название темы!']);
if ($post) {
$validator->length($msg, 5, setting('forumtextlength'), ['msg' => 'Слишком длинный или короткий текст сообщения!']);
}
if ($validator->isValid()) {
$title = antimat($title);
$msg = antimat($msg);
$topic->update(['title' => $title]);
if ($post) {
$post->update([
'text' => $msg,
'edit_user_id' => getUser('id'),
'updated_at' => SITETIME,
]);
}
setFlash('success', 'Тема успешно изменена!');
redirect('/topic/' . $topic->id);
} else {
setInput(Request::all());
setFlash('danger', $validator->getErrors());
}
}
return view('forum/topic_edit', compact('post', 'topic'));
}
/**
* Редактирование сообщения
*/
public function editPost($id)
{
$page = int(Request::input('page'));
if (! getUser()) {
abort(403, 'Авторизуйтесь для изменения сообщения!');
}
$post = Post::query()
->select('posts.*', 'moderators', 'closed')
->leftJoin('topics', 'posts.topic_id', '=', 'topics.id')
->where('posts.id', $id)
->first();
$isModer = in_array(getUser('id'), explode(',', $post->moderators)) ? true : false;
if (! $post) {
abort(404, 'Данного сообщения не существует!');
}
if ($post->closed) {
abort('default', 'Редактирование невозможно, данная тема закрыта!');
}
if (! $isModer && $post->user_id != getUser('id')) {
abort('default', 'Редактировать сообщения может только автор или кураторы темы!');
}
if (! $isModer && $post->created_at + 600 < SITETIME) {
abort('default', 'Редактирование невозможно, прошло более 10 минут!');
}
if (Request::isMethod('post')) {
$token = check(Request::input('token'));
$msg = check(Request::input('msg'));
$delfile = intar(Request::input('delfile'));
$validator = new Validator();
$validator->equal($token, $_SESSION['token'], 'Неверный идентификатор сессии, повторите действие!')
->length($msg, 5, setting('forumtextlength'), ['msg' => 'Слишком длинное или короткое сообщение!']);
if ($validator->isValid()) {
$msg = antimat($msg);
$post->update([
'text' => $msg,
'edit_user_id' => getUser('id'),
'updated_at' => SITETIME,
]);
// ------ Удаление загруженных файлов -------//
if ($delfile) {
$files = File::query()
->where('relate_type', Post::class)
->where('relate_id', $post->id)
->whereIn('id', $delfile)
->get();
if ($files->isNotEmpty()) {
foreach ($files as $file) {
deleteFile(UPLOADS . '/forum/' . $post->topic_id . '/' . $file->hash);
$file->delete();
}
}
}
setFlash('success', 'Сообщение успешно отредактировано!');
redirect('/topic/' . $post->topic_id . '?page=' . $page);
} else {
setInput(Request::all());
setFlash('danger', $validator->getErrors());
}
}
return view('forum/topic_edit_post', compact('post', 'page'));
}
/**
* Голосование
*/
public function vote($id)
{
if (! getUser()) {
abort(403, 'Авторизуйтесь для голосования!');
}
$vote = Vote::query()->where('topic_id', $id)->first();
if (! $vote) {
abort(404, 'Голосование не найдено!');
}
$token = check(Request::input('token'));
$poll = int(Request::input('poll'));
$page = int(Request::input('page'));
$validator = new Validator();
$validator->equal($token, $_SESSION['token'], 'Неверный идентификатор сессии, повторите действие!');
if ($vote->closed) {
$validator->addError('Данное голосование закрыто!');
}
$polling = $vote->pollings()
->where('user_id', getUser('id'))
->first();
if ($polling) {
$validator->addError('Вы уже проголосовали в этом опросе!');
}
$voteAnswer = $vote->answers()
->where('id', $poll)
->where('vote_id', $vote->id)
->first();
if (! $voteAnswer) {
$validator->addError('Вы не выбрали вариант ответа!');
}
if ($validator->isValid()) {
$vote->increment('count');
$voteAnswer->increment('result');
Polling::query()->create([
'relate_type' => Vote::class,
'relate_id' => $vote->id,
'user_id' => getUser('id'),
'vote' => '+',
'created_at' => SITETIME,
]);
setFlash('success', 'Ваш голос успешно принят!');
} else {
setFlash('danger', $validator->getErrors());
}
redirect('/topic/' . $vote->topic_id . '?page=' . $page);
}
/**
* Печать темы
*/
public function print($id)
{
$topic = Topic::query()->find($id);
if (! $topic) {
abort(404, 'Данной темы не существует!');
}
$posts = Post::query()
->where('topic_id', $topic->id)
->with('user')
->orderBy('created_at')
->get();
return view('forum/print', compact('topic', 'posts'));
}
/**
* Переход к сообщению
*/
public function viewpost($id, $pid)
{
$countTopics = Post::query()
->where('id', '<=', $pid)
->where('topic_id', $id)
->count();
if (! $countTopics) {
abort(404, 'Выбранная вами тема не существует, возможно она была удалена!');
}
$end = ceil($countTopics / setting('forumpost'));
redirect('/topic/' . $id . '?page=' . $end . '#post_' . $pid);
}
/**
* Переадресация к последнему сообщению
*/
public function end($id)
{
$topic = Topic::query()->find($id);
if (! $topic) {
abort(404, 'Выбранная вами тема не существует, возможно она была удалена!');
}
$end = ceil($topic->count_posts / setting('forumpost'));
redirect('/topic/' . $topic->id . '?page=' . $end);
}
}