Sfoglia il codice sorgente

Add moderate functions for topic page

Visman 5 anni fa
parent
commit
410b8b4e5c

+ 5 - 6
app/Models/Pages/Forum.php

@@ -48,7 +48,7 @@ class Forum extends Page
             $this->c->Lang->load('misc');
 
             $this->enableMod = true;
-            $this->formMod   = $this->formMod($forum->id, $forum->page);
+            $this->formMod   = $this->formMod($forum);
         }
 
         return $this;
@@ -57,20 +57,19 @@ class Forum extends Page
     /**
      * Создает массив данных для формы модерации
      *
-     * @param int $id
-     * @param int $page
+     * @param ForumModel $forum
      *
      * @return array
      */
-    protected function formMod(int $id, int $page): array
+    protected function formMod(ForumModel $forum): array
     {
         $form = [
             'id'     => 'id-form-mod',
             'action' => $this->c->Router->link('Moderate'),
             'hidden' => [
                 'token' => $this->c->Csrf->create('Moderate'),
-                'forum' => $id,
-                'page'  => $page,
+                'forum' => $forum->id,
+                'page'  => $forum->page,
                 'step'  => 1,
             ],
             'sets'   => [],

+ 196 - 59
app/Models/Pages/Moderate.php

@@ -7,20 +7,29 @@ use ForkBB\Core\Validator;
 use ForkBB\Models\Page;
 use ForkBB\Models\Forum\Model as Forum;
 use ForkBB\Models\Topic\Model as Topic;
+use ForkBB\Models\Post\Model as Post;
 
 class Moderate extends Page
 {
+    const INFORUM = 1; // действие для форума
+    const INTOPIC = 2; // действие для темы
+    const TOTOPIC = 4; // список постов сменить на тему
+    const IFTOTPC = 8; // список постов сменить на тему, если в нем только первый пост темы
+
     /**
      * Список действий
      * @var array
      */
     protected $actions = [
-        'open'   => true,
-        'close'  => true,
-        'delete' => true,
-        'move'   => true,
-        'merge'  => true,
-        'cancel' => true,
+        '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,
+        'unstick' => self::INTOPIC + self::TOTOPIC,
+        'stick'   => self::INTOPIC + self::TOTOPIC,
+        'split'   => self::INTOPIC,
     ];
 
     /**
@@ -43,7 +52,7 @@ class Moderate extends Page
     /**
      * Составление списка категорий/разделов для выбора
      */
-    protected function calcList(int $curForum): void
+    protected function calcList(int $curForum, bool $noUseCurForum = true): void
     {
         $cid     = null;
         $options = [];
@@ -58,7 +67,7 @@ class Moderate extends Page
 
                 $indent = \str_repeat(\ForkBB\__('Forum indent'), $f->depth);
 
-                if ($f->redirect_url || $f->id === $curForum) {
+                if ($f->redirect_url || ($noUseCurForum && $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];
@@ -81,28 +90,38 @@ class Moderate extends Page
     public function vActionProcess(Validator $v, $action)
     {
         if (empty($v->getErrors())) {
-            $sum = 0;
+            $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) {
+            // нажата не одна кнопка или недоступная кнопка
+            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) {
-                $this->calcList($v->forum);
+            // перенос тем или разделение постов
+            } 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)) {
                     $v->addError('Invalid destination');
+                } elseif ('split' === $action && 1 === $v->confirm && '' == $v->subject) {
+                    $v->addError('No subject');
                 }
             }
         }
@@ -132,6 +151,7 @@ class Moderate extends Page
                 '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',
@@ -139,6 +159,9 @@ class Moderate extends Page
                 'move'        => 'string',
                 'merge'       => 'string',
                 'cancel'      => 'string',
+                'unstick'     => 'string',
+                'stick'       => 'string',
+                'split'       => 'string',
                 'action'      => 'action_process',
             ])->addAliases([
             ])->addArguments([
@@ -166,7 +189,27 @@ class Moderate extends Page
             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,
@@ -180,6 +223,7 @@ class Moderate extends Page
                     return $this->c->Message->message('Bad request');
                 }
             }
+
             $this->backLink = $this->c->Router->link('Forum', [
                 'id'   => $this->curForum->id,
                 'name' => $this->curForum->forum_name,
@@ -201,7 +245,7 @@ class Moderate extends Page
             case 1:
                 $this->formTitle   = \ForkBB\__('Open topics');
                 $this->buttonValue = \ForkBB\__('Open');
-                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $v->topic ? $this->curTopic : $this->curForum);
                 $this->form        = $this->formConfirm($topics, $v);
                 return $this;
             case 2:
@@ -224,7 +268,7 @@ class Moderate extends Page
             case 1:
                 $this->formTitle   = \ForkBB\__('Close topics');
                 $this->buttonValue = \ForkBB\__('Close');
-                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $v->topic ? $this->curTopic : $this->curForum);
                 $this->form        = $this->formConfirm($topics, $v);
                 return $this;
             case 2:
@@ -241,11 +285,13 @@ class Moderate extends Page
         }
     }
 
