diff --git a/VKAPI/Handlers/Messages.php b/VKAPI/Handlers/Messages.php index 5767acc4..c1a85a0a 100644 --- a/VKAPI/Handlers/Messages.php +++ b/VKAPI/Handlers/Messages.php @@ -284,7 +284,7 @@ final class Messages extends VKAPIRequestHandler return (object) $output; } - function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0): object + function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0, string $fields = ""): object { $this->requireUser(); @@ -316,10 +316,18 @@ final class Messages extends VKAPIRequestHandler $results[] = $rMsg; } - return (object) [ + $output = [ "count" => sizeof($results), "items" => $results, ]; + + if ($extended == 1) { + $users[] = $this->getUser()->getId(); + $users[] = $user_id; + $output["profiles"] = (!empty($users) ? (new APIUsers($this->getUser()))->get(implode(',', $users), $fields) : []); + } + + return (object) $output; } function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object diff --git a/VKAPI/Handlers/Newsfeed.php b/VKAPI/Handlers/Newsfeed.php index 19cbfa48..06474220 100644 --- a/VKAPI/Handlers/Newsfeed.php +++ b/VKAPI/Handlers/Newsfeed.php @@ -26,7 +26,7 @@ final class Newsfeed extends VKAPIRequestHandler ->select("id") ->where("wall IN (?)", $ids) ->where("deleted", 0) - ->where("created < (?)", empty($start_from) ? time()+1 : $start_from) + ->where("id < (?)", empty($start_from) ? time()+1 : $start_from) ->order("created DESC"); $rposts = []; @@ -34,7 +34,7 @@ final class Newsfeed extends VKAPIRequestHandler $rposts[] = (new PostsRepo)->get($post->id)->getPrettyId(); $response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser()); - $response->next_from = end($response->items)->date; + $response->next_from = end(end($posts->page((int) ($offset + 1), $count))); // ну и костыли пиздец конечно) return $response; } } diff --git a/Web/Models/Entities/BannedLink.php b/Web/Models/Entities/BannedLink.php new file mode 100644 index 00000000..09c42e39 --- /dev/null +++ b/Web/Models/Entities/BannedLink.php @@ -0,0 +1,49 @@ +getRecord()->id; + } + + function getDomain(): string + { + return $this->getRecord()->domain; + } + + function getReason(): string + { + return $this->getRecord()->reason ?? tr("url_is_banned_default_reason"); + } + + function getInitiator(): ?User + { + return (new Users)->get($this->getRecord()->initiator); + } + + function getComment(): string + { + return OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["showReason"] + ? tr("url_is_banned_comment_r", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"], $this->getReason()) + : tr("url_is_banned_comment", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"]); + } + + function getRegexpRule(): string + { + return addslashes("/" . $this->getDomain() . $this->getRawRegexp() . "/"); + } + + function getRawRegexp(): string + { + return $this->getRecord()->regexp_rule; + } +} diff --git a/Web/Models/Entities/Traits/TRichText.php b/Web/Models/Entities/Traits/TRichText.php index edde140b..f2229250 100644 --- a/Web/Models/Entities/Traits/TRichText.php +++ b/Web/Models/Entities/Traits/TRichText.php @@ -35,12 +35,12 @@ trait TRichText "%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%", (function (array $matches): string { $href = str_replace("#", "#", $matches[1]); - $href = str_replace(";", ";", $matches[1]); + $href = rawurlencode(str_replace(";", ";", $matches[1])); $link = str_replace("#", "#", $matches[3]); $link = str_replace(";", ";", $matches[3]); $rel = $this->isAd() ? "sponsored" : "ugc"; - return "$link" . htmlentities($matches[4]); + return "$link" . htmlentities($matches[4]); }), $text ); diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 0f048b1b..7103a6cb 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -771,7 +771,7 @@ class User extends RowModel ]); } - function ban(string $reason, bool $deleteSubscriptions = true): void + function ban(string $reason, bool $deleteSubscriptions = true, ?int $unban_time = NULL): void { if($deleteSubscriptions) { $subs = DatabaseConnection::i()->getContext()->table("subscriptions"); @@ -785,6 +785,7 @@ class User extends RowModel } $this->setBlock_Reason($reason); + $this->setUnblock_time($unban_time); $this->save(); } @@ -1026,5 +1027,21 @@ class User extends RowModel return $this->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL); } + function getUnbanTime(): ?string + { + return !is_null($this->getRecord()->unblock_time) ? date('d.m.Y', $this->getRecord()->unblock_time) : NULL; + } + + function canUnbanThemself(): bool + { + if (!$this->isBanned()) + return false; + + if ($this->getRecord()->unblock_time > time() || $this->getRecord()->unblock_time == 0) + return false; + + return true; + } + use Traits\TSubscribable; } diff --git a/Web/Models/Repositories/BannedLinks.php b/Web/Models/Repositories/BannedLinks.php new file mode 100644 index 00000000..8f93e6fd --- /dev/null +++ b/Web/Models/Repositories/BannedLinks.php @@ -0,0 +1,73 @@ +context = DB::i()->getContext(); + $this->bannedLinks = $this->context->table("links_banned"); + } + + function toBannedLink(?ActiveRow $ar): ?BannedLink + { + return is_null($ar) ? NULL : new BannedLink($ar); + } + + function get(int $id): ?BannedLink + { + return $this->toBannedLink($this->bannedLinks->get($id)); + } + + function getList(?int $page = 1): \Traversable + { + foreach($this->bannedLinks->order("id DESC")->page($page, OPENVK_DEFAULT_PER_PAGE) as $link) + yield new BannedLink($link); + } + + function getCount(int $page = 1): int + { + return sizeof($this->bannedLinks->fetch()); + } + + function getByDomain(string $domain): ?Selection + { + return $this->bannedLinks->where("domain", $domain); + } + + function isDomainBanned(string $domain): bool + { + return sizeof($this->bannedLinks->where(["link" => $domain, "regexp_rule" => ""])) > 0; + } + + function genLinks($rules): \Traversable + { + foreach ($rules as $rule) + yield $this->get($rule->id); + } + + function genEntries($links, $uri): \Traversable + { + foreach($links as $link) + if (preg_match($link->getRegexpRule(), $uri)) + yield $link->getId(); + } + + function check(string $url): ?array + { + $uri = strstr(str_replace(["https://", "http://"], "", $url), "/", true); + $domain = str_replace("www.", "", $uri); + $rules = $this->getByDomain($domain); + + if (is_null($rules)) + return NULL; + + return iterator_to_array($this->genEntries($this->genLinks($rules), $uri)); + } +} \ No newline at end of file diff --git a/Web/Presenters/AdminPresenter.php b/Web/Presenters/AdminPresenter.php index 4a484a4d..ceda6f6b 100644 --- a/Web/Presenters/AdminPresenter.php +++ b/Web/Presenters/AdminPresenter.php @@ -1,7 +1,8 @@ users = $users; $this->clubs = $clubs; $this->vouchers = $vouchers; $this->gifts = $gifts; + $this->bannedLinks = $bannedLinks; parent::__construct(); } @@ -339,12 +342,14 @@ final class AdminPresenter extends OpenVKPresenter function renderQuickBan(int $id): void { $this->assertNoCSRF(); - + + $unban_time = strtotime($this->queryParam("date")) ?: NULL; + $user = $this->users->get($id); if(!$user) exit(json_encode([ "error" => "User does not exist" ])); - $user->ban($this->queryParam("reason")); + $user->ban($this->queryParam("reason"), true, $unban_time); exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ])); } @@ -356,7 +361,8 @@ final class AdminPresenter extends OpenVKPresenter if(!$user) exit(json_encode([ "error" => "User does not exist" ])); - $user->setBlock_Reason(null); + $user->setBlock_Reason(NULL); + $user->setUnblock_time(NULL); $user->save(); exit(json_encode([ "success" => true ])); } @@ -372,4 +378,73 @@ final class AdminPresenter extends OpenVKPresenter $user->adminNotify("⚠️ " . $this->queryParam("message")); exit(json_encode([ "message" => $this->queryParam("message") ])); } + + function renderBannedLinks(): void + { + $this->template->links = $this->bannedLinks->getList((int) $this->queryParam("p") ?: 1); + $this->template->users = new Users; + } + + function renderBannedLink(int $id): void + { + $this->template->form = (object) []; + + if($id === 0) { + $this->template->form->id = 0; + $this->template->form->link = NULL; + $this->template->form->reason = NULL; + } else { + $link = (new BannedLinks)->get($id); + if(!$link) + $this->notFound(); + + $this->template->form->id = $link->getId(); + $this->template->form->link = $link->getDomain(); + $this->template->form->reason = $link->getReason(); + $this->template->form->regexp = $link->getRawRegexp(); + } + + if($_SERVER["REQUEST_METHOD"] !== "POST") + return; + + $link = (new BannedLinks)->get($id); + + $new_domain = parse_url($this->postParam("link"))["host"]; + $new_reason = $this->postParam("reason") ?: NULL; + + $lid = $id; + + if ($link) { + $link->setDomain($new_domain ?? $this->postParam("link")); + $link->setReason($new_reason); + $link->setRegexp_rule($this->postParam("regexp")); + $link->save(); + } else { + if (!$new_domain) + $this->flashFail("err", tr("error"), tr("admin_banned_link_not_specified")); + + $link = new BannedLink; + $link->setDomain($new_domain); + $link->setReason($new_reason); + $link->setRegexp_rule($this->postParam("regexp")); + $link->setInitiator($this->user->identity->getId()); + $link->save(); + + $lid = $link->getId(); + } + + $this->redirect("/admin/bannedLink/id" . $lid); + } + + function renderUnbanLink(int $id): void + { + $link = (new BannedLinks)->get($id); + + if (!$link) + $this->flashFail("err", tr("error"), tr("admin_banned_link_not_found")); + + $link->delete(false); + + $this->redirect("/admin/bannedLinks"); + } } diff --git a/Web/Presenters/AuthPresenter.php b/Web/Presenters/AuthPresenter.php index ac5a30c2..f934f7fe 100644 --- a/Web/Presenters/AuthPresenter.php +++ b/Web/Presenters/AuthPresenter.php @@ -322,4 +322,39 @@ final class AuthPresenter extends OpenVKPresenter $this->redirect("/"); } + + function renderUnbanThemself(): void + { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(); + + if(!$this->user->identity->canUnbanThemself()) + $this->flashFail("err", tr("error"), tr("forbidden")); + + $user = $this->users->get($this->user->id); + + $user->setBlock_Reason(NULL); + $user->setUnblock_Time(NULL); + $user->save(); + + $this->flashFail("succ", tr("banned_unban_title"), tr("banned_unban_description")); + } + + /* + * This function will revoke all tokens, including API and Web tokens and except active one + * + * OF COURSE it requires CSRF + */ + function renderRevokeAllTokens(): void + { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(); + $this->assertNoCSRF(); + + // API tokens + $this->db->table("api_tokens")->where("user", $this->user->identity->getId())->delete(); + // Web tokens + $this->db->table("ChandlerTokens")->where("user", $this->user->identity->getChandlerGUID())->where("token != ?", Session::i()->get("tok"))->delete(); + $this->flashFail("succ", tr("information_-1"), tr("end_all_sessions_done")); + } } diff --git a/Web/Presenters/AwayPresenter.php b/Web/Presenters/AwayPresenter.php index a65ba7af..18c7cca7 100644 --- a/Web/Presenters/AwayPresenter.php +++ b/Web/Presenters/AwayPresenter.php @@ -1,13 +1,29 @@ check($this->queryParam("to") . "/"); + if (OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["warnings"]) + if (sizeof($checkBanEntries) > 0) + $this->pass("openvk!Away->view", $checkBanEntries[0]); + header("HTTP/1.0 302 Found"); header("X-Robots-Tag: noindex, nofollow, noarchive"); header("Location: " . $this->queryParam("to")); exit; } + + function renderView(int $lid) { + $this->template->link = (new BannedLinks)->get($lid); + + if (!$this->template->link) + $this->notFound(); + + $this->template->to = $this->queryParam("to"); + } } diff --git a/Web/Presenters/BannedLinkPresenter.php b/Web/Presenters/BannedLinkPresenter.php new file mode 100644 index 00000000..6a11b3fc --- /dev/null +++ b/Web/Presenters/BannedLinkPresenter.php @@ -0,0 +1,13 @@ +template->link = (new BannedLinks)->get($lid); + $this->template->to = $this->queryParam("to"); + } +} diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index db9a6427..55e9c60e 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -471,7 +471,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", "privacy", "finance", "finance.top-up", "interface", "blacklist" + "main", "security", "privacy", "finance", "finance.top-up", "interface", "blacklist" ]) ? $this->queryParam("act") : "main"; diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index f8997cd7..55c90518 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -113,14 +113,14 @@ final class WallPresenter extends OpenVKPresenter $feed = new Feed(); $channel = new Channel(); - $channel->title($post->getOwner()->getCanonicalName() . " — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["SERVER_NAME"])->appendTo($feed); + $channel->title($owner->getCanonicalName() . " — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["HTTP_HOST"])->appendTo($feed); foreach($posts as $post) { $item = new Item(); $item ->title($post->getOwner()->getCanonicalName()) ->description($post->getText()) - ->url(ovk_scheme(true).$_SERVER["SERVER_NAME"]."/wall{$post->getPrettyId()}") + ->url(ovk_scheme(true).$_SERVER["HTTP_HOST"]."/wall{$post->getPrettyId()}") ->pubDate($post->getPublicationTime()->timestamp()) ->appendTo($channel); } diff --git a/Web/Presenters/templates/@banned.xml b/Web/Presenters/templates/@banned.xml index c3c894d3..48c29ddb 100644 --- a/Web/Presenters/templates/@banned.xml +++ b/Web/Presenters/templates/@banned.xml @@ -12,6 +12,16 @@
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
+
+ {if !$thisUser->getUnbanTime()}
+ {_banned_perm}
+ {else}
+ {tr("banned_until_time", $thisUser->getUnbanTime())|noescape}
+ {/if}
+
+
diff --git a/Web/Presenters/templates/About/Version.xml b/Web/Presenters/templates/About/Version.xml index bf0cb5f0..160e2181 100644 --- a/Web/Presenters/templates/About/Version.xml +++ b/Web/Presenters/templates/About/Version.xml @@ -396,8 +396,8 @@
ID | +{_admin_banned_domain} | +REGEXP | +{_admin_banned_link_reason} | +{_admin_banned_link_initiator} | +{_admin_actions} | +
---|---|---|---|---|---|
{$link->getId()} | +{$link->getDomain()} | +{$link->getRegexpRule()} | +{$link->getReason() ?? "-"} | +{$link->getInitiator()->getCanonicalName()} | ++ + + + | +
- {_current_email_address} - |
- {$user->getEmail()}
+
+ {tr("end_all_sessions_description", OPENVK_ROOT_CONF['openvk']['appearance']['name'])}
+
|
- {_new_email_address} - | -- - | -
- {_password} - | -- - | -
- {_"2fa_code"} - | -- - | -
- - | -- - | -
- {_page_id} - | -- {$user->getId()} - | -|
- {_page_address} - | -- - | -|
- - | -+ | - + |
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}
- {_user_banned_comment} {$user->getBanReason()}.
+ {_user_banned_comment} {$user->getBanReason()}.
+ Пользователь заблокирован до: {$user->getUnbanTime()}
diff --git a/Web/di.yml b/Web/di.yml
index 29cb1f2b..0546fc76 100644
--- a/Web/di.yml
+++ b/Web/di.yml
@@ -24,6 +24,7 @@ services:
- openvk\Web\Presenters\ThemepacksPresenter
- openvk\Web\Presenters\VKAPIPresenter
- openvk\Web\Presenters\BlacklistPresenter
+ - openvk\Web\Presenters\BannedLinkPresenter
- openvk\Web\Models\Repositories\Users
- openvk\Web\Models\Repositories\Posts
- openvk\Web\Models\Repositories\Photos
@@ -44,3 +45,4 @@ services:
- openvk\Web\Models\Repositories\Applications
- openvk\Web\Models\Repositories\ContentSearchRepository
- openvk\Web\Models\Repositories\Blacklists
+ - openvk\Web\Models\Repositories\BannedLinks
diff --git a/Web/routes.yml b/Web/routes.yml
index 1f90f1d8..1d8397a9 100644
--- a/Web/routes.yml
+++ b/Web/routes.yml
@@ -65,6 +65,10 @@ routes:
handler: "Auth->verifyEmail"
- url: "/setSID/{slug}"
handler: "Auth->su"
+ - url: "/unban.php"
+ handler: "Auth->unbanThemself"
+ - url: "/revokeAllTokens"
+ handler: "Auth->revokeAllTokens"
- url: "/settings"
handler: "User->settings"
- url: "/settings/2fa"
@@ -257,6 +261,8 @@ routes:
handler: "About->invite"
- url: "/away.php"
handler: "Away->away"
+ - url: "/away.php/{num}"
+ handler: "Away->view"
- url: "/gift{num}_{num}.png"
handler: "Gifts->giftImage"
- url: "/gifts{num}"
@@ -303,6 +309,12 @@ routes:
handler: "Support->quickBanInSupport"
- url: "/admin/support/unban/{num}"
handler: "Support->quickUnbanInSupport"
+ - url: "/admin/bannedLinks"
+ handler: "Admin->bannedLinks"
+ - url: "/admin/bannedLink/id{num}"
+ handler: "Admin->bannedLink"
+ - url: "/admin/bannedLink/id{num}/unban"
+ handler: "Admin->unbanLink"
- url: "/upload/photo/{text}"
handler: "VKAPI->photoUpload"
- url: "/method/{text}.{text}"
diff --git a/install/sqls/00031-ban-page-until.sql b/install/sqls/00031-ban-page-until.sql
new file mode 100644
index 00000000..23a5f0e7
--- /dev/null
+++ b/install/sqls/00031-ban-page-until.sql
@@ -0,0 +1 @@
+ALTER TABLE `profiles` ADD `unblock_time` BIGINT UNSIGNED DEFAULT NULL AFTER `block_reason`;
diff --git a/install/sqls/00031-banned-urls.sql b/install/sqls/00031-banned-urls.sql
new file mode 100644
index 00000000..c208eb37
--- /dev/null
+++ b/install/sqls/00031-banned-urls.sql
@@ -0,0 +1,14 @@
+CREATE TABLE `links_banned` (
+ `id` bigint UNSIGNED NOT NULL,
+ `domain` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
+ `regexp_rule` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
+ `reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
+ `initiator` bigint UNSIGNED NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+ALTER TABLE `links_banned`
+ ADD PRIMARY KEY (`id`);
+
+ALTER TABLE `links_banned`
+ MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
+COMMIT;
\ No newline at end of file
diff --git a/locales/en.strings b/locales/en.strings
index aa840738..cf6a53b0 100644
--- a/locales/en.strings
+++ b/locales/en.strings
@@ -418,6 +418,7 @@
"avatar" = "Avatar";
"privacy" = "Privacy";
"interface" = "Interface";
+"security" = "Security";
"profile_picture" = "Profile picture";
@@ -490,6 +491,7 @@
"ui_settings_view_of_posts_old" = "Old";
"ui_settings_view_of_posts_microblog" = "Microblog";
"ui_settings_main_page" = "Main page";
+"ui_settings_sessions" = "Sessions";
"additional_links" = "Additional links";
"ad_poster" = "Ad poster";
@@ -518,6 +520,11 @@
"share_with_friends" = "Share with friends";
+"end_all_sessions" = "End all sessions";
+"end_all_sessions_description" = "If you wanna logout from $1 on all devices, click on button below";
+
+"end_all_sessions_done" = "All sessions was ended, including mobile apps";
+
/* Two-factor authentication */
"two_factor_authentication" = "Two-factor authentication";
@@ -835,8 +842,13 @@
"banned_header" = "You are banned";
"banned_alt" = "The user is blocked.";
"banned_1" = "Sorry $1, but you have been banned.";
-"banned_2" = "And the reason for this is simple: $1. Unfortunately, this time we had to block you forever.";
+"banned_2" = "And the reason for this is simple: $1.";
+"banned_perm" = "Unfortunately, this time we had to block you forever.";
+"banned_until_time" = "This time we had to block you until $1";
"banned_3" = "You can still write to the support if you think there was an error or logout.";
+"banned_unban_myself" = "Unban myself";
+"banned_unban_title" = "Your account has been unbanned";
+"banned_unban_description" = "Try not to break the rules anymore.";
/* Registration confirm */
@@ -1064,6 +1076,17 @@
"admin_privacy_warning" = "Be careful with this information";
+"admin_banned_links" = "Blocked links";
+"admin_banned_link" = "Link";
+"admin_banned_domain" = "Domain";
+"admin_banned_link_description" = "With the protocol (https://example.com/)";
+"admin_banned_link_regexp" = "Regular expression";
+"admin_banned_link_regexp_description" = "It is substituted after the domain specified above. Don't fill it out if you want to block the entire domain";
+"admin_banned_link_reason" = "Reason";
+"admin_banned_link_initiator" = "Initiator";
+"admin_banned_link_not_specified" = "The link is not specified";
+"admin_banned_link_not_found" = "Link not found";
+
/* Paginator (deprecated) */
"paginator_back" = "Back";
@@ -1121,4 +1144,13 @@
/* Blacklist */
"blacklist" = "Blacklist";
-"user_blacklisted_you" = "This user has blacklisted you.";
\ No newline at end of file
+"user_blacklisted_you" = "This user has blacklisted you.";
+
+/* Away */
+
+"url_is_banned" = "Link is not allowed";
+"url_is_banned_comment" = "The $1 administration recommends not to follow this link.";
+"url_is_banned_comment_r" = "The $1 administration recommends not to follow this link.
The reason is: $2";
+"url_is_banned_default_reason" = "The link you are trying to open may lead you to a site that was created for the purpose of deceiving users with the intention of gaining profit.";
+"url_is_banned_title" = "Link to a suspicious site";
+"url_is_banned_proceed" = "Follow the link";
diff --git a/locales/ru.strings b/locales/ru.strings
index 2743d186..6081fdfe 100644
--- a/locales/ru.strings
+++ b/locales/ru.strings
@@ -452,6 +452,7 @@
"avatar" = "Аватар";
"privacy" = "Приватность";
"interface" = "Внешний вид";
+"security" = "Безопасность";
"profile_picture" = "Изображение страницы";
@@ -526,6 +527,7 @@
"ui_settings_view_of_posts_old" = "Старый";
"ui_settings_view_of_posts_microblog" = "Микроблог";
"ui_settings_main_page" = "Главная страница";
+"ui_settings_sessions" = "Сессии";
"additional_links" = "Дополнительные ссылки";
"ad_poster" = "Рекламный плакат";
@@ -554,6 +556,11 @@
"share_with_friends" = "Рассказать друзьям";
+"end_all_sessions" = "Сбросить все сессии";
+"end_all_sessions_description" = "Если вы хотите выйти из $1 со всех устройств, нажмите на кнопку ниже";
+
+"end_all_sessions_done" = "Все сессии сброшены, включая мобильные приложения";
+
/* Two-factor authentication */
"two_factor_authentication" = "Двухфакторная аутентификация";
@@ -880,8 +887,13 @@
"banned_header" = "Вы были верискокнуты";
"banned_alt" = "Пользователь заблокирован.";
"banned_1" = "Извините, $1, но вы были верискокнуты.";
-"banned_2" = "А причина этому проста: $1. К сожалению, на этот раз нам пришлось заблокировать вас навсегда.";
+"banned_2" = "А причина этому проста: $1.";
+"banned_perm" = "К сожалению, на этот раз нам пришлось заблокировать вас навсегда";
+"banned_until_time" = "На этот раз нам пришлось заблокировать вас до $1";
"banned_3" = "Вы всё ещё можете написать в службу поддержки, если считаете что произошла ошибка или выйти.";
+"banned_unban_myself" = "Разморозить страницу";
+"banned_unban_title" = "Ваш аккаунт разблокирован";
+"banned_unban_description" = "Постарайтесь больше не нарушать правила.";
/* Registration confirm */
@@ -1113,6 +1125,17 @@
"admin_privacy_warning" = "Будьте осторожны с этой информацией";
+"admin_banned_links" = "Заблокированные ссылки";
+"admin_banned_link" = "Ссылка";
+"admin_banned_domain" = "Домен";
+"admin_banned_link_description" = "С протоколом (https://example.com/)";
+"admin_banned_link_regexp" = "Регулярное выражение";
+"admin_banned_link_regexp_description" = "Подставляется после домена, указанного выше. Не заполняйте, если хотите заблокировать весь домен";
+"admin_banned_link_reason" = "Причина";
+"admin_banned_link_initiator" = "Инициатор";
+"admin_banned_link_not_specified" = "Ссылка не указана";
+"admin_banned_link_not_found" = "Ссылка не найдена";
+
/* Paginator (deprecated) */
"paginator_back" = "Назад";
@@ -1180,4 +1203,13 @@
/* Blacklist */
"blacklist" = "Чёрный список";
-"user_blacklisted_you" = "Пользователь внёс Вас в чёрный список.";
\ No newline at end of file
+"user_blacklisted_you" = "Пользователь внёс Вас в чёрный список.";
+
+/* Away */
+
+"url_is_banned" = "Переход невозможен";
+"url_is_banned_comment" = "Администрация $1 не рекомендует переходить по этой ссылке.";
+"url_is_banned_comment_r" = "Администрация $1 не рекомендует переходить по этой ссылке.
Причина: $2";
+"url_is_banned_default_reason" = "Ссылка, по которой вы попытались перейти, может вести на сайт, который был создан с целью обмана пользователей и получения за счёт этого прибыли.";
+"url_is_banned_title" = "Ссылка на подозрительный сайт";
+"url_is_banned_proceed" = "Перейти по ссылке";
diff --git a/locales/su.strings b/locales/su.strings
index 4f757812..e95476e7 100644
--- a/locales/su.strings
+++ b/locales/su.strings
@@ -11,17 +11,17 @@
/* Login */
"log_in" = "Вход";
-"password" = "Пароль";
+"password" = "Проходное слово";
"registration" = "Регистрация";
-"forgot_password" = "Забыли пароль?";
+"forgot_password" = "Забыли проходное слово?";
"login_failed" = "Не удалось войти";
"invalid_username_or_password" = "Неверное имя пользователя или пароль. Забыли пароль?";
"failed_to_register" = "Не удалось зарегистрироваться";
"referral_link_invalid" = "Пригласительная ссылка недействительна.";
-"registration_disabled" = "Регистрация отключена системным администратором.";
-"user_already_exists" = "Пользователь с таким email уже существует.";
+"registration_disabled" = "Товарищ, регистрация отключена Имперской Канцелярией.";
+"user_already_exists" = "Гражданин с таким почтовым ящиком уже существует.";
"access_recovery" = "Восстановление доступа";
"page_access_recovery" = "Восстановить доступ к странице";
@@ -30,25 +30,25 @@
"reset_password" = "Сбросить пароль";
"2fa_code_2" = "Код двухфакторной аутентификации";
-"password_successfully_reset" = "Ваш пароль был успешно сброшен.";
-"password_reset_email_sent" = "Если вы зарегистрированы, вы получите инструкции на email.";
-"password_reset_error" = "Непредвиденная ошибка при сбросе пароля.";
+"password_successfully_reset" = "Ваше проходное слово было успешно сброшено.";
+"password_reset_email_sent" = "Если вы зарегистрированы, вы получите инструкции на почтовый ящик.";
+"password_reset_error" = "Непредвиденная ошибка при сбросе проходного слова.";
"password_reset_rate_limit_error" = "Нельзя делать это так часто, извините.";
-"registration_disabled_info" = "Регистрация отключена системным администратором. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте.";
+"registration_disabled_info" = "Товарищ, регистрация отключена Имперской Канцелярией. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте.";
"registration_closed" = "Регистрация закрыта.";
"invites_you_to" = "$1 приглашает вас в $2";
"register_meta_desc" = "Зарегистрируйтесь в $1 прямо сейчас!";
"register_referer_meta_title" = "$1 приглашает вас в $2!";
-"register_referer_meta_desc" = "Присоединяйтесь к $1 и множеству других пользователей в $2!";
+"register_referer_meta_desc" = "Присоединяйтесь к $1 и множеству других граждан в $2!";
-"users" = "Пользователи";
+"users" = "Граждане";
/* Profile information */
-"select_language" = "Выбрать язык";
-"edit" = "Изменить";
+"select_language" = "Выбрать менталитет";
+"edit" = "Корректировать";
"birth_date" = "День рождения";
"registration_date" = "Дата регистрации";
"hometown" = "Родной город";
@@ -90,7 +90,7 @@
"relationship_2" = "Встречаюсь";
"relationship_3" = "Помолвлен";
"relationship_4" = "Женат";
-"relationship_5" = "В гражданском браке";
+"relationship_5" = "Сожительствую";
"relationship_6" = "Влюблен";
"relationship_7" = "Всё сложно";
"relationship_8" = "В активном поиске";
@@ -98,29 +98,29 @@
"politViews" = "Полит. взгляды";
"politViews_0" = "Не выбраны";
-"politViews_1" = "Индифферентные";
+"politViews_1" = "Не в партии";
"politViews_2" = "Коммунистические";
"politViews_3" = "Социалистические";
-"politViews_4" = "Умеренные";
-"politViews_5" = "Либеральные";
+"politViews_4" = "Центристские";
+"politViews_5" = "Антисоветские";
"politViews_6" = "Консервативные";
-"politViews_7" = "Монархические";
+"politViews_7" = "Контрреволюционные";
"politViews_8" = "Ультраконсервативные";
"politViews_9" = "Либертарианские";
"contact_information" = "Контактная информация";
"email" = "Почтовый ящик";
-"phone" = "Телефон";
+"phone" = "Стационарный телефон";
"telegram" = "Telegram";
"personal_website" = "Личная визитка";
"city" = "Город";
"address" = "Адрес";
-"personal_information" = "Личная информация";
+"personal_information" = "Личность";
"interests" = "Интересы";
-"favorite_music" = "Любимые аудиозаписи";
+"favorite_music" = "Любимые звукозаписи";
"favorite_films" = "Любимые киноленты";
"favorite_shows" = "Любимые программы";
"favorite_books" = "Любимые книги";
@@ -189,8 +189,8 @@
"friends" = "Товарищи";
"followers" = "Подписчики";
"follower" = "Подписчик";
-"friends_add" = "Добавить в товарищи";
-"friends_delete" = "Удалить из товарищей";
+"friends_add" = "Взять в товарищи";
+"friends_delete" = "Отвергнуть товарища";
"friends_reject" = "Порвать приглашение в товарищи";
"friends_accept" = "Прочитать приглашение в товарищи";
"send_message" = "Отправить телеграмму";
@@ -222,13 +222,13 @@
"subscribe" = "Подписаться";
"unsubscribe" = "Отписаться";
"subscriptions" = "Подписки";
-"join_community" = "Вступить в группу";
-"leave_community" = "Выйти из группы";
+"join_community" = "Вступить в клуб";
+"leave_community" = "Выйти из клуба";
"min_6_community" = "Название должно быть не менее 6 символов";
"participants" = "Участники";
-"groups" = "Группы";
+"groups" = "Клубы";
"meetings" = "Встречи";
-"create_group" = "Создать группу";
+"create_group" = "Создать клуб";
"group_managers" = "Руководство";
"group_type" = "Тип группы";
"group_type_open" = "Это открытая группа. В неё может вступить любой желающий.";
@@ -263,7 +263,7 @@
"group_dont_display_administrators_list" = "Ничего не отображать";
"group_changeowner_modal_title" = "Передача прав владельца";
-"group_changeowner_modal_text" = "Внимание! Вы передаёте права владельца пользователю $1. Это действие необратимо. После передави вы останетесь адмиинстратором, но сможете легко перестать им быть.";
+"group_changeowner_modal_text" = "Внимание! Вы передаёте права владельца пользователю $1. Это действие необратимо. После передачи вы останетесь адмиинстратором, но сможете легко перестать им быть.";
"group_owner_setted" = "Новый владелец ($1) успешно назначен в сообщество $2. Вам выданы права администратора в сообществе. Если Вы хотите вернуть роль владельца, обратитесь в техническую поддержку сайта.";
"participants_zero" = "Ни одного участника";
@@ -340,8 +340,8 @@
"menu_registration" = "Регистрация";
"menu_help" = "Справка";
-"menu_logout" = "Выйти";
-"menu_support" = "Поддержка";
+"menu_logout" = "Эмигрировать";
+"menu_support" = "Справочная";
"header_home" = "главная";
"header_groups" = "клубы";
@@ -646,12 +646,12 @@
"support_new_title" = "Введите тему вашего обращения";
"support_new_content" = "Опишите проблему или предложение";
-"support_rate_good_answer" = "Это хороший ответ";
-"support_rate_bad_answer" = "Это плохой ответ";
-"support_good_answer_user" = "Вы оставили положительный отзыв.";
-"support_bad_answer_user" = "Вы оставили негативный отзыв.";
-"support_good_answer_agent" = "Гражданин оставил положительный отзыв";
-"support_bad_answer_agent" = "Гражданин оставил негативный отзыв";
+"support_rate_good_answer" = "Подарить конфеты";
+"support_rate_bad_answer" = "Закатить скандал";
+"support_good_answer_user" = "Вы подарили конфеты сотруднику справочной.";
+"support_bad_answer_user" = "Вы закатили скандал сотруднику справочной.";
+"support_good_answer_agent" = "Гражданин подарил конфеты сотруднику справочной";
+"support_bad_answer_agent" = "Гражданин закатил скандал сотруднику справочной";
"support_rated_good" = "Вы оставили положительный отзыв об ответе.";
"support_rated_bad" = "Вы оставили негативный отзыв об ответе.";
"wrong_parameters" = "Неверные параметры запроса.";
@@ -683,7 +683,7 @@
"banned_alt" = "Гражданин был отправлен в тюрьму.";
"banned_1" = "Извините, $1, но вы были отправлены в тюрьму.";
"banned_2" = "А причина этому проста: $1. Органу в этот раз пришлось отправить вас под стражу навсегда.";
-"banned_3" = "Вы всё ещё можете написать в службу поддержки, если считаете что произошла ошибка или выйти.";
+"banned_3" = "Вы всё ещё можете написать в Справочную, если считаете что произошла ошибка или эмигрировать.";
/* Discussions */
@@ -822,15 +822,15 @@
"about_users_many" = "$1 гражданинов";
"about_users_other" = "$1 гражданинов";
-"about_online_users_one" = "1 пользователь в сети";
-"about_online_users_few" = "$1 пользователя в сети";
-"about_online_users_many" = "$1 гражданинов в сети";
-"about_online_users_other" = "$1 гражданинов в сети";
+"about_online_users_one" = "1 гражданин в сети";
+"about_online_users_few" = "$1 гражданина в сети";
+"about_online_users_many" = "$1 граждан в сети";
+"about_online_users_other" = "$1 граждан в сети";
-"about_active_users_one" = "1 активный пользователь";
-"about_active_users_few" = "$1 активных пользователя";
-"about_active_users_many" = "$1 активных гражданинов";
-"about_active_users_other" = "$1 активных гражданинов";
+"about_active_users_one" = "1 активный гражданин";
+"about_active_users_few" = "$1 активных граждан";
+"about_active_users_many" = "$1 активных граждан";
+"about_active_users_other" = "$1 активных граждан";
"about_groups_one" = "1 клуб";
"about_groups_few" = "$1 клубы";
@@ -860,7 +860,6 @@
"user_alert_scam" = "Органу управления было дозволено, что данный гражданин обманывает товарищей на денежные средства. Будьте осторожны при разговоре с ним.";
-
"ec_header" = "Подтверждение регистрации прописки";
"ec_title" = "Спасибо!";
"ec_1" = "$1, на ваш почтовый ящик должно придти письмо с подтверждением регистрации.";
@@ -872,4 +871,17 @@
"email_error" = "Непредвиденная ошибка при отправке письма.";
"email_rate_limit_error" = "Нельзя делать это так часто, извините.";
-"email_verify_success" = "Ваша регистрация была подтверждена. Приятного времяпрепровождения!";
\ No newline at end of file
+"email_verify_success" = "Ваша регистрация была подтверждена. Приятного времяпрепровождения!";
+
+"you_still_have_x_points" = "У Вас $1 неиспользованных совестких рублей.";
+"top_up_your_account" = "Пополнить баланс";
+
+"transfer_trough_ton" = "Пополнить с помощью ТОН";
+"transfer_ton_contents" = "Вы можете пополнить ваш баланс с помощью криптовалюты ТОН. Достаточно отсканировать КуАр-код приложением Tonkeeper, или вручную отправить ТОН по реквизитам. В течении нескольких минут вам придут определенное количество рублей.";
+"transfer_ton_address" = "Адрес кошелька: $1
Содержание телеграммы: $2";
+"transfer_ton_currency_per_ton" = "$1 TON";
+
+"about_links" = "Ссылки";
+"instance_links" = "Ссылки страны:";
+
+"my_apps" = "Досуг и отдых";
diff --git a/locales/ua.strings b/locales/ua.strings
index c0dcf8a2..ef701a0b 100644
--- a/locales/ua.strings
+++ b/locales/ua.strings
@@ -1,5 +1,3 @@
-#include
команда підтримки $1.";
@@ -880,9 +885,14 @@
"banned_title" = "Обліковий запис заблоковано";
"banned_header" = "Ваш обліковий запис заблоковано.";
"banned_alt" = "Користувача заблоковано.";
-"banned_1" = "$1, ваш акаунт заблоковано за порушення правил користування сайту.";
-"banned_2" = "Підстава: $1. На цей раз, нам довелося заблокувати вас назавжди.";
+"banned_1" = "$1, ваш обліковий запис заблоковано за порушення правил користування сайту.";
+"banned_2" = "Привід: $1.";
+"banned_perm" = "На цей раз, Ви заблоковані назавжди.";
+"banned_until_time" = "На цей раз, Ви заблоковані до $1";
"banned_3" = "Ви все ще можете написати в службу підтримки, якщо вважаєте, що сталася помилка або вийти.";
+"banned_unban_myself" = "Розблокувати сторінку";
+"banned_unban_title" = "Ваш обліковий запис розблокований";
+"banned_unban_description" = "Намагайтеся, більше не порушувати правила.";
/* Registration confirm */
diff --git a/openvk-example.yml b/openvk-example.yml
index f682c27d..05b4207b 100644
--- a/openvk-example.yml
+++ b/openvk-example.yml
@@ -57,6 +57,9 @@ openvk:
processingLimit: 3000
emojiProcessingLimit: 1000
commerce: false
+ susLinks:
+ warnings: true
+ showReason: true
ton:
enabled: false
address: "🅿"
diff --git a/themepacks/openvk_modern/stylesheet.css b/themepacks/openvk_modern/stylesheet.css
index 590710e4..0c11c672 100644
--- a/themepacks/openvk_modern/stylesheet.css
+++ b/themepacks/openvk_modern/stylesheet.css
@@ -223,7 +223,7 @@ input[type=checkbox] {
}
.ovk-diag-action {
- border-bottom-left-radius: 2px;
+ border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
@@ -231,20 +231,6 @@ input[type=checkbox] {
border-bottom: none;
}
-.floating_sidebar {
- position: fixed;
- top: 50px;
- right: 0;
- align-items: start;
- width: 21%;
-}
-.minilink .counter {
- background-color: #2B587A;
- margin: 0;
- padding: 0.5px 2px;
- left: 10px;
- font-size: 7px;
- color: #fff;
- position: absolute;
- margin-top: -6px;
+.floating_sidebar,.floating_sidebar.show {
+ display:none
}