123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- <?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\Poll;
- use ForkBB\Core\Container;
- use ForkBB\Models\DataModel;
- use ForkBB\Models\Topic\Topic;
- use PDO;
- use RuntimeException;
- use function \ForkBB\__;
- class Poll extends DataModel
- {
- const JSON_OPTIONS = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR;
- /**
- * Ключ модели для контейнера
- * @var string
- */
- protected $cKey = 'Poll';
- /**
- * Возвращает родительскую тему
- */
- protected function getparent(): Topic
- {
- if ($this->tid < 1) {
- throw new RuntimeException('Parent is not defined');
- }
- $topic = $this->c->topics->get($this->tid);
- if (! $topic instanceof Topic) {
- throw new RuntimeException('Parent not found');
- }
- return $topic;
- }
- /**
- * Устанавливает родительскую тему
- */
- protected function setparent(Topic $topic): void
- {
- if ($topic->id < 1) {
- throw new RuntimeException('Parent has a bad id');
- }
- if (
- $this->tid > 0
- && $this->tid !== $topic->id
- ) {
- throw new RuntimeException('Alien parent');
- }
- $this->tid = $topic->id;
- }
- /**
- * Ссылка на обработчик опроса
- */
- protected function getlink(): ?string
- {
- if ($this->tid > 0) {
- return $this->c->Router->link('Poll', ['tid' => $this->tid]);
- } else {
- return null;
- }
- }
- /**
- * Значение токена для формы голосования
- */
- protected function gettoken(): ?string
- {
- if ($this->tid > 0) {
- return $this->c->Csrf->create('Poll', ['tid' => $this->tid]);
- } else {
- return null;
- }
- }
- /**
- * Статус голосования для текущего пользователя
- */
- protected function getuserVoted(): bool
- {
- if (
- $this->c->user->isGuest
- || $this->tid < 1
- ) {
- return false;
- }
- $vars = [
- ':tid' => $this->tid,
- ':uid' => $this->c->user->id,
- ];
- $query = 'SELECT 1
- FROM ::poll_voted
- WHERE tid=?i:tid AND uid=?i:uid';
- return ! empty($this->c->DB->query($query, $vars)->fetch());
- }
- /**
- * Статус открытости
- */
- protected function getisOpen(): bool
- {
- return $this->tid < 1
- || (
- 0 === $this->parent->closed
- && ($type = $this->parent->poll_type) > 0
- && (
- 1 === $type
- || (
- $type > 1000
- && ($type - 1000) * 86400 > \time() - $this->parent->poll_time
- )
- )
- );
- }
- /**
- * Статус возможности голосовать
- */
- protected function getcanVote(): bool
- {
- return $this->tid > 0
- && $this->c->user->usePoll
- && $this->isOpen
- && ! $this->userVoted;
- }
- /**
- * Статус возможности видеть результат
- */
- protected function getcanSeeResult(): bool
- {
- return (
- $this->c->user->usePoll
- || 1 === $this->c->config->b_poll_guest
- )
- && (
- 0 === $this->parent->poll_term
- || $this->parent->poll_term < \max($this->total)
- || ! $this->isOpen
- );
- }
- /**
- * Статус возможности редактировать опрос
- */
- protected function getcanEdit(): bool
- {
- return $this->c->user->usePoll
- && (
- 0 === $this->c->config->i_poll_time
- || $this->tid < 1
- || $this->c->user->isAdmin
- || $this->c->user->isModerator($this)
- || 60 * $this->c->config->i_poll_time > \time() - $this->parent->poll_time
- );
- }
- /**
- * Возвращает максимум голосов за один ответ по каждому вопросу
- */
- protected function getmaxVote(): array
- {
- $result = [];
- foreach (\array_keys($this->question) as $q) {
- $result[$q] = \min(\max($this->vote[$q]), $this->total[$q]);
- }
- return $result;
- }
- /**
- * Возвращает процент голосов по каждому ответу кажого вопроса
- */
- protected function getpercVote(): array
- {
- $result = [];
- foreach (\array_keys($this->question) as $q) {
- if ($this->total[$q] > 0) {
- $total = $this->total[$q] / 100;
- foreach (\array_keys($this->answer[$q]) as $a) {
- $result[$q][$a] = \min(100, \round($this->vote[$q][$a] / $total, 2));
- }
- } else {
- foreach (\array_keys($this->answer[$q]) as $a) {
- $result[$q][$a] = 0;
- }
- }
- }
- return $result;
- }
- /**
- * Возвращает ширину ответа в процентах
- */
- protected function getwidthVote(): array
- {
- $result = [];
- foreach (\array_keys($this->question) as $q) {
- if ($this->maxVote[$q] > 0) {
- $max = $this->maxVote[$q] / 100;
- foreach (\array_keys($this->answer[$q]) as $a) {
- $result[$q][$a] = \min(100, \round($this->vote[$q][$a] / $max));
- }
- } else {
- foreach (\array_keys($this->answer[$q]) as $a) {
- $result[$q][$a] = 0;
- }
- }
- }
- return $result;
- }
- /**
- * Возвращает статус опроса для текущего пользователя или null
- */
- protected function getstatus() /* : null|string|array */
- {
- if ($this->tid < 1) {
- return null;
- } elseif (
- $this->c->user->isGuest
- && 1 !== $this->c->config->b_poll_guest
- ) {
- return 'Poll results are hidden from the guests';
- } elseif (! $this->isOpen) {
- return 'This poll is closed';
- } elseif (! $this->canSeeResult) {
- return ['Poll results are hidden up to %s voters', $this->parent->poll_term];
- } elseif ($this->userVoted) {
- return 'You voted';
- } elseif ($this->c->user->isGuest) {
- return 'Guest cannot vote';
- } else {
- return 'Poll status is undefined';
- }
- }
- /**
- * Голосование
- * Возвращает текст ошибки или null
- */
- public function vote(array $vote): ?string
- {
- if (! $this->canVote) {
- return __('You cannot vote on this poll');
- }
- $data = [];
- foreach (\array_keys($this->question) as $q) {
- if ($this->type[$q] > 1) {
- $count = \count($vote[$q]);
- if (0 == $count) {
- return __(['No vote on question %s', $q]);
- } elseif ($count > $this->type[$q]) {
- return __(['Too many answers selected in question %s', $q]);
- }
- foreach (\array_keys($vote[$q]) as $a) {
- if (! isset($this->answer[$q][$a])) {
- return __(['The selected answer is not present in question %s', $q]);
- }
- $data[] = [$q, $a];
- }
- } else {
- if (! isset($vote[$q][0])) {
- return __(['No vote on question %s', $q]);
- } elseif (! isset($this->answer[$q][$vote[$q][0]])) {
- return __(['The selected answer is not present in question %s', $q]);
- }
- $data[] = [$q, $vote[$q][0]];
- }
- $data[] = [$q, 0];
- }
- $vars = [
- ':tid' => $this->tid,
- ':uid' => $this->c->user->id,
- ':rez' => \json_encode($data, self::JSON_OPTIONS),
- ];
- $query = 'INSERT INTO ::poll_voted (tid, uid, rez)
- VALUES (?i:tid, ?i:uid, ?s:rez)';
- $this->c->DB->exec($query, $vars);
- $vars = [
- ':tid' => $this->tid,
- ];
- $query = 'UPDATE ::poll
- SET votes=votes+1
- WHERE tid=?i:tid AND question_id=?i:qid AND field_id=?i:fid';
- foreach ($data as list($vars[':qid'], $vars[':fid'])) {
- $this->c->DB->exec($query, $vars);
- }
- $this->c->polls->reset($this->tid);
- return null;
- }
- }
|