-    protected function actionDelete(array $topics, Validator $v): Page
+    protected function actionDelete(array $objects, Validator $v): Page
     {
         if (! $this->user->isAdmin) { //???? разобраться с правами на удаление
-            foreach ($topics as $topic) {
-                if (isset($this->c->admins->list[$topic->poster_id])) {
+            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); //???? причина
                 }
             }
@@ -253,18 +299,24 @@ class Moderate extends Page
 
         switch ($v->step) {
             case 1:
-                $this->formTitle   = \ForkBB\__('Delete topics');
+                $this->formTitle   = \ForkBB\__(true === $this->processAsPosts ? 'Delete posts' : 'Delete topics');
                 $this->buttonValue = \ForkBB\__('Delete');
-                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
-                $this->form        = $this->formConfirm($topics, $v);
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $v->topic ? $this->curTopic : $this->curForum);
+                $this->form        = $this->formConfirm($objects, $v);
                 return $this;
             case 2:
                 if (1 === $v->confirm) {
-                    $this->c->topics->delete(...$topics);
+                    if (true === $this->processAsPosts) {
+                        $this->c->posts->delete(...$objects);
+                        $message = 'Delete posts redirect';
+                    } else {
+                        $this->c->topics->delete(...$objects);
+                        $message = 'Delete topics redirect';
+                    }
 
-                    return $this->c->Redirect->url($this->curForum->link)->message('Delete topics redirect');
+                    return $this->c->Redirect->url($this->curForum->link)->message($message);
                 } else {
-                    return $this->actionCancel($topics, $v);
+                    return $this->actionCancel($objects, $v);
                 }
             default:
                 return $this->c->Message->message('Bad request');
@@ -277,7 +329,7 @@ class Moderate extends Page
             case 1:
                 $this->formTitle   = \ForkBB\__('Move topics');
                 $this->buttonValue = \ForkBB\__('Move');
-                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $this->curForum);
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('Moderate'), $v->topic ? $this->curTopic : $this->curForum);
                 $this->chkRedirect = true;
                 $this->form        = $this->formConfirm($topics, $v);
                 return $this;
@@ -330,6 +382,86 @@ class Moderate extends Page
         }
     }
 
