Merge branch 'master' into blacklist

This commit is contained in:
n1rwana 2022-09-05 22:05:06 +03:00 committed by GitHub
commit 1dc57f766e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 778 additions and 228 deletions

View file

@ -284,7 +284,7 @@ final class Messages extends VKAPIRequestHandler
return (object) $output; 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(); $this->requireUser();
@ -316,10 +316,18 @@ final class Messages extends VKAPIRequestHandler
$results[] = $rMsg; $results[] = $rMsg;
} }
return (object) [ $output = [
"count" => sizeof($results), "count" => sizeof($results),
"items" => $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 function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object

View file

@ -26,7 +26,7 @@ final class Newsfeed extends VKAPIRequestHandler
->select("id") ->select("id")
->where("wall IN (?)", $ids) ->where("wall IN (?)", $ids)
->where("deleted", 0) ->where("deleted", 0)
->where("created < (?)", empty($start_from) ? time()+1 : $start_from) ->where("id < (?)", empty($start_from) ? time()+1 : $start_from)
->order("created DESC"); ->order("created DESC");
$rposts = []; $rposts = [];
@ -34,7 +34,7 @@ final class Newsfeed extends VKAPIRequestHandler
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId(); $rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser()); $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; return $response;
} }
} }

View file

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{User};
use openvk\Web\Models\Repositories\{Users};
use Nette\Database\Table\ActiveRow;
class BannedLink extends RowModel
{
protected $tableName = "links_banned";
private $overrideContentColumn = "reason";
function getId(): int
{
return $this->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;
}
}

View file

@ -35,12 +35,12 @@ trait TRichText
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%", "%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
(function (array $matches): string { (function (array $matches): string {
$href = str_replace("#", "&num;", $matches[1]); $href = str_replace("#", "&num;", $matches[1]);
$href = str_replace(";", "&#59;", $matches[1]); $href = rawurlencode(str_replace(";", "&#59;", $matches[1]));
$link = str_replace("#", "&num;", $matches[3]); $link = str_replace("#", "&num;", $matches[3]);
$link = str_replace(";", "&#59;", $matches[3]); $link = str_replace(";", "&#59;", $matches[3]);
$rel = $this->isAd() ? "sponsored" : "ugc"; $rel = $this->isAd() ? "sponsored" : "ugc";
return "<a href='$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]); return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
}), }),
$text $text
); );

View file

@ -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) { if($deleteSubscriptions) {
$subs = DatabaseConnection::i()->getContext()->table("subscriptions"); $subs = DatabaseConnection::i()->getContext()->table("subscriptions");
@ -785,6 +785,7 @@ class User extends RowModel
} }
$this->setBlock_Reason($reason); $this->setBlock_Reason($reason);
$this->setUnblock_time($unban_time);
$this->save(); $this->save();
} }
@ -1026,5 +1027,21 @@ class User extends RowModel
return $this->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL); 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; use Traits\TSubscribable;
} }

View file

@ -0,0 +1,73 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Repositories;
use Chandler\Database\DatabaseConnection as DB;
use Nette\Database\Table\{ActiveRow, Selection};
use openvk\Web\Models\Entities\BannedLink;
class BannedLinks
{
private $context;
private $bannedLinks;
function __construct()
{
$this->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));
}
}

View file

@ -1,7 +1,8 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Presenters; namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User}; use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts}; use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts, BannedLinks};
use Chandler\Database\DatabaseConnection;
final class AdminPresenter extends OpenVKPresenter final class AdminPresenter extends OpenVKPresenter
{ {
@ -9,13 +10,15 @@ final class AdminPresenter extends OpenVKPresenter
private $clubs; private $clubs;
private $vouchers; private $vouchers;
private $gifts; private $gifts;
private $bannedLinks;
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts) function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks)
{ {
$this->users = $users; $this->users = $users;
$this->clubs = $clubs; $this->clubs = $clubs;
$this->vouchers = $vouchers; $this->vouchers = $vouchers;
$this->gifts = $gifts; $this->gifts = $gifts;
$this->bannedLinks = $bannedLinks;
parent::__construct(); parent::__construct();
} }
@ -340,11 +343,13 @@ final class AdminPresenter extends OpenVKPresenter
{ {
$this->assertNoCSRF(); $this->assertNoCSRF();
$unban_time = strtotime($this->queryParam("date")) ?: NULL;
$user = $this->users->get($id); $user = $this->users->get($id);
if(!$user) if(!$user)
exit(json_encode([ "error" => "User does not exist" ])); 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") ])); exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
} }
@ -356,7 +361,8 @@ final class AdminPresenter extends OpenVKPresenter
if(!$user) if(!$user)
exit(json_encode([ "error" => "User does not exist" ])); exit(json_encode([ "error" => "User does not exist" ]));
$user->setBlock_Reason(null); $user->setBlock_Reason(NULL);
$user->setUnblock_time(NULL);
$user->save(); $user->save();
exit(json_encode([ "success" => true ])); exit(json_encode([ "success" => true ]));
} }
@ -372,4 +378,73 @@ final class AdminPresenter extends OpenVKPresenter
$user->adminNotify("⚠️ " . $this->queryParam("message")); $user->adminNotify("⚠️ " . $this->queryParam("message"));
exit(json_encode([ "message" => $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");
}
} }

View file

@ -322,4 +322,39 @@ final class AuthPresenter extends OpenVKPresenter
$this->redirect("/"); $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"));
}
} }

View file

@ -1,13 +1,29 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Presenters; namespace openvk\Web\Presenters;
use openvk\Web\Models\Repositories\BannedLinks;
use openvk\Web\Models\Entities\BannedLink;
final class AwayPresenter extends OpenVKPresenter final class AwayPresenter extends OpenVKPresenter
{ {
function renderAway(): void function renderAway(): void
{ {
$checkBanEntries = (new BannedLinks)->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("HTTP/1.0 302 Found");
header("X-Robots-Tag: noindex, nofollow, noarchive"); header("X-Robots-Tag: noindex, nofollow, noarchive");
header("Location: " . $this->queryParam("to")); header("Location: " . $this->queryParam("to"));
exit; exit;
} }
function renderView(int $lid) {
$this->template->link = (new BannedLinks)->get($lid);
if (!$this->template->link)
$this->notFound();
$this->template->to = $this->queryParam("to");
}
} }

View file

@ -0,0 +1,13 @@
<?php declare(strict_types=1);
namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\BannedLink;
use openvk\Web\Models\Repositories\BannedLinks;
final class BannedLinkPresenter extends OpenVKPresenter
{
function renderView(int $lid) {
$this->template->link = (new BannedLinks)->get($lid);
$this->template->to = $this->queryParam("to");
}
}

