diff --git a/VKAPI/Handlers/Account.php b/VKAPI/Handlers/Account.php index dcdf18c0..c1f3ef23 100644 --- a/VKAPI/Handlers/Account.php +++ b/VKAPI/Handlers/Account.php @@ -228,4 +228,72 @@ final class Account extends VKAPIRequestHandler return (object) ['votes' => $this->getUser()->getCoins()]; } + + function ban(int $owner_id): int + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if($owner_id < 0) + return 1; + + if($owner_id == $this->getUser()->getId()) + $this->fail(15, "Access denied: cannot blacklist yourself"); + + $config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['blacklists']['limit'] ?? 100; + $user_blocks = $this->getUser()->getBlacklistSize(); + if(($user_blocks + 1) > $config_limit) + $this->fail(-7856, "Blacklist limit exceeded"); + + $entity = get_entity_by_id($owner_id); + if(!$entity || $entity->isDeleted()) + return 0; + + if($entity->isBlacklistedBy($this->getUser())) + return 1; + + $this->getUser()->addToBlacklist($entity); + + return 1; + } + + function unban(int $owner_id): int + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if($owner_id < 0) + return 1; + + if($owner_id == $this->getUser()->getId()) + return 1; + + $entity = get_entity_by_id($owner_id); + if(!$entity || $entity->isDeleted()) + return 0; + + if(!$entity->isBlacklistedBy($this->getUser())) + return 1; + + $this->getUser()->removeFromBlacklist($entity); + + return 1; + } + + function getBanned(int $offset = 0, int $count = 100, string $fields = ""): object + { + $this->requireUser(); + + $result = (object)[ + 'count' => $this->getUser()->getBlacklistSize(), + 'items' => [], + ]; + $banned = $this->getUser()->getBlacklist($offset, $count); + foreach($banned as $ban) { + if(!$ban) continue; + $result->items[] = $ban->toVkApiStruct($this->getUser(), $fields); + } + + return $result; + } } diff --git a/VKAPI/Handlers/Users.php b/VKAPI/Handlers/Users.php index 704823be..7dc68f3b 100644 --- a/VKAPI/Handlers/Users.php +++ b/VKAPI/Handlers/Users.php @@ -266,6 +266,20 @@ final class Users extends VKAPIRequestHandler case 'nickname': $response[$i]->nickname = $usr->getPseudo(); break; + case 'blacklisted_by_me': + if(!$authuser) { + continue; + } + + $response[$i]->blacklisted_by_me = (int)$usr->isBlacklistedBy($this->getUser()); + break; + case 'blacklisted': + if(!$authuser) { + continue; + } + + $response[$i]->blacklisted = (int)$this->getUser()->isBlacklistedBy($usr); + break; } } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index a0574f3d..61ea120b 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -5,7 +5,7 @@ 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\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; @@ -511,7 +511,7 @@ class User extends RowModel else if($user->getId() === $this->getId()) return true; - if($permission != "messages.write" && !$this->canBeViewedBy($user)) + if(/*$permission != "messages.write" && */!$this->canBeViewedBy($user, true)) return false; switch($permStatus) { @@ -1228,11 +1228,16 @@ class User extends RowModel return (bool) $this->getRecord()->activated; } + function isAdmin(): bool + { + return $this->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL); + } + function isDead(): bool { return $this->onlineStatus() == 2; } - + function getUnbanTime(): ?string { $ban = (new Bans)->get((int) $this->getRecord()->block_reason); @@ -1289,17 +1294,21 @@ class User extends RowModel return $this->getRecord()->profile_type; } - function canBeViewedBy(?User $user = NULL): bool + function canBeViewedBy(?User $user = NULL, bool $blacklist_check = true): bool { if(!is_null($user)) { if($this->getId() == $user->getId()) { return true; } - if($user->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)) { + 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 { @@ -1409,6 +1418,20 @@ class User extends RowModel 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; } } @@ -1486,6 +1509,76 @@ class User extends RowModel return DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->count(); } + 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; + } + + 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; + } + + function removeFromBlacklist(?User $user): bool + { + DatabaseConnection::i()->getContext()->table("blacklist_relations")->where([ + "author" => $this->getRealId(), + "target" => $user->getRealId(), + ])->delete(); + + return true; + } + + 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; + } + + function getBlacklistSize() + { + return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count(); + } + use Traits\TBackDrops; use Traits\TSubscribable; use Traits\TAudioStatuses; diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 61b6d67c..fd0e9a3d 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -119,7 +119,7 @@ final class GroupPresenter extends OpenVKPresenter $this->template->paginatorConf = (object) [ "count" => $this->template->count, "page" => $this->queryParam("p") ?? 1, - "amount" => NULL, + "amount" => 10, "perPage" => OPENVK_DEFAULT_PER_PAGE, ]; } diff --git a/Web/Presenters/PhotosPresenter.php b/Web/Presenters/PhotosPresenter.php index cec426b8..029d2aac 100644 --- a/Web/Presenters/PhotosPresenter.php +++ b/Web/Presenters/PhotosPresenter.php @@ -27,6 +27,7 @@ final class PhotosPresenter extends OpenVKPresenter if(!$user) $this->notFound(); if (!$user->getPrivacyPermission('photos.read', $this->user->identity ?? NULL)) $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); + $this->template->albums = $this->albums->getUserAlbums($user, (int)($this->queryParam("p") ?? 1)); $this->template->count = $this->albums->getUserAlbumsCount($user); $this->template->owner = $user; @@ -161,8 +162,10 @@ final class PhotosPresenter extends OpenVKPresenter { $photo = $this->photos->getByOwnerAndVID($ownerId, $photoId); if(!$photo || $photo->isDeleted()) $this->notFound(); + if(!$photo->canBeViewedBy($this->user->identity)) $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); + if(!is_null($this->queryParam("from"))) { if(preg_match("%^album([0-9]++)$%", $this->queryParam("from"), $matches) === 1) { $album = $this->albums->get((int) $matches[1]); diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 6120bfec..21f837b6 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -22,17 +22,29 @@ final class UserPresenter extends OpenVKPresenter function __construct(Users $users) { $this->users = $users; - + parent::__construct(); } function renderView(int $id): void { $user = $this->users->get($id); + if(!$user || $user->isDeleted() || !$user->canBeViewedBy($this->user->identity)) { if(!is_null($user) && $user->isDeactivated()) { $this->template->_template = "User/deactivated.xml"; + $this->template->user = $user; + } else if($this->user->identity->isBlacklistedBy($user)) { + $this->template->_template = "User/blacklisted.xml"; + + $this->template->blacklist_status = $user->isBlacklistedBy($this->user->identity); + $this->template->ignore_status = $user->isIgnoredBy($this->user->identity); + $this->template->user = $user; + } else if($user->isBlacklistedBy($this->user->identity)) { + $this->template->_template = "User/blacklisted_pov.xml"; + + $this->template->ignore_status = $user->isIgnoredBy($this->user->identity); $this->template->user = $user; } else if(!is_null($user) && !$user->canBeViewedBy($this->user->identity)) { $this->template->_template = "User/private.xml"; @@ -57,6 +69,7 @@ final class UserPresenter extends OpenVKPresenter if($id !== $this->user->id) { $this->template->ignore_status = $user->isIgnoredBy($this->user->identity); + $this->template->blacklist_status = $user->isBlacklistedBy($this->user->identity); } } } @@ -578,7 +591,7 @@ final class UserPresenter extends OpenVKPresenter $this->flash("succ", tr("changes_saved"), tr("changes_saved_comment")); } $this->template->mode = in_array($this->queryParam("act"), [ - "main", "security", "privacy", "finance", "finance.top-up", "interface" + "main", "security", "privacy", "finance", "finance.top-up", "interface", "blacklist" ]) ? $this->queryParam("act") : "main"; @@ -591,6 +604,19 @@ final class UserPresenter extends OpenVKPresenter $this->template->qrCodeType = substr($qrCode[0], 5); $this->template->qrCodeData = $qrCode[1]; + } else if($this->template->mode === "blacklist") { + $page = (int)($this->queryParam('p') ?? 1); + $count = 10; + $offset = ($page - 1) * $count; + + $this->template->blSize = $this->user->identity->getBlacklistSize(); + $this->template->blItems = $this->user->identity->getBlacklist($offset, $count); + $this->template->paginatorConf = (object) [ + "count" => $this->template->blSize, + "page" => $page, + "amount" => sizeof($this->template->blItems), + "perPage" => OPENVK_DEFAULT_PER_PAGE, + ]; } $this->template->user = $user; diff --git a/Web/Presenters/templates/User/Settings.xml b/Web/Presenters/templates/User/Settings.xml index c08c9d2c..e7f4b5ac 100644 --- a/Web/Presenters/templates/User/Settings.xml +++ b/Web/Presenters/templates/User/Settings.xml @@ -13,6 +13,7 @@ {var $isFinance = $mode === 'finance'} {var $isFinanceTU = $mode === 'finance.top-up'} {var $isInterface = $mode === 'interface'} +{var $isBl = $mode === 'blacklist'}
$1
exist and have correct rights and ownership.";
@@ -1815,6 +1816,28 @@
"cookies_popup_content" = "Just like how kids love cookies, this website uses Cookies to identify your session and nothing more. Check our privacy policy for more information.";
"cookies_popup_agree" = "Accept";
+/* Blacklist */
+
+"blacklist" = "Blacklist";
+"user_blacklisted_you" = "This user has blacklisted you.";
+"user_blacklisted" = "$1 has been blacklisted";
+"user_removed_from_the_blacklist" = "$1 has been removed from the blacklist.";
+
+"adding_to_bl_sure" = "You sure you want to blacklist $1?";
+
+"bl_count_zero_desc" = "There are no users on your blacklist yet.";
+"bl_count_zero" = "There are no users on your blacklist";
+"bl_count_one" = "You have one user on your blacklist";
+"bl_count_few" = "You have $1 users on your blacklist";
+"bl_count_many" = "You have $1 users on your blacklist";
+"bl_count_other" = "You have $1 users on your blacklist";
+
+"you_blacklisted" = "You blacklisted $1";
+"bl_add" = "Add to blacklist";
+"bl_remove" = "Remove from blacklist";
+
+"addition_to_bl" = "Addition to blacklist";
+
/* Away */
"transition_is_blocked" = "Transition is blocked";
diff --git a/locales/ru.strings b/locales/ru.strings
index 7337b27f..67e8116d 100644
--- a/locales/ru.strings
+++ b/locales/ru.strings
@@ -1591,8 +1591,11 @@
"admin_about_instance" = "Инстанция";
"admin_commerce_disabled" = "Коммерция отключена системным администратором";
"admin_commerce_disabled_desc" = "Настройки ваучеров и подарков будут сохранены, но не будут оказывать никакого влияния.";
+
+"admin_privacy_warning" = "Будьте осторожны с этой информацией";
"admin_longpool_broken" = "Longpool сломан!";
"admin_longpool_broken_desc" = "Проверьте, существует ли файл по пути $1
и выданы ли у него правильные права на запись.";
+
"admin_banned_links" = "Заблокированные ссылки";
"admin_banned_link" = "Ссылка";
"admin_banned_domain" = "Домен";
@@ -1690,6 +1693,7 @@
"edit_action" = "Изменить";
"transfer" = "Передать";
"close" = "Закрыть";
+"success" = "Успех";
"warning" = "Внимание";
"question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?";
"confirm_m" = "Подтвердить";
@@ -1708,6 +1712,28 @@
"cookies_popup_content" = "Все дети любят печенье, поэтому этот веб-сайт использует Cookies для того, чтобы идентифицировать вашу сессию и ничего более. Ознакомьтесь с нашей политикой конфиденциальности для получения дополнительной информации.";
"cookies_popup_agree" = "Согласен";
+/* Blacklist */
+
+"blacklist" = "Чёрный список";
+"user_blacklisted_you" = "Пользователь внёс Вас в чёрный список.";
+"user_blacklisted" = "$1 занесён в чёрный список.";
+"user_removed_from_the_blacklist" = "$1 удалён из чёрного списка.";
+
+"adding_to_bl_sure" = "Вы уверены, что хотите внести $1 в чёрный список?";
+
+"bl_count_zero_desc" = "В вашем чёрном списке ещё нет пользователей.";
+"bl_count_zero" = "В вашем чёрном списке нет пользователей";
+"bl_count_one" = "В вашем чёрном списке один пользователь";
+"bl_count_few" = "В вашем чёрном списке $1 пользователя";
+"bl_count_many" = "В вашем чёрном списке $1 пользователей";
+"bl_count_other" = "В вашем чёрном списке $1 пользователей";
+
+"you_blacklisted" = "Вы внесли $1 в чёрный список";
+"bl_add" = "Добавить в чёрный список";
+"bl_remove" = "Удалить из чёрного списка";
+
+"addition_to_bl" = "Добавление в чёрный список";
+
/* Away */
"transition_is_blocked" = "Переход по ссылке заблокирован";
diff --git a/openvk-example.yml b/openvk-example.yml
index b58660f7..b64a589e 100644
--- a/openvk-example.yml
+++ b/openvk-example.yml
@@ -38,6 +38,9 @@ openvk:
maxViolations: 50
maxViolationsAge: 120
autoban: true
+ blacklists:
+ limit: 100
+ applyToAdmins: true
registration:
enable: true
disablingReason: ""