openvk/Web/Models/Entities/Post.php

533 lines
16 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
2020-06-07 19:04:43 +03:00
namespace openvk\Web\Models\Entities;
2021-09-20 15:19:15 +03:00
use Chandler\Database\DatabaseConnection as DB;
use openvk\Web\Models\Repositories\{Clubs, Users};
2020-06-07 19:04:43 +03:00
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\Notifications\LikeNotification;
2020-06-07 19:04:43 +03:00
class Post extends Postable
{
use Traits\TRichText;
2020-06-07 19:04:43 +03:00
protected $tableName = "posts";
protected $upperNodeReferenceColumnName = "wall";
private function setLikeRecursively(bool $liked, User $user, int $depth): void
{
$searchData = [
"origin" => $user->getId(),
"model" => static::class,
"target" => $this->getRecord()->id,
];
if ((sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) !== $liked) {
if ($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment) {
(new LikeNotification($this->getOwner(false), $this, $user))->emit();
}
parent::setLike($liked, $user);
}
if ($depth < ovkGetQuirk("wall.repost-liking-recursion-limit")) {
foreach ($this->getChildren() as $attachment) {
if ($attachment instanceof Post) {
$attachment->setLikeRecursively($liked, $user, $depth + 1);
}
}
}
}
2020-06-07 19:04:43 +03:00
/**
* May return fake owner (group), if flags are [1, (*)]
*
2020-06-07 19:04:43 +03:00
* @param bool $honourFlags - check flags
*/
public function getOwner(bool $honourFlags = true, bool $real = false): RowModel
2020-06-07 19:04:43 +03:00
{
if ($honourFlags && $this->isPostedOnBehalfOfGroup()) {
if ($this->getRecord()->wall < 0) {
return (new Clubs())->get(abs($this->getRecord()->wall));
}
2020-06-07 19:04:43 +03:00
}
2021-11-15 22:45:48 +03:00
return parent::getOwner($real);
2020-06-07 19:04:43 +03:00
}
public function getPrettyId(): string
2020-06-07 19:04:43 +03:00
{
return $this->getRecord()->wall . "_" . $this->getVirtualId();
}
public function getTargetWall(): int
2020-06-07 19:04:43 +03:00
{
return $this->getRecord()->wall;
}
public function getWallOwner()
{
$w = $this->getRecord()->wall;
if ($w < 0) {
return (new Clubs())->get(abs($w));
}
return (new Users())->get($w);
}
public function getRepostCount(): int
2020-06-07 19:04:43 +03:00
{
return sizeof(
$this->getRecord()
->related("attachments.attachable_id")
->where("attachable_type", get_class($this))
);
}
public function isPinned(): bool
2021-09-20 15:19:15 +03:00
{
return (bool) $this->getRecord()->pinned;
}
public function hasSource(): bool
{
return $this->getRecord()->source != null;
}
public function getSource(bool $format = false)
{
$orig_source = $this->getRecord()->source;
if (!str_contains($orig_source, "https://") && !str_contains($orig_source, "http://")) {
$orig_source = "https://" . $orig_source;
}
if (!$format) {
return $orig_source;
}
return $this->formatLinks($orig_source);
}
public function setSource(string $source)
{
$result = check_copyright_link($source);
$this->stateChanges("source", $source);
}
public function resetSource()
{
$this->stateChanges("source", null);
}
public function getVkApiCopyright(): object
{
return (object) [
'id' => 0,
'link' => $this->getSource(false),
'name' => $this->getSource(false),
'type' => 'link',
];
}
public function isAd(): bool
2020-06-07 19:04:43 +03:00
{
return (bool) $this->getRecord()->ad;
}
public function isPostedOnBehalfOfGroup(): bool
2020-06-07 19:04:43 +03:00
{
return ($this->getRecord()->flags & 0b10000000) > 0;
}
public function isSigned(): bool
2020-06-07 19:04:43 +03:00
{
return ($this->getRecord()->flags & 0b01000000) > 0;
}
2022-08-05 23:00:52 +03:00
public function isDeactivationMessage(): bool
2022-08-05 23:00:52 +03:00
{
return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0);
2022-08-05 23:00:52 +03:00
}
public function isUpdateAvatarMessage(): bool
{
return (($this->getRecord()->flags & 0b00010000) > 0) && ($this->getRecord()->owner > 0);
}
public function isExplicit(): bool
2020-06-07 19:04:43 +03:00
{
return (bool) $this->getRecord()->nsfw;
2020-06-07 19:04:43 +03:00
}
public function isDeleted(): bool
2020-06-07 19:04:43 +03:00
{
return (bool) $this->getRecord()->deleted;
}
public function getOwnerPost(): int
2020-06-07 19:04:43 +03:00
{
return $this->getOwner(false)->getId();
2020-06-07 19:04:43 +03:00
}
2022-12-17 02:03:02 +03:00
public function getPlatform(bool $forAPI = false): ?string
2022-12-17 02:03:02 +03:00
{
$platform = $this->getRecord()->api_source_name;
if ($forAPI) {
2022-12-17 02:03:02 +03:00
switch ($platform) {
2023-02-03 14:22:19 +03:00
case 'openvk_refresh_android':
2022-12-17 02:03:02 +03:00
case 'openvk_legacy_android':
return 'android';
break;
case 'openvk_ios':
case 'openvk_legacy_ios':
return 'iphone';
break;
case 'windows_phone':
return 'wphone';
break;
2022-12-17 02:03:02 +03:00
case 'vika_touch': // кика хохотач ахахахаххахахахахах
case 'vk4me':
return 'mobile';
break;
case null:
return null;
2022-12-17 02:03:02 +03:00
break;
2022-12-17 02:03:02 +03:00
default:
return 'api';
break;
}
} else {
return $platform;
}
}
public function getPlatformDetails(): array
2022-12-17 02:03:02 +03:00
{
$clients = simplexml_load_file(OPENVK_ROOT . "/data/clients.xml");
foreach ($clients as $client) {
if ($client['tag'] == $this->getPlatform()) {
2022-12-17 02:03:02 +03:00
return [
"tag" => $client['tag'],
"name" => $client['name'],
"url" => $client['url'],
"img" => $client['img'],
2022-12-17 02:03:02 +03:00
];
break;
}
}
return [
"tag" => $this->getPlatform(),
"name" => null,
"url" => null,
"img" => null,
2022-12-17 02:03:02 +03:00
];
}
public function getPostSourceInfo(): array
{
$post_source = ["type" => "vk"];
if ($this->getPlatform(true) !== null) {
$post_source = [
"type" => "api",
"platform" => $this->getPlatform(true),
];
}
if ($this->isUpdateAvatarMessage()) {
$post_source['data'] = 'profile_photo';
}
return $post_source;
}
public function getVkApiType(): string
{
$type = 'post';
if ($this->getSuggestionType() != 0) {
$type = 'suggest';
}
return $type;
}
public function pin(): void
2021-09-20 15:19:15 +03:00
{
DB::i()
->getContext()
->table("posts")
->where([
"wall" => $this->getTargetWall(),
"pinned" => true,
])
->update(["pinned" => false]);
2021-09-20 15:19:15 +03:00
$this->stateChanges("pinned", true);
$this->save();
}
public function unpin(): void
2021-09-20 15:19:15 +03:00
{
$this->stateChanges("pinned", false);
$this->save();
}
public function canBePinnedBy(User $user = null): bool
{
if (!$user) {
2024-11-02 16:31:35 +03:00
return false;
}
if ($this->getTargetWall() < 0) {
return (new Clubs())->get(abs($this->getTargetWall()))->canBeModifiedBy($user);
}
2024-11-02 16:31:35 +03:00
return $this->getTargetWall() === $user->getId();
}
public function canBeDeletedBy(User $user = null): bool
{
if (!$user) {
2024-11-02 16:31:35 +03:00
return false;
}
if ($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0) {
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 false;
}
return $this->getOwnerPost() === $user->getId() || $this->canBePinnedBy($user);
}
public function setContent(string $content): void
2020-06-07 19:04:43 +03:00
{
if (ctype_space($content)) {
2020-06-07 19:04:43 +03:00
throw new \LengthException("Content length must be at least 1 character (not counting whitespaces).");
} elseif (iconv_strlen($content) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxSize"]) {
2020-08-20 15:58:40 +03:00
throw new \LengthException("Content is too large.");
}
2020-06-07 19:04:43 +03:00
$this->stateChanges("content", $content);
}
public function toggleLike(User $user): bool
{
$liked = parent::toggleLike($user);
if (!$user->isPrivateLikes() && $this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment) {
(new LikeNotification($this->getOwner(false), $this, $user))->emit();
}
foreach ($this->getChildren() as $attachment) {
if ($attachment instanceof Post) {
$attachment->setLikeRecursively($liked, $user, 2);
}
}
return $liked;
}
public function setLike(bool $liked, User $user): void
{
$this->setLikeRecursively($liked, $user, 1);
}
public function deletePost(): void
2020-06-07 19:04:43 +03:00
{
$this->setDeleted(1);
$this->unwire();
$this->save();
}
2023-12-02 20:21:16 +03:00
public function canBeViewedBy(?User $user = null): bool
2023-12-02 20:21:16 +03:00
{
if ($this->isDeleted()) {
2023-12-02 20:21:16 +03:00
return false;
}
2023-12-02 20:21:16 +03:00
return $this->getWallOwner()->canBeViewedBy($user);
}
public function getSuggestionType()
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->getRecord()->suggested;
}
public function getPageURL(): string
{
return "/wall" . $this->getPrettyId();
}
public function toNotifApiStruct()
{
$res = (object) [];
$res->id = $this->getVirtualId();
$res->to_id = $this->getOwner() instanceof Club ? $this->getOwner()->getId() * -1 : $this->getOwner()->getId();
$res->from_id = $res->to_id;
$res->date = $this->getPublicationTime()->timestamp();
$res->text = $this->getText(false);
$res->attachments = []; # todo
$res->copy_owner_id = null; # todo
$res->copy_post_id = null; # todo
return $res;
}
public function canBeEditedBy(?User $user = null): bool
{
if (!$user) {
return false;
}
if ($this->isDeactivationMessage() || $this->isUpdateAvatarMessage()) {
return false;
}
if ($this->getTargetWall() > 0) {
2023-09-16 19:51:36 +03:00
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
} else {
if ($this->isPostedOnBehalfOfGroup()) {
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->getWallOwner()->canBeModifiedBy($user);
} else {
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 $user->getId() == $this->getOwner(false)->getId();
}
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 $user->getId() == $this->getOwner(false)->getId();
}
public function toRss(): \Bhaktaraz\RSSGenerator\Item
{
$domain = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
$description = $this->getText(false);
2024-11-23 13:16:55 +03:00
$title = str_replace("\n", "", ovk_proc_strtr($description, 79));
$description_html = $description;
$url = $domain . "/wall" . $this->getPrettyId();
if ($this->isUpdateAvatarMessage()) {
2024-11-23 13:16:55 +03:00
$title = tr('upd_in_general');
}
if ($this->isDeactivationMessage()) {
2024-11-23 13:16:55 +03:00
$title = tr('post_deact_in_general');
}
2024-11-23 13:16:55 +03:00
$author = $this->getOwner();
2024-11-23 13:16:55 +03:00
$target_wall = $this->getWallOwner();
$author_name = escape_html($author->getCanonicalName());
if ($this->isExplicit()) {
2024-11-23 13:16:55 +03:00
$title = 'NSFW: ' . $title;
}
foreach ($this->getChildren() as $child) {
if ($child instanceof Photo) {
$child_page = $domain . $child->getPageURL();
2024-11-23 13:16:55 +03:00
$child_url = $child->getURL();
$description_html .= "<br /><a href='$child_page'><img src='$child_url'></a><br />";
} elseif ($child instanceof Video) {
$child_page = $domain . '/video' . $child->getPrettyId();
if ($child->getType() != 1) {
$description_html .= "" .
"<br />" .
"<video width=\"320\" height=\"240\" controls><source src=\"" . $child->getURL() . "\" type=\"video/mp4\"></video><br />" .
"<b>" . escape_html($child->getName()) . "</b><br />";
2024-11-23 13:16:55 +03:00
} else {
$description_html .= "" .
"<br />" .
"<a href=\"" . $child->getVideoDriver()->getURL() . "\"><b>" . escape_html($child->getName()) . "</b></a><br />";
2024-11-23 13:16:55 +03:00
}
} elseif ($child instanceof Audio) {
if (!$child->isWithdrawn()) {
2024-11-23 13:16:55 +03:00
$description_html .= "<br />"
. "<b>" . escape_html($child->getName()) . "</b>:"
. "<br />"
. "<audio controls>"
. "<source src=\"" . $child->getOriginalURL() . "\" type=\"audio/mpeg\"></audio>"
. "<br />";
2024-11-23 13:16:55 +03:00
}
} elseif ($child instanceof Poll) {
$description_html .= "<br />" . tr('poll') . ": " . escape_html($child->getTitle());
} elseif ($child instanceof Note) {
$description_html .= "<br />" . tr('note') . ": " . escape_html($child->getName());
}
}
$description_html .= "<br />" . tr('author') . ": <img width='15px' src='" . $author->getAvatarURL() . "'><a href='" . $author->getURL() . "'>" . $author_name . "</a>";
if ($target_wall->getRealId() != $author->getRealId()) {
$description_html .= "<br />" . tr('on_wall') . ": <img width='15px' src='" . $target_wall->getAvatarURL() . "'><a href='" . $target_wall->getURL() . "'>" . escape_html($target_wall->getCanonicalName()) . "</a>";
}
if ($this->isSigned()) {
2024-11-23 13:16:55 +03:00
$signer = $this->getOwner(false);
$description_html .= "<br />" . tr('sign_short') . ": <img width='15px' src='" . $signer->getAvatarURL() . "'><a href='" . $signer->getURL() . "'>" . escape_html($signer->getCanonicalName()) . "</a>";
}
if ($this->hasSource()) {
$description_html .= "<br />" . tr('source') . ": " . escape_html($this->getSource());
}
2024-11-23 13:16:55 +03:00
$item = new \Bhaktaraz\RSSGenerator\Item();
2024-11-23 13:16:55 +03:00
$item->title($title)
->url($url)
->guid($url)
->creator($author_name)
->pubDate($this->getPublicationTime()->timestamp())
->content(str_replace("\n", "<br />", $description_html));
return $item;
}
public function getGeo(): ?object
{
if (!$this->getRecord()->geo) {
return null;
}
return (object) json_decode($this->getRecord()->geo, true, JSON_UNESCAPED_UNICODE);
}
public function setGeo($encoded_object): void
{
$final_geo = $encoded_object['name'];
$neutral_names = ["Россия", "Russia", "Росія", "Россія", "Украина", "Ukraine", "Україна", "Украіна"];
foreach ($neutral_names as $name) {
if (str_contains($final_geo, $name . ", ")) {
$final_geo = str_replace($name . ", ", "", $final_geo);
}
}
$encoded_object['name'] = ovk_proc_strtr($final_geo, 255);
$encoded = json_encode($encoded_object);
$this->stateChanges("geo", $encoded);
}
public function getLat(): ?float
{
return (float) $this->getRecord()->geo_lat ?? null;
}
public function getLon(): ?float
{
return (float) $this->getRecord()->geo_lon ?? null;
}
public function getVkApiGeo(): object
{
return (object) [
'type' => 'point',
'coordinates' => $this->getLat() . ',' . $this->getLon(),
'name' => $this->getGeo()->name,
];
}
2020-06-07 19:04:43 +03:00
}