View file

@ -471,7 +471,7 @@ final class UserPresenter extends OpenVKPresenter
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment")); $this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
} }
$this->template->mode = in_array($this->queryParam("act"), [ $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") ]) ? $this->queryParam("act")
: "main"; : "main";

View file

@ -113,14 +113,14 @@ final class WallPresenter extends OpenVKPresenter
$feed = new Feed(); $feed = new Feed();
$channel = new Channel(); $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) { foreach($posts as $post) {
$item = new Item(); $item = new Item();
$item $item
->title($post->getOwner()->getCanonicalName()) ->title($post->getOwner()->getCanonicalName())
->description($post->getText()) ->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()) ->pubDate($post->getPublicationTime()->timestamp())
->appendTo($channel); ->appendTo($channel);
} }

View file

@ -12,6 +12,16 @@
<p> <p>
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/> {tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape} {tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
{if !$thisUser->getUnbanTime()}
{_banned_perm}
{else}
{tr("banned_until_time", $thisUser->getUnbanTime())|noescape}
{/if}
</p>
<p n:if="$thisUser->canUnbanThemself()">
<hr/>
<center><a class="button" href="/unban.php">{_banned_unban_myself}</a></center>
</p> </p>
<hr/> <hr/>
<p> <p>

View file

@ -396,8 +396,8 @@
<tr> <tr>
<td class="e"> <td class="e">
Vladimir Barinov (veselcraft), Celestora, Konstantin Kichulkin (kosfurler), Vladimir Barinov (veselcraft), Celestora, Konstantin Kichulkin (kosfurler),
Nikita Volkov (sup_ban), Daniel Myslivets, Alexander Kotov (l-lacker), Nikita Volkov (sup_ban), Daniel Myslivets, Maxim Leshchenko (maksales / maksalees)
Alexey Assemblerov (BiosNod), Ilya Prokopenko (dsrev) and Maxim Leshchenko (maksales / maksalees) and n1rwana
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -56,6 +56,9 @@
<li> <li>
<a href="/admin/clubs">{_groups}</a> <a href="/admin/clubs">{_groups}</a>
</li> </li>
<li>
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
</li>
</ul> </ul>
<div class="aui-nav-heading"> <div class="aui-nav-heading">
<strong>{_admin_services}</strong> <strong>{_admin_services}</strong>

View file

@ -0,0 +1,48 @@
{extends "@layout.xml"}
{block title}
{_edit}
{/block}
{block heading}
{_edit} #{$form->id ?? "undefined"}
{/block}
{block content}
<div style="margin: 8px -8px;" class="aui-tabs horizontal-tabs">
<ul class="tabs-menu">
<li class="menu-item active-tab">
<a href="#info">{_admin_banned_link}</a>
</li>
</ul>
<div class="tabs-pane active-pane" id="info">
<form class="aui" method="POST">
<div class="field-group">
<label for="id">ID</label>
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
</div>
<div class="field-group">
<label for="token">{_admin_banned_domain}</label>
<input class="text long-field" type="text" id="link" name="link" value="{$form->link}" />
<div class="description">{_admin_banned_link_description}</div>
</div>
<div class="field-group">
<label for="token">{_admin_banned_link_regexp}</label>
<input class="text long-field" type="text" id="regexp" name="regexp" value="{$form->regexp}" />
<div class="description">{_admin_banned_link_regexp_description}</div>
</div>
<div class="field-group">
<label for="coins">{_admin_banned_link_reason}</label>
<input class="text long-field" type="text" id="reason" name="reason" value="{$form->reason}" />
</div>
<div class="buttons-container">
<div class="buttons">
<input type="hidden" name="hash" value="{$csrfToken}" />
<input class="aui-button aui-button-primary submit" type="submit" value="{_save}">
<a class="aui-button aui-button-secondary" href="/admin/bannedLink/id{$form->id}/unban">{_delete}</a>
</div>
</div>
</form>
</div>
</div>
{/block}

View file

@ -0,0 +1,46 @@
{extends "@layout.xml"}
{block title}
{_admin_banned_links}
{/block}
{block heading}
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/bannedLink/id0">
{_create}
</a>
{_admin_banned_links}
{/block}
{block content}
<table class="aui aui-table-list">
<thead>
<tr>
<th>ID</th>
<th>{_admin_banned_domain}</th>
<th>REGEXP</th>
<th>{_admin_banned_link_reason}</th>
<th>{_admin_banned_link_initiator}</th>
<th>{_admin_actions}</th>
</tr>
</thead>
<tbody>
<tr n:foreach="$links as $link">
<td>{$link->getId()}</td>
<td>{$link->getDomain()}</td>
<td>{$link->getRegexpRule()}</td>
<td>{$link->getReason() ?? "-"}</td>
<td>{$link->getInitiator()->getCanonicalName()}</td>
<td>
<a class="aui-button aui-button-primary" href="/admin/bannedLink/id{$link->getId()}">
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
</a>
</td>
</tr>
</tbody>
</table>
<div align="right">
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
<a class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
</div>
{/block}

View file

@ -0,0 +1,22 @@
{extends "../@layout.xml"}
{block title}Переход по ссылке заблокирован{/block}
{block header}
Предупреждение
{/block}
{block content}
<div style="min-height: 120px;">
<img src="/assets/packages/static/openvk/img/oof.apng" width="110" height="110" style="margin-left: 20px;">
<div style="padding-left: 150px; margin-top: -100px;">
<h4 style="font-size: 14px; margin-bottom: 8px;">{_url_is_banned_title}</h4>
<span>
{$link->getComment()|noescape}
</span>
<br><br>
<a href="{$to}" class="button" target="_blank">{_url_is_banned_proceed}</a>
</div>
</div>
{/block}

View file

@ -8,6 +8,7 @@
{block content} {block content}
{var $isMain = $mode === 'main'} {var $isMain = $mode === 'main'}
{var $isSecurity = $mode === 'security'}
{var $isPrivacy = $mode === 'privacy'} {var $isPrivacy = $mode === 'privacy'}
{var $isFinance = $mode === 'finance'} {var $isFinance = $mode === 'finance'}
{var $isFinanceTU = $mode === 'finance.top-up'} {var $isFinanceTU = $mode === 'finance.top-up'}
@ -18,6 +19,9 @@
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab"> <div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
<a n:attr="id => ($isMain ? 'act_tab_a' : 'ki')" href="/settings">{_main}</a> <a n:attr="id => ($isMain ? 'act_tab_a' : 'ki')" href="/settings">{_main}</a>
</div> </div>
<div n:attr="id => ($isSecurity ? 'activetabs' : 'ki')" class="tab">
<a n:attr="id => ($isSecurity ? 'act_tab_a' : 'ki')" href="/settings?act=security">{_security}</a>
</div>
<div n:attr="id => ($isPrivacy ? 'activetabs' : 'ki')" class="tab"> <div n:attr="id => ($isPrivacy ? 'activetabs' : 'ki')" class="tab">
<a n:attr="id => ($isPrivacy ? 'act_tab_a' : 'ki')" href="/settings?act=privacy">{_privacy}</a> <a n:attr="id => ($isPrivacy ? 'act_tab_a' : 'ki')" href="/settings?act=privacy">{_privacy}</a>
</div> </div>
@ -36,117 +40,6 @@
{if $isMain} {if $isMain}
<form action="/settings?act=main" method="POST" enctype="multipart/form-data"> <form action="/settings?act=main" method="POST" enctype="multipart/form-data">
<h4>{_change_password}</h4>
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top">
<span class="nobold">{_old_password}</span>
</td>
<td>
<input type="password" name="old_pass" style="width: 100%;" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_new_password}</span>
</td>
<td>
<input type="password" name="new_pass" style="width: 100%;" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_repeat_password}</span>
</td>
<td>
<input type="password" name="repeat_pass" style="width: 100%;" />
</td>
</tr>
<tr n:if="$user->is2faEnabled()">
<td width="120" valign="top">
<span class="nobold">{_"2fa_code"}</span>
</td>
<td>
<input type="text" name="password_change_code" style="width: 100%;" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" value="{_change_password}" class="button" />
</td>
</tr>
</tbody>
</table>
<br/>
<h4>{_two_factor_authentication}</h4>
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tbody>
{if $user->is2faEnabled()}
<tr>
<td>
<div class="accent-box">
{_two_factor_authentication_enabled}
</div>
</td>
</tr>
<tr>
<td style="text-align: center;">
<a class="button" href="javascript:viewBackupCodes()">{_view_backup_codes}</a>
<a class="button" href="javascript:disableTwoFactorAuth()">{_disable}</a>
</td>
</tr>
<script>
function viewBackupCodes() {
MessageBox("Просмотр резервных кодов", `
<form id="back-codes-view-form" method="post" action="/settings/2fa">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required />
<input type="hidden" name="hash" value={$csrfToken} />
</form>
`, ["Просмотреть", "Отменить"], [
() => {
document.querySelector("#back-codes-view-form").submit();
}, Function.noop
]);
}
function disableTwoFactorAuth() {
MessageBox("Отключить 2FA", `
<form id="two-factor-auth-disable-form" method="post" action="/settings/2fa/disable">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required />
<input type="hidden" name="hash" value={$csrfToken} />
</form>
`, ["Отключить", "Отменить"], [
() => {
document.querySelector("#two-factor-auth-disable-form").submit();
}, Function.noop
]);
}
</script>
{else}
<tr>
<td>
<div class="accent-box">
{_two_factor_authentication_disabled}
</div>
</td>
</tr>
<tr>
<td style="text-align: center;">
<a class="button" href="/settings/2fa">{_connect}</a>
</td>
</tr>
{/if}
</tbody>
</table>
<br/>
<h4>{_your_email_address}</h4> <h4>{_your_email_address}</h4>
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center"> <table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tbody> <tbody>
@ -230,6 +123,141 @@
{_you_can_also} <a onClick="showProfileDeactivateDialog({$csrfToken})">{_delete_your_page}</a>. {_you_can_also} <a onClick="showProfileDeactivateDialog({$csrfToken})">{_delete_your_page}</a>.
</div> </div>
{elseif $isSecurity}
<form action="/settings?act=main" method="POST" enctype="multipart/form-data">
<h4>{_change_password}</h4>
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top">
<span class="nobold">{_old_password}</span>
</td>
<td>
<input type="password" name="old_pass" style="width: 100%;" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_new_password}</span>
</td>
<td>
<input type="password" name="new_pass" style="width: 100%;" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_repeat_password}</span>
</td>
<td>
<input type="password" name="repeat_pass" style="width: 100%;" />
</td>
</tr>
<tr n:if="$user->is2faEnabled()">
<td width="120" valign="top">
<span class="nobold">{_"2fa_code"}</span>
</td>
<td>
<input type="text" name="password_change_code" style="width: 100%;" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" value="{_change_password}" class="button" />
</td>
</tr>
</tbody>
</table>
<br/>
</form>
<br/>
<h4>{_two_factor_authentication}</h4>
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tbody>
{if $user->is2faEnabled()}
<tr>
<td>
<div class="accent-box">
{_two_factor_authentication_enabled}
</div>
</td>
</tr>
<tr>
<td style="text-align: center;">
<a class="button" href="javascript:viewBackupCodes()">{_view_backup_codes}</a>
<a class="button" href="javascript:disableTwoFactorAuth()">{_disable}</a>
</td>
</tr>
<script>
function viewBackupCodes() {
MessageBox("Просмотр резервных кодов", `
<form id="back-codes-view-form" method="post" action="/settings/2fa">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required />
<input type="hidden" name="hash" value={$csrfToken} />
</form>
`, ["Просмотреть", "Отменить"], [
() => {
document.querySelector("#back-codes-view-form").submit();
}, Function.noop
]);
}
function disableTwoFactorAuth() {
MessageBox("Отключить 2FA", `
<form id="two-factor-auth-disable-form" method="post" action="/settings/2fa/disable">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required />
<input type="hidden" name="hash" value={$csrfToken} />
</form>
`, ["Отключить", "Отменить"], [
() => {
document.querySelector("#two-factor-auth-disable-form").submit();
}, Function.noop
]);
}
</script>
{else}
<tr>
<td>
<div class="accent-box">
{_two_factor_authentication_disabled}
</div>
</td>
</tr>
<tr>
<td style="text-align: center;">
<a class="button" href="/settings/2fa">{_connect}</a>
</td>
</tr>
{/if}
</tbody>
</table>
<h4>{_ui_settings_sessions}</h4>
<form action="/revokeAllTokens" method="POST">
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tbody>
<tr>
<td>
<div class="accent-box">
{tr("end_all_sessions_description", OPENVK_ROOT_CONF['openvk']['appearance']['name'])}
</div>
</td>
</tr>
<tr>
<td width="120" valign="top" style="text-align: center;">
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" value="{_end_all_sessions}" class="button" />
</td>
</tr>
</tbody>
</table>
</form>
{elseif $isPrivacy} {elseif $isPrivacy}
<form action="/settings?act=privacy" method="POST" enctype="multipart/form-data"> <form action="/settings?act=privacy" method="POST" enctype="multipart/form-data">

