diff --git a/VKAPI/Handlers/Newsfeed.php b/VKAPI/Handlers/Newsfeed.php index 64ee1862..bf36f2b5 100644 --- a/VKAPI/Handlers/Newsfeed.php +++ b/VKAPI/Handlers/Newsfeed.php @@ -32,6 +32,7 @@ final class Newsfeed extends VKAPIRequestHandler ->select("id") ->where("wall IN (?)", $ids) ->where("deleted", 0) + ->where("suggested", 0) ->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from) ->where("? <= created", empty($start_time) ? 0 : $start_time) ->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time) @@ -56,6 +57,15 @@ final class Newsfeed extends VKAPIRequestHandler if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT) $queryBase .= " AND `nsfw` = 0"; + + if($return_banned == 0) { + $ignored_sources_ids = $this->getUser()->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true); + + if(sizeof($ignored_sources_ids) > 0) { + $imploded_ids = implode("', '", $ignored_sources_ids); + $queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')"; + } + } $start_from = empty($start_from) ? PHP_INT_MAX : $start_from; $start_time = empty($start_time) ? 0 : $start_time; @@ -74,4 +84,152 @@ final class Newsfeed extends VKAPIRequestHandler return $response; } + + function getByType(string $feed_type = 'top', string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $return_banned = 0) + { + $this->requireUser(); + + switch($feed_type) { + case 'top': + return $this->getGlobal($fields, $start_from, $start_time, $end_time, $offset, $count, $extended, $return_banned); + break; + default: + return $this->get($fields, $start_from, $start_time, $end_time, $offset, $count, $extended); + break; + } + } + + function getBanned(int $extended = 0, string $fields = "", string $name_case = "nom", int $merge = 0): object + { + $this->requireUser(); + + $offset = 0; + $count = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50; + $banned = $this->getUser()->getIgnoredSources($offset, $count, ($extended != 1)); + $return_object = (object) [ + 'groups' => [], + 'members' => [], + ]; + + if($extended == 0) { + foreach($banned as $ban) { + if($ban > 0) + $return_object->members[] = $ban; + else + $return_object->groups[] = $ban; + } + } else { + if($merge == 1) { + $return_object = (object) [ + 'count' => sizeof($banned), + 'items' => [], + ]; + + foreach($banned as $ban) { + $return_object->items[] = $ban->toVkApiStruct($this->getUser(), $fields); + } + } else { + $return_object = (object) [ + 'groups' => [], + 'profiles' => [], + ]; + + foreach($banned as $ban) { + if($ban->getRealId() > 0) + $return_object->profiles[] = $ban->toVkApiStruct($this->getUser(), $fields); + else + $return_object->groups[] = $ban->toVkApiStruct($this->getUser(), $fields); + } + } + } + + return $return_object; + } + + function addBan(string $user_ids = "", string $group_ids = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + # Formatting input ids + if(!empty($user_ids)) { + $user_ids = array_map(function($el) { + return (int)$el; + }, explode(',', $user_ids)); + $user_ids = array_unique($user_ids); + } else + $user_ids = []; + + if(!empty($group_ids)) { + $group_ids = array_map(function($el) { + return abs((int)$el) * -1; + }, explode(',', $group_ids)); + $group_ids = array_unique($group_ids); + } else + $group_ids = []; + + $ids = array_merge($user_ids, $group_ids); + if(sizeof($ids) < 1) + return 0; + + if(sizeof($ids) > 10) + $this->fail(-10, "Limit of 'ids' is 10"); + + $config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50; + $user_ignores = $this->getUser()->getIgnoredSourcesCount(); + if(($user_ignores + sizeof($ids)) > $config_limit) { + $this->fail(-50, "Ignoring limit exceeded"); + } + + $entities = get_entities($ids); + $successes = 0; + foreach($entities as $entity) { + if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || $entity->isHideFromGlobalFeedEnabled() || $entity->isIgnoredBy($this->getUser())) continue; + + $entity->addIgnore($this->getUser()); + $successes += 1; + } + + return 1; + } + + function deleteBan(string $user_ids = "", string $group_ids = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if(!empty($user_ids)) { + $user_ids = array_map(function($el) { + return (int)$el; + }, explode(',', $user_ids)); + $user_ids = array_unique($user_ids); + } else + $user_ids = []; + + if(!empty($group_ids)) { + $group_ids = array_map(function($el) { + return abs((int)$el) * -1; + }, explode(',', $group_ids)); + $group_ids = array_unique($group_ids); + } else + $group_ids = []; + + $ids = array_merge($user_ids, $group_ids); + if(sizeof($ids) < 1) + return 0; + + if(sizeof($ids) > 10) + $this->fail(-10, "Limit of ids is 10"); + + $entities = get_entities($ids); + $successes = 0; + foreach($entities as $entity) { + if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || !$entity->isIgnoredBy($this->getUser())) continue; + + $entity->removeIgnore($this->getUser()); + $successes += 1; + } + + return 1; + } } diff --git a/Web/Models/Entities/Club.php b/Web/Models/Entities/Club.php index c919b059..6121b69d 100644 --- a/Web/Models/Entities/Club.php +++ b/Web/Models/Entities/Club.php @@ -42,10 +42,11 @@ class Club extends RowModel return iterator_to_array($avPhotos)[0] ?? NULL; } - function getAvatarUrl(string $size = "miniscule"): string + function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string { $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; - $avPhoto = $this->getAvatarPhoto(); + if(!$avPhoto) + $avPhoto = $this->getAvatarPhoto(); return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size); } @@ -443,30 +444,57 @@ class Club extends RowModel $res->id = $this->getId(); $res->name = $this->getName(); - $res->screen_name = $this->getShortCode(); - $res->is_closed = 0; + $res->screen_name = $this->getShortCode() ?? "club".$this->getId(); + $res->is_closed = false; + $res->type = 'group'; + $res->is_member = $user ? (int)$this->getSubscriptionStatus($user) : 0; $res->deactivated = NULL; - $res->is_admin = $user && $this->canBeModifiedBy($user); + $res->can_access_closed = true; - if($user && $this->canBeModifiedBy($user)) { - $res->admin_level = 3; + if(!is_array($fields)) + $fields = explode(',', $fields); + + $avatar_photo = $this->getAvatarPhoto(); + foreach($fields as $field) { + switch($field) { + case 'verified': + $res->verified = (int)$this->isVerified(); + break; + case 'site': + $res->site = $this->getWebsite(); + break; + case 'description': + $res->description = $this->getDescription(); + break; + case 'background': + $res->background = $this->getBackDropPictureURLs(); + 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 'members_count': + $res->members_count = $this->getFollowersCount(); + break; + case 'real_id': + $res->real_id = $this->getRealId(); + break; + } } - $res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0; - - $res->type = "group"; - $res->photo_50 = $this->getAvatarUrl("miniscule"); - $res->photo_100 = $this->getAvatarUrl("tiny"); - $res->photo_200 = $this->getAvatarUrl("normal"); - - $res->can_create_topic = $user && $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0); - - $res->can_post = $user && $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0); - return $res; } use Traits\TBackDrops; use Traits\TSubscribable; use Traits\TAudioStatuses; + use Traits\TIgnorable; } diff --git a/Web/Models/Entities/Traits/TIgnorable.php b/Web/Models/Entities/Traits/TIgnorable.php new file mode 100644 index 00000000..b55ea926 --- /dev/null +++ b/Web/Models/Entities/Traits/TIgnorable.php @@ -0,0 +1,52 @@ +getContext(); + $data = [ + "owner" => $user->getId(), + "source" => $this->getRealId(), + ]; + + $sub = $ctx->table("ignored_sources")->where($data); + return $sub->count() > 0; + } + + function addIgnore(User $for_user): bool + { + DatabaseConnection::i()->getContext()->table("ignored_sources")->insert([ + "owner" => $for_user->getId(), + "source" => $this->getRealId(), + ]); + + return true; + } + + function removeIgnore(User $for_user): bool + { + DatabaseConnection::i()->getContext()->table("ignored_sources")->where([ + "owner" => $for_user->getId(), + "source" => $this->getRealId(), + ])->delete(); + + return true; + } + + function toggleIgnore(User $for_user): bool + { + if($this->isIgnoredBy($for_user)) { + $this->removeIgnore($for_user); + + return false; + } else { + $this->addIgnore($for_user); + + return true; + } + } +} diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 18d7fea9..1e17dd82 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -112,7 +112,7 @@ class User extends RowModel return "/id" . $this->getId(); } - function getAvatarUrl(string $size = "miniscule"): string + function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string { $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; @@ -121,7 +121,9 @@ class User extends RowModel else if($this->isBanned()) return "$serverUrl/assets/packages/static/openvk/img/banned.jpg"; - $avPhoto = $this->getAvatarPhoto(); + if(!$avPhoto) + $avPhoto = $this->getAvatarPhoto(); + if(is_null($avPhoto)) return "$serverUrl/assets/packages/static/openvk/img/camera_200.png"; else @@ -1344,11 +1346,6 @@ class User extends RowModel $res->first_name = $this->getFirstName(); $res->last_name = $this->getLastName(); $res->deactivated = $this->isDeactivated(); - $res->photo_50 = $this->getAvatarURL(); - $res->photo_100 = $this->getAvatarURL("tiny"); - $res->photo_200 = $this->getAvatarURL("normal"); - $res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL; - $res->is_closed = $this->isClosed(); if(!is_null($user)) @@ -1357,17 +1354,60 @@ class User extends RowModel if(!is_array($fields)) $fields = explode(',', $fields); + $avatar_photo = $this->getAvatarPhoto(); foreach($fields as $field) { switch($field) { case 'is_dead': - $res->is_dead = $user->isDead(); + $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; } } return $res; } - + function getAudiosCollectionSize() { return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this); @@ -1407,7 +1447,40 @@ class User extends RowModel return $returnArr; } + 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; + } + + function getIgnoredSourcesCount() + { + return DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->count(); + } + use Traits\TBackDrops; use Traits\TSubscribable; use Traits\TAudioStatuses; + use Traits\TIgnorable; } diff --git a/Web/Models/Repositories/Clubs.php b/Web/Models/Repositories/Clubs.php index b393952f..de5d9269 100644 --- a/Web/Models/Repositories/Clubs.php +++ b/Web/Models/Repositories/Clubs.php @@ -43,6 +43,18 @@ class Clubs return $this->toClub($this->clubs->get($id)); } + function getByIds(array $ids = []): array + { + $clubs = $this->clubs->select('*')->where('id IN (?)', $ids); + $clubs_array = []; + + foreach($clubs as $club) { + $clubs_array[] = $this->toClub($club); + } + + return $clubs_array; + } + function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable { $query = "%$query%"; diff --git a/Web/Models/Repositories/Users.php b/Web/Models/Repositories/Users.php index cc75b50f..ed5a87a5 100644 --- a/Web/Models/Repositories/Users.php +++ b/Web/Models/Repositories/Users.php @@ -28,6 +28,18 @@ class Users { return $this->toUser($this->users->get($id)); } + + function getByIds(array $ids = []): array + { + $users = $this->users->select('*')->where('id IN (?)', $ids); + $users_array = []; + + foreach($users as $user) { + $users_array[] = $this->toUser($user); + } + + return $users_array; + } function getByShortURL(string $url): ?User { diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 88844fbe..61b6d67c 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -43,6 +43,7 @@ final class GroupPresenter extends OpenVKPresenter } $this->template->club = $club; + $this->template->ignore_status = $club->isIgnoredBy($this->user->identity); } } diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index baf8f046..b64823fb 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -54,6 +54,10 @@ final class UserPresenter extends OpenVKPresenter $this->template->audioStatus = $user->getCurrentAudioStatus(); $this->template->user = $user; + + if($id !== $this->user->id) { + $this->template->ignore_status = $user->isIgnoredBy($this->user->identity); + } } } diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index 8cc28efe..ca4c3ece 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -197,6 +197,16 @@ final class WallPresenter extends OpenVKPresenter if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT) $queryBase .= " AND `nsfw` = 0"; + if(((int)$this->queryParam('return_banned')) == 0) { + $ignored_sources_ids = $this->user->identity->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true); + + if(sizeof($ignored_sources_ids) > 0) { + $imploded_ids = implode("', '", $ignored_sources_ids); + + $queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')"; + } + } + $posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " ORDER BY `created` DESC LIMIT " . $pPage . " OFFSET " . ($page - 1) * $pPage); $count = DatabaseConnection::i()->getConnection()->query("SELECT COUNT(*) " . $queryBase)->fetch()->{"COUNT(*)"}; diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml index b54ef44c..7edecea5 100644 --- a/Web/Presenters/templates/@layout.xml +++ b/Web/Presenters/templates/@layout.xml @@ -382,6 +382,7 @@ {ifset $thisUser} {script "js/al_notifs.js"} + {script "js/al_feed.js"} {/ifset} diff --git a/Web/Presenters/templates/Group/View.xml b/Web/Presenters/templates/Group/View.xml index 79ef3704..13eb85b4 100644 --- a/Web/Presenters/templates/Group/View.xml +++ b/Web/Presenters/templates/Group/View.xml @@ -194,6 +194,9 @@ } {/if} + + {if !$ignore_status}{_ignore_club}{else}{_unignore_club}{/if} +
diff --git a/Web/Presenters/templates/User/View.xml b/Web/Presenters/templates/User/View.xml index 2c16b519..79535a8b 100644 --- a/Web/Presenters/templates/User/View.xml +++ b/Web/Presenters/templates/User/View.xml @@ -166,6 +166,9 @@ {/if} {_report} + + {if !$ignore_status}{_ignore_user}{else}{_unignore_user}{/if} +