$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 */ public 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); } public function getPrettyId(): string { return $this->getRecord()->wall . "_" . $this->getVirtualId(); } public function getTargetWall(): int { 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 { return sizeof( $this->getRecord() ->related("attachments.attachable_id") ->where("attachable_type", get_class($this)) ); } public function isPinned(): bool { 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 { return (bool) $this->getRecord()->ad; } public function isPostedOnBehalfOfGroup(): bool { return ($this->getRecord()->flags & 0b10000000) > 0; } public function isSigned(): bool { return ($this->getRecord()->flags & 0b01000000) > 0; } public function isDeactivationMessage(): bool { return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0); } public function isUpdateAvatarMessage(): bool { return (($this->getRecord()->flags & 0b00010000) > 0) && ($this->getRecord()->owner > 0); } public function isExplicit(): bool { return (bool) $this->getRecord()->nsfw; } public function isDeleted(): bool { return (bool) $this->getRecord()->deleted; } public function getOwnerPost(): int { return $this->getOwner(false)->getId(); } public 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; } } public 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, ]; } 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 { DB::i() ->getContext() ->table("posts") ->where([ "wall" => $this->getTargetWall(), "pinned" => true, ]) ->update(["pinned" => false]); $this->stateChanges("pinned", true); $this->save(); } public function unpin(): void { $this->stateChanges("pinned", false); $this->save(); } public 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(); } public 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); } public function setContent(string $content): void { if (ctype_space($content)) { 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"]) { throw new \LengthException("Content is too large."); } $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 { $this->setDeleted(1); $this->unwire(); $this->save(); } public function canBeViewedBy(?User $user = null): bool { if ($this->isDeleted()) { return false; } return $this->getWallOwner()->canBeViewedBy($user); } public function getSuggestionType() { 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) { 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(); } public 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 .= "

"; } elseif ($child instanceof Video) { $child_page = $domain . '/video' . $child->getPrettyId(); if ($child->getType() != 1) { $description_html .= "" . "
" . "
" . "" . escape_html($child->getName()) . "
"; } else { $description_html .= "" . "
" . "getVideoDriver()->getURL() . "\">" . escape_html($child->getName()) . "
"; } } elseif ($child instanceof Audio) { if (!$child->isWithdrawn()) { $description_html .= "
" . "" . escape_html($child->getName()) . ":" . "
" . "" . "
"; } } elseif ($child instanceof Poll) { $description_html .= "
" . tr('poll') . ": " . escape_html($child->getTitle()); } elseif ($child instanceof Note) { $description_html .= "
" . tr('note') . ": " . escape_html($child->getName()); } } $description_html .= "
" . tr('author') . ": " . $author_name . ""; if ($target_wall->getRealId() != $author->getRealId()) { $description_html .= "
" . tr('on_wall') . ": " . escape_html($target_wall->getCanonicalName()) . ""; } if ($this->isSigned()) { $signer = $this->getOwner(false); $description_html .= "
" . tr('sign_short') . ": " . escape_html($signer->getCanonicalName()) . ""; } if ($this->hasSource()) { $description_html .= "
" . 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", "
", $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, ]; } }