View file

@ -561,12 +561,14 @@
uBanMsgTxt = "Вы собираетесь забанить пользователя " + {$user->getCanonicalName()} + "."; uBanMsgTxt = "Вы собираетесь забанить пользователя " + {$user->getCanonicalName()} + ".";
uBanMsgTxt += "<br/><b>Предупреждение</b>: Это действие удалит все подписки пользователя и отпишет всех от него."; uBanMsgTxt += "<br/><b>Предупреждение</b>: Это действие удалит все подписки пользователя и отпишет всех от него.";
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />" uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />"
uBanMsgTxt += "<br/><br/><b>Заблокировать до</b>: <input type='date' id='uBanMsgDate' />";
MessageBox("Забанить " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [ MessageBox("Забанить " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
(function() { (function() {
res = document.querySelector("#uBanMsgInput").value; res = document.querySelector("#uBanMsgInput").value;
date = document.querySelector("#uBanMsgDate").value;
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true); xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&date=" + date + "&hash=" + {rawurlencode($csrfToken)}, true);
xhr.onload = (function() { xhr.onload = (function() {
if(xhr.responseText.indexOf("success") === -1) if(xhr.responseText.indexOf("success") === -1)
MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]); MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]);

View file

@ -2,7 +2,8 @@
<img src="/assets/packages/static/openvk/img/oof.apng" alt="Пользователь заблокирован." style="width: 20%;" /> <img src="/assets/packages/static/openvk/img/oof.apng" alt="Пользователь заблокирован." style="width: 20%;" />
<p> <p>
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/> {tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
{_user_banned_comment} <b>{$user->getBanReason()}</b>. {_user_banned_comment} <b>{$user->getBanReason()}</b>.<br/>
Пользователь заблокирован до: <b>{$user->getUnbanTime()}</b>
</p> </p>
{if isset($thisUser)} {if isset($thisUser)}
<p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)"> <p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">

View file

@ -24,6 +24,7 @@ services:
- openvk\Web\Presenters\ThemepacksPresenter - openvk\Web\Presenters\ThemepacksPresenter
- openvk\Web\Presenters\VKAPIPresenter - openvk\Web\Presenters\VKAPIPresenter
- openvk\Web\Presenters\BlacklistPresenter - openvk\Web\Presenters\BlacklistPresenter
- openvk\Web\Presenters\BannedLinkPresenter
- openvk\Web\Models\Repositories\Users - openvk\Web\Models\Repositories\Users
- openvk\Web\Models\Repositories\Posts - openvk\Web\Models\Repositories\Posts
- openvk\Web\Models\Repositories\Photos - openvk\Web\Models\Repositories\Photos
@ -44,3 +45,4 @@ services:
- openvk\Web\Models\Repositories\Applications - openvk\Web\Models\Repositories\Applications
- openvk\Web\Models\Repositories\ContentSearchRepository - openvk\Web\Models\Repositories\ContentSearchRepository
- openvk\Web\Models\Repositories\Blacklists - openvk\Web\Models\Repositories\Blacklists
- openvk\Web\Models\Repositories\BannedLinks

View file

@ -65,6 +65,10 @@ routes:
handler: "Auth->verifyEmail" handler: "Auth->verifyEmail"
- url: "/setSID/{slug}" - url: "/setSID/{slug}"
handler: "Auth->su" handler: "Auth->su"
- url: "/unban.php"
handler: "Auth->unbanThemself"
- url: "/revokeAllTokens"
handler: "Auth->revokeAllTokens"
- url: "/settings" - url: "/settings"
handler: "User->settings" handler: "User->settings"
- url: "/settings/2fa" - url: "/settings/2fa"
@ -257,6 +261,8 @@ routes:
handler: "About->invite" handler: "About->invite"
- url: "/away.php" - url: "/away.php"
handler: "Away->away" handler: "Away->away"
- url: "/away.php/{num}"
handler: "Away->view"
- url: "/gift{num}_{num}.png" - url: "/gift{num}_{num}.png"
handler: "Gifts->giftImage" handler: "Gifts->giftImage"
- url: "/gifts{num}" - url: "/gifts{num}"
@ -303,6 +309,12 @@ routes:
handler: "Support->quickBanInSupport" handler: "Support->quickBanInSupport"
- url: "/admin/support/unban/{num}" - url: "/admin/support/unban/{num}"
handler: "Support->quickUnbanInSupport" 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}" - url: "/upload/photo/{text}"
handler: "VKAPI->photoUpload" handler: "VKAPI->photoUpload"
- url: "/method/{text}.{text}" - url: "/method/{text}.{text}"

View file

@ -0,0 +1 @@
ALTER TABLE `profiles` ADD `unblock_time` BIGINT UNSIGNED DEFAULT NULL AFTER `block_reason`;

View file

@ -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;

View file

@ -418,6 +418,7 @@
"avatar" = "Avatar"; "avatar" = "Avatar";
"privacy" = "Privacy"; "privacy" = "Privacy";
"interface" = "Interface"; "interface" = "Interface";
"security" = "Security";
"profile_picture" = "Profile picture"; "profile_picture" = "Profile picture";
@ -490,6 +491,7 @@
"ui_settings_view_of_posts_old" = "Old"; "ui_settings_view_of_posts_old" = "Old";
"ui_settings_view_of_posts_microblog" = "Microblog"; "ui_settings_view_of_posts_microblog" = "Microblog";
"ui_settings_main_page" = "Main page"; "ui_settings_main_page" = "Main page";
"ui_settings_sessions" = "Sessions";
"additional_links" = "Additional links"; "additional_links" = "Additional links";
"ad_poster" = "Ad poster"; "ad_poster" = "Ad poster";
@ -518,6 +520,11 @@
"share_with_friends" = "Share with friends"; "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" = "Two-factor authentication"; "two_factor_authentication" = "Two-factor authentication";
@ -835,8 +842,13 @@
"banned_header" = "You are banned"; "banned_header" = "You are banned";
"banned_alt" = "The user is blocked."; "banned_alt" = "The user is blocked.";
"banned_1" = "Sorry <b>$1</b>, but you have been banned."; "banned_1" = "Sorry <b>$1</b>, but you have been banned.";
"banned_2" = "And the reason for this is simple: <b>$1</b>. Unfortunately, this time we had to block you forever."; "banned_2" = "And the reason for this is simple: <b>$1</b>.";
"banned_perm" = "Unfortunately, this time we had to block you forever.";
"banned_until_time" = "This time we had to block you until <b>$1</b>";
"banned_3" = "You can still <a href=\"/support?act=new\">write to the support</a> if you think there was an error or <a href=\"/logout?hash=$1\">logout</a>."; "banned_3" = "You can still <a href=\"/support?act=new\">write to the support</a> if you think there was an error or <a href=\"/logout?hash=$1\">logout</a>.";
"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 */ /* Registration confirm */
@ -1064,6 +1076,17 @@
"admin_privacy_warning" = "Be careful with this information"; "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 (deprecated) */
"paginator_back" = "Back"; "paginator_back" = "Back";
@ -1122,3 +1145,12 @@
"blacklist" = "Blacklist"; "blacklist" = "Blacklist";
"user_blacklisted_you" = "This user has blacklisted you."; "user_blacklisted_you" = "This user has blacklisted you.";
/* Away */
"url_is_banned" = "Link is not allowed";
"url_is_banned_comment" = "The <b>$1</b> administration recommends not to follow this link.";
"url_is_banned_comment_r" = "The <b>$1</b> administration recommends not to follow this link.<br><br>The reason is: <b>$2</b>";
"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";

View file

@ -452,6 +452,7 @@
"avatar" = "Аватар"; "avatar" = "Аватар";
"privacy" = "Приватность"; "privacy" = "Приватность";
"interface" = "Внешний вид"; "interface" = "Внешний вид";
"security" = "Безопасность";
"profile_picture" = "Изображение страницы"; "profile_picture" = "Изображение страницы";
@ -526,6 +527,7 @@
"ui_settings_view_of_posts_old" = "Старый"; "ui_settings_view_of_posts_old" = "Старый";
"ui_settings_view_of_posts_microblog" = "Микроблог"; "ui_settings_view_of_posts_microblog" = "Микроблог";
"ui_settings_main_page" = "Главная страница"; "ui_settings_main_page" = "Главная страница";
"ui_settings_sessions" = "Сессии";
"additional_links" = "Дополнительные ссылки"; "additional_links" = "Дополнительные ссылки";
"ad_poster" = "Рекламный плакат"; "ad_poster" = "Рекламный плакат";
@ -554,6 +556,11 @@
"share_with_friends" = "Рассказать друзьям"; "share_with_friends" = "Рассказать друзьям";
"end_all_sessions" = "Сбросить все сессии";
"end_all_sessions_description" = "Если вы хотите выйти из $1 со всех устройств, нажмите на кнопку ниже";
"end_all_sessions_done" = "Все сессии сброшены, включая мобильные приложения";
/* Two-factor authentication */ /* Two-factor authentication */
"two_factor_authentication" = "Двухфакторная аутентификация"; "two_factor_authentication" = "Двухфакторная аутентификация";
@ -880,8 +887,13 @@
"banned_header" = "Вы были верискокнуты"; "banned_header" = "Вы были верискокнуты";
"banned_alt" = "Пользователь заблокирован."; "banned_alt" = "Пользователь заблокирован.";
"banned_1" = "Извините, <b>$1</b>, но вы были верискокнуты."; "banned_1" = "Извините, <b>$1</b>, но вы были верискокнуты.";
"banned_2" = "А причина этому проста: <b>$1</b>. К сожалению, на этот раз нам пришлось заблокировать вас навсегда."; "banned_2" = "А причина этому проста: <b>$1</b>.";
"banned_perm" = "К сожалению, на этот раз нам пришлось заблокировать вас навсегда";
"banned_until_time" = "На этот раз нам пришлось заблокировать вас до <b>$1</b>";
"banned_3" = "Вы всё ещё можете <a href=\"/support?act=new\">написать в службу поддержки</a>, если считаете что произошла ошибка или <a href=\"/logout?hash=$1\">выйти</a>."; "banned_3" = "Вы всё ещё можете <a href=\"/support?act=new\">написать в службу поддержки</a>, если считаете что произошла ошибка или <a href=\"/logout?hash=$1\">выйти</a>.";
"banned_unban_myself" = "Разморозить страницу";
"banned_unban_title" = "Ваш аккаунт разблокирован";
"banned_unban_description" = "Постарайтесь больше не нарушать правила.";
/* Registration confirm */ /* Registration confirm */
@ -1113,6 +1125,17 @@
"admin_privacy_warning" = "Будьте осторожны с этой информацией"; "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 (deprecated) */
"paginator_back" = "Назад"; "paginator_back" = "Назад";
@ -1181,3 +1204,12 @@
"blacklist" = "Чёрный список"; "blacklist" = "Чёрный список";
"user_blacklisted_you" = "Пользователь внёс Вас в чёрный список."; "user_blacklisted_you" = "Пользователь внёс Вас в чёрный список.";
/* Away */
"url_is_banned" = "Переход невозможен";
"url_is_banned_comment" = "Администрация <b>$1</b> не рекомендует переходить по этой ссылке.";
"url_is_banned_comment_r" = "Администрация <b>$1</b> не рекомендует переходить по этой ссылке.<br><br>Причина: <b>$2</b>";
"url_is_banned_default_reason" = "Ссылка, по которой вы попытались перейти, может вести на сайт, который был создан с целью обмана пользователей и получения за счёт этого прибыли.";
"url_is_banned_title" = "Ссылка на подозрительный сайт";
"url_is_banned_proceed" = "Перейти по ссылке";

View file

@ -11,17 +11,17 @@
/* Login */ /* Login */
"log_in" = "Вход"; "log_in" = "Вход";
"password" = "Пароль"; "password" = "Проходное слово";
"registration" = "Регистрация"; "registration" = "Регистрация";
"forgot_password" = "Забыли пароль?"; "forgot_password" = "Забыли проходное слово?";
"login_failed" = "Не удалось войти"; "login_failed" = "Не удалось войти";
"invalid_username_or_password" = "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>"; "invalid_username_or_password" = "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>";
"failed_to_register" = "Не удалось зарегистрироваться"; "failed_to_register" = "Не удалось зарегистрироваться";
"referral_link_invalid" = "Пригласительная ссылка недействительна."; "referral_link_invalid" = "Пригласительная ссылка недействительна.";
"registration_disabled" = "Регистрация отключена системным администратором."; "registration_disabled" = "Товарищ, регистрация отключена Имперской Канцелярией.";
"user_already_exists" = "Пользователь с таким email уже существует."; "user_already_exists" = "Гражданин с таким почтовым ящиком уже существует.";
"access_recovery" = "Восстановление доступа"; "access_recovery" = "Восстановление доступа";
"page_access_recovery" = "Восстановить доступ к странице"; "page_access_recovery" = "Восстановить доступ к странице";
@ -30,25 +30,25 @@
"reset_password" = "Сбросить пароль"; "reset_password" = "Сбросить пароль";
"2fa_code_2" = "Код двухфакторной аутентификации"; "2fa_code_2" = "Код двухфакторной аутентификации";
"password_successfully_reset" = "Ваш пароль был успешно сброшен."; "password_successfully_reset" = "Ваше проходное слово было успешно сброшено.";
"password_reset_email_sent" = "Если вы зарегистрированы, вы получите инструкции на email."; "password_reset_email_sent" = "Если вы зарегистрированы, вы получите инструкции на почтовый ящик.";
"password_reset_error" = "Непредвиденная ошибка при сбросе пароля."; "password_reset_error" = "Непредвиденная ошибка при сбросе проходного слова.";
"password_reset_rate_limit_error" = "Нельзя делать это так часто, извините."; "password_reset_rate_limit_error" = "Нельзя делать это так часто, извините.";
"registration_disabled_info" = "Регистрация отключена системным администратором. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте."; "registration_disabled_info" = "Товарищ, регистрация отключена Имперской Канцелярией. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте.";
"registration_closed" = "Регистрация закрыта."; "registration_closed" = "Регистрация закрыта.";
"invites_you_to" = "<strong>$1</strong> приглашает вас в $2"; "invites_you_to" = "<strong>$1</strong> приглашает вас в $2";
"register_meta_desc" = "Зарегистрируйтесь в $1 прямо сейчас!"; "register_meta_desc" = "Зарегистрируйтесь в $1 прямо сейчас!";
"register_referer_meta_title" = "$1 приглашает вас в $2!"; "register_referer_meta_title" = "$1 приглашает вас в $2!";
"register_referer_meta_desc" = "Присоединяйтесь к $1 и множеству других пользователей в $2!"; "register_referer_meta_desc" = "Присоединяйтесь к $1 и множеству других граждан в $2!";
"users" = "Пользователи"; "users" = "Граждане";
/* Profile information */ /* Profile information */
"select_language" = "Выбрать язык"; "select_language" = "Выбрать менталитет";
"edit" = "Изменить"; "edit" = "Корректировать";
"birth_date" = "День рождения"; "birth_date" = "День рождения";
"registration_date" = "Дата регистрации"; "registration_date" = "Дата регистрации";
"hometown" = "Родной город"; "hometown" = "Родной город";
@ -90,7 +90,7 @@
"relationship_2" = "Встречаюсь"; "relationship_2" = "Встречаюсь";
"relationship_3" = "Помолвлен"; "relationship_3" = "Помолвлен";
"relationship_4" = "Женат"; "relationship_4" = "Женат";
"relationship_5" = "В гражданском браке"; "relationship_5" = "Сожительствую";
"relationship_6" = "Влюблен"; "relationship_6" = "Влюблен";
"relationship_7" = "Всё сложно"; "relationship_7" = "Всё сложно";
"relationship_8" = "В активном поиске"; "relationship_8" = "В активном поиске";
@ -98,29 +98,29 @@
"politViews" = "Полит. взгляды"; "politViews" = "Полит. взгляды";
"politViews_0" = "Не выбраны"; "politViews_0" = "Не выбраны";
"politViews_1" = "Индифферентные"; "politViews_1" = "Не в партии";
"politViews_2" = "Коммунистические"; "politViews_2" = "Коммунистические";
"politViews_3" = "Социалистические"; "politViews_3" = "Социалистические";
"politViews_4" = "Умеренные"; "politViews_4" = "Центристские";
"politViews_5" = "Либеральные"; "politViews_5" = "Антисоветские";
"politViews_6" = "Консервативные"; "politViews_6" = "Консервативные";
"politViews_7" = "Монархические"; "politViews_7" = "Контрреволюционные";
"politViews_8" = "Ультраконсервативные"; "politViews_8" = "Ультраконсервативные";
"politViews_9" = "Либертарианские"; "politViews_9" = "Либертарианские";
"contact_information" = "Контактная информация"; "contact_information" = "Контактная информация";
"email" = "Почтовый ящик"; "email" = "Почтовый ящик";
"phone" = "Телефон"; "phone" = "Стационарный телефон";
"telegram" = "Telegram"; "telegram" = "Telegram";
"personal_website" = "Личная визитка"; "personal_website" = "Личная визитка";
"city" = "Город"; "city" = "Город";
"address" = "Адрес"; "address" = "Адрес";
"personal_information" = "Личная информация"; "personal_information" = "Личность";
"interests" = "Интересы"; "interests" = "Интересы";
"favorite_music" = "Любимые аудиозаписи"; "favorite_music" = "Любимые звукозаписи";
"favorite_films" = "Любимые киноленты"; "favorite_films" = "Любимые киноленты";
"favorite_shows" = "Любимые программы"; "favorite_shows" = "Любимые программы";
"favorite_books" = "Любимые книги"; "favorite_books" = "Любимые книги";
@ -189,8 +189,8 @@
"friends" = "Товарищи"; "friends" = "Товарищи";
"followers" = "Подписчики"; "followers" = "Подписчики";
"follower" = "Подписчик"; "follower" = "Подписчик";
"friends_add" = "Добавить в товарищи"; "friends_add" = "Взять в товарищи";
"friends_delete" = "Удалить из товарищей"; "friends_delete" = "Отвергнуть товарища";
"friends_reject" = "Порвать приглашение в товарищи"; "friends_reject" = "Порвать приглашение в товарищи";
"friends_accept" = "Прочитать приглашение в товарищи"; "friends_accept" = "Прочитать приглашение в товарищи";
"send_message" = "Отправить телеграмму"; "send_message" = "Отправить телеграмму";
@ -222,13 +222,13 @@
"subscribe" = "Подписаться"; "subscribe" = "Подписаться";
"unsubscribe" = "Отписаться"; "unsubscribe" = "Отписаться";
"subscriptions" = "Подписки"; "subscriptions" = "Подписки";
"join_community" = "Вступить в группу"; "join_community" = "Вступить в клуб";
"leave_community" = "Выйти из группы"; "leave_community" = "Выйти из клуба";
"min_6_community" = "Название должно быть не менее 6 символов"; "min_6_community" = "Название должно быть не менее 6 символов";
"participants" = "Участники"; "participants" = "Участники";
"groups" = "Группы"; "groups" = "Клубы";
"meetings" = "Встречи"; "meetings" = "Встречи";
"create_group" = "Создать группу"; "create_group" = "Создать клуб";
"group_managers" = "Руководство"; "group_managers" = "Руководство";
"group_type" = "Тип группы"; "group_type" = "Тип группы";
"group_type_open" = "Это открытая группа. В неё может вступить любой желающий."; "group_type_open" = "Это открытая группа. В неё может вступить любой желающий.";
@ -263,7 +263,7 @@
"group_dont_display_administrators_list" = "Ничего не отображать"; "group_dont_display_administrators_list" = "Ничего не отображать";
"group_changeowner_modal_title" = "Передача прав владельца"; "group_changeowner_modal_title" = "Передача прав владельца";
"group_changeowner_modal_text" = "Внимание! Вы передаёте права владельца пользователю $1. Это действие необратимо. После передави вы останетесь адмиинстратором, но сможете легко перестать им быть."; "group_changeowner_modal_text" = "Внимание! Вы передаёте права владельца пользователю $1. Это действие необратимо. После передачи вы останетесь адмиинстратором, но сможете легко перестать им быть.";
"group_owner_setted" = "Новый владелец ($1) успешно назначен в сообщество $2. Вам выданы права администратора в сообществе. Если Вы хотите вернуть роль владельца, обратитесь в <a href='/support?act=new'>техническую поддержку сайта</a>."; "group_owner_setted" = "Новый владелец ($1) успешно назначен в сообщество $2. Вам выданы права администратора в сообществе. Если Вы хотите вернуть роль владельца, обратитесь в <a href='/support?act=new'>техническую поддержку сайта</a>.";
"participants_zero" = "Ни одного участника"; "participants_zero" = "Ни одного участника";
@ -340,8 +340,8 @@
"menu_registration" = "Регистрация"; "menu_registration" = "Регистрация";
"menu_help" = "Справка"; "menu_help" = "Справка";
"menu_logout" = "Выйти"; "menu_logout" = "Эмигрировать";
"menu_support" = "Поддержка"; "menu_support" = "Справочная";
"header_home" = "главная"; "header_home" = "главная";
"header_groups" = "клубы"; "header_groups" = "клубы";
@ -646,12 +646,12 @@
"support_new_title" = "Введите тему вашего обращения"; "support_new_title" = "Введите тему вашего обращения";
"support_new_content" = "Опишите проблему или предложение"; "support_new_content" = "Опишите проблему или предложение";
"support_rate_good_answer" = "Это хороший ответ"; "support_rate_good_answer" = "Подарить конфеты";
"support_rate_bad_answer" = "Это плохой ответ"; "support_rate_bad_answer" = "Закатить скандал";
"support_good_answer_user" = "Вы оставили положительный отзыв."; "support_good_answer_user" = "Вы подарили конфеты сотруднику справочной.";
"support_bad_answer_user" = "Вы оставили негативный отзыв."; "support_bad_answer_user" = "Вы закатили скандал сотруднику справочной.";
"support_good_answer_agent" = "Гражданин оставил положительный отзыв"; "support_good_answer_agent" = "Гражданин подарил конфеты сотруднику справочной";
"support_bad_answer_agent" = "Гражданин оставил негативный отзыв"; "support_bad_answer_agent" = "Гражданин закатил скандал сотруднику справочной";
"support_rated_good" = "Вы оставили положительный отзыв об ответе."; "support_rated_good" = "Вы оставили положительный отзыв об ответе.";
"support_rated_bad" = "Вы оставили негативный отзыв об ответе."; "support_rated_bad" = "Вы оставили негативный отзыв об ответе.";
"wrong_parameters" = "Неверные параметры запроса."; "wrong_parameters" = "Неверные параметры запроса.";
@ -683,7 +683,7 @@
"banned_alt" = "Гражданин был отправлен в тюрьму."; "banned_alt" = "Гражданин был отправлен в тюрьму.";
"banned_1" = "Извините, <b>$1</b>, но вы были отправлены в тюрьму."; "banned_1" = "Извините, <b>$1</b>, но вы были отправлены в тюрьму.";
"banned_2" = "А причина этому проста: <b>$1</b>. Органу в этот раз пришлось отправить вас под стражу навсегда."; "banned_2" = "А причина этому проста: <b>$1</b>. Органу в этот раз пришлось отправить вас под стражу навсегда.";
"banned_3" = "Вы всё ещё можете <a href=\"/support?act=new\">написать в службу поддержки</a>, если считаете что произошла ошибка или <a href=\"/logout?hash=$1\">выйти</a>."; "banned_3" = "Вы всё ещё можете <a href=\"/support?act=new\">написать в Справочную</a>, если считаете что произошла ошибка или <a href=\"/logout?hash=$1\">эмигрировать</a>.";
/* Discussions */ /* Discussions */
@ -822,15 +822,15 @@
"about_users_many" = "<b>$1</b> гражданинов"; "about_users_many" = "<b>$1</b> гражданинов";
"about_users_other" = "<b>$1</b> гражданинов"; "about_users_other" = "<b>$1</b> гражданинов";
"about_online_users_one" = "<b>1</b> пользователь в сети"; "about_online_users_one" = "<b>1</b> гражданин в сети";
"about_online_users_few" = "<b>$1</b> пользователя в сети"; "about_online_users_few" = "<b>$1</b> гражданина в сети";
"about_online_users_many" = "<b>$1</b> гражданинов в сети"; "about_online_users_many" = "<b>$1</b> граждан в сети";
"about_online_users_other" = "<b>$1</b> гражданинов в сети"; "about_online_users_other" = "<b>$1</b> граждан в сети";
"about_active_users_one" = "<b>1</b> активный пользователь"; "about_active_users_one" = "<b>1</b> активный гражданин";
"about_active_users_few" = "<b>$1</b> активных пользователя"; "about_active_users_few" = "<b>$1</b> активных граждан";
"about_active_users_many" = "<b>$1</b> активных гражданинов"; "about_active_users_many" = "<b>$1</b> активных граждан";
"about_active_users_other" = "<b>$1</b> активных гражданинов"; "about_active_users_other" = "<b>$1</b> активных граждан";
"about_groups_one" = "<b>1</b> клуб"; "about_groups_one" = "<b>1</b> клуб";
"about_groups_few" = "<b>$1</b> клубы"; "about_groups_few" = "<b>$1</b> клубы";
@ -860,7 +860,6 @@
"user_alert_scam" = "Органу управления было дозволено, что данный гражданин обманывает товарищей на денежные средства. Будьте осторожны при разговоре с ним."; "user_alert_scam" = "Органу управления было дозволено, что данный гражданин обманывает товарищей на денежные средства. Будьте осторожны при разговоре с ним.";
"ec_header" = "Подтверждение регистрации прописки"; "ec_header" = "Подтверждение регистрации прописки";
"ec_title" = "Спасибо!"; "ec_title" = "Спасибо!";
"ec_1" = "<b>$1</b>, на ваш почтовый ящик должно придти письмо с подтверждением регистрации."; "ec_1" = "<b>$1</b>, на ваш почтовый ящик должно придти письмо с подтверждением регистрации.";
@ -873,3 +872,16 @@
"email_rate_limit_error" = "Нельзя делать это так часто, извините."; "email_rate_limit_error" = "Нельзя делать это так часто, извините.";
"email_verify_success" = "Ваша регистрация была подтверждена. Приятного времяпрепровождения!"; "email_verify_success" = "Ваша регистрация была подтверждена. Приятного времяпрепровождения!";
"you_still_have_x_points" = "У Вас <b>$1</b> неиспользованных совестких рублей.";
"top_up_your_account" = "Пополнить баланс";
"transfer_trough_ton" = "Пополнить с помощью ТОН";
"transfer_ton_contents" = "Вы можете пополнить ваш баланс с помощью криптовалюты ТОН. Достаточно отсканировать КуАр-код приложением Tonkeeper, или вручную отправить ТОН по реквизитам. В течении нескольких минут вам придут определенное количество рублей.";
"transfer_ton_address" = "<b>Адрес кошелька:</b> $1<br/><b>Содержание телеграммы:</b> $2";
"transfer_ton_currency_per_ton" = "$1 TON";
"about_links" = "Ссылки";
"instance_links" = "Ссылки страны:";
"my_apps" = "Досуг и отдых";

View file

@ -1,5 +1,3 @@
#include <ru>
"__locale" = "uk_UA.utf8;Ukr"; "__locale" = "uk_UA.utf8;Ukr";
"__WinEncoding" = "Windows-1251"; "__WinEncoding" = "Windows-1251";
@ -453,6 +451,7 @@
"avatar" = "Аватар"; "avatar" = "Аватар";
"privacy" = "Приватність"; "privacy" = "Приватність";
"interface" = "Зовнішній вид"; "interface" = "Зовнішній вид";
"security" = "Безпека";
"profile_picture" = "Зображення сторінки"; "profile_picture" = "Зображення сторінки";
@ -527,6 +526,7 @@
"ui_settings_view_of_posts_old" = "Старий"; "ui_settings_view_of_posts_old" = "Старий";
"ui_settings_view_of_posts_microblog" = "Мікроблог"; "ui_settings_view_of_posts_microblog" = "Мікроблог";
"ui_settings_main_page" = "Головна сторінка"; "ui_settings_main_page" = "Головна сторінка";
"ui_settings_sessions" = "Сеанси";
"additional_links" = "Додаткові посилання"; "additional_links" = "Додаткові посилання";
"ad_poster" = "Рекламний плакат"; "ad_poster" = "Рекламний плакат";
@ -555,6 +555,11 @@
"share_with_friends" = "Розповісти друзям"; "share_with_friends" = "Розповісти друзям";
"end_all_sessions" = "Завершити всі сеанси";
"end_all_sessions_description" = "Якщо ви хочете вийти з $1 на всіх пристроях, натисніть кнопку нижче";
"end_all_sessions_done" = "<b>Усі</b> сеанси було завершено";
/* Two-factor authentication */ /* Two-factor authentication */
"two_factor_authentication" = "Двофакторна автентифікація"; "two_factor_authentication" = "Двофакторна автентифікація";
@ -827,12 +832,12 @@
"support_opened" = "Відкриті"; "support_opened" = "Відкриті";
"support_answered" = "З відповіддю"; "support_answered" = "З відповіддю";
"support_closed" = "Закриті"; "support_closed" = "Зачинені";
"support_ticket" = "Звернення"; "support_ticket" = "Звернення";
"support_tickets" = "Звернення"; "support_tickets" = "Звернення";
"support_status_0" = "Питання на розгляді"; "support_status_0" = "Питання на розгляді";
"support_status_1" = "Є відповідь"; "support_status_1" = "Є відповідь";
"support_status_2" = "Закрито"; "support_status_2" = "Зачинено";
"support_greeting_hi" = "Вітаємо, $1!"; "support_greeting_hi" = "Вітаємо, $1!";
"support_greeting_regards" = "З повагою,<br/>команда підтримки $1."; "support_greeting_regards" = "З повагою,<br/>команда підтримки $1.";
@ -880,9 +885,14 @@
"banned_title" = "Обліковий запис заблоковано"; "banned_title" = "Обліковий запис заблоковано";
"banned_header" = "Ваш обліковий запис заблоковано."; "banned_header" = "Ваш обліковий запис заблоковано.";
"banned_alt" = "Користувача заблоковано."; "banned_alt" = "Користувача заблоковано.";
"banned_1" = "<b>$1</b>, ваш акаунт заблоковано за порушення правил користування сайту."; "banned_1" = "<b>$1</b>, ваш обліковий запис заблоковано за порушення правил користування сайту.";
"banned_2" = "Підстава: <b>$1</b>. На цей раз, нам довелося заблокувати вас назавжди."; "banned_2" = "Привід: <b>$1</b>.";
"banned_perm" = "На цей раз, Ви заблоковані назавжди.";
"banned_until_time" = "На цей раз, Ви заблоковані до <b>$1</b>";
"banned_3" = "Ви все ще можете <a href=\"/support?act=new\">написати в службу підтримки</a>, якщо вважаєте, що сталася помилка або <a href=\"/logout?hash= $1\">вийти</a>."; "banned_3" = "Ви все ще можете <a href=\"/support?act=new\">написати в службу підтримки</a>, якщо вважаєте, що сталася помилка або <a href=\"/logout?hash= $1\">вийти</a>.";
"banned_unban_myself" = "Розблокувати сторінку";
"banned_unban_title" = "Ваш обліковий запис розблокований";
"banned_unban_description" = "Намагайтеся, більше не порушувати правила.";
/* Registration confirm */ /* Registration confirm */

View file

@ -57,6 +57,9 @@ openvk:
processingLimit: 3000 processingLimit: 3000
emojiProcessingLimit: 1000 emojiProcessingLimit: 1000
commerce: false commerce: false
susLinks:
warnings: true
showReason: true
ton: ton:
enabled: false enabled: false
address: "🅿" address: "🅿"

View file

@ -231,20 +231,6 @@ input[type=checkbox] {
border-bottom: none; border-bottom: none;
} }
.floating_sidebar { .floating_sidebar,.floating_sidebar.show {
position: fixed; display:none
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;
} }