123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- <?php
- /**
- * This file is part of the ForkBB <https://github.com/forkbb>.
- *
- * @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
- * @license The MIT License (MIT)
- */
- declare(strict_types=1);
- namespace ForkBB\Models\Pages;
- use ForkBB\Core\Container;
- use ForkBB\Core\Validator;
- use ForkBB\Models\Page;
- use ForkBB\Models\Forum\Forum;
- use ForkBB\Models\Topic\Topic;
- use ForkBB\Models\Post\Post;
- use function \ForkBB\__;
- use function \ForkBB\dt;
- class Moderate extends Page
- {
- const INFORUM = 1; // действие для форума
- const INTOPIC = 2; // действие для темы
- const TOTOPIC = 4; // список постов сменить на тему
- const IFTOTPC = 8; // список постов сменить на тему, если в нем только первый пост темы
- /**
- * Список действий
- * @var array
- */
- protected $actions = [
- 'open' => self::INFORUM + self::INTOPIC + self::TOTOPIC,
- 'close' => self::INFORUM + self::INTOPIC + self::TOTOPIC,
- 'delete' => self::INFORUM + self::INTOPIC + self::IFTOTPC,
- 'move' => self::INFORUM + self::INTOPIC + self::IFTOTPC,
- 'merge' => self::INFORUM,
- 'cancel' => self::INFORUM + self::INTOPIC + self::TOTOPIC + self::IFTOTPC,
- 'unstick' => self::INTOPIC + self::TOTOPIC,
- 'stick' => self::INTOPIC + self::TOTOPIC,
- 'split' => self::INTOPIC,
- ];
- public function __construct(Container $container)
- {
- parent::__construct($container);
- $this->nameTpl = 'moderate';
- $this->onlinePos = 'moderate';
- $this->robots = 'noindex, nofollow';
- $this->hhsLevel = 'secure';
- $container->Lang->load('validator');
- $container->Lang->load('misc');
- }
- /**
- * Составление списка категорий/разделов для выбора
- */
- protected function calcList(int $curForum, bool $noUseCurForum = true): void
- {
- $cid = null;
- $options = [];
- $idxs = [];
- $root = $this->c->forums->get(0);
- if ($root instanceof Forum) {
- foreach ($this->c->forums->depthList($root, -1) as $f) {
- if ($cid !== $f->cat_id) {
- $cid = $f->cat_id;
- $options[] = [__('Category prefix') . $f->cat_name];
- }
- $indent = \str_repeat(__('Forum indent'), $f->depth);
- if (
- $f->redirect_url
- || (
- $noUseCurForum
- && $f->id === $curForum
- )
- ) {
- $options[] = [$f->id, $indent . __('Forum prefix') . $f->forum_name, true];
- } else {
- $options[] = [$f->id, $indent . __('Forum prefix') . $f->forum_name];
- $idxs[] = $f->id;
- }
- }
- }
- $this->listOfIndexes = $idxs;
- $this->listForOptions = $options;
- }
- /**
- * Определяет действие
- */
- public function vActionProcess(Validator $v, ?string $action): ?string
- {
- if (empty($v->getErrors())) {
- $type = $v->topic ? self::INTOPIC : self::INFORUM;
- $sum = 0;
- foreach ($this->actions as $key => $val) {
- if (isset($v->{$key})) {
- $action = $key;
- ++$sum;
- }
- }
- // нажата не одна кнопка или недоступная кнопка
- if (
- 1 !== $sum
- || ! ($type & $this->actions[$action])
- ) {
- $v->addError('Action not available');
- // не выбрано ни одного сообщения для действий прямо этого требующих
- } elseif (
- $v->topic
- && 1 === \count($v->ids)
- && ! ((self::TOTOPIC + self::IFTOTPC) & $this->actions[$action])
- ) {
- $v->addError('No object selected');
- }
- // объединение тем
- if (
- 'merge' === $action
- && \count($v->ids) < 2
- ) {
- $v->addError('Not enough topics selected');
- // перенос тем или разделение постов
- } elseif (
- 'move' === $action
- || 'split' === $action
- ) {
- $this->calcList($v->forum, 'move' === $action);
- if (empty($this->listOfIndexes)) {
- $v->addError('Nowhere to move');
- } elseif (
- 1 === $v->confirm
- && ! \in_array($v->destination, $this->listOfIndexes, true)
- ) {
- $v->addError('Invalid destination');
- } elseif (
- 'split' === $action
- && 1 === $v->confirm
- && '' == $v->subject
- ) {
- $v->addError('No subject');
- }
- }
- }
- return $action;
- }
- /**
- * Обрабатывает модерирование разделов
- */
- public function action(array $args): Page
- {
- $v = $this->c->Validator->reset()
- ->addValidators([
- 'action_process' => [$this, 'vActionProcess'],
- ])->addRules([
- 'token' => 'token:Moderate',
- 'step' => 'required|integer|min:1',
- 'forum' => 'required|integer|min:1|max:9999999999',
- 'topic' => 'integer|min:1|max:9999999999',
- 'page' => 'integer|min:1',
- 'ids' => 'required|array',
- 'ids.*' => 'required|integer|min:1|max:9999999999',
- 'confirm' => 'integer',
- 'redirect' => 'integer',
- 'subject' => 'string:trim,spaces|min:1|max:70',
- 'destination' => 'integer',
- 'open' => 'string',
- 'close' => 'string',
- 'delete' => 'string',
- 'move' => 'string',
- 'merge' => 'string',
- 'cancel' => 'string',
- 'unstick' => 'string',
- 'stick' => 'string',
- 'split' => 'string',
- 'action' => 'action_process',
- ])->addAliases([
- ])->addArguments([
- ])->addMessages([
- 'ids' => 'No object selected',
- ]);
- if (! $v->validation($_POST)) {
- $message = $this->c->Message;
- $message->fIswev = $v->getErrors();
- return $message->message('');
- }
- $this->curForum = $this->c->forums->loadTree($v->forum);
- if (! $this->curForum instanceof Forum) {
- return $this->c->Message->message('Bad request');
- } elseif (
- ! $this->user->isAdmin
- && ! $this->user->isModerator($this->curForum)
- ) {
- return $this->c->Message->message('No permission', true, 403);
- }
- $page = $v->page ?? 1;
- if ($v->topic) {
- $this->curTopic = $this->c->topics->load($v->topic);
- if (
- ! $this->curTopic instanceof Topic
- || $this->curTopic->parent !== $this->curForum
- ) {
- return $this->c->Message->message('Bad request');
- }
- $objects = null;
- $curType = $this->actions[$v->action];
- $ids = $v->ids;
- $firstId = $this->curTopic->first_post_id;
- if (self::TOTOPIC & $curType) {
- $objects = [$this->curTopic];
- } elseif (self::IFTOTPC & $curType) {
- if (
- 1 === \count($ids)
- && \reset($ids) === $firstId
- ) {
- $objects = [$this->curTopic];
- }
- }
- if (null === $objects) {
- $objects = $this->c->posts->loadByIds(\array_diff($ids, [$firstId]), false);
- foreach ($objects as $post) {
- if (
- ! $post instanceof Post
- || $post->parent !== $this->curTopic
- ) {
- return $this->c->Message->message('Bad request');
- }
- }
- $this->processAsPosts = true;
- }
- $this->backLink = $this->c->Router->link(
- 'Topic',
- [
- 'id' => $this->curTopic->id,
- 'name' => $this->curTopic->name,
- 'page' => $page,
- ]
- );
- } else {
- $objects = $this->c->topics->loadByIds($v->ids, false);
- foreach ($objects as $topic) {
- if (
- ! $topic instanceof Topic
- || $topic->parent !== $this->curForum
- ) {
- return $this->c->Message->message('Bad request');
- }
- }
- $this->backLink = $this->c->Router->link(
- 'Forum',
- [
- 'id' => $this->curForum->id,
- 'name' => $this->curForum->forum_name,
- 'page' => $page,
- ]
- );
- }
- $this->numObj = \count($objects);
- return $this->{'action' . \ucfirst($v->action)}($objects, $v);
- }
- protected function actionCancel(array $objects, Validator $v): Page
- {
- return $this->c->Redirect->url($this->backLink)->message('No confirm redirect');
- }
- protected function actionOpen(array $topics, Validator $v): Page
- {
- switch ($v->step) {
- case 1:
- $this->formTitle = ['Open topic title', $this->numObj];
- $this->buttonValue = ['Open topic btn', $this->numObj];
- $this->crumbs = $this->crumbs(
- [null, $this->formTitle],
- 'Moderate',
- $v->topic ? $this->curTopic : $this->curForum
- );
- $this->form = $this->formConfirm($topics, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- $this->c->topics->access(true, ...$topics);
- return $this->c->Redirect->url($this->backLink)->message(['Open topic redirect', $this->numObj]);
- } else {
- return $this->actionCancel($topics, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionClose(array $topics, Validator $v): Page
- {
- switch ($v->step) {
- case 1:
- $this->formTitle = ['Close topic title', $this->numObj];
- $this->buttonValue = ['Close topic btn', $this->numObj];
- $this->crumbs = $this->crumbs(
- [null, $this->formTitle],
- 'Moderate',
- $v->topic ? $this->curTopic : $this->curForum
- );
- $this->form = $this->formConfirm($topics, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- $this->c->topics->access(false, ...$topics);
- return $this->c->Redirect->url($this->backLink)->message(['Close topic redirect', $this->numObj]);
- } else {
- return $this->actionCancel($topics, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionDelete(array $objects, Validator $v): Page
- {
- if (! $this->user->isAdmin) { //???? разобраться с правами на удаление
- foreach ($objects as $object) {
- if (
- (
- $object instanceof Topic
- && isset($this->c->admins->list[$object->poster_id])
- )
- || (
- $object instanceof Post
- && ! $object->canDelete
- )
- ) {
- return $this->c->Message->message('No permission', true, 403); //???? причина
- }
- }
- }
- switch ($v->step) {
- case 1:
- $this->formTitle = [
- true === $this->processAsPosts
- ? 'Delete post title'
- : 'Delete topic title',
- $this->numObj,
- ];
- $this->buttonValue = [
- true === $this->processAsPosts
- ? 'Delete post btn'
- : 'Delete topic btn',
- $this->numObj,
- ];
- $this->crumbs = $this->crumbs(
- [null, $this->formTitle],
- 'Moderate',
- $v->topic ? $this->curTopic : $this->curForum
- );
- $this->form = $this->formConfirm($objects, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- if (true === $this->processAsPosts) {
- $this->c->posts->delete(...$objects);
- $message = 'Delete post redirect';
- } else {
- $this->c->topics->delete(...$objects);
- $message = 'Delete topic redirect';
- }
- return $this->c->Redirect->url($this->curForum->link)->message([$message, $this->numObj]);
- } else {
- return $this->actionCancel($objects, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionMove(array $topics, Validator $v): Page
- {
- switch ($v->step) {
- case 1:
- $this->formTitle = ['Move topic title', $this->numObj];
- $this->buttonValue = ['Move topic btn', $this->numObj];
- $this->crumbs = $this->crumbs(
- [null, $this->formTitle],
- 'Moderate',
- $v->topic ? $this->curTopic : $this->curForum
- );
- $this->chkRedirect = true;
- $this->form = $this->formConfirm($topics, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- $forum = $this->c->forums->get($v->destination);
- $this->c->topics->move(1 === $v->redirect, $forum, ...$topics);
- return $this->c->Redirect->url($this->curForum->link)->message(['Move topic redirect', $this->numObj]);
- } else {
- return $this->actionCancel($topics, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionMerge(array $topics, Validator $v): Page
- {
- foreach ($topics as $topic) {
- if ($topic->moved_to) {
- return $this->c->Message->message('Topic links cannot be merged');
- }
- if (
- ! $this->firstTopic instanceof Topic
- || $topic->first_post_id < $this->firstTopic->first_post_id
- ) {
- $this->firstTopic = $topic;
- }
- }
- foreach ($topics as $topic) {
- if (
- $this->firstTopic !== $topic
- && $topic->poll_type > 0
- ) {
- return $this->c->Message->message('Poll cannot be attached');
- }
- }
- switch ($v->step) {
- case 1:
- $this->formTitle = 'Merge topics title';
- $this->buttonValue = 'Merge btn';
- $this->crumbs = $this->crumbs($this->formTitle, 'Moderate', $this->curForum);
- $this->chkRedirect = true;
- $this->form = $this->formConfirm($topics, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- $this->c->topics->merge(1 === $v->redirect, ...$topics);
- return $this->c->Redirect->url($this->curForum->link)->message('Merge topics redirect');
- } else {
- return $this->actionCancel($topics, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionUnstick(array $topics, Validator $v): Page
- {
- switch ($v->step) {
- case 1:
- $this->formTitle = ['Unstick topic title', $this->numObj];
- $this->buttonValue = ['Unstick btn', $this->numObj];
- $this->crumbs = $this->crumbs(
- [null, $this->formTitle],
- 'Moderate',
- $v->topic ? $this->curTopic : $this->curForum
- );
- $this->form = $this->formConfirm($topics, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- foreach ($topics as $topic) {
- $topic->sticky = 0;
- $this->c->topics->update($topic);
- }
- return $this->c->Redirect->url($this->backLink)->message(['Unstick topic redirect', $this->numObj]);
- } else {
- return $this->actionCancel($topics, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionStick(array $topics, Validator $v): Page
- {
- switch ($v->step) {
- case 1:
- $this->formTitle = ['Stick topic title', $this->numObj];
- $this->buttonValue = ['Stick btn', $this->numObj];
- $this->crumbs = $this->crumbs(
- [null, $this->formTitle],
- 'Moderate',
- $v->topic ? $this->curTopic : $this->curForum
- );
- $this->form = $this->formConfirm($topics, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- foreach ($topics as $topic) {
- $topic->sticky = 1;
- $this->c->topics->update($topic);
- }
- return $this->c->Redirect->url($this->backLink)->message(['Stick topic redirect', $this->numObj]);
- } else {
- return $this->actionCancel($topics, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- protected function actionSplit(array $posts, Validator $v): Page
- {
- switch ($v->step) {
- case 1:
- $this->formTitle = 'Split posts title';
- $this->buttonValue = 'Split btn';
- $this->needSubject = true;
- $this->crumbs = $this->crumbs($this->formTitle, 'Moderate', $this->curTopic);
- $this->form = $this->formConfirm($posts, $v);
- return $this;
- case 2:
- if (1 === $v->confirm) {
- $newTopic = $this->c->topics->create();
- $newTopic->subject = $v->subject;
- $newTopic->forum_id = $v->forum;
- $this->c->topics->insert($newTopic);
- $this->c->posts->move(false, $newTopic, ...$posts);
- return $this->c->Redirect->url($this->curForum->link)->message('Split posts redirect');
- } else {
- return $this->actionCancel($posts, $v);
- }
- default:
- return $this->c->Message->message('Bad request');
- }
- }
- /**
- * Подготавливает массив данных для формы подтверждения
- */
- protected function formConfirm(array $objects, Validator $v): array
- {
- $form = [
- 'action' => $this->c->Router->link('Moderate'),
- 'hidden' => [
- 'token' => $this->c->Csrf->create('Moderate'),
- 'step' => $v->step + 1,
- 'forum' => $v->forum,
- 'ids' => $v->ids,
- ],
- 'sets' => [],
- 'btns' => [],
- ];
- $autofocus = true;
- if ($v->topic) {
- $form['hidden']['topic'] = $v->topic;
- }
- $headers = [];
- foreach ($objects as $object) {
- if ($object instanceof Topic) {
- $headers[] = __(['Topic «%s»', $object->name]);
- } else {
- $headers[] = __(['Post «%1$s by %2$s»', dt($object->posted), $object->poster]);
- }
- }
- $form['sets']['info'] = [
- 'info' => [
- [
- 'value' => \implode('<br>', $headers),
- 'html' => true,
- ],
- ],
- ];
- if ($this->firstTopic instanceof Topic) {
- $form['sets']['info']['info'][] = [
- 'value' => __(['All posts will be posted in the «%s» topic', $this->firstTopic->name]),
- 'html' => true,
- ];
- }
- $fields = [];
- if ($this->needSubject) {
- $fields['subject'] = [
- 'type' => 'text',
- 'maxlength' => '70',
- 'caption' => 'New subject',
- 'required' => true,
- 'value' => '' == $v->subject ? $this->curTopic->subject : $v->subject,
- 'autofocus' => $autofocus,
- ];
- $autofocus = null;
- }
- if ($this->listForOptions) {
- $fields['destination'] = [
- 'type' => 'select',
- 'options' => $this->listForOptions,
- 'value' => null,
- 'caption' => 'Move to',
- 'autofocus' => $autofocus,
- ];
- $autofocus = null;
- }
- if (true === $this->chkRedirect) {
- $fields['redirect'] = [
- 'type' => 'checkbox',
- 'label' => 'Leave redirect',
- 'checked' => true,
- ];
- }
- $fields['confirm'] = [
- 'type' => 'checkbox',
- 'label' => 'Confirm action',
- 'checked' => false,
- ];
- $form['sets']['moderate']['fields'] = $fields;
- $form['btns'][$v->action] = [
- 'type' => 'submit',
- 'value' => __($this->buttonValue),
- ];
- $form['btns']['cancel'] = [
- 'type' => 'submit',
- 'value' => __('Cancel'),
- ];
- return $form;
- }
- }
|