+    protected function actionUnstick(array $topics, Validator $v): Page
+    {
+        switch ($v->step) {
+            case 1:
+                $this->formTitle   = \ForkBB\__('Unstick topics');
+                $this->buttonValue = \ForkBB\__('Unstick');
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('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);
+                    }
+
+                    $message = 1 === \count($topics) ? 'Unstick topic redirect' : 'Unstick 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 actionStick(array $topics, Validator $v): Page
+    {
+        switch ($v->step) {
+            case 1:
+                $this->formTitle   = \ForkBB\__('Stick topics');
+                $this->buttonValue = \ForkBB\__('Stick');
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('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);
+                    }
+
+                    $message = 1 === \count($topics) ? 'Stick topic redirect' : 'Stick 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 actionSplit(array $posts, Validator $v): Page
+    {
+        switch ($v->step) {
+            case 1:
+                $this->formTitle   = \ForkBB\__('Split posts');
+                $this->buttonValue = \ForkBB\__('Split');
+                $this->needSubject = true;
+                $this->crumbs      = $this->crumbs($this->formTitle, \ForkBB\__('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');
+        }
+    }
+
     /**
      * Подготавливает массив данных для формы подтверждения
      *
@@ -346,25 +478,26 @@ class Moderate extends Page
                 '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 = [];
-        $ids     = [];
         foreach ($objects as $object) {
             if ($object instanceof Topic) {
                 $headers[] = \ForkBB\__('Topic «%s»', \ForkBB\cens(($object->subject)));
-                $ids[]     = $object->id;
+            } else {
+                $headers[] = \ForkBB\__('Post «%1$s by %2$s»', \ForkBB\dt($object->posted), $object->poster);
             }
         }
 
-        $form['hidden']['ids'] = $ids;
         $form['sets']['info'] = [
             'info' => [
                 'info1' => [
@@ -383,44 +516,48 @@ class Moderate extends Page
             ];
         }
 
+        $fields = [];
+
+        if ($this->needSubject) {
+            $fields['subject'] = [
+                'type'      => 'text',
+                'maxlength' => 70,
+                'caption'   => \ForkBB\__('New subject'),
+                'required'  => true,
+                'value'     => '' == $v->subject ? $this->curTopic->subject : $v->subject,
+                'autofocus' => $autofocus,
+            ];
+            $autofocus = null;
+        }
+
         if ($this->listForOptions) {
-            $form['sets']['destination'] = [
-                'fields' => [
-                    'destination' => [
-                        'type'     => 'select',
-                        'options'  => $this->listForOptions,
-                        'value'    => null,
-                        'caption'  => \ForkBB\__('Move to'),
-                        'required' => true,
-                    ],
-                ],
+            $fields['destination'] = [
+                'type'      => 'select',
+                'options'   => $this->listForOptions,
+                'value'     => null,
+                'caption'   => \ForkBB\__('Move to'),
+                'required'  => true,
+                'autofocus' => $autofocus,
             ];
+            $autofocus = null;
         }
 
         if (true === $this->chkRedirect) {
-            $form['sets']['redirect'] = [
-                'fields' => [
-                    'redirect' => [
-                        'type'    => 'checkbox',
-                        'label'   => \ForkBB\__('Leave redirect'),
-                        'value'   => '1',
-                        'checked' => true,
-                    ],
-                ],
+            $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,
-                ],
-            ],
+        $fields['confirm'] = [
+            'type'    => 'checkbox',
+            'label'   => \ForkBB\__('Confirm action'),
+            'value'   => '1',
+            'checked' => false,
         ];
-
+        $form['sets']['moderate']['fields'] = $fields;
         $form['btns'][$v->action] = [
             'type'      => 'submit',
             'value'     => $this->buttonValue,

+ 73 - 0
app/Models/Pages/Topic.php

@@ -155,6 +155,13 @@ class Topic extends Page
             $this->form     = $this->messageForm(['id' => $topic->id], $topic, 'NewReply', false, false, true);
         }
 
+        if ($this->user->isAdmin || $this->user->isModerator($topic)) {
+            $this->c->Lang->load('misc');
+
+            $this->enableMod = true;
+            $this->formMod   = $this->formMod($topic);
+        }
+
         if ($topic->showViews) {
             $topic->incViews();
         }
@@ -162,4 +169,70 @@ class Topic extends Page
 
         return $this;
     }
+
+    /**
+     * Создает массив данных для формы модерации
+     *
+     * @param TopicModel $topic
+     *
+     * @return array
+     */
+    protected function formMod(TopicModel $topic): array
+    {
+        $form = [
+            'id'     => 'id-form-mod',
+            'action' => $this->c->Router->link('Moderate'),
+            'hidden' => [
+                'token' => $this->c->Csrf->create('Moderate'),
+                'forum' => $topic->parent->id,
+                'topic' => $topic->id,
+                'page'  => $topic->page,
+                'ids'   => [$topic->first_post_id => $topic->first_post_id],
+                'step'  => 1,
+            ],
+            'sets'   => [],
+            'btns'   => [],
+        ];
+
+        if ($topic->closed) {
+            $form['btns']['open'] = [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Open topic'),
+            ];
+        } else {
+            $form['btns']['close'] = [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Close topic'),
+            ];
+        }
+
+        if ($topic->sticky) {
+            $form['btns']['unstick'] = [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Unstick topic'),
+            ];
+        } else {
+            $form['btns']['stick'] = [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Stick topic'),
+            ];
+        }
+
+        $form['btns'] += [
+            'move' => [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Move topic'),
+            ],
+            'delete' => [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Delete'),
+            ],
+            'split' => [
+                'type'      => 'submit',
+                'value'     => \ForkBB\__('Split'),
+            ],
+        ];
+
+        return $form;
+    }
 }

+ 48 - 0
app/Models/Post/Move.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace ForkBB\Models\Post;
+
+use ForkBB\Models\Action;
+use ForkBB\Models\Topic\Model as Topic;
+use ForkBB\Models\Post\Model as Post;
+
+class Move extends Action
+{
+    /**
+     * Перенос сообщений
+     *
+     * @param bool $useFrom
+     * @param Topic $toTopic
+     * @param Post ...$posts
+     */
+    public function move(bool $useFrom, Topic $toTopic, Post ...$posts): void
+    {
+        $topics = [
+            $toTopic->id => $toTopic,
+        ];
+
+        foreach ($posts as $post) {
+            $topics[$post->topic_id] = $post->parent;
+
+            if ($useFrom) {
+                $post->message = "[from]{$post->parent->subject}[/from]\n" . $post->message;
+            }
+            $post->topic_id = $toTopic->id;
+            $this->c->posts->update($post);
+        }
+
+        //???? переиндексация поискового индекса? для первого сообщения?
+        //???? перерасчет количества тем у пользователей? или нет?
+
+        $forums = [];
+        foreach ($topics as $topic) {
+            $forums[$topic->forum_id] = $topic->parent;
+
+            $this->c->topics->update($topic->calcStat());
+        }
+
+        foreach ($forums as $forum) {
+            $this->c->forums->update($forum->calcStat());
+        }
+    }
+}

+ 1 - 0
app/config/main.dist.php

@@ -262,6 +262,7 @@ return [
         'PostManagerRebuildIndex' => \ForkBB\Models\Post\RebuildIndex::class,
         'PostManagerUserInfoFromIP' => \ForkBB\Models\Post\UserInfoFromIP::class,
         'PostManagerUserStat'     => \ForkBB\Models\Post\UserStat::class,
+        'PostManagerMove'         => \ForkBB\Models\Post\Move::class,
 
         'ReportModel'             => \ForkBB\Models\Report\Model::class,
         'ReportManagerSave'       => \ForkBB\Models\Report\Save::class,

+ 3 - 24
app/lang/en/common.po

@@ -364,30 +364,6 @@ msgstr "Jump to"
 msgid "Go"
 msgstr " Go "
 
-msgid "Moderate topic"
-msgstr "Moderate topic"
-
-msgid "All"
-msgstr "All"
-
-msgid "Move topic"
-msgstr "Move topic"
-
-msgid "Open topic"
-msgstr "Open topic"
-
-msgid "Close topic"
-msgstr "Close topic"
-
-msgid "Unstick topic"
-msgstr "Unstick topic"
-
-msgid "Stick topic"
-msgstr "Stick topic"
-
-msgid "Moderate forum"
-msgstr "Moderate forum"
-
 msgid "Powered by"
 msgstr "Powered by <a href=\"https://github.com/forkbb\">ForkBB</a>"
 
@@ -522,3 +498,6 @@ msgstr "Your passphrase"
 
 msgid "Invalid passphrase"
 msgstr "Invalid passphrase"
+
+msgid "Stick topic"
+msgstr "Stick topic"

+ 47 - 2
app/lang/en/misc.po

@@ -93,8 +93,8 @@ msgstr "Your subscription has been removed. Redirecting …"
 msgid "Moderate"
 msgstr "Moderate"
 
-msgid "Select"
-msgstr "Select"
+msgid "Select for moderation"
+msgstr "Select for moderation"
 
 msgid "Move"
 msgstr "Move"
@@ -171,9 +171,15 @@ msgstr "You must select at least two topics for merge."
 msgid "Stick topic redirect"
 msgstr "Topic sticked. Redirecting …"
 
+msgid "Stick topics redirect"
+msgstr "Topics sticked. Redirecting …"
+
 msgid "Unstick topic redirect"
 msgstr "Topic unsticked. Redirecting …"
 
+msgid "Unstick topics redirect"
+msgstr "Topics unsticked. Redirecting …"
+
 msgid "Merge topics"
 msgstr "Merge topics"
 
@@ -186,6 +192,9 @@ msgstr "Please confirm merge"
 msgid "New subject"
 msgstr "New subject"
 
+msgid "No subject"
+msgstr "No subject for new topic."
+
 msgid "Confirm split legend"
 msgstr "Please confirm split of selected posts and select destination of move."
 
@@ -234,6 +243,9 @@ msgstr "This post is moved from topic"
 msgid "Topic «%s»"
 msgstr "Topic «%s»"
 
+msgid "Post «%1$s by %2$s»"
+msgstr "Post «%1$s by %2$s»"
+
 msgid "All posts will be posted in the «%s» topic"
 msgstr "All posts will be posted in the «%s» topic."
 
@@ -263,3 +275,36 @@ msgstr ""
 
 msgid "Forum indent"
 msgstr "◦ ◦ "
+
+msgid "Moderate topic"
+msgstr "Moderate topic"
+
+msgid "All"
+msgstr "All"
+
+msgid "Move topic"
+msgstr "Move topic"
+
+msgid "Open topic"
+msgstr "Open topic"
+
+msgid "Close topic"
+msgstr "Close topic"
+
+msgid "Unstick topic"
+msgstr "Unstick topic"
+
+msgid "Unstick topics"
+msgstr "Unstick topics"
+
+msgid "Unstick"
+msgstr "Unstick"
+
+msgid "Stick topics"
+msgstr "Stick topics"
+
+msgid "Stick"
+msgstr "Stick"
+
+msgid "Moderate forum"
+msgstr "Moderate forum"

+ 3 - 24
app/lang/ru/common.po

@@ -366,30 +366,6 @@ msgstr "Перейти"
 msgid "Go"
 msgstr " Иди "
 
-msgid "Moderate topic"
-msgstr "Модерирование темы"
-
-msgid "All"
-msgstr "All"
-
-msgid "Move topic"
-msgstr "Перенести тему"
-
-msgid "Open topic"
-msgstr "Открыть тему"
-
-msgid "Close topic"
-msgstr "Закрыть тему"
-
-msgid "Unstick topic"
-msgstr "Снять выделение темы"
-
-msgid "Stick topic"
-msgstr "Выделить тему"
-
-msgid "Moderate forum"
-msgstr "Модерирование раздела"
-
 msgid "Powered by"
 msgstr "Под управлением <a href=\"https://github.com/forkbb\">ForkBB</a>"
 
@@ -524,3 +500,6 @@ msgstr "Ваша кодовая фраза"
 
 msgid "Invalid passphrase"
 msgstr "Неверная кодовая фраза"
+
+msgid "Stick topic"
+msgstr "Выделить тему"

+ 47 - 2
app/lang/ru/misc.po

@@ -93,8 +93,8 @@ msgstr "Подписка удалена. Переадресация &hellip;"
 msgid "Moderate"
 msgstr "Модерирование"
 
-msgid "Select"
-msgstr "Выбрать"
+msgid "Select for moderation"
+msgstr "Выбрать для модерирования"
 
 msgid "Move"
 msgstr "Перенести"
@@ -171,9 +171,15 @@ msgstr "Вы должны выбрать хотя бы две темы для о
 msgid "Stick topic redirect"
 msgstr "Тема выделена. Переадресация &hellip;"
 
+msgid "Stick topics redirect"
+msgstr "Темы выделены. Переадресация &hellip;"
+
 msgid "Unstick topic redirect"
 msgstr "Выделение темы снято. Переадресация &hellip;"
 
+msgid "Unstick topics redirect"
+msgstr "Выделение тем снято. Переадресация &hellip;"
+
 msgid "Merge topics"
 msgstr "Объединение тем"
 
@@ -186,6 +192,9 @@ msgstr "Пожалуйста, подтвердите объединение"
 msgid "New subject"
 msgstr "Название новой темы"
 
+msgid "No subject"
+msgstr "Не задан заголовок для новой темы."
+
 msgid "Confirm split legend"
 msgstr "Пожалуйста, подтвердите разделение выбранных сообщений и укажите место переноса."
 
@@ -234,6 +243,9 @@ msgstr "Это сообщение перенесено из темы"
 msgid "Topic «%s»"
 msgstr "Тема «%s»"
 
+msgid "Post «%1$s by %2$s»"
+msgstr "Сообщение «%1$s от %2$s»"
+
 msgid "All posts will be posted in the «%s» topic"
 msgstr "Все сообщения будут размещены в теме «%s»."
 
@@ -263,3 +275,36 @@ msgstr ""
 
 msgid "Forum indent"
 msgstr "◦ ◦ "
+
+msgid "Moderate topic"
+msgstr "Модерирование темы"
+
+msgid "All"
+msgstr "All"
+
+msgid "Move topic"
+msgstr "Перенести тему"
+
+msgid "Open topic"
+msgstr "Открыть тему"
+
+msgid "Close topic"
+msgstr "Закрыть тему"
+
+msgid "Unstick topic"
+msgstr "Снять выделение темы"
+
+msgid "Unstick topics"
+msgstr "Снятие выделение тем"
+
+msgid "Unstick"
+msgstr "Снять выделение"
+
+msgid "Stick topics"
+msgstr "Выделение тем"
+
+msgid "Stick"
+msgstr "Выделить"
+
+msgid "Moderate forum"
+msgstr "Модерирование раздела"

+ 2 - 2
app/templates/forum.forkbb.php

@@ -86,7 +86,7 @@
             <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>
+              <label class="f-ficon" for="checkbox-{!! $topic->id !!}" title="{{ __('Select for moderation') }}"></label>
             @else
               <div class="f-ficon"></div>
             @endif
@@ -100,7 +100,7 @@
             <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>
+              <label class="f-ficon" for="checkbox-{!! $topic->id !!}" title="{{ __('Select for moderation') }}"></label>
             @else
               <div class="f-ficon"></div>
             @endif

+ 15 - 0
app/templates/topic.forkbb.php

@@ -60,9 +60,16 @@
         @include ('layouts/iswev')
     @else
       <article id="p{!! $post->id !!}" class="f-post @if (1 == $post->user->gender) f-user-male @elseif (2 == $post->user->gender) f-user-female @endif @if ($post->user->online) f-user-online @endif @if (1 === $post->postNumber) f-post-first @endif">
+        @if ($p->enableMod && $post->postNumber > 1)
+        <input id="checkbox-{!! $post->id !!}" class="f-post-checkbox" type="checkbox" name="ids[{!! $post->id !!}]" value="{!! $post->id !!}" form="id-form-mod">
+        @endif
         <header class="f-post-header">
           <h3>@if ($post->postNumber > 1) {!! __('Re') !!} @endif {{ cens($p->model->subject) }}</h3>
+        @if ($p->enableMod && $post->postNumber > 1)
+          <label class="f-post-posted" for="checkbox-{!! $post->id !!}" title="{{ __('Select for moderation') }}"><time datetime="{{ utc($post->posted) }}">{{ dt($post->posted) }}</time></label>
+        @else
           <span class="f-post-posted"><time datetime="{{ utc($post->posted) }}">{{ dt($post->posted) }}</time></span>
+        @endif
         @if ($post->edited)
           <span class="f-post-edited" title="{!! __('Last edit', $post->edited_by, dt($post->edited)) !!}">{!! __('Edited') !!}</span>
         @endif
@@ -164,3 +171,11 @@
       </div>
     </section>
 @endif
+@if ($p->enableMod && $form = $p->formMod)
+    <section class="f-moderate">
+      <h2>{!! __('Moderate') !!}</h2>
+      <div class="f-fdivm">
+    @include ('layouts/form')
+      </div>
+    </section>
+@endif

+ 30 - 17
public/style/ForkBB/style.css

@@ -774,6 +774,7 @@ body,
 #fork .f-nav-links .f-actions-links {
   margin-left: auto;
   order: 1;
+  text-align: right;
 }
 
 #fork .f-nav-links .f-page {
@@ -1176,23 +1177,6 @@ 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);
 }
@@ -1797,6 +1781,35 @@ body,
   display: inline-block;
 }
 
+#fork .f-ftlist .f-fch,
+#fork .f-post-checkbox {
+  display: none;
+}
+
+#fork .f-ftlist label.f-ficon {
+  position: relative;
+  cursor: pointer;
+}
+
+#fork .f-post-checkbox + .f-post-header {
+  position: relative;
+}
+
+#fork label.f-post-posted {
+  cursor: pointer;
+}
+
+#fork .f-ftlist .f-fch:checked + .f-ficon:after,
+#fork .f-post-checkbox:checked + .f-post-header:before {
+  content: "✔";
+  color: red;
+  position: absolute;
+  bottom: 0;
+  left: 0.625rem;
+  font-size: 1rem;
+  font-weight: bold;
+}
+
 /*********************/
 /* Админка/Категории */
 /*********************/