Browse Source

Add some pages for PM

Visman 4 years ago
parent
commit
1da4df45ab

+ 75 - 0
app/Models/Pages/PM.php

@@ -0,0 +1,75 @@
+<?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\Models\Page;
+use ForkBB\Models\PM\Cnst;
+use function \ForkBB\__;
+
+class PM extends Page
+{
+    /**
+     * Точка входа для приватных сообщений
+     */
+    public function action(array $args, string $method): Page
+    {
+        $second = null;
+
+        if (isset($args['second'])) {
+            if ('' === \trim($args['second'], '1234567890')) {
+                $second = (int) $args['second'];
+            } else {
+                $second = $args['second'];
+            }
+        }
+
+        $pms = $this->c->pms->init($second);
+
+        if (
+            null !== $second
+            && empty($pms->idsCurrent)
+            && empty($pms->idsArchive)
+        ) {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        $this->c->Lang->load('pm');
+
+        $action = $args['action'] ?? ($this->user->u_pm_num_new > 0 ? Cnst::ACTION_NEW : Cnst::ACTION_CURRENT);
+
+        switch ($action) {
+            case Cnst::ACTION_NEW:
+            case Cnst::ACTION_CURRENT:
+            case Cnst::ACTION_ARCHIVE:
+                $pms->area = $action;
+
+                return $this->c->PMView->view($args, $method);
+            case Cnst::ACTION_SEND:
+                $pms->area = Cnst::ACTION_CURRENT;
+
+                return $this->c->PMPost->post($args, $method);
+            case Cnst::ACTION_TOPIC:
+                return $this->c->PMTopic->topic($args, $method);
+            case Cnst::ACTION_POST:
+                return $this->c->PMTopic->post($args, $method);
+            case Cnst::ACTION_DELETE:
+                return $this->c->PMDelete->delete($args, $method);
+            case Cnst::ACTION_EDIT:
+                return $this->c->PMEdit->edit($args, $method);
+            case Cnst::ACTION_BLOCK:
+                return $this->c->PMBlock->block($args, $method);
+            case Cnst::ACTION_CONFIG:
+                return $this->c->PMConfig->config($args, $method);
+            default:
+                return $this->c->Message->message('Bad request');
+        }
+    }
+}

+ 189 - 0
app/Models/Pages/PM/AbstractPM.php

@@ -0,0 +1,189 @@
+<?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\PM;
+
+use ForkBB\Core\Container;
+use ForkBB\Models\Page;
+use ForkBB\Models\PM\Cnst;
+use ForkBB\Models\User\Model as User;
+use function \ForkBB\__;
+
+abstract class AbstractPM extends Page
+{
+    /**
+     * @var array
+     */
+    protected $pmCrumbs = [];
+
+    /**
+     * @var  ForkBB\Models\PM\Manager
+     */
+    protected $pms;
+
+    public function __construct(Container $container)
+    {
+        parent::__construct($container);
+
+        $this->pms       = $container->pms;
+        $this->pmIndex   = Cnst::ACTION_CURRENT; # string Указатель на активный пункт навигации в меню ЛС
+        $this->fIndex    = self::FI_PM;
+        $this->onlinePos = 'pm';
+        $this->robots    = 'noindex, nofollow';
+        $this->hhsLevel  = 'secure';
+    }
+
+    /**
+     * Подготовка страницы к отображению
+     */
+    public function prepare(): void
+    {
+        $this->pmNavigation = $this->pmNavigation();
+        $this->crumbs       = $this->crumbs(...$this->pmCrumbs);
+
+        parent::prepare();
+    }
+
+    /**
+     * Возвращает массив ссылок с описанием для построения навигации админки
+     */
+    protected function pmNavigation(): array
+    {
+        $r    = $this->c->Router;
+        $args = [
+            'second' => $this->pms->second,
+        ];
+        $nav  = [
+            'pm-boxes' => [true, 'PM Folders'],
+            Cnst::ACTION_NEW => [
+                $r->link('PMAction', $args + ['action' => Cnst::ACTION_NEW]),
+                $this->pms->numNew > 0 ? ['New messages %s', $this->pms->numNew] : 'New messages',
+            ],
+            Cnst::ACTION_CURRENT => [
+                $r->link('PMAction', $args + ['action' => Cnst::ACTION_CURRENT]),
+                $this->pms->numCurrent > 0 ? ['My talks %s', $this->pms->numCurrent] : 'My talks',
+            ],
+            Cnst::ACTION_ARCHIVE => [
+                $r->link('PMAction', $args + ['action' => Cnst::ACTION_ARCHIVE]),
+                $this->pms->numArchive > 0 ? ['Archive messages %s', $this->pms->numArchive] : 'Archive messages',
+            ],
+            'pm-sp1' => [null, null],
+        ];
+
+        $nav += [
+            'pm-storage' => [true, 'PM Storage'],
+            'pm-active' => [
+                false,
+                [
+                    'Active: %s',
+                    $this->user->g_pm_limit < 1 ? 0 : (int) (100 * $this->pms->totalCurrent / $this->user->g_pm_limit),
+                ],
+            ],
+            'pm-archive' => [
+                false,
+                [
+                    'Archive: %s',
+                    $this->user->g_pm_limit < 1 ? 0 : (int) (100 * $this->pms->totalArchive / $this->user->g_pm_limit),
+                ],
+            ],
+            'pm-sp2' => [null, null],
+        ];
+
+
+
+        return $nav;
+    }
+
+    /**
+     * Возвращает массив хлебных крошек
+     * Заполняет массив титула страницы
+     */
+    protected function crumbs(/* mixed */ ...$crumbs): array
+    {
+        $pms      = $this->pms;
+        $action   = $this->args['action'] ?? ($this->user->u_pm_num_new > 0 ? Cnst::ACTION_NEW : Cnst::ACTION_CURRENT);
+        $viewArea = false;
+
+        switch ($action) {
+            case Cnst::ACTION_NEW:
+            case Cnst::ACTION_CURRENT:
+            case Cnst::ACTION_ARCHIVE:
+            case Cnst::ACTION_SEND:
+            case Cnst::ACTION_TOPIC:
+            case Cnst::ACTION_POST:
+                $viewArea = true;
+                break;
+            case Cnst::ACTION_DELETE:
+            case Cnst::ACTION_EDIT:
+            case Cnst::ACTION_BLOCK:
+            case Cnst::ACTION_CONFIG:
+            default:
+                $crumbs[] = 'unknown';
+        }
+
+        if ($viewArea) {
+            if (null !== $pms->second) {
+                if (
+                    \is_int($pms->second)
+                    && ($user = $this->c->users->load($pms->second)) instanceof User
+                    && ! $user->isGuest
+                ) {
+                    $name = $user->username;
+                } else {
+                    $name = $pms->second;
+                }
+
+                switch ($pms->area) {
+                    case Cnst::ACTION_NEW:     $m = ['New messages with %s', $name]; break;
+                    case Cnst::ACTION_CURRENT: $m = ['My talks with %s', $name]; break;
+                    case Cnst::ACTION_ARCHIVE: $m = ['Archive messages with %s', $name]; break;
+                }
+            } else {
+                if ($this->targetUser instanceof User) {
+                    $crumbs[] = [
+                        $this->c->Router->link(
+                            'PMAction',
+                            [
+                                'second' => $this->targetUser->id,
+                                'action' => $pms->area,
+                            ]
+                        ),
+                        __(['"%s"', $this->targetUser->username]),
+                    ];
+                }
+
+                switch ($pms->area) {
+                    case Cnst::ACTION_NEW:     $m = 'New messages'; break;
+                    case Cnst::ACTION_CURRENT: $m = 'My talks'; break;
+                    case Cnst::ACTION_ARCHIVE: $m = 'Archive messages'; break;
+                }
+            }
+
+            $crumbs[] = [
+                $this->c->Router->link(
+                    'PMAction',
+                    [
+                        'second' => $pms->second,
+                        'action' => $pms->area,
+                    ]
+                ),
+                __($m),
+            ];
+
+            if (null === $this->title) {
+                $this->title = $m;
+            }
+        }
+
+        $crumbs[] = [$this->c->Router->link('PM'), __('PM')];
+
+        return parent::crumbs(...$crumbs);
+    }
+}

+ 204 - 0
app/Models/Pages/PM/PMPost.php

@@ -0,0 +1,204 @@
+<?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\PM;
+
+use ForkBB\Core\Validator;
+use ForkBB\Models\Page;
+use ForkBB\Models\Pages\PM\AbstractPM;
+use ForkBB\Models\Pages\PostFormTrait;
+use ForkBB\Models\Pages\PostValidatorTrait;
+use ForkBB\Models\PM\Cnst;
+use ForkBB\Models\PM\PPost;
+use ForkBB\Models\PM\PTopic;
+use InvalidArgumentException;
+use function \ForkBB\__;
+
+class PMPost extends AbstractPM
+{
+    use PostFormTrait;
+    use PostValidatorTrait;
+
+    /**
+     * Создание новой приватной темы или сообщения
+     */
+    public function post(array $args, string $method): Page
+    {
+        $this->args = $args;
+        $quote      = null;
+
+        if (
+            isset($args['more2'])
+            && '' !== \trim($args['more2'], '1234567890')
+        ) {
+            $topic = null;
+            $hash  = $args['more2'];
+            $temp  = $args;
+
+            unset($temp['more2']);
+
+            if (! $this->c->Csrf->verify($hash, 'PMAction', $temp)) {
+                return $this->c->Message->message($this->c->Csrf->getError());
+            }
+
+            $this->targetUser = $this->c->users->load($args['more1']);
+            $this->newTopic   = true;
+            $this->formTitle  = 'New PT title';
+            $this->pmCrumbs[] = [
+                $this->c->Router->link('PMAction', $args),
+                __('New dialogue'),
+            ];
+        } elseif ($this->pms->accessTopic($args['more1'])) {
+            $topic = $this->pms->load(Cnst::PTOPIC, $args['more1']);
+
+            if (isset($args['more2'])) {
+                $quote = (int) $args['more2'];
+            }
+
+            $this->targetUser = $topic->ztUser;
+            $this->pms->area  = $this->pms->inArea($topic);
+            $this->newTopic   = false;
+            $this->formTitle  = 'New PM title';
+            $this->pmCrumbs[] = [
+                $this->c->Router->link('PMAction', $args),
+                __('New message'),
+            ];
+            $this->pmCrumbs[] = $topic;
+        } else {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        $this->c->Lang->load('post');
+
+        if ('POST' === $method) {
+            $v = $this->messageValidatorPM(null, 'PMAction', $args, false, $this->newTopic);
+
+            if (
+                $v->validation($_POST)
+                && null === $v->preview
+                && (
+                    null !== $v->submit
+                    || null !== $v->archive
+                )
+            ) {
+                return $this->endPost($topic, $v);
+            }
+
+            $this->fIswev  = $v->getErrors();
+            $args['_vars'] = $v->getData();
+
+            if (
+                null !== $v->preview
+                && empty($this->fIswev)
+            ) {
+                $this->previewHtml = $this->c->censorship->censor(
+                    $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies)
+                );
+            }
+        } elseif ($quote) {
+            $quote = $this->pms->load(Cnst::PPOST, $quote);
+
+            if (
+                ! $quote instanceof PPost
+                || $topic !== $quote->parent
+            ) {
+                return $this->c->Message->message('Bad request');
+            }
+
+            $args['_vars'] = [
+                'message' => "[quote=\"{$quote->poster}\"]{$quote->message}[/quote]",
+            ];
+
+            unset($args['more2']);
+        }
+
+        $this->pmIndex    = $this->pms->area;
+        $this->nameTpl    = 'pm/post';
+        $this->form       = $this->messageFormPM(null, 'PMAction', $args, false, $this->newTopic, false);
+        $this->posts      = $this->newTopic ? null : $topic->review();
+        $this->postsTitle = 'Topic review';
+
+        return $this;
+    }
+
+    protected function messageFormPM(?Model $model, string $marker, array $args, bool $edit, bool $first, bool $quick): array
+    {
+        $form = $this->messageForm($model, $marker, $args, $edit, $first, $quick);
+
+        if ($this->newTopic) {
+            $form['btns']['archive'] = [
+                'type'  => 'submit',
+                'value' => __('Archive Send later'),
+            ];
+        } elseif (Cnst::ACTION_ARCHIVE === $this->pmIndex) {
+            $form['btns']['submit']['value'] = __('Save');
+        }
+
+        return $form;
+    }
+
+    protected function messageValidatorPM(?Model $model, string $marker, array $args, bool $edit, bool $first): Validator
+    {
+        $v = $this->messageValidator($model, $marker, $args, $edit, $first)
+            ->addRules([
+                'archive' => $this->newTopic ? 'string' : 'absent',
+                'message' => 'required|string:trim|max:65535 bytes|check_message',
+            ]);
+
+        return $v;
+    }
+
+    /**
+     * Создание приватной темы/сообщения
+     */
+    protected function endPost(?PTopic $topic, Validator $v): Page
+    {
+        $now = \time();
+
+        if (! $topic instanceof PTopic) {
+            $topic            = $this->pms->create(Cnst::PTOPIC);
+            $topic->subject   = $v->subject;
+            $topic->sender    = $this->user;
+            $topic->recipient = $this->targetUser;
+            $topic->status    = null !== $v->archive ? Cnst::PT_ARCHIVE : Cnst::PT_NORMAL;
+
+            $this->pms->insert(Cnst::PTOPIC, $topic);
+        }
+
+        $post               = $this->pms->create(Cnst::PPOST);
+        $post->user         = $this->user;
+        $post->poster_ip    = $this->user->ip;
+        $post->message      = $v->message;
+        $post->hide_smilies = $v->hide_smilies ? 1 : 0;
+        $post->posted       = $now;
+        $post->topic_id     = $topic->id;
+
+        $this->pms->insert(Cnst::PPOST, $post);
+
+        if ($this->newTopic) {
+            $topic->first_post_id = $post->id;
+        }
+
+        $this->user->u_pm_last_post = $now;
+
+        $this->pms->update(Cnst::PTOPIC, $topic->calcStat());
+        $this->pms->updateFromPTopics(true, $topic);
+
+        if (null !== $v->archive) {
+            $message = 'PM to archive Redirect';
+        } elseif ($this->newTopic) {
+            $message = 'PM created Redirect';
+        } else {
+            $message = 'PM sent Redirect';
+        }
+
+        return $this->c->Redirect->url($post->link)->message($message);
+    }
+}

+ 133 - 0
app/Models/Pages/PM/PMTopic.php

@@ -0,0 +1,133 @@
+<?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\PM;
+
+use ForkBB\Models\Page;
+use ForkBB\Models\Pages\PM\AbstractPM;
+use ForkBB\Models\Pages\PostFormTrait;
+use ForkBB\Models\PM\Cnst;
+use ForkBB\Models\PM\PPost;
+use ForkBB\Models\PM\PTopic;
+use function \ForkBB\__;
+
+class PMTopic extends AbstractPM
+{
+    use PostFormTrait;
+
+    /**
+     * Отображает приватный диалог по номеру поста
+     */
+    public function post(array $args, string $method): Page
+    {
+        if (
+            ! isset($args['more1'])
+            || isset($args['more2'])
+        ) {
+            return $this->c->Message->message('Bad request');
+        }
+
+        $post = $this->pms->load(Cnst::PPOST, $args['more1']);
+
+        if (! $post instanceof PPost) {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        $this->model = $post->parent;
+
+        $this->model->calcPage($post->id);
+
+        return $this->view($args, $method);
+    }
+
+    /**
+     * Отображает приватный диалог по его номеру
+     */
+    public function topic(array $args, string $method): Page
+    {
+        if (! isset($args['more1'])) {
+            return $this->c->Message->message('Bad request');
+        }
+
+        $this->model = $this->pms->load(Cnst::PTOPIC, $args['more1']);
+
+        if (! $this->model instanceof PTopic) {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        if (! isset($args['more2'])) {
+            $this->model->page = 1;
+        } elseif ('new' === $args['more2']) {
+            $new = $this->model->firstNew;
+
+            if ($new > 0) {
+                $new = $this->pms->load(Cnst::PPOST, $new);
+            }
+
+            if ($new instanceof PPost) {
+                return $this->c->Redirect->url($new->link);
+            } else {
+                return $this->c->Redirect->url($this->model->linkLast);
+            }
+        } elseif ('' === \trim($args['more2'], '1234567890')) {
+            $this->model->page = (int) $args['more2'];
+        } else {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        return $this->view($args, $method);
+    }
+
+
+    /**
+     * Подготовка данных для шаблона
+     */
+    protected function view(array $args, string $method): Page
+    {
+        if (! $this->model->hasPage()) {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        $this->posts = $this->model->pageData();
+
+        if (
+            empty($this->posts)
+            && $this->model->page > 1
+        ) {
+            return $this->c->Redirect->url($this->model->link);
+        }
+
+        $this->c->Lang->load('topic');
+
+        $this->args       = $args;
+        $this->targetUser = $this->model->ztUser;
+        $this->pms->area  = $this->pms->inArea($this->model);
+        $this->pmIndex    = $this->pms->area;
+        $this->nameTpl    = 'pm/topic';
+        $this->pmCrumbs[] = $this->model;
+
+        if (
+            $this->model->canReply
+            && '1' == $this->c->config->o_quickpost
+        ) {
+            $form = $this->messageForm(null, 'PMAction', $this->model->dataReply, false, false, true);
+
+            if (Cnst::ACTION_ARCHIVE === $this->pmIndex) {
+                $form['btns']['submit']['value'] = __('Save');
+            }
+
+            $this->form = $form;
+        }
+
+        $this->model->updateVisit();
+
+        return $this;
+    }
+}

+ 45 - 0
app/Models/Pages/PM/PMView.php

@@ -0,0 +1,45 @@
+<?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\PM;
+
+use ForkBB\Core\Validator;
+use ForkBB\Models\Page;
+use ForkBB\Models\Pages\PM\AbstractPM;
+use ForkBB\Models\Forum\Model as Forum;
+use ForkBB\Models\User\Model as User;
+use InvalidArgumentException;
+use function \ForkBB\__;
+
+class PMView extends AbstractPM
+{
+    /**
+     * Списки новых, текущих и архивных приватных топиков
+     */
+    public function view(array $args, string $method): Page
+    {
+        $this->args      = $args;
+        $this->pms->page = $args['more1'] ?? 1;
+
+        if (
+            isset($args['more2'])
+            || ! $this->pms->hasPage()
+        ) {
+            return $this->c->Message->message('Not Found', true, 404);
+        }
+
+        $this->pmIndex    = $this->pms->area;
+        $this->nameTpl    = 'pm/view';
+        $this->pmList     = $this->pms->pmListCurPage();
+        $this->pagination = $this->pms->pagination;
+
+        return $this;
+    }
+}