Browse Source

Add Models\PM\PTopic

Visman 4 years ago
parent
commit
8fd97d4140
1 changed files with 455 additions and 0 deletions
  1. 455 0
      app/Models/PM/PTopic.php

+ 455 - 0
app/Models/PM/PTopic.php

@@ -0,0 +1,455 @@
+<?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\PM;
+
+use ForkBB\Core\Container;
+use ForkBB\Models\DataModel;
+use ForkBB\Models\PM\Cnst;
+use ForkBB\Models\User\Model as User;
+use PDO;
+use RuntimeException;
+
+class PTopic extends DataModel
+{
+    public function __construct(Container $container)
+    {
+        parent::__construct($container);
+
+        $this->zDepend = [
+            'id'            => ['link', 'hasNew', 'linkNew', 'firstNew', 'pagination', 'dataReply', 'linkReply'],
+            'last_number'   => ['last_poster'],
+            'last_post_id'  => ['linkLast'],
+            'num_replies'   => ['numPages', 'pagination'],
+            'poster'        => ['last_poster', 'byOrFor', 'zpUser', 'ztUser'],
+            'poster_id'     => ['closed', 'firstNew', 'zp', 'zt', 'zpUser', 'ztUser'],
+            'poster_status' => ['closed'],
+            'poster_visit'  => ['firstNew'],
+            'subject'       => ['name'],
+            'target'        => ['last_poster', 'byOrFor', 'zpUser', 'ztUser'],
+            'target_id'     => ['closed', 'firstNew', 'zp', 'zt', 'zpUser', 'ztUser'],
+            'target_status' => ['closed'],
+            'target_visit'  => ['firstNew'],
+        ];
+    }
+
+    /**
+     * Префикс текущего пользователя
+     */
+    protected function getzp(): string
+    {
+        if ($this->poster_id === $this->c->user->id) {
+            return 'poster';
+        } elseif ($this->target_id === $this->c->user->id) {
+            return 'target';
+        } else {
+            throw new RuntimeException('Bad current user');
+        }
+    }
+
+    /**
+     * Префикс второго пользователя
+     */
+    protected function getzt(): string
+    {
+        if ($this->poster_id === $this->c->user->id) {
+            return 'target';
+        } elseif ($this->target_id === $this->c->user->id) {
+            return 'poster';
+        } else {
+            throw new RuntimeException('Bad current user');
+        }
+    }
+
+    /**
+     * Возвращает отцензурированное название темы
+     */
+    protected function getname(): ?string
+    {
+        return $this->censorSubject;
+    }
+
+    /**
+     * Ссылка на тему
+     */
+    protected function getlink(): string
+    {
+        return $this->c->Router->link(
+            'PMAction',
+            [
+                'second' => $this->c->pms->second,
+                'action' => Cnst::ACTION_TOPIC,
+                'more1'  => $this->id,
+            ]
+        );
+    }
+
+    /**
+     * Ссылка для перехода на последнее сообщение темы
+     */
+    protected function getlinkLast(): string
+    {
+        if ($this->last_post_id < 1) {
+            return '';
+        } else {
+            return $this->c->Router->link(
+                'PMAction',
+                [
+                    'second'  => $this->c->pms->second,
+                    'action'  => Cnst::ACTION_POST,
+                    'more1'   => $this->last_post_id,
+                    'numPost' => $this->last_post_id,
+                ]
+            );
+        }
+    }
+
+    /**
+     * Ссылка для перехода на первое новое сообщение в теме
+     */
+    protected function getlinkNew(): string
+    {
+        return $this->c->Router->link(
+            'PMAction',
+            [
+                'second' => $this->c->pms->second,
+                'action' => Cnst::ACTION_TOPIC,
+                'more1'  => $this->id,
+                'more2'  => 'new',
+            ]
+        );
+    }
+
+    /**
+     * Номер первого нового сообщения в теме
+     */
+    protected function getfirstNew(): int
+    {
+        if (! $this->hasNew) {
+            return 0;
+        }
+
+        $vars = [
+            ':tid'   => $this->id,
+            ':visit' => $this->{"{$this->zp}_visit"},
+        ];
+        $query = 'SELECT MIN(pp.id)
+            FROM ::pm_posts AS pp
+            WHERE pp.topic_id=?i:tid AND pp.posted>?i:visit';
+
+        $pid = $this->c->DB->query($query, $vars)->fetchColumn();
+
+        return $pid ?: 0;
+    }
+
+    protected function setsender(User $user): void
+    {
+        $this->poster    = $user->username;
+        $this->poster_id = $user->id;
+    }
+
+    protected function setrecipient(User $user): void
+    {
+        $this->target    = $user->username;
+        $this->target_id = $user->id;
+    }
+
+    protected function user(string $prx): User
+    {
+        $user = $this->c->users->load($this->{"{$prx}_id"});
+
+        if (! $user instanceof User) {
+            throw new RuntimeException('User model could not be loaded ');
+        } elseif ($user->isGuest) {
+            $user = clone $user;
+
+            $user->__username = $this->{$prx};
+        }
+
+        return $user;
+    }
+
+    protected function getzpUser(): User
+    {
+        return $this->user($this->zp);
+    }
+
+    protected function getztUser(): User
+    {
+        return $this->user($this->zt);
+    }
+
+    protected function setstatus(int $status): void
+    {
+        if ('poster' === $this->zp) {
+            $tStatus = $status;
+
+            switch ($status) {
+                case Cnst::PT_ARCHIVE:
+                    $tStatus = Cnst::PT_NOTSENT;
+                case Cnst::PT_DELETED:
+                case Cnst::PT_NORMAL:
+                    $this->poster_status = $status;
+
+                    if (null === $this->target_status) {
+                        $this->target_status = $tStatus;
+                    }
+
+                    return;
+            }
+        } else {
+            switch ($status) {
+                case Cnst::PT_ARCHIVE:
+                case Cnst::PT_DELETED:
+                    $this->target_status = $status;
+
+                    return;
+            }
+        }
+
+        throw new RuntimeException("Bad status: {$status}");
+    }
+
+    /**
+     * Возвращает имя автора последнего поста или пустую строку
+     */
+    protected function getlast_poster(): string
+    {
+        if (0 === $this->last_number) {
+            return $this->poster;
+        } elseif (1 === $this->last_number) {
+            return $this->target;
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Статус наличия новых сообщений в теме
+     */
+    protected function gethasNew(): bool
+    {
+        return isset($this->c->pms->idsNew[$this->id]);
+    }
+
+    /**
+     * Статус закрытия темы
+     */
+    protected function getclosed(): bool
+    {
+        $p = $this->{"{$this->zp}_status"};
+        $t = $this->{"{$this->zt}_status"};
+
+        return Cnst::PT_DELETED === $t
+            || Cnst::PT_ARCHIVE === $t
+            || (
+                Cnst::PT_ARCHIVE === $p
+                && Cnst::PT_NOTSENT !== $t
+            );
+    }
+
+    protected function getbyOrFor(): array
+    {
+        if ('poster' === $this->zp) {
+            return ['for %s', $this->target];
+        } else {
+            return ['by %s', $this->poster];
+        }
+    }
+
+    /**
+     * Количество страниц в теме
+     */
+    protected function getnumPages(): int
+    {
+        if (null === $this->num_replies) {
+            throw new RuntimeException('The model does not have the required data');
+        }
+
+        return (int) \ceil(($this->num_replies + 1) / $this->c->user->disp_posts);
+    }
+
+
+    /**
+     * Статус наличия установленной страницы в теме
+     */
+    public function hasPage(): bool
+    {
+        return $this->page > 0 && $this->page <= $this->numPages;
+    }
+
+    /**
+     * Массив страниц темы
+     */
+    protected function getpagination(): array
+    {
+        $page = (int) $this->page;
+
+        if (
+            $page < 1
+            && 1 === $this->numPages
+        ) {
+            // 1 страницу в списке тем раздела не отображаем
+            return [];
+        } else {
+            return $this->c->Func->paginate(
+                $this->numPages,
+                $page,
+                'PMAction',
+                [
+                    'second' => $this->c->pms->second,
+                    'action' => Cnst::ACTION_TOPIC,
+                    'more1'  => $this->id,
+                    'page'   => 'more2', // нестандарная переменная для page
+                ]
+            );
+        }
+    }
+
+    /**
+     * Вычисляет страницу темы на которой находится данное сообщение
+     */
+    public function calcPage(int $pid): void
+    {
+        $vars = [
+            ':tid' => $this->id,
+            ':pid' => $pid,
+        ];
+        $query = 'SELECT COUNT(pp.id) AS pnum, MAX(pp.id) as pmax
+            FROM ::pm_posts AS pp
+            WHERE pp.topic_id=?i:tid AND pp.id<=?i:pid';
+
+        $result = $this->c->DB->query($query, $vars)->fetch();
+
+        if (
+            empty($result['pmax'])
+            || $result['pmax'] !== $pid
+        ) {
+            $this->page = null;
+        } else {
+            $this->page = (int) \ceil($result['pnum'] / $this->c->user->disp_posts);
+        }
+    }
+
+    /**
+     * Возвращает массив сообщений с установленной страницы
+     */
+    public function pageData(): array
+    {
+        if (! $this->hasPage()) {
+            throw new InvalidArgumentException('Bad number of displayed page');
+        }
+
+        $count = ($this->page - 1) * $this->c->user->disp_posts;
+        $vars  = [
+            ':tid'    => $this->id,
+            ':offset' => $count,
+            ':rows'   => $this->c->user->disp_posts,
+        ];
+        $query = 'SELECT pp.id
+            FROM ::pm_posts AS pp
+            WHERE pp.topic_id=?i:tid
+            ORDER BY pp.id
+            LIMIT ?i:offset, ?i:rows';
+
+        $list  = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
+        $posts = $this->c->pms->loadByIds(Cnst::PPOST, $list);
+
+        foreach ($posts as $post) {
+            ++$count;
+
+            if ($post instanceof PPost) {
+                $post->__postNumber = $count;
+            }
+        }
+
+        return $posts;
+    }
+
+    /**
+     * Обновляет метку визита
+     */
+    public function updateVisit(): void
+    {
+        $visit = $this->{"{$this->zp}_visit"};
+
+        if ($visit >= $this->last_post) {
+            return;
+        }
+
+        $this->{"{$this->zp}_visit"} = $this->last_post;
+
+        $this->c->pms->update(Cnst::PTOPIC, $this);
+        $this->c->pms->updateFromPTopics(false, $this);
+    }
+
+    /**
+     * Возвращает массив сообщений обзора темы
+     */
+    public function review(): array
+    {
+        if ($this->c->config->i_topic_review < 1) {
+            return [];
+        }
+
+        $count = $this->num_replies + 1;
+        $vars  = [
+            ':tid'  => $this->id,
+            ':rows' => $this->c->config->i_topic_review,
+        ];
+        $query = 'SELECT pp.id
+            FROM ::pm_posts AS pp
+            WHERE pp.topic_id=?i:tid
+            ORDER BY pp.id DESC
+            LIMIT 0, ?i:rows';
+
+        $list  = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
+        $posts = $this->c->pms->loadByIds(Cnst::PPOST, $list);
+
+        foreach ($posts as $post) {
+            if ($post instanceof PPost) {
+                $post->__postNumber = $count;
+            }
+
+            --$count;
+        }
+
+        return $posts;
+    }
+
+    /**
+     * Статус возможности ответа в теме
+     */
+    protected function getcanReply(): bool
+    {
+        return ! $this->closed;
+        // + куча условий!!!!!!!!!!
+    }
+
+    /**
+     * Аргументы для ссылки для ответа в теме
+     */
+    protected function getdataReply(): array
+    {
+        return [
+            'second' => $this->c->pms->second,
+            'action' => Cnst::ACTION_SEND,
+            'more1'  => $this->id,
+        ];
+    }
+
+    /**
+     * Ссылка для ответа в теме
+     */
+    protected function getlinkReply(): string
+    {
+        return $this->c->Router->link('PMAction', $this->dataReply);
+    }
+}