mirror of
https://github.com/openvk/openvk
synced 2025-02-02 13:05:37 +03:00
Alexander Minkin
6ec54a379d
* feat(lint): add php-cs-fixer for linting Removing previous CODE_STYLE as it was not enforced anyway and using PER-CS 2.0. This is not the reformatting commit. * style: format code according to PER-CS 2.0 with php-cs-fixer * ci(actions): add lint action Resolves #1132.
1730 lines
51 KiB
PHP
1730 lines
51 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace openvk\Web\Models\Entities;
|
||
|
||
use morphos\Gender;
|
||
use openvk\Web\Themes\{Themepack, Themepacks};
|
||
use openvk\Web\Util\DateTime;
|
||
use openvk\Web\Models\RowModel;
|
||
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift, Audio};
|
||
use openvk\Web\Models\Repositories\{Applications, Bans, Comments, Notes, Posts, Users, Clubs, Albums, Gifts, Notifications, Videos, Photos};
|
||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||
use Nette\Database\Table\ActiveRow;
|
||
use Chandler\Database\DatabaseConnection;
|
||
use Chandler\Security\User as ChandlerUser;
|
||
|
||
use function morphos\Russian\inflectName;
|
||
|
||
class User extends RowModel
|
||
{
|
||
use Traits\TBackDrops;
|
||
use Traits\TSubscribable;
|
||
use Traits\TAudioStatuses;
|
||
use Traits\TIgnorable;
|
||
protected $tableName = "profiles";
|
||
|
||
public const TYPE_DEFAULT = 0;
|
||
public const TYPE_BOT = 1;
|
||
|
||
public const SUBSCRIPTION_ABSENT = 0;
|
||
public const SUBSCRIPTION_INCOMING = 1;
|
||
public const SUBSCRIPTION_OUTGOING = 2;
|
||
public const SUBSCRIPTION_MUTUAL = 3;
|
||
|
||
public const PRIVACY_NO_ONE = 0;
|
||
public const PRIVACY_ONLY_FRIENDS = 1;
|
||
public const PRIVACY_ONLY_REGISTERED = 2;
|
||
public const PRIVACY_EVERYONE = 3;
|
||
|
||
public const NSFW_INTOLERANT = 0;
|
||
public const NSFW_TOLERANT = 1;
|
||
public const NSFW_FULL_TOLERANT = 2;
|
||
|
||
protected function _abstractRelationGenerator(string $filename, int $page = 1, int $limit = 6): \Traversable
|
||
{
|
||
$id = $this->getId();
|
||
$query = "SELECT id FROM\n" . file_get_contents(__DIR__ . "/../sql/$filename.tsql");
|
||
$query .= "\n LIMIT " . $limit . " OFFSET " . (($page - 1) * $limit);
|
||
|
||
$ids = [];
|
||
$rels = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
||
foreach ($rels as $rel) {
|
||
$rel = (new Users())->get($rel->id);
|
||
if (!$rel) {
|
||
continue;
|
||
}
|
||
if (in_array($rel->getId(), $ids)) {
|
||
continue;
|
||
}
|
||
$ids[] = $rel->getId();
|
||
|
||
yield $rel;
|
||
}
|
||
}
|
||
|
||
protected function _abstractRelationCount(string $filename): int
|
||
{
|
||
$id = $this->getId();
|
||
$query = "SELECT COUNT(*) AS cnt FROM\n" . file_get_contents(__DIR__ . "/../sql/$filename.tsql");
|
||
|
||
return (int) DatabaseConnection::i()->getConnection()->query($query, $id, $id)->fetch()->cnt;
|
||
}
|
||
|
||
public function getId(): int
|
||
{
|
||
return $this->getRecord()->id;
|
||
}
|
||
|
||
public function getStyle(): string
|
||
{
|
||
return $this->getRecord()->style;
|
||
}
|
||
|
||
public function getTheme(): ?Themepack
|
||
{
|
||
return Themepacks::i()[$this->getStyle()] ?? null;
|
||
}
|
||
|
||
public function getStyleAvatar(): int
|
||
{
|
||
return $this->getRecord()->style_avatar;
|
||
}
|
||
|
||
public function hasMilkshakeEnabled(): bool
|
||
{
|
||
return (bool) $this->getRecord()->milkshake;
|
||
}
|
||
|
||
public function hasMicroblogEnabled(): bool
|
||
{
|
||
return (bool) $this->getRecord()->microblog;
|
||
}
|
||
|
||
public function getMainPage(): int
|
||
{
|
||
return $this->getRecord()->main_page;
|
||
}
|
||
|
||
public function getChandlerGUID(): string
|
||
{
|
||
return $this->getRecord()->user;
|
||
}
|
||
|
||
public function getChandlerUser(): ChandlerUser
|
||
{
|
||
return new ChandlerUser($this->getRecord()->ref("ChandlerUsers", "user"));
|
||
}
|
||
|
||
public function getURL(): string
|
||
{
|
||
if (!is_null($this->getShortCode())) {
|
||
return "/" . $this->getShortCode();
|
||
} else {
|
||
return "/id" . $this->getId();
|
||
}
|
||
}
|
||
|
||
public function getAvatarUrl(string $size = "miniscule", $avPhoto = null): string
|
||
{
|
||
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||
|
||
if ($this->getRecord()->deleted) {
|
||
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
|
||
} elseif ($this->isBanned()) {
|
||
return "$serverUrl/assets/packages/static/openvk/img/banned.jpg";
|
||
}
|
||
|
||
if (!$avPhoto) {
|
||
$avPhoto = $this->getAvatarPhoto();
|
||
}
|
||
|
||
if (is_null($avPhoto)) {
|
||
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
|
||
} else {
|
||
return $avPhoto->getURLBySizeId($size);
|
||
}
|
||
}
|
||
|
||
public function getAvatarLink(): string
|
||
{
|
||
$avPhoto = $this->getAvatarPhoto();
|
||
if (!$avPhoto) {
|
||
return "javascript:void(0)";
|
||
}
|
||
|
||
$pid = $avPhoto->getPrettyId();
|
||
$aid = (new Albums())->getUserAvatarAlbum($this)->getId();
|
||
|
||
return "/photo$pid?from=album$aid";
|
||
}
|
||
|
||
public function getAvatarPhoto(): ?Photo
|
||
{
|
||
$avAlbum = (new Albums())->getUserAvatarAlbum($this);
|
||
$avCount = $avAlbum->getPhotosCount();
|
||
$avPhotos = $avAlbum->getPhotos($avCount, 1);
|
||
|
||
return iterator_to_array($avPhotos)[0] ?? null;
|
||
}
|
||
|
||
public function getFirstName(bool $pristine = false): string
|
||
{
|
||
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE));
|
||
$tsn = tr("__transNames");
|
||
if (($tsn !== "@__transNames" && !empty($tsn)) && !$pristine) {
|
||
return mb_convert_case(transliterator_transliterate($tsn, $name), MB_CASE_TITLE);
|
||
} else {
|
||
return $name;
|
||
}
|
||
}
|
||
|
||
public function getLastName(bool $pristine = false): string
|
||
{
|
||
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE));
|
||
$tsn = tr("__transNames");
|
||
if (($tsn !== "@__transNames" && !empty($tsn)) && !$pristine) {
|
||
return mb_convert_case(transliterator_transliterate($tsn, $name), MB_CASE_TITLE);
|
||
} else {
|
||
return $name;
|
||
}
|
||
}
|
||
|
||
public function getPseudo(): ?string
|
||
{
|
||
return ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : $this->getRecord()->pseudo);
|
||
}
|
||
|
||
public function getFullName(): string
|
||
{
|
||
if ($this->isDeleted() && !$this->isDeactivated()) {
|
||
return "DELETED";
|
||
}
|
||
|
||
$pseudo = $this->getPseudo();
|
||
if (!$pseudo) {
|
||
$pseudo = " ";
|
||
} else {
|
||
$pseudo = " ($pseudo) ";
|
||
}
|
||
|
||
return $this->getFirstName() . $pseudo . $this->getLastName();
|
||
}
|
||
|
||
public function getMorphedName(string $case = "genitive", bool $fullName = true): string
|
||
{
|
||
$name = $fullName ? ($this->getLastName() . " " . $this->getFirstName()) : $this->getFirstName();
|
||
if (!preg_match("%[А-яё\-]+$%", $name)) {
|
||
return $name;
|
||
} # name is probably not russian
|
||
|
||
$inflected = inflectName($name, $case, $this->isFemale() ? Gender::FEMALE : Gender::MALE);
|
||
|
||
return $inflected ?: $name;
|
||
}
|
||
|
||
public function getCanonicalName(): string
|
||
{
|
||
if ($this->isDeleted() && !$this->isDeactivated()) {
|
||
return "DELETED";
|
||
} else {
|
||
return $this->getFirstName() . " " . $this->getLastName();
|
||
}
|
||
}
|
||
|
||
public function getPhone(): ?string
|
||
{
|
||
return $this->getRecord()->phone;
|
||
}
|
||
|
||
public function getEmail(): ?string
|
||
{
|
||
return $this->getRecord()->email;
|
||
}
|
||
|
||
public function getOnline(): DateTime
|
||
{
|
||
return new DateTime($this->getRecord()->online);
|
||
}
|
||
|
||
public function getDescription(): ?string
|
||
{
|
||
return $this->getRecord()->about;
|
||
}
|
||
|
||
public function getAbout(): ?string
|
||
{
|
||
return $this->getRecord()->about;
|
||
}
|
||
|
||
public function getStatus(): ?string
|
||
{
|
||
return $this->getRecord()->status;
|
||
}
|
||
|
||
public function getShortCode(): ?string
|
||
{
|
||
return $this->getRecord()->shortcode;
|
||
}
|
||
|
||
public function getAlert(): ?string
|
||
{
|
||
return $this->getRecord()->alert;
|
||
}
|
||
|
||
public function getTextForContentBan(string $type): string
|
||
{
|
||
switch ($type) {
|
||
case "post": return "за размещение от Вашего лица таких <b>записей</b>:";
|
||
case "photo": return "за размещение от Вашего лица таких <b>фотографий</b>:";
|
||
case "video": return "за размещение от Вашего лица таких <b>видеозаписей</b>:";
|
||
case "group": return "за подозрительное вступление от Вашего лица <b>в группу:</b>";
|
||
case "comment": return "за размещение от Вашего лица таких <b>комментариев</b>:";
|
||
case "note": return "за размещение от Вашего лица таких <b>заметок</b>:";
|
||
case "app": return "за создание от Вашего имени <b>подозрительных приложений</b>.";
|
||
default: return "за размещение от Вашего лица такого <b>контента</b>:";
|
||
}
|
||
}
|
||
|
||
public function getRawBanReason(): ?string
|
||
{
|
||
return $this->getRecord()->block_reason;
|
||
}
|
||
|
||
public function getBanReason(?string $for = null)
|
||
{
|
||
$ban = (new Bans())->get((int) $this->getRecord()->block_reason);
|
||
if (!$ban || $ban->isOver()) {
|
||
return null;
|
||
}
|
||
|
||
$reason = $ban->getReason();
|
||
|
||
preg_match('/\*\*content-(post|photo|video|group|comment|note|app|noSpamTemplate|user)-(\d+)\*\*$/', $reason, $matches);
|
||
if (sizeof($matches) === 3) {
|
||
$content_type = $matches[1];
|
||
$content_id = (int) $matches[2];
|
||
if (in_array($content_type, ["noSpamTemplate", "user"])) {
|
||
$reason = "Подозрительная активность";
|
||
} else {
|
||
if ($for !== "banned") {
|
||
$reason = "Подозрительная активность";
|
||
} else {
|
||
$reason = [$this->getTextForContentBan($content_type), $content_type];
|
||
switch ($content_type) {
|
||
case "post": $reason[] = (new Posts())->get($content_id);
|
||
break;
|
||
case "photo": $reason[] = (new Photos())->get($content_id);
|
||
break;
|
||
case "video": $reason[] = (new Videos())->get($content_id);
|
||
break;
|
||
case "group": $reason[] = (new Clubs())->get($content_id);
|
||
break;
|
||
case "comment": $reason[] = (new Comments())->get($content_id);
|
||
break;
|
||
case "note": $reason[] = (new Notes())->get($content_id);
|
||
break;
|
||
case "app": $reason[] = (new Applications())->get($content_id);
|
||
break;
|
||
case "user": $reason[] = (new Users())->get($content_id);
|
||
break;
|
||
default: $reason[] = null;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return $reason;
|
||
}
|
||
|
||
public function getBanInSupportReason(): ?string
|
||
{
|
||
return $this->getRecord()->block_in_support_reason;
|
||
}
|
||
|
||
public function getType(): int
|
||
{
|
||
return $this->getRecord()->type;
|
||
}
|
||
|
||
public function getCoins(): float
|
||
{
|
||
if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"]) {
|
||
return 0.0;
|
||
}
|
||
|
||
return $this->getRecord()->coins;
|
||
}
|
||
|
||
public function getRating(): int
|
||
{
|
||
return OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"] ? $this->getRecord()->rating : 0;
|
||
}
|
||
|
||
public function getReputation(): int
|
||
{
|
||
return $this->getRecord()->reputation;
|
||
}
|
||
|
||
public function getRegistrationTime(): DateTime
|
||
{
|
||
return new DateTime($this->getRecord()->since->getTimestamp());
|
||
}
|
||
|
||
public function getRegistrationIP(): string
|
||
{
|
||
return $this->getRecord()->registering_ip;
|
||
}
|
||
|
||
public function getHometown(): ?string
|
||
{
|
||
return $this->getRecord()->hometown;
|
||
}
|
||
|
||
public function getPoliticalViews(): int
|
||
{
|
||
return $this->getRecord()->polit_views;
|
||
}
|
||
|
||
public function getMaritalStatus(): int
|
||
{
|
||
return $this->getRecord()->marital_status;
|
||
}
|
||
|
||
public function getLocalizedMaritalStatus(?bool $prefix = false): string
|
||
{
|
||
$status = $this->getMaritalStatus();
|
||
$string = "relationship_$status";
|
||
if ($prefix) {
|
||
$string .= "_prefix";
|
||
}
|
||
if ($this->isFemale()) {
|
||
$res = tr($string . "_fem");
|
||
if ($res != ("@" . $string . "_fem")) {
|
||
return $res;
|
||
} # If fem version exists, return
|
||
}
|
||
|
||
return tr($string);
|
||
}
|
||
|
||
public function getMaritalStatusUser(): ?User
|
||
{
|
||
if (!$this->getRecord()->marital_status_user) {
|
||
return null;
|
||
}
|
||
return (new Users())->get($this->getRecord()->marital_status_user);
|
||
}
|
||
|
||
public function getMaritalStatusUserPrefix(): ?string
|
||
{
|
||
return $this->getLocalizedMaritalStatus(true);
|
||
}
|
||
|
||
public function getContactEmail(): ?string
|
||
{
|
||
return $this->getRecord()->email_contact;
|
||
}
|
||
|
||
public function getTelegram(): ?string
|
||
{
|
||
return $this->getRecord()->telegram;
|
||
}
|
||
|
||
public function getInterests(): ?string
|
||
{
|
||
return $this->getRecord()->interests;
|
||
}
|
||
|
||
public function getFavoriteMusic(): ?string
|
||
{
|
||
return $this->getRecord()->fav_music;
|
||
}
|
||
|
||
public function getFavoriteFilms(): ?string
|
||
{
|
||
return $this->getRecord()->fav_films;
|
||
}
|
||
|
||
public function getFavoriteShows(): ?string
|
||
{
|
||
return $this->getRecord()->fav_shows;
|
||
}
|
||
|
||
public function getFavoriteBooks(): ?string
|
||
{
|
||
return $this->getRecord()->fav_books;
|
||
}
|
||
|
||
public function getFavoriteQuote(): ?string
|
||
{
|
||
return $this->getRecord()->fav_quote;
|
||
}
|
||
|
||
public function getFavoriteGames(): ?string
|
||
{
|
||
return $this->getRecord()->fav_games;
|
||
}
|
||
|
||
public function getCity(): ?string
|
||
{
|
||
return $this->getRecord()->city;
|
||
}
|
||
|
||
public function getPhysicalAddress(): ?string
|
||
{
|
||
return $this->getRecord()->address;
|
||
}
|
||
|
||
public function getAdditionalFields(bool $split = false): array
|
||
{
|
||
$all = \openvk\Web\Models\Entities\UserInfoEntities\AdditionalField::getByOwner($this->getId());
|
||
$result = [
|
||
"interests" => [],
|
||
"contacts" => [],
|
||
];
|
||
|
||
if ($split) {
|
||
foreach ($all as $field) {
|
||
if ($field->getPlace() == "contact") {
|
||
$result["contacts"][] = $field;
|
||
} elseif ($field->getPlace() == "interest") {
|
||
$result["interests"][] = $field;
|
||
}
|
||
}
|
||
} else {
|
||
$result = [];
|
||
foreach ($all as $field) {
|
||
$result[] = $field;
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
public function getNotificationOffset(): int
|
||
{
|
||
return $this->getRecord()->notification_offset;
|
||
}
|
||
|
||
public function getBirthday(): ?DateTime
|
||
{
|
||
if (is_null($this->getRecord()->birthday)) {
|
||
return null;
|
||
} else {
|
||
return new DateTime($this->getRecord()->birthday);
|
||
}
|
||
}
|
||
|
||
public function getBirthdayPrivacy(): int
|
||
{
|
||
return $this->getRecord()->birthday_privacy;
|
||
}
|
||
|
||
public function getAge(): ?int
|
||
{
|
||
return (int) floor((time() - $this->getBirthday()->timestamp()) / YEAR);
|
||
}
|
||
|
||
public function get2faSecret(): ?string
|
||
{
|
||
return $this->getRecord()["2fa_secret"];
|
||
}
|
||
|
||
public function is2faEnabled(): bool
|
||
{
|
||
return !is_null($this->get2faSecret());
|
||
}
|
||
|
||
public function updateNotificationOffset(): void
|
||
{
|
||
$this->stateChanges("notification_offset", time());
|
||
}
|
||
|
||
public function getLeftMenuItemStatus(string $id): bool
|
||
{
|
||
return (bool) bmask($this->getRecord()->left_menu, [
|
||
"length" => 1,
|
||
"mappings" => [
|
||
"photos",
|
||
"audios",
|
||
"videos",
|
||
"messages",
|
||
"notes",
|
||
"groups",
|
||
"news",
|
||
"links",
|
||
"poster",
|
||
"apps",
|
||
"docs",
|
||
],
|
||
])->get($id);
|
||
}
|
||
|
||
public function getPrivacySetting(string $id): int
|
||
{
|
||
return (int) bmask($this->getRecord()->privacy, [
|
||
"length" => 2,
|
||
"mappings" => [
|
||
"page.read",
|
||
"page.info.read",
|
||
"groups.read",
|
||
"photos.read",
|
||
"videos.read",
|
||
"notes.read",
|
||
"friends.read",
|
||
"friends.add",
|
||
"wall.write",
|
||
"messages.write",
|
||
"audios.read",
|
||
"likes.read",
|
||
],
|
||
])->get($id);
|
||
}
|
||
|
||
public function getPrivacyPermission(string $permission, ?User $user = null): bool
|
||
{
|
||
$permStatus = $this->getPrivacySetting($permission);
|
||
if (!$user) {
|
||
return $permStatus === User::PRIVACY_EVERYONE;
|
||
} elseif ($user->getId() === $this->getId()) {
|
||
return true;
|
||
}
|
||
|
||
if (/*$permission != "messages.write" && */!$this->canBeViewedBy($user, true)) {
|
||
return false;
|
||
}
|
||
|
||
switch ($permStatus) {
|
||
case User::PRIVACY_ONLY_FRIENDS:
|
||
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
|
||
case User::PRIVACY_ONLY_REGISTERED:
|
||
case User::PRIVACY_EVERYONE:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public function getProfileCompletenessReport(): object
|
||
{
|
||
$incompleteness = 0;
|
||
$unfilled = [];
|
||
|
||
if (!$this->getRecord()->status) {
|
||
$unfilled[] = "status";
|
||
$incompleteness += 15;
|
||
}
|
||
if (!$this->getRecord()->telegram) {
|
||
$unfilled[] = "telegram";
|
||
$incompleteness += 15;
|
||
}
|
||
if (!$this->getRecord()->email) {
|
||
$unfilled[] = "email";
|
||
$incompleteness += 20;
|
||
}
|
||
if (!$this->getRecord()->city) {
|
||
$unfilled[] = "city";
|
||
$incompleteness += 20;
|
||
}
|
||
if (!$this->getRecord()->interests) {
|
||
$unfilled[] = "interests";
|
||
$incompleteness += 20;
|
||
}
|
||
|
||
$total = max(100 - $incompleteness + $this->getRating(), 0);
|
||
if (ovkGetQuirk("profile.rating-bar-behaviour") === 0) {
|
||
if ($total >= 100) {
|
||
$percent = round(($total / 10 ** strlen(strval($total))) * 100, 0);
|
||
} else {
|
||
$percent = min($total, 100);
|
||
}
|
||
}
|
||
|
||
return (object) [
|
||
"total" => $total,
|
||
"percent" => $percent,
|
||
"unfilled" => $unfilled,
|
||
];
|
||
}
|
||
|
||
public function getFriends(int $page = 1, int $limit = 6): \Traversable
|
||
{
|
||
return $this->_abstractRelationGenerator("get-friends", $page, $limit);
|
||
}
|
||
|
||
public function getFriendsCount(): int
|
||
{
|
||
return $this->_abstractRelationCount("get-friends");
|
||
}
|
||
|
||
public function getFriendsOnline(int $page = 1, int $limit = 6): \Traversable
|
||
{
|
||
return $this->_abstractRelationGenerator("get-online-friends", $page, $limit);
|
||
}
|
||
|
||
public function getFriendsOnlineCount(): int
|
||
{
|
||
return $this->_abstractRelationCount("get-online-friends");
|
||
}
|
||
|
||
public function getFollowers(int $page = 1, int $limit = 6): \Traversable
|
||
{
|
||
return $this->_abstractRelationGenerator("get-followers", $page, $limit);
|
||
}
|
||
|
||
public function getFollowersCount(): int
|
||
{
|
||
return $this->_abstractRelationCount("get-followers");
|
||
}
|
||
|
||
public function getRequests(int $page = 1, int $limit = 6): \Traversable
|
||
{
|
||
return $this->_abstractRelationGenerator("get-requests", $page, $limit);
|
||
}
|
||
|
||
public function getRequestsCount(): int
|
||
{
|
||
return $this->_abstractRelationCount("get-requests");
|
||
}
|
||
|
||
public function getSubscriptions(int $page = 1, int $limit = 6): \Traversable
|
||
{
|
||
return $this->_abstractRelationGenerator("get-subscriptions-user", $page, $limit);
|
||
}
|
||
|
||
public function getSubscriptionsCount(): int
|
||
{
|
||
return $this->_abstractRelationCount("get-subscriptions-user");
|
||
}
|
||
|
||
public function getUnreadMessagesCount(): int
|
||
{
|
||
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
||
}
|
||
|
||
public function getClubs(int $page = 1, bool $admin = false, int $count = OPENVK_DEFAULT_PER_PAGE, bool $offset = false): \Traversable
|
||
{
|
||
if (!$offset) {
|
||
$page = ($page - 1) * $count;
|
||
}
|
||
|
||
if ($admin) {
|
||
$id = $this->getId();
|
||
$query = "SELECT `id` FROM `groups` WHERE `owner` = ? UNION SELECT `club` as `id` FROM `group_coadmins` WHERE `user` = ?";
|
||
$query .= " LIMIT " . $count . " OFFSET " . $page;
|
||
|
||
$sel = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
||
foreach ($sel as $target) {
|
||
$target = (new Clubs())->get($target->id);
|
||
if (!$target) {
|
||
continue;
|
||
}
|
||
|
||
yield $target;
|
||
}
|
||
} else {
|
||
$sel = $this->getRecord()->related("subscriptions.follower")->limit($count, $page);
|
||
foreach ($sel->where("model", "openvk\\Web\\Models\\Entities\\Club") as $target) {
|
||
$target = (new Clubs())->get($target->target);
|
||
if (!$target) {
|
||
continue;
|
||
}
|
||
|
||
yield $target;
|
||
}
|
||
}
|
||
}
|
||
|
||
public function getClubCount(bool $admin = false): int
|
||
{
|
||
if ($admin) {
|
||
$id = $this->getId();
|
||
$query = "SELECT COUNT(*) AS `cnt` FROM (SELECT `id` FROM `groups` WHERE `owner` = ? UNION SELECT `club` as `id` FROM `group_coadmins` WHERE `user` = ?) u0;";
|
||
|
||
return (int) DatabaseConnection::i()->getConnection()->query($query, $id, $id)->fetch()->cnt;
|
||
} else {
|
||
$sel = $this->getRecord()->related("subscriptions.follower");
|
||
$sel = $sel->where("model", "openvk\\Web\\Models\\Entities\\Club");
|
||
|
||
return sizeof($sel);
|
||
}
|
||
}
|
||
|
||
public function getPinnedClubs(): \Traversable
|
||
{
|
||
foreach ($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true) as $target) {
|
||
$target = (new Clubs())->get($target->id);
|
||
if (!$target) {
|
||
continue;
|
||
}
|
||
|
||
yield $target;
|
||
}
|
||
|
||
foreach ($this->getRecord()->related("group_coadmins.user")->where("club_pinned", true) as $target) {
|
||
$target = (new Clubs())->get($target->club);
|
||
if (!$target) {
|
||
continue;
|
||
}
|
||
|
||
yield $target;
|
||
}
|
||
}
|
||
|
||
public function getPinnedClubCount(): int
|
||
{
|
||
return sizeof($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true)) + sizeof($this->getRecord()->related("group_coadmins.user")->where("club_pinned", true));
|
||
}
|
||
|
||
public function isClubPinned(Club $club): bool
|
||
{
|
||
if ($club->getOwner()->getId() === $this->getId()) {
|
||
return $club->isOwnerClubPinned();
|
||
}
|
||
|
||
$manager = $club->getManager($this);
|
||
if (!is_null($manager)) {
|
||
return $manager->isClubPinned();
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public function getMeetings(int $page = 1): \Traversable
|
||
{
|
||
$sel = $this->getRecord()->related("event_turnouts.user")->page($page, OPENVK_DEFAULT_PER_PAGE);
|
||
foreach ($sel as $target) {
|
||
$target = (new Clubs())->get($target->event);
|
||
if (!$target) {
|
||
continue;
|
||
}
|
||
|
||
yield $target;
|
||
}
|
||
}
|
||
|
||
public function getMeetingCount(): int
|
||
{
|
||
return sizeof($this->getRecord()->related("event_turnouts.user"));
|
||
}
|
||
|
||
public function getGifts(int $page = 1, ?int $perPage = null): \Traversable
|
||
{
|
||
$gifts = $this->getRecord()->related("gift_user_relations.receiver")->order("sent DESC")->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||
foreach ($gifts as $rel) {
|
||
yield (object) [
|
||
"sender" => (new Users())->get($rel->sender),
|
||
"gift" => (new Gifts())->get($rel->gift),
|
||
"caption" => $rel->comment,
|
||
"anon" => $rel->anonymous,
|
||
"sent" => new DateTime($rel->sent),
|
||
];
|
||
}
|
||
}
|
||
|
||
public function getGiftCount(): int
|
||
{
|
||
return sizeof($this->getRecord()->related("gift_user_relations.receiver"));
|
||
}
|
||
|
||
public function get2faBackupCodes(): \Traversable
|
||
{
|
||
$sel = $this->getRecord()->related("2fa_backup_codes.owner");
|
||
foreach ($sel as $target) {
|
||
yield $target->code;
|
||
}
|
||
}
|
||
|
||
public function get2faBackupCodeCount(): int
|
||
{
|
||
return sizeof($this->getRecord()->related("2fa_backup_codes.owner"));
|
||
}
|
||
|
||
public function generate2faBackupCodes(): void
|
||
{
|
||
$codes = [];
|
||
|
||
for ($i = 0; $i < 10 - $this->get2faBackupCodeCount(); $i++) {
|
||
$codes[] = [
|
||
"owner" => $this->getId(),
|
||
"code" => random_int(10000000, 99999999),
|
||
];
|
||
}
|
||
|
||
if (sizeof($codes) > 0) {
|
||
DatabaseConnection::i()->getContext()->table("2fa_backup_codes")->insert($codes);
|
||
}
|
||
}
|
||
|
||
public function use2faBackupCode(int $code): bool
|
||
{
|
||
return (bool) $this->getRecord()->related("2fa_backup_codes.owner")->where("code", $code)->delete();
|
||
}
|
||
|
||
public function getSubscriptionStatus(User $user): int
|
||
{
|
||
$subbed = !is_null($this->getRecord()->related("subscriptions.follower")->where([
|
||
"model" => static::class,
|
||
"target" => $user->getId(),
|
||
])->fetch());
|
||
$followed = !is_null($this->getRecord()->related("subscriptions.target")->where([
|
||
"model" => static::class,
|
||
"follower" => $user->getId(),
|
||
])->fetch());
|
||
|
||
if ($subbed && $followed) {
|
||
return User::SUBSCRIPTION_MUTUAL;
|
||
}
|
||
if ($subbed) {
|
||
return User::SUBSCRIPTION_INCOMING;
|
||
}
|
||
if ($followed) {
|
||
return User::SUBSCRIPTION_OUTGOING;
|
||
}
|
||
|
||
return User::SUBSCRIPTION_ABSENT;
|
||
}
|
||
|
||
public function getNotificationsCount(bool $archived = false): int
|
||
{
|
||
return (new Notifications())->getNotificationCountByUser($this, $this->getNotificationOffset(), $archived);
|
||
}
|
||
|
||
public function getNotifications(int $page, bool $archived = false): \Traversable
|
||
{
|
||
return (new Notifications())->getNotificationsByUser($this, $this->getNotificationOffset(), $archived, $page);
|
||
}
|
||
|
||
public function getPendingPhoneVerification(): ?ActiveRow
|
||
{
|
||
return $this->getRecord()->ref("number_verification", "id");
|
||
}
|
||
|
||
public function getRefLinkId(): string
|
||
{
|
||
$hash = hash_hmac("Snefru", (string) $this->getId(), CHANDLER_ROOT_CONF["security"]["secret"], true);
|
||
|
||
return dechex($this->getId()) . " " . base64_encode($hash);
|
||
}
|
||
|
||
public function getNsfwTolerance(): int
|
||
{
|
||
return $this->getRecord()->nsfw_tolerance;
|
||
}
|
||
|
||
public function isFemale(): bool
|
||
{
|
||
return $this->getRecord()->sex == 1;
|
||
}
|
||
|
||
public function isNeutral(): bool
|
||
{
|
||
return (bool) $this->getRecord()->sex == 2;
|
||
}
|
||
|
||
public function getLocalizedPronouns(): string
|
||
{
|
||
switch ($this->getRecord()->sex) {
|
||
case 0:
|
||
return tr('male');
|
||
case 1:
|
||
return tr('female');
|
||
case 2:
|
||
return tr('neutral');
|
||
}
|
||
}
|
||
|
||
public function getPronouns(): int
|
||
{
|
||
return $this->getRecord()->sex;
|
||
}
|
||
|
||
public function isVerified(): bool
|
||
{
|
||
return (bool) $this->getRecord()->verified;
|
||
}
|
||
|
||
public function isBanned(): bool
|
||
{
|
||
return !is_null($this->getBanReason());
|
||
}
|
||
|
||
public function isBannedInSupport(): bool
|
||
{
|
||
return !is_null($this->getBanInSupportReason());
|
||
}
|
||
|
||
public function isOnline(): bool
|
||
{
|
||
return time() - $this->getRecord()->online <= 300;
|
||
}
|
||
|
||
public function getOnlinePlatform(bool $forAPI = false): ?string
|
||
{
|
||
$platform = $this->getRecord()->client_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 'vika_touch': // кика хохотач ахахахаххахахахахах
|
||
case 'vk4me':
|
||
return 'mobile';
|
||
break;
|
||
|
||
case null:
|
||
return null;
|
||
break;
|
||
|
||
default:
|
||
return 'api';
|
||
break;
|
||
}
|
||
} else {
|
||
return $platform;
|
||
}
|
||
}
|
||
|
||
public function getOnlinePlatformDetails(): array
|
||
{
|
||
$clients = simplexml_load_file(OPENVK_ROOT . "/data/clients.xml");
|
||
|
||
foreach ($clients as $client) {
|
||
if ($client['tag'] == $this->getOnlinePlatform()) {
|
||
return [
|
||
"tag" => $client['tag'],
|
||
"name" => $client['name'],
|
||
"url" => $client['url'],
|
||
"img" => $client['img'],
|
||
];
|
||
break;
|
||
}
|
||
}
|
||
|
||
return [
|
||
"tag" => $this->getOnlinePlatform(),
|
||
"name" => null,
|
||
"url" => null,
|
||
"img" => null,
|
||
];
|
||
}
|
||
|
||
public function prefersNotToSeeRating(): bool
|
||
{
|
||
return !((bool) $this->getRecord()->show_rating);
|
||
}
|
||
|
||
public function hasPendingNumberChange(): bool
|
||
{
|
||
return !is_null($this->getPendingPhoneVerification());
|
||
}
|
||
|
||
public function gift(User $sender, Gift $gift, ?string $comment = null, bool $anonymous = false): void
|
||
{
|
||
DatabaseConnection::i()->getContext()->table("gift_user_relations")->insert([
|
||
"sender" => $sender->getId(),
|
||
"receiver" => $this->getId(),
|
||
"gift" => $gift->getId(),
|
||
"comment" => $comment,
|
||
"anonymous" => $anonymous,
|
||
"sent" => time(),
|
||
]);
|
||
}
|
||
|
||
public function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = null, ?int $initiator = null): void
|
||
{
|
||
if ($deleteSubscriptions) {
|
||
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
||
$subs = $subs->where(
|
||
"follower = ? OR (target = ? AND model = ?)",
|
||
$this->getId(),
|
||
$this->getId(),
|
||
get_class($this),
|
||
);
|
||
$subs->delete();
|
||
}
|
||
|
||
$iat = time();
|
||
$ban = new Ban();
|
||
$ban->setUser($this->getId());
|
||
$ban->setReason($reason);
|
||
$ban->setInitiator($initiator);
|
||
$ban->setIat($iat);
|
||
$ban->setExp($unban_time !== "permanent" ? $unban_time : 0);
|
||
$ban->setTime($unban_time === "permanent" ? 0 : ($unban_time ? ($unban_time - $iat) : 0));
|
||
$ban->save();
|
||
|
||
$this->setBlock_Reason($ban->getId());
|
||
// $this->setUnblock_time($unban_time);
|
||
$this->save();
|
||
}
|
||
|
||
public function unban(int $removed_by): void
|
||
{
|
||
$ban = (new Bans())->get((int) $this->getRawBanReason());
|
||
if (!$ban || $ban->isOver()) {
|
||
return;
|
||
}
|
||
|
||
$ban->setRemoved_Manually(true);
|
||
$ban->setRemoved_By($removed_by);
|
||
$ban->save();
|
||
|
||
$this->setBlock_Reason(null);
|
||
// $user->setUnblock_time(NULL);
|
||
$this->save();
|
||
}
|
||
|
||
public function deactivate(?string $reason): void
|
||
{
|
||
$this->setDeleted(1);
|
||
$this->setDeact_Date(time() + (MONTH * 7));
|
||
$this->setDeact_Reason($reason);
|
||
$this->save();
|
||
}
|
||
|
||
public function reactivate(): void
|
||
{
|
||
$this->setDeleted(0);
|
||
$this->setDeact_Date(0);
|
||
$this->setDeact_Reason("");
|
||
$this->save();
|
||
}
|
||
|
||
public function getDeactivationDate(): DateTime
|
||
{
|
||
return new DateTime($this->getRecord()->deact_date);
|
||
}
|
||
|
||
public function verifyNumber(string $code): bool
|
||
{
|
||
$ver = $this->getPendingPhoneVerification();
|
||
if (!$ver) {
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
if (sodium_memcmp((string) $ver->code, $code) === -1) {
|
||
return false;
|
||
}
|
||
} catch (\SodiumException $ex) {
|
||
return false;
|
||
}
|
||
|
||
$this->setPhone($ver->number);
|
||
$this->save();
|
||
|
||
DatabaseConnection::i()->getContext()
|
||
->table("number_verification")
|
||
->where("user", $this->getId())
|
||
->delete();
|
||
|
||
return true;
|
||
}
|
||
|
||
public function setFirst_Name(string $firstName): void
|
||
{
|
||
$firstName = mb_convert_case($firstName, MB_CASE_TITLE);
|
||
if (!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?(?:[\p{L&}\p{Lo}]\p{Mn}?){1,64}$%u', $firstName)) {
|
||
throw new InvalidUserNameException();
|
||
}
|
||
|
||
$this->stateChanges("first_name", $firstName);
|
||
}
|
||
|
||
public function setLast_Name(string $lastName): void
|
||
{
|
||
if (!empty($lastName)) {
|
||
$lastName = mb_convert_case($lastName, MB_CASE_TITLE);
|
||
if (!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?([\p{L&}\p{Lo}]\p{Mn}?){1,64}(\-\g<1>+)?$%u', $lastName)) {
|
||
throw new InvalidUserNameException();
|
||
}
|
||
}
|
||
|
||
$this->stateChanges("last_name", $lastName);
|
||
}
|
||
|
||
public function setNsfwTolerance(int $tolerance): void
|
||
{
|
||
$this->stateChanges("nsfw_tolerance", $tolerance);
|
||
}
|
||
|
||
public function setPrivacySetting(string $id, int $status): void
|
||
{
|
||
$this->stateChanges("privacy", bmask($this->changes["privacy"] ?? $this->getRecord()->privacy, [
|
||
"length" => 2,
|
||
"mappings" => [
|
||
"page.read",
|
||
"page.info.read",
|
||
"groups.read",
|
||
"photos.read",
|
||
"videos.read",
|
||
"notes.read",
|
||
"friends.read",
|
||
"friends.add",
|
||
"wall.write",
|
||
"messages.write",
|
||
"audios.read",
|
||
"likes.read",
|
||
],
|
||
])->set($id, $status)->toInteger());
|
||
}
|
||
|
||
public function setLeftMenuItemStatus(string $id, bool $status): void
|
||
{
|
||
$mask = bmask($this->changes["left_menu"] ?? $this->getRecord()->left_menu, [
|
||
"length" => 1,
|
||
"mappings" => [
|
||
"photos",
|
||
"audios",
|
||
"videos",
|
||
"messages",
|
||
"notes",
|
||
"groups",
|
||
"news",
|
||
"links",
|
||
"poster",
|
||
"apps",
|
||
"docs",
|
||
],
|
||
])->set($id, (int) $status)->toInteger();
|
||
|
||
$this->stateChanges("left_menu", $mask);
|
||
}
|
||
|
||
public function setShortCode(?string $code = null, bool $force = false): ?bool
|
||
{
|
||
if (!is_null($code)) {
|
||
if (strlen($code) < OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["minLength"] && !$force) {
|
||
return false;
|
||
}
|
||
if (!preg_match("%^[a-z][a-z0-9\\.\\_]{0,30}[a-z0-9]$%", $code)) {
|
||
return false;
|
||
}
|
||
if (in_array($code, OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["forbiddenNames"])) {
|
||
return false;
|
||
}
|
||
if (\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$code")[0]->presenter !== "UnknownTextRouteStrategy") {
|
||
return false;
|
||
}
|
||
|
||
$pClub = DatabaseConnection::i()->getContext()->table("groups")->where("shortcode", $code)->fetch();
|
||
if (!is_null($pClub)) {
|
||
return false;
|
||
}
|
||
|
||
$pAlias = DatabaseConnection::i()->getContext()->table("aliases")->where("shortcode", $code)->fetch();
|
||
if (!is_null($pAlias)) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
$this->stateChanges("shortcode", $code);
|
||
return true;
|
||
}
|
||
|
||
public function setPhoneWithVerification(string $phone): string
|
||
{
|
||
$code = unpack("S", openssl_random_pseudo_bytes(2))[1];
|
||
|
||
if ($this->hasPendingNumberChange()) {
|
||
DatabaseConnection::i()->getContext()
|
||
->table("number_verification")
|
||
->where("user", $this->getId())
|
||
->update(["number" => $phone, "code" => $code]);
|
||
} else {
|
||
DatabaseConnection::i()->getContext()
|
||
->table("number_verification")
|
||
->insert(["user" => $this->getId(), "number" => $phone, "code" => $code]);
|
||
}
|
||
|
||
return (string) $code;
|
||
}
|
||
|
||
# KABOBSQL temporary fix
|
||
# Tuesday, the 7th of January 2020 @ 22:43 <Menhera>: implementing quick fix to this problem and monitoring
|
||
# NOTICE: this is an ongoing conversation, add your comments just above this line. Thanks!
|
||
public function setOnline(int $time): bool
|
||
{
|
||
$this->stateChanges("shortcode", $this->getRecord()->shortcode); #fix KABOBSQL
|
||
$this->stateChanges("online", $time);
|
||
|
||
return true;
|
||
}
|
||
|
||
public function updOnline(string $platform): bool
|
||
{
|
||
$this->setOnline(time());
|
||
$this->setClient_name($platform);
|
||
$this->save(false);
|
||
|
||
return true;
|
||
}
|
||
|
||
public function changeEmail(string $email): void
|
||
{
|
||
DatabaseConnection::i()->getContext()->table("ChandlerUsers")
|
||
->where("id", $this->getChandlerUser()->getId())->update([
|
||
"login" => $email,
|
||
]);
|
||
|
||
$this->stateChanges("email", $email);
|
||
$this->save();
|
||
}
|
||
|
||
public function adminNotify(string $message): bool
|
||
{
|
||
$admId = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||
if (!$admId) {
|
||
return false;
|
||
} elseif (is_null($admin = (new Users())->get($admId))) {
|
||
return false;
|
||
}
|
||
|
||
$cor = new Correspondence($admin, $this);
|
||
$msg = new Message();
|
||
$msg->setContent($message);
|
||
$cor->sendMessage($msg, true);
|
||
|
||
return true;
|
||
}
|
||
|
||
public function isDeleted(): bool
|
||
{
|
||
if ($this->getRecord()->deleted == 1) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public function isDeactivated(): bool
|
||
{
|
||
if ($this->getDeactivationDate()->timestamp() > time()) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 0 - Default status
|
||
* 1 - Incognito online status
|
||
* 2 - Page of a dead person
|
||
*/
|
||
public function onlineStatus(): int
|
||
{
|
||
switch ($this->getRecord()->online) {
|
||
case 1:
|
||
return 1;
|
||
break;
|
||
|
||
case 2:
|
||
return 2;
|
||
break;
|
||
|
||
default:
|
||
return 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
public function getWebsite(): ?string
|
||
{
|
||
return $this->getRecord()->website;
|
||
}
|
||
|
||
# ты устрица
|
||
public function isActivated(): bool
|
||
{
|
||
return (bool) $this->getRecord()->activated;
|
||
}
|
||
|
||
public function isAdmin(): bool
|
||
{
|
||
return $this->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(null);
|
||
}
|
||
|
||
public function isDead(): bool
|
||
{
|
||
return $this->onlineStatus() == 2;
|
||
}
|
||
|
||
public function getUnbanTime(): ?string
|
||
{
|
||
$ban = (new Bans())->get((int) $this->getRecord()->block_reason);
|
||
if (!$ban || $ban->isOver() || $ban->isPermanent()) {
|
||
return null;
|
||
}
|
||
if ($this->canUnbanThemself()) {
|
||
return tr("today");
|
||
}
|
||
|
||
return date('d.m.Y', $ban->getEndTime());
|
||
}
|
||
|
||
public function canUnbanThemself(): bool
|
||
{
|
||
if (!$this->isBanned()) {
|
||
return false;
|
||
}
|
||
|
||
$ban = (new Bans())->get((int) $this->getRecord()->block_reason);
|
||
if (!$ban || $ban->isOver() || $ban->isPermanent()) {
|
||
return false;
|
||
}
|
||
|
||
return $ban->getEndTime() <= time() && !$ban->isPermanent();
|
||
}
|
||
|
||
public function getNewBanTime()
|
||
{
|
||
$bans = iterator_to_array((new Bans())->getByUser($this->getid()));
|
||
if (!$bans || count($bans) === 0) {
|
||
return 0;
|
||
}
|
||
|
||
$last_ban = end($bans);
|
||
if (!$last_ban) {
|
||
return 0;
|
||
}
|
||
|
||
if ($last_ban->isPermanent()) {
|
||
return "0";
|
||
}
|
||
|
||
$values = [0, 3600, 7200, 86400, 172800, 604800, 1209600, 3024000, 9072000];
|
||
$response = 0;
|
||
$i = 0;
|
||
|
||
foreach ($values as $value) {
|
||
$i++;
|
||
if ($last_ban->getTime() === 0 && $value === 0) {
|
||
continue;
|
||
}
|
||
if ($last_ban->getTime() < $value) {
|
||
$response = $value;
|
||
break;
|
||
} elseif ($last_ban->getTime() >= $value) {
|
||
if ($i < count($values)) {
|
||
continue;
|
||
}
|
||
$response = "0";
|
||
break;
|
||
}
|
||
}
|
||
return $response;
|
||
}
|
||
|
||
public function getProfileType(): int
|
||
{
|
||
# 0 — открытый профиль, 1 — закрытый
|
||
return $this->getRecord()->profile_type;
|
||
}
|
||
|
||
public function canBeViewedBy(?User $user = null, bool $blacklist_check = true): bool
|
||
{
|
||
if (!is_null($user)) {
|
||
if ($this->getId() == $user->getId()) {
|
||
return true;
|
||
}
|
||
|
||
if ($user->isAdmin() && !(OPENVK_ROOT_CONF['openvk']['preferences']['blacklists']['applyToAdmins'] ?? true)) {
|
||
return true;
|
||
}
|
||
|
||
if ($blacklist_check && ($this->isBlacklistedBy($user) || $user->isBlacklistedBy($this))) {
|
||
return false;
|
||
}
|
||
|
||
if ($this->getProfileType() == 0) {
|
||
return true;
|
||
} else {
|
||
if ($user->getSubscriptionStatus($this) == User::SUBSCRIPTION_MUTUAL) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
if ($this->getProfileType() == 0) {
|
||
if ($this->getPrivacySetting("page.read") == 3) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public function isClosed(): bool
|
||
{
|
||
return (bool) $this->getProfileType();
|
||
}
|
||
|
||
public function isHideFromGlobalFeedEnabled(): bool
|
||
{
|
||
return $this->isClosed();
|
||
}
|
||
|
||
public function getRealId()
|
||
{
|
||
return $this->getId();
|
||
}
|
||
|
||
public function isPrivateLikes(): bool
|
||
{
|
||
return $this->getPrivacySetting("likes.read") == User::PRIVACY_NO_ONE;
|
||
}
|
||
|
||
public function toVkApiStruct(?User $user = null, string $fields = ''): object
|
||
{
|
||
$res = (object) [];
|
||
|
||
$res->id = $this->getId();
|
||
$res->first_name = $this->getFirstName();
|
||
$res->last_name = $this->getLastName();
|
||
$res->deactivated = $this->isDeactivated();
|
||
$res->is_closed = $this->isClosed();
|
||
|
||
if (!is_null($user)) {
|
||
$res->can_access_closed = (bool) $this->canBeViewedBy($user);
|
||
}
|
||
|
||
if (!is_array($fields)) {
|
||
$fields = explode(',', $fields);
|
||
}
|
||
|
||
$avatar_photo = $this->getAvatarPhoto();
|
||
foreach ($fields as $field) {
|
||
switch ($field) {
|
||
case 'is_dead':
|
||
$res->is_dead = $this->isDead();
|
||
break;
|
||
case 'verified':
|
||
$res->verified = (int) $this->isVerified();
|
||
break;
|
||
case 'sex':
|
||
$res->sex = $this->isFemale() ? 1 : ($this->isNeutral() ? 0 : 2);
|
||
break;
|
||
case 'photo_50':
|
||
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
|
||
break;
|
||
case 'photo_100':
|
||
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
|
||
break;
|
||
case 'photo_200':
|
||
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
|
||
break;
|
||
case 'photo_max':
|
||
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
|
||
break;
|
||
case 'photo_id':
|
||
$res->photo_id = $avatar_photo ? $avatar_photo->getPrettyId() : null;
|
||
break;
|
||
case 'background':
|
||
$res->background = $this->getBackDropPictureURLs();
|
||
break;
|
||
case 'reg_date':
|
||
$res->reg_date = $this->getRegistrationTime()->timestamp();
|
||
break;
|
||
case 'nickname':
|
||
$res->nickname = $this->getPseudo();
|
||
break;
|
||
case 'rating':
|
||
$res->rating = $this->getRating();
|
||
break;
|
||
case 'status':
|
||
$res->status = $this->getStatus();
|
||
break;
|
||
case 'screen_name':
|
||
$res->screen_name = $this->getShortCode() ?? "id" . $this->getId();
|
||
break;
|
||
case 'real_id':
|
||
$res->real_id = $this->getRealId();
|
||
break;
|
||
case "blacklisted_by_me":
|
||
if (!$user) {
|
||
continue;
|
||
}
|
||
|
||
$res->blacklisted_by_me = (int) $this->isBlacklistedBy($user);
|
||
break;
|
||
case "blacklisted":
|
||
if (!$user) {
|
||
continue;
|
||
}
|
||
|
||
$res->blacklisted = (int) $user->isBlacklistedBy($this);
|
||
break;
|
||
case "games":
|
||
$res->games = $this->getFavoriteGames();
|
||
break;
|
||
}
|
||
}
|
||
|
||
return $res;
|
||
}
|
||
|
||
public function getAudiosCollectionSize()
|
||
{
|
||
return (new \openvk\Web\Models\Repositories\Audios())->getUserCollectionSize($this);
|
||
}
|
||
|
||
public function getBroadcastList(string $filter = "friends", bool $shuffle = false)
|
||
{
|
||
$dbContext = DatabaseConnection::i()->getContext();
|
||
$entityIds = [];
|
||
$query = $dbContext->table("subscriptions")->where("follower", $this->getRealId());
|
||
|
||
if ($filter != "all") {
|
||
$query = $query->where("model = ?", "openvk\\Web\\Models\\Entities\\" . ($filter == "groups" ? "Club" : "User"));
|
||
}
|
||
|
||
foreach ($query as $_rel) {
|
||
$entityIds[] = $_rel->model == "openvk\\Web\\Models\\Entities\\Club" ? $_rel->target * -1 : $_rel->target;
|
||
}
|
||
|
||
if ($shuffle) {
|
||
$shuffleSeed = openssl_random_pseudo_bytes(6);
|
||
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
|
||
|
||
$entityIds = knuth_shuffle($entityIds, $shuffleSeed);
|
||
}
|
||
|
||
$entityIds = array_slice($entityIds, 0, 10);
|
||
|
||
$returnArr = [];
|
||
|
||
foreach ($entityIds as $id) {
|
||
$entit = $id > 0 ? (new Users())->get($id) : (new Clubs())->get(abs($id));
|
||
|
||
if ($id > 0 && $entit->isDeleted()) {
|
||
continue;
|
||
}
|
||
$returnArr[] = $entit;
|
||
}
|
||
|
||
return $returnArr;
|
||
}
|
||
|
||
public function getIgnoredSources(int $offset = 0, int $limit = 10, bool $onlyIds = false)
|
||
{
|
||
$sources = DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->limit($limit, $offset)->order('id DESC');
|
||
$output_array = [];
|
||
|
||
foreach ($sources as $source) {
|
||
if ($onlyIds) {
|
||
$output_array[] = (int) $source->source;
|
||
} else {
|
||
$ignored_source_model = null;
|
||
$ignored_source_id = (int) $source->source;
|
||
|
||
if ($ignored_source_id > 0) {
|
||
$ignored_source_model = (new Users())->get($ignored_source_id);
|
||
} else {
|
||
$ignored_source_model = (new Clubs())->get(abs($ignored_source_id));
|
||
}
|
||
|
||
if (!$ignored_source_model) {
|
||
continue;
|
||
}
|
||
|
||
$output_array[] = $ignored_source_model;
|
||
}
|
||
}
|
||
|
||
return $output_array;
|
||
}
|
||
|
||
public function getIgnoredSourcesCount()
|
||
{
|
||
return DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->count();
|
||
}
|
||
|
||
public function isBlacklistedBy(?User $user = null): bool
|
||
{
|
||
if (!$user) {
|
||
return false;
|
||
}
|
||
|
||
$ctx = DatabaseConnection::i()->getContext();
|
||
$data = [
|
||
"author" => $user->getId(),
|
||
"target" => $this->getRealId(),
|
||
];
|
||
|
||
$sub = $ctx->table("blacklist_relations")->where($data);
|
||
return $sub->count() > 0;
|
||
}
|
||
|
||
public function addToBlacklist(?User $user)
|
||
{
|
||
DatabaseConnection::i()->getContext()->table("blacklist_relations")->insert([
|
||
"author" => $this->getRealId(),
|
||
"target" => $user->getRealId(),
|
||
"created" => time(),
|
||
]);
|
||
|
||
DatabaseConnection::i()->getContext()->table("subscriptions")->where([
|
||
"follower" => $user->getId(),
|
||
"model" => static::class,
|
||
"target" => $this->getId(),
|
||
])->delete();
|
||
|
||
DatabaseConnection::i()->getContext()->table("subscriptions")->where([
|
||
"follower" => $this->getId(),
|
||
"model" => static::class,
|
||
"target" => $user->getId(),
|
||
])->delete();
|
||
|
||
return true;
|
||
}
|
||
|
||
public function removeFromBlacklist(?User $user): bool
|
||
{
|
||
DatabaseConnection::i()->getContext()->table("blacklist_relations")->where([
|
||
"author" => $this->getRealId(),
|
||
"target" => $user->getRealId(),
|
||
])->delete();
|
||
|
||
return true;
|
||
}
|
||
|
||
public function getBlacklist(int $offset = 0, int $limit = 10)
|
||
{
|
||
$sources = DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->limit($limit, $offset)->order('created ASC');
|
||
$output_array = [];
|
||
|
||
foreach ($sources as $source) {
|
||
$entity_id = (int) $source->target ;
|
||
$entity = (new Users())->get($entity_id);
|
||
if (!$entity) {
|
||
continue;
|
||
}
|
||
|
||
$output_array[] = $entity;
|
||
}
|
||
|
||
return $output_array;
|
||
}
|
||
|
||
public function getBlacklistSize()
|
||
{
|
||
return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count();
|
||
}
|
||
}
|