openvk/Web/Models/Entities/Post.php
mrilyew 959a6d6ac3 feat(geo): add ability to change point name and...
...fix federal cities name and territorial integrity
2024-12-13 20:02:49 +03:00

501 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection as DB;
use openvk\Web\Models\Repositories\{Clubs, Users};
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\Notifications\LikeNotification;
class Post extends Postable
{
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);
}
/**
* May return fake owner (group), if flags are [1, (*)]
*
* @param bool $honourFlags - check flags
*/
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
{
if($honourFlags && $this->isPostedOnBehalfOfGroup()) {
if($this->getRecord()->wall < 0)
return (new Clubs)->get(abs($this->getRecord()->wall));
}
return parent::getOwner($real);
}
function getPrettyId(): string
{
return $this->getRecord()->wall . "_" . $this->getVirtualId();
}
function getTargetWall(): int
{
return $this->getRecord()->wall;
}
function getWallOwner()
{
$w = $this->getRecord()->wall;
if($w < 0)
return (new Clubs)->get(abs($w));
return (new Users)->get($w);
}
function getRepostCount(): int
{
return sizeof(
$this->getRecord()
->related("attachments.attachable_id")
->where("attachable_type", get_class($this))
);
}
function isPinned(): bool
{
return (bool) $this->getRecord()->pinned;
}
function hasSource(): bool
{
return $this->getRecord()->source != NULL;
}
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);
}
function setSource(string $source)
{
$result = check_copyright_link($source);
$this->stateChanges("source", $source);
}
function resetSource()
{
$this->stateChanges("source", NULL);
}
function getVkApiCopyright(): object
{
return (object)[
'id' => 0,
'link' => $this->getSource(false),
'name' => $this->getSource(false),
'type' => 'link',
];
}
function isAd(): bool
{
return (bool) $this->getRecord()->ad;
}
function isPostedOnBehalfOfGroup(): bool
{
return ($this->getRecord()->flags & 0b10000000) > 0;
}
function isSigned(): bool
{
return ($this->getRecord()->flags & 0b01000000) > 0;
}
function isDeactivationMessage(): bool
{
return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0);
}
function isUpdateAvatarMessage(): bool
{
return (($this->getRecord()->flags & 0b00010000) > 0) && ($this->getRecord()->owner > 0);
}
function isExplicit(): bool
{
return (bool) $this->getRecord()->nsfw;
}
function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
function getOwnerPost(): int
{
return $this->getOwner(false)->getId();
}
function getPlatform(bool $forAPI = false): ?string
{
$platform = $this->getRecord()->api_source_name;
if($forAPI) {
switch ($platform) {
case 'openvk_refresh_android':
case 'openvk_legacy_android':
return 'android';
break;
case 'openvk_ios':
case 'openvk_legacy_ios':
return 'iphone';
break;
case 'windows_phone':
return 'wphone';
break;
case 'vika_touch': // кика хохотач ахахахаххахахахахах
case 'vk4me':
return 'mobile';
break;
case NULL:
return NULL;
break;
default:
return 'api';
break;
}
} else {
return $platform;
}
}
function getPlatformDetails(): array
{
$clients = simplexml_load_file(OPENVK_ROOT . "/data/clients.xml");
foreach($clients as $client) {
if($client['tag'] == $this->getPlatform()) {
return [
"tag" => $client['tag'],
"name" => $client['name'],
"url" => $client['url'],
"img" => $client['img']
];
break;
}
}
return [
"tag" => $this->getPlatform(),
"name" => NULL,
"url" => NULL,
"img" => NULL
];
}
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;
}
function getVkApiType(): string
{
$type = 'post';
if($this->getSuggestionType() != 0)
$type = 'suggest';
return $type;
}
function pin(): void
{
DB::i()
->getContext()
->table("posts")
->where([
"wall" => $this->getTargetWall(),
"pinned" => true,
])
->update(["pinned" => false]);
$this->stateChanges("pinned", true);
$this->save();
}
function unpin(): void
{
$this->stateChanges("pinned", false);
$this->save();
}
function canBePinnedBy(User $user = NULL): bool
{
if(!$user)
return false;
if($this->getTargetWall() < 0)
return (new Clubs)->get(abs($this->getTargetWall()))->canBeModifiedBy($user);
return $this->getTargetWall() === $user->getId();
}
function canBeDeletedBy(User $user = NULL): bool
{
if(!$user)
return false;
if($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0)
return false;
return $this->getOwnerPost() === $user->getId() || $this->canBePinnedBy($user);
}
function setContent(string $content): void
{
if(ctype_space($content))
throw new \LengthException("Content length must be at least 1 character (not counting whitespaces).");
else if(iconv_strlen($content) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxSize"])
throw new \LengthException("Content is too large.");
$this->stateChanges("content", $content);
}
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;
}
function setLike(bool $liked, User $user): void
{
$this->setLikeRecursively($liked, $user, 1);
}
function deletePost(): void
{
$this->setDeleted(1);
$this->unwire();
$this->save();
}
function canBeViewedBy(?User $user = NULL): bool
{
if($this->isDeleted()) {
return false;
}
return $this->getWallOwner()->canBeViewedBy($user);
}
function getSuggestionType()
{
return $this->getRecord()->suggested;
}
function getPageURL(): string
{
return "/wall".$this->getPrettyId();
}
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;
}
function canBeEditedBy(?User $user = NULL): bool
{
if(!$user)
return false;
if($this->isDeactivationMessage() || $this->isUpdateAvatarMessage())
return false;
if($this->getTargetWall() > 0)
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
else {
if($this->isPostedOnBehalfOfGroup())
return $this->getWallOwner()->canBeModifiedBy($user);
else
return $user->getId() == $this->getOwner(false)->getId();
}
return $user->getId() == $this->getOwner(false)->getId();
}
function toRss(): \Bhaktaraz\RSSGenerator\Item
{
$domain = ovk_scheme(true).$_SERVER["HTTP_HOST"];
$description = $this->getText(false);
$title = str_replace("\n", "", ovk_proc_strtr($description, 79));
$description_html = $description;
$url = $domain."/wall".$this->getPrettyId();
if($this->isUpdateAvatarMessage())
$title = tr('upd_in_general');
if($this->isDeactivationMessage())
$title = tr('post_deact_in_general');
$author = $this->getOwner();
$target_wall = $this->getWallOwner();
$author_name = escape_html($author->getCanonicalName());
if($this->isExplicit())
$title = 'NSFW: ' . $title;
foreach($this->getChildren() as $child) {
if($child instanceof Photo) {
$child_page = $domain.$child->getPageURL();
$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 />";
} else {
$description_html .= "".
"<br />".
"<a href=\"".$child->getVideoDriver()->getURL()."\"><b>".escape_html($child->getName())."</b></a><br />";
}
} elseif($child instanceof Audio) {
if(!$child->isWithdrawn()) {
$description_html .= "<br />"
."<b>".escape_html($child->getName())."</b>:"
."<br />"
."<audio controls>"
."<source src=\"".$child->getOriginalURL()."\" type=\"audio/mpeg\"></audio>"
."<br />";
}
} 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()) {
$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());
$item = new \Bhaktaraz\RSSGenerator\Item();
$item->title($title)
->url($url)
->guid($url)
->creator($author_name)
->pubDate($this->getPublicationTime()->timestamp())
->content(str_replace("\n", "<br />", $description_html));
return $item;
}
function getGeo(): ?object
{
if (!$this->getRecord()->geo) return NULL;
return (object) json_decode($this->getRecord()->geo, true, JSON_UNESCAPED_UNICODE);
}
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);
}
function getLat(): ?float
{
return (float) $this->getRecord()->geo_lat ?? NULL;
}
function getLon(): ?float
{
return (float) $this->getRecord()->geo_lon ?? NULL;
}
function getVkApiGeo(): object
{
return (object) [
'type' => 'point',
'coordinates' => $this->getLat() . ',' . $this->getLon(),
'name' => $this->getGeo()->name,
];
}
use Traits\TRichText;
}