Add moderate functions for forum page
This commit is contained in:
parent
3fb3a5e1e3
commit
b09c359d59
14 changed files with 864 additions and 17 deletions
|
@ -162,6 +162,9 @@ class Routing
|
|||
$r->add(self::GET, '/admin/reports', 'AdminReports:view', 'AdminReports');
|
||||
$r->add(self::GET, '/admin/reports/zap/{id:[1-9]\d*}/{token}', 'AdminReports:zap', 'AdminReportsZap');
|
||||
}
|
||||
|
||||
$r->add(self::PST, '/moderate', 'Moderate:action', 'Moderate');
|
||||
|
||||
}
|
||||
// только админ
|
||||
if ($user->isAdmin) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace ForkBB\Models\Pages;
|
||||
|
||||
use ForkBB\Models\Page;
|
||||
use ForkBB\Models\Forum\Model as ForumModel;
|
||||
|
||||
class Forum extends Page
|
||||
{
|
||||
|
@ -18,8 +19,8 @@ class Forum extends Page
|
|||
$this->c->Lang->load('forum');
|
||||
$this->c->Lang->load('subforums');
|
||||
|
||||
$forum = $this->c->forums->loadTree($args['id']);
|
||||
if (null === $forum) {
|
||||
$forum = $this->c->forums->loadTree((int) $args['id']);
|
||||
if (! $forum instanceof ForumModel) {
|
||||
return $this->c->Message->message('Bad request');
|
||||
}
|
||||
|
||||
|
@ -43,8 +44,60 @@ class Forum extends Page
|
|||
|
||||
if (empty($this->topics)) {
|
||||
$this->fIswev = ['i', \ForkBB\__('Empty forum')];
|
||||
} elseif ($this->user->isAdmin || $this->user->isModerator($forum)) {
|
||||
$this->c->Lang->load('misc');
|
||||
|
||||
$this->enableMod = true;
|
||||
$this->formMod = $this->formMod($forum->id, $forum->page);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает массив данных для формы модерации
|
||||
*
|
||||
* @param int $id
|
||||
* @param int $page
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function formMod(int $id, int $page): array
|
||||
{
|
||||
$form = [
|
||||
'id' => 'id-form-mod',
|
||||
'action' => $this->c->Router->link('Moderate'),
|
||||
'hidden' => [
|
||||
'token' => $this->c->Csrf->create('Moderate'),
|
||||
'forum' => $id,
|
||||
'page' => $page,
|
||||
'step' => 1,
|
||||
],
|
||||
'sets' => [],
|
||||
'btns' => [
|
||||
'open' => [
|
||||
'type' => 'submit',
|
||||
'value' => \ForkBB\__('Open'),
|
||||
],
|
||||
'close' => [
|
||||
'type' => 'submit',
|
||||
'value' => \ForkBB\__('Close'),
|
||||
],
|
||||
'delete' => [
|
||||
'type' => 'submit',
|
||||
'value' => \ForkBB\__('Delete'),
|
||||
],
|
||||
'move' => [
|
||||
'type' => 'submit',
|
||||
'value' => \ForkBB\__('Move'),
|
||||
],
|
||||
'merge' => [
|
||||
'type' => 'submit',
|
||||
'value' => \ForkBB\__('Merge'),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
|
436
app/Models/Pages/Moderate.php
Normal file
436
app/Models/Pages/Moderate.php
Normal file
|
@ -0,0 +1,436 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Models\Pages;
|
||||
|
||||
use ForkBB\Core\Container;
|
||||
use ForkBB\Core\Validator;
|
||||
use ForkBB\Models\Page;
|
||||
use ForkBB\Models\Forum\Model as Forum;
|
||||
use ForkBB\Models\Topic\Model as Topic;
|
||||
|
||||
class Moderate extends Page
|
||||
{
|
||||
/**
|
||||
* Список действий
|
||||
* @var array
|
||||
*/
|
||||
protected $actions = [
|
||||
'open' => true,
|
||||
'close' => true,
|
||||
'delete' => true,
|
||||
'move' => true,
|
||||
'merge' => true,
|
||||
'cancel' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
||||
$this->fIndex = 'index';
|
||||
$this->nameTpl = 'moderate';
|
||||
$this->onlinePos = 'moderate';
|
||||
$this->robots = 'noindex, nofollow';
|
||||
|
||||
$container->Lang->load('misc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Составление списка категорий/разделов для выбора
|
||||
*/
|
||||
protected function calcList(int $curForum): 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[] = [\ForkBB\__('Category prefix') . $f->cat_name];
|
||||
}
|
||||
|
||||
$indent = \str_repeat(\ForkBB\__('Forum indent'), $f->depth);
|
||||
|
||||
if ($f->redirect_url || $f->id === $curForum) {
|
||||
$options[] = [$f->id, $indent . \ForkBB\__('Forum prefix') . $f->forum_name, true];
|
||||
} else {
|
||||
$options[] = [$f->id, $indent . \ForkBB\__('Forum prefix') . $f->forum_name];
|
||||
$idxs[] = $f->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->listOfIndexes = $idxs;
|
||||
$this->listForOptions = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет действие
|
||||
*
|
||||
* @param Validator $v
|
||||
* @param null|string $action
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function vActionProcess(Validator $v, $action)
|
||||
{
|
||||
if (empty($v->getErrors())) {
|
||||
$sum = 0;
|
||||
foreach ($this->actions as $key => $val) {
|
||||
if (isset($v->{$key})) {
|
||||
$action = $key;
|
||||
++$sum;
|
||||
}
|
||||
}
|
||||
// нажата только одна кнопка из доступных
|
||||
if (1 !== $sum) {
|
||||
$v->addError('Action not available');
|
||||
}
|
||||
// объединение тем
|
||||
if ('merge' === $action && \count($v->ids) < 2) {
|
||||
$v->addError('Not enough topics selected');
|
||||
// перенос тем
|
||||
} elseif ('move' === $action) {
|
||||
$this->calcList($v->forum);
|
||||
|
||||
if (empty($this->listOfIndexes)) {
|
||||
$v->addError('Nowhere to move');
|
||||
} elseif (1 === $v->confirm && ! \in_array($v->destination, $this->listOfIndexes)) {
|
||||
$v->addError('Invalid destination');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает модерирование разделов
|
||||
*
|
||||
* @param array $args
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
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',
|
||||
'destination' => 'integer',
|
||||
'open' => 'string',
|
||||
'close' => 'string',
|
||||
'delete' => 'string',
|
||||
'move' => 'string',
|
||||
'merge' => 'string',
|
||||
'cancel' => 'string',
|
||||
'action' => 'action_process',
|
||||
])->addAliases([
|
||||
])->addArguments([
|
||||
])->addMessages([
|
||||
'ids' => 'No object selected',
|
||||
]);
|
||||
|
||||
if (! $v->validation($_POST)) {
|
||||
$message = $this->c->Message->message('Bad request');
|
||||
$message->fIswev = $v->getErrors();
|
||||
return $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');
|
||||
}
|
||||
// посты
|
||||
|
||||
$this->backLink = $this->c->Router->link('Topic', [
|
||||
'id' => $this->curTopic->id,
|
||||
'name' => $this->curTopic->subject,
|
||||
'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
|
||||
]);
|
||||
}
|
||||
|
||||
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 = \ForkBB\__('Open topics');
|
||||
$this->buttonValue = \ForkBB\__('Open');
|
||||
$this->crumbs = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
|
||||
$this->form = $this->formConfirm($topics, $v);
|
||||
return $this;
|
||||
case 2:
|
||||
if (1 === $v->confirm) {
|
||||
$this->c->topics->access(true, ...$topics);
|
||||
|
||||
$message = 1 === \count($topics) ? 'Open topic redirect' : 'Open topics redirect';
|
||||
return $this->c->Redirect->url($this->backLink)->message($message);
|
||||
} 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 = \ForkBB\__('Close topics');
|
||||
$this->buttonValue = \ForkBB\__('Close');
|
||||
$this->crumbs = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
|
||||
$this->form = $this->formConfirm($topics, $v);
|
||||
return $this;
|
||||
case 2:
|
||||
if (1 === $v->confirm) {
|
||||
$this->c->topics->access(false, ...$topics);
|
||||
|
||||
$message = 1 === \count($topics) ? 'Close topic redirect' : 'Close topics redirect';
|
||||
return $this->c->Redirect->url($this->backLink)->message($message);
|
||||
} else {
|
||||
return $this->actionCancel($topics, $v);
|
||||
}
|
||||
default:
|
||||
return $this->c->Message->message('Bad request');
|
||||
}
|
||||
}
|
||||
|
||||
protected function actionDelete(array $topics, Validator $v): Page
|
||||
{
|
||||
if (! $this->user->isAdmin) { //???? разобраться с правами на удаление
|
||||
foreach ($topics as $topic) {
|
||||
if (isset($this->c->admins->list[$topic->poster_id])) {
|
||||
return $this->c->Message->message('No permission', true, 403); //???? причина
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ($v->step) {
|
||||
case 1:
|
||||
$this->formTitle = \ForkBB\__('Delete topics');
|
||||
$this->buttonValue = \ForkBB\__('Delete');
|
||||
$this->crumbs = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
|
||||
$this->form = $this->formConfirm($topics, $v);
|
||||
return $this;
|
||||
case 2:
|
||||
if (1 === $v->confirm) {
|
||||
$this->c->topics->delete(...$topics);
|
||||
|
||||
return $this->c->Redirect->url($this->curForum->link)->message('Delete topics redirect');
|
||||
} else {
|
||||
return $this->actionCancel($topics, $v);
|
||||
}
|
||||
default:
|
||||
return $this->c->Message->message('Bad request');
|
||||
}
|
||||
}
|
||||
|
||||
protected function actionMove(array $topics, Validator $v): Page
|
||||
{
|
||||
switch ($v->step) {
|
||||
case 1:
|
||||
$this->formTitle = \ForkBB\__('Move topics');
|
||||
$this->buttonValue = \ForkBB\__('Move');
|
||||
$this->crumbs = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $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);
|
||||
|
||||
$message = 1 === \count($topics) ? 'Move topic redirect' : 'Move topics redirect';
|
||||
return $this->c->Redirect->url($this->curForum->link)->message($message);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($v->step) {
|
||||
case 1:
|
||||
$this->formTitle = \ForkBB\__('Merge topics');
|
||||
$this->buttonValue = \ForkBB\__('Merge');
|
||||
$this->crumbs = $this->crumbs($this->formTitle, \ForkBB\__('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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготавливает массив данных для формы подтверждения
|
||||
*
|
||||
* @param array $objects
|
||||
* @param Validator $v
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
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,
|
||||
],
|
||||
'sets' => [],
|
||||
'btns' => [],
|
||||
];
|
||||
|
||||
if ($v->topic) {
|
||||
$form['hidden']['topic'] = $v->topic;
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
$ids = [];
|
||||
foreach ($objects as $object) {
|
||||
if ($object instanceof Topic) {
|
||||
$headers[] = \ForkBB\__('Topic «%s»', \ForkBB\cens(($object->subject)));
|
||||
$ids[] = $object->id;
|
||||
}
|
||||
}
|
||||
|
||||
$form['hidden']['ids'] = $ids;
|
||||
$form['sets']['info'] = [
|
||||
'info' => [
|
||||
'info1' => [
|
||||
'type' => '', //????
|
||||
'value' => \implode('<br>', $headers),
|
||||
'html' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->firstTopic instanceof Topic) {
|
||||
$form['sets']['info']['info']['info2'] = [
|
||||
'type' => '', //????
|
||||
'value' => \ForkBB\__('All posts will be posted in the «%s» topic', $this->firstTopic->subject),
|
||||
// 'html' => true,
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->listForOptions) {
|
||||
$form['sets']['destination'] = [
|
||||
'fields' => [
|
||||
'destination' => [
|
||||
'type' => 'select',
|
||||
'options' => $this->listForOptions,
|
||||
'value' => null,
|
||||
'caption' => \ForkBB\__('Move to'),
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if (true === $this->chkRedirect) {
|
||||
$form['sets']['redirect'] = [
|
||||
'fields' => [
|
||||
'redirect' => [
|
||||
'type' => 'checkbox',
|
||||
'label' => \ForkBB\__('Leave redirect'),
|
||||
'value' => '1',
|
||||
'checked' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$form['sets']['confirm'] = [
|
||||
'fields' => [
|
||||
'confirm' => [
|
||||
'type' => 'checkbox',
|
||||
'label' => \ForkBB\__('Confirm action'),
|
||||
'value' => '1',
|
||||
'checked' => false,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$form['btns'][$v->action] = [
|
||||
'type' => 'submit',
|
||||
'value' => $this->buttonValue,
|
||||
// 'accesskey' => 's',
|
||||
];
|
||||
$form['btns']['cancel'] = [
|
||||
'type' => 'submit',
|
||||
'value' => \ForkBB\__('Cancel'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
33
app/Models/Topic/Access.php
Normal file
33
app/Models/Topic/Access.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Models\Topic;
|
||||
|
||||
use ForkBB\Models\Action;
|
||||
use ForkBB\Models\Topic\Model as Topic;
|
||||
|
||||
class Access extends Action
|
||||
{
|
||||
/**
|
||||
* Устанавливает/снимает флаг закрытия тем(ы)
|
||||
*
|
||||
* @param bool $open
|
||||
* @param Topic ...$topics
|
||||
*/
|
||||
public function access(bool $open, Topic ...$topics): void
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($topics as $topic) {
|
||||
$ids[] = $topic->id;
|
||||
$topic->__closed = $open ? 0 : 1;
|
||||
}
|
||||
|
||||
if (! empty($ids)) {
|
||||
$vars = [
|
||||
':ids' => $ids,
|
||||
':closed' => $open ? 0 : 1,
|
||||
];
|
||||
$sql = 'UPDATE ::topics SET closed=?i:closed WHERE id IN (?ai:ids)';
|
||||
$this->c->DB->exec($sql, $vars);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -118,6 +118,8 @@ class Delete extends Action
|
|||
|
||||
//???? подписки, опросы, предупреждения
|
||||
|
||||
// удаление тем-ссылок на удаляемые темы
|
||||
|
||||
if ($users) {
|
||||
$vars = [
|
||||
':users' => $users,
|
||||
|
@ -168,7 +170,7 @@ class Delete extends Action
|
|||
WHERE id IN (?ai:topics)';
|
||||
$this->c->DB->exec($sql, $vars);
|
||||
|
||||
foreach($parents as $forum) {
|
||||
foreach ($parents as $forum) {
|
||||
$this->c->forums->update($forum->calcStat());
|
||||
}
|
||||
}
|
||||
|
|
112
app/Models/Topic/Merge.php
Normal file
112
app/Models/Topic/Merge.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Models\Topic;
|
||||
|
||||
use ForkBB\Models\Action;
|
||||
use ForkBB\Models\Topic\Model as Topic;
|
||||
use PDO;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
class Merge extends Action
|
||||
{
|
||||
/**
|
||||
* Объединяет темы
|
||||
*
|
||||
* @param bool $redirect
|
||||
* @param Topic ...$topics
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function merge(bool $redirect, Topic ...$topics): void
|
||||
{
|
||||
if (\count($topics) < 2) {
|
||||
throw new InvalidArgumentException('Expected at least 2 topics.');
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
$users = [];
|
||||
$forums = [];
|
||||
$firstTopic = null;
|
||||
$otherTopics = [];
|
||||
|
||||
foreach ($topics as $topic) {
|
||||
if ($topic->moved_to) {
|
||||
throw new RuntimeException('Topic links cannot be merged');
|
||||
}
|
||||
|
||||
$users[$topic->poster_id] = $topic->poster_id;
|
||||
$forums[$topic->forum_id] = $topic->parent;
|
||||
|
||||
if (! $firstTopic instanceof Topic) {
|
||||
$firstTopic = $topic;
|
||||
} elseif ($topic->first_post_id < $firstTopic->first_post_id) {
|
||||
$otherTopics[] = $firstTopic;
|
||||
$ids[] = $firstTopic->id;
|
||||
$firstTopic = $topic;
|
||||
} else {
|
||||
$otherTopics[] = $topic;
|
||||
$ids[] = $topic->id;
|
||||
}
|
||||
}
|
||||
|
||||
//???? перенести обработку в посты?
|
||||
$vars = [
|
||||
'start' => "[from]",
|
||||
'end' => "[/from]\n",
|
||||
'topics' => $ids,
|
||||
];
|
||||
$sql = 'UPDATE ::posts AS p, ::topics as t
|
||||
SET p.message=CONCAT(?s:start, t.subject, ?s:end, p.message)
|
||||
WHERE p.topic_id IN (?ai:topics) AND t.id=p.topic_id';
|
||||
$this->c->DB->exec($sql, $vars);
|
||||
|
||||
$vars = [
|
||||
'id' => $firstTopic->id,
|
||||
'topics' => $ids,
|
||||
];
|
||||
$sql = 'UPDATE ::posts AS p
|
||||
SET p.topic_id=?i:id
|
||||
WHERE p.topic_id IN (?ai:topics)';
|
||||
$this->c->DB->exec($sql, $vars);
|
||||
|
||||
// добавить перенос подписок на первую тему?
|
||||
|
||||
if ($redirect) {
|
||||
foreach ($otherTopics as $topic) {
|
||||
$topic->moved_to = $firstTopic->id;
|
||||
$this->c->topics->update($topic->calcStat());
|
||||
}
|
||||
|
||||
$vars = [
|
||||
'topics' => $ids,
|
||||
];
|
||||
$sql = 'SELECT t.id
|
||||
FROM ::topics AS t
|
||||
WHERE t.moved_to IN (?ai:topics)';
|
||||
$linkTopics = $this->c->DB->query($sql, $vars)->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
foreach ($linkTopics as $topic) {
|
||||
$topic->moved_to = $firstTopic->id;
|
||||
$this->c->topics->update($topic->calcStat());
|
||||
}
|
||||
|
||||
$this->c->topics->update($firstTopic->calcStat());
|
||||
|
||||
foreach ($forums as $forum) {
|
||||
$this->c->forums->update($forum->calcStat());
|
||||
}
|
||||
|
||||
if ($users) {
|
||||
$this->c->users->UpdateCountTopics(...$users);
|
||||
}
|
||||
} else {
|
||||
$this->c->topics->update($firstTopic->calcStat());
|
||||
|
||||
$this->manager->delete(...$otherTopics);
|
||||
|
||||
$this->c->forums->update($firstTopic->parent->calcStat());
|
||||
}
|
||||
}
|
||||
}
|
47
app/Models/Topic/Move.php
Normal file
47
app/Models/Topic/Move.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace ForkBB\Models\Topic;
|
||||
|
||||
use ForkBB\Models\Action;
|
||||
use ForkBB\Models\Forum\Model as Forum;
|
||||
use ForkBB\Models\Topic\Model as Topic;
|
||||
|
||||
class Move extends Action
|
||||
{
|
||||
/**
|
||||
* Перенос тем
|
||||
*
|
||||
* @param bool $redirect
|
||||
* @param Forum $toForum
|
||||
* @param Topic ...$topics
|
||||
*/
|
||||
public function move(bool $redirect, Forum $toForum, Topic ...$topics): void
|
||||
{
|
||||
$forums = [
|
||||
$toForum->id => $toForum,
|
||||
];
|
||||
|
||||
foreach ($topics as $topic) {
|
||||
if ($topic->parent === $toForum) {
|
||||
continue;
|
||||
}
|
||||
if ($redirect) {
|
||||
$rTopic = clone $topic;
|
||||
$rTopic->id = null;
|
||||
$rTopic->moved_to = $topic->id;
|
||||
$rTopic->num_replies = 0;
|
||||
|
||||
$this->c->topics->insert($rTopic);
|
||||
}
|
||||
|
||||
$forums[$topic->forum_id] = $topic->parent;
|
||||
$topic->forum_id = $toForum->id;
|
||||
|
||||
$this->c->topics->update($topic);
|
||||
}
|
||||
|
||||
foreach ($forums as $forum) {
|
||||
$this->c->forums->update($forum->calcStat());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -159,6 +159,7 @@ return [
|
|||
'Ban' => \ForkBB\Models\Pages\Ban::class,
|
||||
'Debug' => \ForkBB\Models\Pages\Debug::class,
|
||||
'Misc' => \ForkBB\Models\Pages\Misc::class,
|
||||
'Moderate' => \ForkBB\Models\Pages\Moderate::class,
|
||||
'Report' => \ForkBB\Models\Pages\Report::class,
|
||||
'ProfileView' => \ForkBB\Models\Pages\Profile\View::class,
|
||||
'ProfileEdit' => \ForkBB\Models\Pages\Profile\Edit::class,
|
||||
|
@ -248,6 +249,9 @@ return [
|
|||
'TopicManagerSave' => \ForkBB\Models\Topic\Save::class,
|
||||
'TopicManagerDelete' => \ForkBB\Models\Topic\Delete::class,
|
||||
'TopicManagerView' => \ForkBB\Models\Topic\View::class,
|
||||
'TopicManagerAccess' => \ForkBB\Models\Topic\Access::class,
|
||||
'TopicManagerMerge' => \ForkBB\Models\Topic\Merge::class,
|
||||
'TopicManagerMove' => \ForkBB\Models\Topic\Move::class,
|
||||
|
||||
'PostModel' => \ForkBB\Models\Post\Model::class,
|
||||
'PostManagerLoad' => \ForkBB\Models\Post\Load::class,
|
||||
|
|
|
@ -123,8 +123,8 @@ msgstr "Move topic"
|
|||
msgid "Move topics"
|
||||
msgstr "Move topics"
|
||||
|
||||
msgid "Move legend"
|
||||
msgstr "Select destination of move"
|
||||
msgid "Invalid destination"
|
||||
msgstr "Invalid destination."
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "Move to"
|
||||
|
@ -148,7 +148,7 @@ msgid "Delete topics"
|
|||
msgstr "Delete topics"
|
||||
|
||||
msgid "Delete topics comply"
|
||||
msgstr "Are you sure you want to delete the selected topics?"
|
||||
msgstr "Delete the selected topics (<b>All posts from these topics will be deleted</b>)"
|
||||
|
||||
msgid "Delete topics redirect"
|
||||
msgstr "Topics deleted. Redirecting …"
|
||||
|
@ -165,9 +165,6 @@ msgstr "Topic closed. Redirecting …"
|
|||
msgid "Close topics redirect"
|
||||
msgstr "Topics closed. Redirecting …"
|
||||
|
||||
msgid "No topics selected"
|
||||
msgstr "You must select at least one topic for move/delete/open/close."
|
||||
|
||||
msgid "Not enough topics selected"
|
||||
msgstr "You must select at least two topics for merge."
|
||||
|
||||
|
@ -208,7 +205,7 @@ msgid "Cannot select first"
|
|||
msgstr "First post cannot be selected for split/delete."
|
||||
|
||||
msgid "Delete posts comply"
|
||||
msgstr "Are you sure you want to delete the selected posts?"
|
||||
msgstr "Delete the selected posts"
|
||||
|
||||
msgid "Delete posts redirect"
|
||||
msgstr "Posts deleted. Redirecting …"
|
||||
|
@ -233,3 +230,36 @@ msgstr "Posts moved. Redirecting …"
|
|||
|
||||
msgid "Post from topic"
|
||||
msgstr "This post is moved from topic"
|
||||
|
||||
msgid "Topic «%s»"
|
||||
msgstr "Topic «%s»"
|
||||
|
||||
msgid "All posts will be posted in the «%s» topic"
|
||||
msgstr "All posts will be posted in the «%s» topic."
|
||||
|
||||
msgid "Action not available"
|
||||
msgstr "Action not available."
|
||||
|
||||
msgid "No object selected"
|
||||
msgstr "You must select at least one object for the action."
|
||||
|
||||
msgid "Open topics"
|
||||
msgstr "Open topics"
|
||||
|
||||
msgid "Close topics"
|
||||
msgstr "Close topics"
|
||||
|
||||
msgid "Confirm action"
|
||||
msgstr "Confirm action"
|
||||
|
||||
msgid "Topic links cannot be merged"
|
||||
msgstr "Topic links cannot be merged."
|
||||
|
||||
msgid "Category prefix"
|
||||
msgstr ""
|
||||
|
||||
msgid "Forum prefix"
|
||||
msgstr ""
|
||||
|
||||
msgid "Forum indent"
|
||||
msgstr "◦ ◦ "
|
||||
|
|
|
@ -123,8 +123,8 @@ msgstr "Перенос темы"
|
|||
msgid "Move topics"
|
||||
msgstr "Перенос тем"
|
||||
|
||||
msgid "Move legend"
|
||||
msgstr "Выберите раздел-получатель"
|
||||
msgid "Invalid destination"
|
||||
msgstr "Неверная цель переноса."
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "Перенести в"
|
||||
|
@ -148,7 +148,7 @@ msgid "Delete topics"
|
|||
msgstr "Удаление тем"
|
||||
|
||||
msgid "Delete topics comply"
|
||||
msgstr "Вы уверены, что хотите удалить отмеченные темы?"
|
||||
msgstr "Удалить выбранные темы (<b>Все сообщения из этих тем будут удалены</b>)"
|
||||
|
||||
msgid "Delete topics redirect"
|
||||
msgstr "Темы удалены. Переадресация …"
|
||||
|
@ -165,9 +165,6 @@ msgstr "Тема закрыта. Переадресация …"
|
|||
msgid "Close topics redirect"
|
||||
msgstr "Темы закрыты. Переадресация …"
|
||||
|
||||
msgid "No topics selected"
|
||||
msgstr "Вы должны выбрать хотя бы одну тему для переноса/удаления/открытия/закрытия."
|
||||
|
||||
msgid "Not enough topics selected"
|
||||
msgstr "Вы должны выбрать хотя бы две темы для объединения."
|
||||
|
||||
|
@ -208,7 +205,7 @@ msgid "Cannot select first"
|
|||
msgstr "Первое сообщение не может быть выбрано."
|
||||
|
||||
msgid "Delete posts comply"
|
||||
msgstr "Вы уверены, что хотите удалить выбранные сообщения?"
|
||||
msgstr "Удалить выбранные сообщения"
|
||||
|
||||
msgid "Delete posts redirect"
|
||||
msgstr "Сообщения удалены. Переадресация …"
|
||||
|
@ -233,3 +230,36 @@ msgstr "Сообщения перенесены. Переадресация &hel
|
|||
|
||||
msgid "Post from topic"
|
||||
msgstr "Это сообщение перенесено из темы"
|
||||
|
||||
msgid "Topic «%s»"
|
||||
msgstr "Тема «%s»"
|
||||
|
||||
msgid "All posts will be posted in the «%s» topic"
|
||||
msgstr "Все сообщения будут размещены в теме «%s»."
|
||||
|
||||
msgid "Action not available"
|
||||
msgstr "Действие недоступно."
|
||||
|
||||
msgid "No object selected"
|
||||
msgstr "Вы должны выбрать хотя бы один объект для действия."
|
||||
|
||||
msgid "Open topics"
|
||||
msgstr "Открытие тем"
|
||||
|
||||
msgid "Close topics"
|
||||
msgstr "Закрытие тем"
|
||||
|
||||
msgid "Confirm action"
|
||||
msgstr "Подтвердить действие"
|
||||
|
||||
msgid "Topic links cannot be merged"
|
||||
msgstr "Ссылки на темы не могут быть объединены."
|
||||
|
||||
msgid "Category prefix"
|
||||
msgstr ""
|
||||
|
||||
msgid "Forum prefix"
|
||||
msgstr ""
|
||||
|
||||
msgid "Forum indent"
|
||||
msgstr "◦ ◦ "
|
||||
|
|
|
@ -84,7 +84,12 @@
|
|||
@elseif ($topic->moved_to)
|
||||
<li id="topic-{!! $topic->id !!}" class="f-row f-fredir">
|
||||
<div class="f-cell f-cmain">
|
||||
@if ($p->enableMod)
|
||||
<input id="checkbox-{!! $topic->id !!}" class="f-fch" type="checkbox" name="ids[{!! $topic->id !!}]" value="{!! $topic->id !!}" form="id-form-mod">
|
||||
<label class="f-ficon" for="checkbox-{!! $topic->id !!}"></label>
|
||||
@else
|
||||
<div class="f-ficon"></div>
|
||||
@endif
|
||||
<div class="f-finfo">
|
||||
<h3><span class="f-fredirtext">{!! __('Moved') !!}</span> <a class="f-ftname" href="{!! $topic->link !!}">{{ cens($topic->subject) }}</a></h3>
|
||||
</div>
|
||||
|
@ -93,7 +98,12 @@
|
|||
@else
|
||||
<li id="topic-{!! $topic->id !!}" class="f-row @if (false !== $topic->hasNew) f-fnew @endif @if (false !== $topic->hasUnread) f-funread @endif @if ($topic->sticky) f-fsticky @endif @if ($topic->closed) f-fclosed @endif @if ($topic->poll_type) f-fpoll @endif @if ($topic->dot) f-fposted @endif">
|
||||
<div class="f-cell f-cmain">
|
||||
@if ($p->enableMod)
|
||||
<input id="checkbox-{!! $topic->id !!}" class="f-fch" type="checkbox" name="ids[{!! $topic->id !!}]" value="{!! $topic->id !!}" form="id-form-mod">
|
||||
<label class="f-ficon" for="checkbox-{!! $topic->id !!}"></label>
|
||||
@else
|
||||
<div class="f-ficon"></div>
|
||||
@endif
|
||||
<div class="f-finfo">
|
||||
<h3>
|
||||
@if ($topic->dot)
|
||||
|
@ -170,3 +180,11 @@
|
|||
@yield ('crumbs')
|
||||
</div>
|
||||
@endif
|
||||
@if ($p->enableMod && $form = $p->formMod)
|
||||
<section class="f-moderate">
|
||||
<h2>{!! __('Moderate') !!}</h2>
|
||||
<div class="f-fdivm">
|
||||
@include ('layouts/form')
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
|
|
@ -2,7 +2,13 @@
|
|||
<form @if ($form['id']) id="{!! $form['id'] !!}" @endif class="f-form" method="post" action="{!! $form['action'] !!}" @if ($form['enctype']) enctype="{{ $form['enctype'] }}" @endif>
|
||||
@if ($form['hidden'])
|
||||
@foreach ($form['hidden'] as $key => $val)
|
||||
@if (\is_array($val))
|
||||
@foreach ($val as $k => $v)
|
||||
<input type="hidden" name="{{ $key }}[{{ $k }}]" value="{{ $v }}">
|
||||
@endforeach
|
||||
@else
|
||||
<input type="hidden" name="{{ $key }}" value="{{ $val }}">
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
|
|
25
app/templates/moderate.forkbb.php
Normal file
25
app/templates/moderate.forkbb.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
@section ('crumbs')
|
||||
<ul class="f-crumbs">
|
||||
@foreach ($p->crumbs as $cur)
|
||||
<li class="f-crumb"><!-- inline -->
|
||||
@if ($cur[0])
|
||||
<a href="{!! $cur[0] !!}" @if ($cur[2]) class="active" @endif>{{ $cur[1] }}</a>
|
||||
@else
|
||||
<span @if ($cur[2]) class="active" @endif>{{ $cur[1] }}</span>
|
||||
@endif
|
||||
</li><!-- endinline -->
|
||||
@endforeach
|
||||
</ul>
|
||||
@endsection
|
||||
@extends ('layouts/main')
|
||||
<div class="f-nav-links">
|
||||
@yield ('crumbs')
|
||||
</div>
|
||||
@if ($form = $p->form)
|
||||
<section class="f-moderate-form f-main">
|
||||
<h2>{!! $p->formTitle !!}</h2>
|
||||
<div class="f-fdiv">
|
||||
@include ('layouts/form')
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
|
@ -515,6 +515,10 @@ body,
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
#fork .f-fdiv .f-btn[name=delete] {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#fork .f-fdiv .f-label {
|
||||
font-size: 1rem;
|
||||
display: inline-block;
|
||||
|
@ -1168,6 +1172,23 @@ body,
|
|||
width: 2rem;
|
||||
}
|
||||
|
||||
#fork .f-ftlist .f-fch {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#fork .f-ftlist .f-fch + .f-ficon {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#fork .f-ftlist .f-fch:checked + .f-ficon:after {
|
||||
content: "\2713";
|
||||
color: red;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#fork .f-ftlist .f-finfo {
|
||||
width: calc(100% - 2rem);
|
||||
}
|
||||
|
@ -1741,6 +1762,33 @@ body,
|
|||
display: none;
|
||||
}
|
||||
|
||||
/*****************/
|
||||
/* Модерирование */
|
||||
/*****************/
|
||||
#fork .f-moderate {
|
||||
border-top: 0.0625rem solid #AA7939;
|
||||
margin: 1rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#fork .f-moderate .f-fdivm {
|
||||
padding: 1rem 0.625rem 0 0.625rem;
|
||||
}
|
||||
|
||||
#fork .f-moderate .f-btn {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
#fork .f-moderate .f-btns:hover .f-btn,
|
||||
#fork .f-moderate .f-btn:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#fork .f-moderate-form .f-btn {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*********************/
|
||||
/* Админка/Категории */
|
||||
/*********************/
|
||||
|
|
Loading…
Add table
Reference in a new issue