123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- <?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'],
- 'first_post_id' => ['firstNew'],
- 'last_number' => ['last_poster'],
- 'last_post' => ['firstNew'],
- 'last_post_id' => ['linkLast', 'firstNew'],
- 'num_replies' => ['numPages', 'pagination'],
- 'poster' => ['last_poster', 'byOrFor', 'zpUser', 'ztUser'],
- 'poster_id' => ['closed', 'firstNew', 'zp', 'zt', 'zpUser', 'ztUser', 'actionsAllowed', 'canReply', 'canSend'],
- 'poster_status' => ['closed', 'actionsAllowed', 'canReply', 'isFullDeleted', 'canSend'],
- 'poster_visit' => ['firstNew'],
- 'subject' => ['name'],
- 'target' => ['last_poster', 'byOrFor', 'zpUser', 'ztUser'],
- 'target_id' => ['closed', 'firstNew', 'zp', 'zt', 'zpUser', 'ztUser', 'actionsAllowed', 'canReply', 'canSend'],
- 'target_status' => ['closed', 'actionsAllowed', 'canReply', 'isFullDeleted', 'canSend'],
- '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' => Cnst::ACTION_NEW,
- ]
- );
- }
- /**
- * Ссылка на уделение темы
- */
- protected function getlinkDelete(): string
- {
- return $this->c->Router->link(
- 'PMAction',
- [
- 'second' => $this->c->pms->second,
- 'action' => Cnst::ACTION_DELETE,
- 'more1' => $this->id,
- 'more2' => Cnst::ACTION_TOPIC,
- ]
- );
- }
- /**
- * Номер первого нового сообщения в теме
- */
- protected function getfirstNew(): int
- {
- if (! $this->hasNew) {
- return 0;
- }
- $visit = $this->{"{$this->zp}_visit"};
- if ($visit < 1) {
- return $this->first_post_id;
- } elseif ($visit >= $this->last_post) {
- return $this->last_post_id;
- }
- $vars = [
- ':tid' => $this->id,
- ':visit' => $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 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->recalculate($this->zpUser);
- }
- /**
- * Возвращает массив сообщений обзора темы
- */
- 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 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);
- }
- /**
- * Статус закрытия темы
- */
- 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 getactionsAllowed(): bool
- {
- return ! $this->closed
- && $this->zpUser->usePM
- && $this->ztUser->usePM;
- }
- /**
- * Статус возможности ответа в теме
- */
- protected function getcanReply(): bool
- {
- return $this->actionsAllowed
- && (
- (
- 1 === $this->zpUser->u_pm
- && 1 === $this->ztUser->u_pm
- )
- || (
- Cnst::PT_ARCHIVE === $this->{"{$this->zp}_status"}
- && Cnst::PT_NOTSENT === $this->{"{$this->zt}_status"}
- )
- || $this->zpUser->isAdmin
- || $this->ztUser->isAdmin
- );
- }
- /**
- * Статус возможности отправить архивный диалог получателю
- */
- protected function getcanSend(): bool
- {
- return Cnst::PT_ARCHIVE === $this->poster_status
- && $this->actionsAllowed
- && (
- (
- 1 === $this->zpUser->u_pm
- && 1 === $this->ztUser->u_pm
- )
- || $this->zpUser->isAdmin
- );
- }
- /**
- * Ссылка для отправки архивного диалога
- */
- protected function getlinkSend(): string
- {
- return $this->c->Router->link(
- 'PMAction',
- [
- 'second' => $this->c->pms->second,
- 'action' => Cnst::ACTION_TOPIC,
- 'more1' => $this->id,
- 'more2' => Cnst::ACTION_SEND,
- ]
- );
- }
- /**
- * Статус удаления диалога у обоих собеседников
- */
- protected function getisFullDeleted(): bool
- {
- return (
- Cnst::PT_DELETED === $this->poster_status
- || Cnst::PT_NOTSENT === $this->poster_status
- )
- && (
- Cnst::PT_DELETED === $this->target_status
- || Cnst::PT_NOTSENT === $this->target_status
- );
- }
- }
|