openvk/Web/Models/Entities/Poll.php

355 lines
9.8 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
2022-10-11 19:04:43 +03:00
namespace openvk\Web\Models\Entities;
2022-10-11 19:04:43 +03:00
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
use openvk\Web\Util\DateTime;
use UnexpectedValueException;
2022-10-11 19:04:43 +03:00
use Nette\InvalidStateException;
Groups: Wall: add suggestions (#935) * Wall: add early suggestions * Fix br * Fix empty posts * fck * Add offset for api * Add notifications of new suggestion posts * Fix mentions in suggested posts * 🤮🤢 * Change regex Теперь оно удаляет все теги а не только <br> * Add da koroche pohuy * Эдд апи метходс Методы нестандартные немного * Pon * Add skloneniyia * newlines * int * Update loaders and add avtopodgruzka postov * Update JOERGK.strings * Blin * Remove repeated code, fix loaded buttons on chr... ...ome and fix getting suggested posts via API.Wall.getPost * Fix polls * Fihes Теперь уведомление о принятии поста не приходит, если вы приняли свой же пост Пофикшен баг перехода в предложку Добавлен старый вид постов в предложке Теперь счётчик постов в предложке у прикреплённой группы обновляется при принятии или отклонении поста Убрано всплывающее уведомление об отклонении поста (оно раздражает) Теперь если вы посмотрели все посты на одной странице (не на первой) и на ней не осталось постов, вас телепортирует на предыдущую страницу * Remove ability to delete your accepted psto * oi blin * Improvements 2 api * g * openvk.uk Возможно, приведение кода к кодстайлу (удаление скобочек то есть) * aiaks * al_wall.js -> al_suggestions.js * 👨‍💻 Add 👨‍💻 fading 👨‍💻 * Add "owner's posts' and "other's posts" Давайте рофлить👨‍💻👨‍💻👨‍💻 * planshet openvk Add tabs for post view, add signer's object in wall get and add person icon in microblog * Simplefai ze kod * PHP 8 FIX WATAFAK * Add indesk
2023-11-16 19:44:12 +03:00
use openvk\Web\Models\Repositories\{Users, Posts};
2022-10-11 19:04:43 +03:00
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Exceptions\PollLockedException;
use openvk\Web\Models\Exceptions\AlreadyVotedException;
use openvk\Web\Models\Exceptions\InvalidOptionException;
class Poll extends Attachable
{
protected $tableName = "polls";
2022-10-11 19:04:43 +03:00
private $choicesToPersist = [];
public function getTitle(): string
2022-10-11 19:04:43 +03:00
{
return $this->getRecord()->title;
}
public function getMetaDescription(): string
2022-10-11 19:04:43 +03:00
{
$props = [];
$props[] = tr($this->isAnonymous() ? "poll_anon" : "poll_public");
if ($this->isMultipleChoice()) {
$props[] = tr("poll_multi");
}
if (!$this->isRevotable()) {
$props[] = tr("poll_lock");
}
if (!is_null($this->endsAt())) {
$props[] = tr("poll_until", $this->endsAt());
}
2022-10-11 19:04:43 +03:00
return implode("", $props);
}
public function getOwner(): User
2022-10-11 19:04:43 +03:00
{
return (new Users())->get($this->getRecord()->owner);
2022-10-11 19:04:43 +03:00
}
public function getOptions(): array
2022-10-11 19:04:43 +03:00
{
$options = $this->getRecord()->related("poll_options.poll");
$res = [];
foreach ($options as $opt) {
2022-10-11 19:04:43 +03:00
$res[$opt->id] = $opt->name;
}
2022-10-11 19:04:43 +03:00
return $res;
}
public function getUserVote(User $user): ?array
2022-10-11 19:04:43 +03:00
{
$ctx = DatabaseConnection::i()->getContext();
$votedOpts = $ctx->table("poll_votes")
->where(["user" => $user->getId(), "poll" => $this->getId()]);
if ($votedOpts->count() == 0) {
return null;
}
2022-10-11 19:04:43 +03:00
$res = [];
foreach ($votedOpts as $votedOpt) {
2022-10-11 19:04:43 +03:00
$option = $ctx->table("poll_options")->get($votedOpt->option);
$res[] = [$option->id, $option->name];
}
2022-10-11 19:04:43 +03:00
return $res;
}
public function getVoters(int $optionId, int $page = 1, ?int $perPage = null): array
2022-10-11 19:04:43 +03:00
{
$res = [];
$ctx = DatabaseConnection::i()->getContext();
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
2022-10-11 19:04:43 +03:00
$voters = $ctx->table("poll_votes")->where(["poll" => $this->getId(), "option" => $optionId]);
foreach ($voters->page($page, $perPage) as $vote) {
$res[] = (new Users())->get($vote->user);
}
2022-10-11 19:04:43 +03:00
return $res;
}
public function getVoterCount(?int $optionId = null): int
2022-10-11 19:04:43 +03:00
{
$votes = DatabaseConnection::i()->getContext()->table("poll_votes");
if (!$optionId) {
2022-10-11 19:04:43 +03:00
return $votes->select("COUNT(DISTINCT user) AS c")->where("poll", $this->getId())->fetch()->c;
}
2022-10-11 19:04:43 +03:00
return $votes->where(["poll" => $this->getId(), "option" => $optionId])->count();
}
public function getResults(?User $user = null): object
2022-10-11 19:04:43 +03:00
{
$ctx = DatabaseConnection::i()->getContext();
$voted = null;
if (!is_null($user)) {
2022-10-11 19:04:43 +03:00
$voted = $this->getUserVote($user);
}
2022-10-11 19:04:43 +03:00
$result = (object) [];
$result->totalVotes = $this->getVoterCount();
2022-10-11 19:04:43 +03:00
$unsOptions = [];
foreach ($this->getOptions() as $id => $title) {
2022-10-11 19:04:43 +03:00
$option = (object) [];
$option->id = $id;
$option->name = $title;
2022-10-11 19:04:43 +03:00
$option->votes = $this->getVoterCount($id);
2022-10-11 19:34:28 +03:00
$option->pct = $result->totalVotes == 0 ? 0 : min(100, floor(($option->votes / $result->totalVotes) * 100));
2022-10-11 19:04:43 +03:00
$option->voters = $this->getVoters($id, 1, 10);
if (!$user || !$voted) {
$option->voted = null;
} else {
2022-10-11 19:04:43 +03:00
$option->voted = in_array([$id, $title], $voted);
}
2022-10-11 19:04:43 +03:00
$unsOptions[$id] = $option;
}
2022-10-11 19:04:43 +03:00
$optionsC = sizeof($unsOptions);
$sOptions = $unsOptions;
usort($sOptions, function ($a, $b) { return $a->votes <=> $b->votes; });
for ($i = 0; $i < $optionsC; $i++) {
2022-10-11 19:04:43 +03:00
$unsOptions[$id]->rate = $optionsC - $i - 1;
}
2022-10-11 19:04:43 +03:00
$result->options = array_values($unsOptions);
2022-10-11 19:04:43 +03:00
return $result;
}
public function isAnonymous(): bool
2022-10-11 19:04:43 +03:00
{
return (bool) $this->getRecord()->is_anonymous;
}
public function isMultipleChoice(): bool
2022-10-11 19:04:43 +03:00
{
return (bool) $this->getRecord()->allows_multiple;
}
public function isRevotable(): bool
2022-10-11 19:04:43 +03:00
{
return (bool) $this->getRecord()->can_revote;
}
public function endsAt(): ?DateTime
2022-10-11 19:04:43 +03:00
{
if (!$this->getRecord()->until) {
return null;
}
2022-10-11 19:04:43 +03:00
return new DateTime($this->getRecord()->until);
}
public function hasEnded(): bool
2022-10-11 19:04:43 +03:00
{
if ($this->getRecord()->ended) {
2022-10-11 19:04:43 +03:00
return true;
}
if (!is_null($this->getRecord()->until)) {
2022-10-11 19:04:43 +03:00
return time() >= $this->getRecord()->until;
}
2022-10-11 19:04:43 +03:00
return false;
}
public function hasVoted(User $user): bool
2022-10-11 19:04:43 +03:00
{
return !is_null($this->getUserVote($user));
}
public function canVote(User $user): bool
2022-10-11 19:04:43 +03:00
{
Groups: Wall: add suggestions (#935) * Wall: add early suggestions * Fix br * Fix empty posts * fck * Add offset for api * Add notifications of new suggestion posts * Fix mentions in suggested posts * 🤮🤢 * Change regex Теперь оно удаляет все теги а не только <br> * Add da koroche pohuy * Эдд апи метходс Методы нестандартные немного * Pon * Add skloneniyia * newlines * int * Update loaders and add avtopodgruzka postov * Update JOERGK.strings * Blin * Remove repeated code, fix loaded buttons on chr... ...ome and fix getting suggested posts via API.Wall.getPost * Fix polls * Fihes Теперь уведомление о принятии поста не приходит, если вы приняли свой же пост Пофикшен баг перехода в предложку Добавлен старый вид постов в предложке Теперь счётчик постов в предложке у прикреплённой группы обновляется при принятии или отклонении поста Убрано всплывающее уведомление об отклонении поста (оно раздражает) Теперь если вы посмотрели все посты на одной странице (не на первой) и на ней не осталось постов, вас телепортирует на предыдущую страницу * Remove ability to delete your accepted psto * oi blin * Improvements 2 api * g * openvk.uk Возможно, приведение кода к кодстайлу (удаление скобочек то есть) * aiaks * al_wall.js -> al_suggestions.js * 👨‍💻 Add 👨‍💻 fading 👨‍💻 * Add "owner's posts' and "other's posts" Давайте рофлить👨‍💻👨‍💻👨‍💻 * planshet openvk Add tabs for post view, add signer's object in wall get and add person icon in microblog * Simplefai ze kod * PHP 8 FIX WATAFAK * Add indesk
2023-11-16 19:44:12 +03:00
return !$this->hasEnded() && !$this->hasVoted($user) && !is_null($this->getAttachedPost()) && $this->getAttachedPost()->getSuggestionType() == 0;
2022-10-11 19:04:43 +03:00
}
public function vote(User $user, array $optionIds): void
2022-10-11 19:04:43 +03:00
{
if ($this->hasEnded()) {
throw new PollLockedException();
}
if ($this->hasVoted($user)) {
throw new AlreadyVotedException();
}
$optionIds = array_map(function ($x) { return (int) $x; }, array_unique($optionIds));
2022-10-11 19:04:43 +03:00
$validOpts = array_keys($this->getOptions());
if (empty($optionIds) || (sizeof($optionIds) > 1 && !$this->isMultipleChoice())) {
throw new UnexpectedValueException();
}
if (sizeof(array_diff($optionIds, $validOpts)) > 0) {
throw new InvalidOptionException();
}
foreach ($optionIds as $opt) {
2022-10-11 19:04:43 +03:00
DatabaseConnection::i()->getContext()->table("poll_votes")->insert([
"user" => $user->getId(),
"poll" => $this->getId(),
"option" => $opt,
]);
}
}
public function revokeVote(User $user): void
2022-10-11 19:04:43 +03:00
{
if (!$this->isRevotable()) {
throw new PollLockedException();
}
2022-10-11 19:04:43 +03:00
$this->getRecord()->related("poll_votes.poll")
->where("user", $user->getId())->delete();
}
public function setOwner(User $owner): void
2022-10-11 19:04:43 +03:00
{
$this->stateChanges("owner", $owner->getId());
}
public function setEndDate(int $timestamp): void
2022-10-11 19:04:43 +03:00
{
if (!is_null($this->getRecord())) {
throw new PollLockedException();
}
2022-10-11 19:04:43 +03:00
$this->stateChanges("until", $timestamp);
}
public function setEnded(): void
2022-10-11 19:04:43 +03:00
{
$this->stateChanges("ended", 1);
}
public function setOptions(array $options): void
2022-10-11 19:04:43 +03:00
{
if (!is_null($this->getRecord())) {
throw new PollLockedException();
}
if (sizeof($options) > ovkGetQuirk("polls.max-opts")) {
throw new TooMuchOptionsException();
}
2022-10-11 19:04:43 +03:00
$this->choicesToPersist = $options;
}
public function setRevotability(bool $canReVote): void
2022-10-11 19:04:43 +03:00
{
if (!is_null($this->getRecord())) {
throw new PollLockedException();
}
$this->stateChanges("can_revote", $canReVote);
2022-10-11 19:04:43 +03:00
}
public function setAnonymity(bool $anonymous): void
2022-10-11 19:04:43 +03:00
{
$this->stateChanges("is_anonymous", $anonymous);
}
public function setMultipleChoice(bool $mc): void
2022-10-11 19:04:43 +03:00
{
$this->stateChanges("allows_multiple", $mc);
}
public function importXML(User $owner, string $xml): void
2022-10-11 19:04:43 +03:00
{
$xml = simplexml_load_string($xml);
$this->setOwner($owner);
$this->setTitle($xml["title"] ?? "Untitled");
$this->setMultipleChoice(($xml["multiple"] ?? "no") == "yes");
$this->setAnonymity(($xml["anonymous"] ?? "no") == "yes");
$this->setRevotability(($xml["locked"] ?? "no") == "no");
if (ctype_digit((string) ($xml["duration"] ?? ""))) {
2022-10-11 19:04:43 +03:00
$this->setEndDate(time() + ((86400 * (int) $xml["duration"])));
}
2022-10-11 19:04:43 +03:00
$options = [];
foreach ($xml->options->option as $opt) {
2022-10-11 19:04:43 +03:00
$options[] = (string) $opt;
}
if (empty($options)) {
throw new UnexpectedValueException();
}
2022-10-11 19:04:43 +03:00
$this->setOptions($options);
}
public static function import(User $owner, string $xml): Poll
2022-10-11 19:04:43 +03:00
{
$poll = new Poll();
2022-10-11 19:04:43 +03:00
$poll->importXML($owner, $xml);
$poll->save();
2022-10-11 19:04:43 +03:00
return $poll;
}
2023-12-02 20:21:16 +03:00
public function canBeViewedBy(?User $user = null): bool
2023-12-02 20:21:16 +03:00
{
# waiting for #935 :(
/*if(!is_null($this->getAttachedPost())) {
return $this->getAttachedPost()->canBeViewedBy($user);
} else {*/
return true;
2023-12-02 20:21:16 +03:00
#}
}
public function save(?bool $log = false): void
2022-10-11 19:04:43 +03:00
{
if (empty($this->choicesToPersist)) {
throw new InvalidStateException();
}
2023-10-28 13:23:47 +03:00
parent::save($log);
foreach ($this->choicesToPersist as $option) {
2022-10-11 19:04:43 +03:00
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
"poll" => $this->getId(),
"name" => $option,
]);
}
}
Groups: Wall: add suggestions (#935) * Wall: add early suggestions * Fix br * Fix empty posts * fck * Add offset for api * Add notifications of new suggestion posts * Fix mentions in suggested posts * 🤮🤢 * Change regex Теперь оно удаляет все теги а не только <br> * Add da koroche pohuy * Эдд апи метходс Методы нестандартные немного * Pon * Add skloneniyia * newlines * int * Update loaders and add avtopodgruzka postov * Update JOERGK.strings * Blin * Remove repeated code, fix loaded buttons on chr... ...ome and fix getting suggested posts via API.Wall.getPost * Fix polls * Fihes Теперь уведомление о принятии поста не приходит, если вы приняли свой же пост Пофикшен баг перехода в предложку Добавлен старый вид постов в предложке Теперь счётчик постов в предложке у прикреплённой группы обновляется при принятии или отклонении поста Убрано всплывающее уведомление об отклонении поста (оно раздражает) Теперь если вы посмотрели все посты на одной странице (не на первой) и на ней не осталось постов, вас телепортирует на предыдущую страницу * Remove ability to delete your accepted psto * oi blin * Improvements 2 api * g * openvk.uk Возможно, приведение кода к кодстайлу (удаление скобочек то есть) * aiaks * al_wall.js -> al_suggestions.js * 👨‍💻 Add 👨‍💻 fading 👨‍💻 * Add "owner's posts' and "other's posts" Давайте рофлить👨‍💻👨‍💻👨‍💻 * planshet openvk Add tabs for post view, add signer's object in wall get and add person icon in microblog * Simplefai ze kod * PHP 8 FIX WATAFAK * Add indesk
2023-11-16 19:44:12 +03:00
public function getAttachedPost()
Groups: Wall: add suggestions (#935) * Wall: add early suggestions * Fix br * Fix empty posts * fck * Add offset for api * Add notifications of new suggestion posts * Fix mentions in suggested posts * 🤮🤢 * Change regex Теперь оно удаляет все теги а не только <br> * Add da koroche pohuy * Эдд апи метходс Методы нестандартные немного * Pon * Add skloneniyia * newlines * int * Update loaders and add avtopodgruzka postov * Update JOERGK.strings * Blin * Remove repeated code, fix loaded buttons on chr... ...ome and fix getting suggested posts via API.Wall.getPost * Fix polls * Fihes Теперь уведомление о принятии поста не приходит, если вы приняли свой же пост Пофикшен баг перехода в предложку Добавлен старый вид постов в предложке Теперь счётчик постов в предложке у прикреплённой группы обновляется при принятии или отклонении поста Убрано всплывающее уведомление об отклонении поста (оно раздражает) Теперь если вы посмотрели все посты на одной странице (не на первой) и на ней не осталось постов, вас телепортирует на предыдущую страницу * Remove ability to delete your accepted psto * oi blin * Improvements 2 api * g * openvk.uk Возможно, приведение кода к кодстайлу (удаление скобочек то есть) * aiaks * al_wall.js -> al_suggestions.js * 👨‍💻 Add 👨‍💻 fading 👨‍💻 * Add "owner's posts' and "other's posts" Давайте рофлить👨‍💻👨‍💻👨‍💻 * planshet openvk Add tabs for post view, add signer's object in wall get and add person icon in microblog * Simplefai ze kod * PHP 8 FIX WATAFAK * Add indesk
2023-11-16 19:44:12 +03:00
{
$post = DatabaseConnection::i()->getContext()->table("attachments")
->where(
["attachable_type" => static::class,
"attachable_id" => $this->getId()]
)->fetch();
Groups: Wall: add suggestions (#935) * Wall: add early suggestions * Fix br * Fix empty posts * fck * Add offset for api * Add notifications of new suggestion posts * Fix mentions in suggested posts * 🤮🤢 * Change regex Теперь оно удаляет все теги а не только <br> * Add da koroche pohuy * Эдд апи метходс Методы нестандартные немного * Pon * Add skloneniyia * newlines * int * Update loaders and add avtopodgruzka postov * Update JOERGK.strings * Blin * Remove repeated code, fix loaded buttons on chr... ...ome and fix getting suggested posts via API.Wall.getPost * Fix polls * Fihes Теперь уведомление о принятии поста не приходит, если вы приняли свой же пост Пофикшен баг перехода в предложку Добавлен старый вид постов в предложке Теперь счётчик постов в предложке у прикреплённой группы обновляется при принятии или отклонении поста Убрано всплывающее уведомление об отклонении поста (оно раздражает) Теперь если вы посмотрели все посты на одной странице (не на первой) и на ней не осталось постов, вас телепортирует на предыдущую страницу * Remove ability to delete your accepted psto * oi blin * Improvements 2 api * g * openvk.uk Возможно, приведение кода к кодстайлу (удаление скобочек то есть) * aiaks * al_wall.js -> al_suggestions.js * 👨‍💻 Add 👨‍💻 fading 👨‍💻 * Add "owner's posts' and "other's posts" Давайте рофлить👨‍💻👨‍💻👨‍💻 * planshet openvk Add tabs for post view, add signer's object in wall get and add person icon in microblog * Simplefai ze kod * PHP 8 FIX WATAFAK * Add indesk
2023-11-16 19:44:12 +03:00
if (!is_null($post->target_id)) {
return (new Posts())->get($post->target_id);
} else {
return null;
}
Groups: Wall: add suggestions (#935) * Wall: add early suggestions * Fix br * Fix empty posts * fck * Add offset for api * Add notifications of new suggestion posts * Fix mentions in suggested posts * 🤮🤢 * Change regex Теперь оно удаляет все теги а не только <br> * Add da koroche pohuy * Эдд апи метходс Методы нестандартные немного * Pon * Add skloneniyia * newlines * int * Update loaders and add avtopodgruzka postov * Update JOERGK.strings * Blin * Remove repeated code, fix loaded buttons on chr... ...ome and fix getting suggested posts via API.Wall.getPost * Fix polls * Fihes Теперь уведомление о принятии поста не приходит, если вы приняли свой же пост Пофикшен баг перехода в предложку Добавлен старый вид постов в предложке Теперь счётчик постов в предложке у прикреплённой группы обновляется при принятии или отклонении поста Убрано всплывающее уведомление об отклонении поста (оно раздражает) Теперь если вы посмотрели все посты на одной странице (не на первой) и на ней не осталось постов, вас телепортирует на предыдущую страницу * Remove ability to delete your accepted psto * oi blin * Improvements 2 api * g * openvk.uk Возможно, приведение кода к кодстайлу (удаление скобочек то есть) * aiaks * al_wall.js -> al_suggestions.js * 👨‍💻 Add 👨‍💻 fading 👨‍💻 * Add "owner's posts' and "other's posts" Давайте рофлить👨‍💻👨‍💻👨‍💻 * planshet openvk Add tabs for post view, add signer's object in wall get and add person icon in microblog * Simplefai ze kod * PHP 8 FIX WATAFAK * Add indesk
2023-11-16 19:44:12 +03:00
}
2022-10-11 19:34:28 +03:00
}