Merge branch 'master' into similartovk

This commit is contained in:
KosFurler 2022-02-19 01:32:46 +03:00 committed by GitHub
commit a9eb0ab31b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1188 additions and 172 deletions

204
Email/verify-email.eml.latte Executable file
View file

@ -0,0 +1,204 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Подтверждение Email</title>
<link rel="stylesheet" href="foundation.css" />
<style>
.container {
border-top: 5px solid pink;
}
</style>
</head>
<body>
<table class="body" data-made-with-foundation="">
<tr>
<td class="float-center" align="center" valign="top">
<center>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<table class="container">
<tr>
<td>
<table class="row header">
<tr>
<th class="small-12 large-12 columns first last">
<table>
<tr>
<th>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<h4 class="text-center">Подтверждение Email</h4>
</th>
</tr>
</table>
</th>
</tr>
</table>
<table class="row">
<tr>
<th class="small-12 large-12 columns first last">
<table class="row">
<tr>
<td>
<center>
<img src="pictures/lock.jpeg" align="center" class="float-center" width=128 height=128 />
</center>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<hr/>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<p class="text-left">
Здравствуйте, {$name}! Вы вероятно зарегистрировались на одном из инстансов OpenVK. Чтобы ваш аккаунт активировался, необходимо подтвердить Email.
</p>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<table class="button large expand success">
<tr>
<td>
<table>
<tr>
<td>
<center>
<a href="http://{$_SERVER['HTTP_HOST']}/regFinish?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a>
</center>
</td>
</tr>
</table>
</td>
</tr>
</table>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<p class="text-left">
Если кнопка не работает, вы можете попробовать скопировать и вставить эту ссылку в адресную строку вашего веб-обозревателя:
</p>
<table class="callout">
<tr>
<th class="callout-inner primary">
<a href="http://{$_SERVER['HTTP_HOST']}/regFinish?key={$key}" style="color: #000; text-decoration: none;">
http://{$_SERVER['HTTP_HOST']}/regFinish?key={$key}
</a>
</th>
</tr>
</table>
<p class="text-left">
Обратите внимание на то, что эту ссылку нельзя:
</p>
<ul>
<li>Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)</li>
<li>Использовать, если прошло более двух дней с её генерации</li>
</ul>
<table class="callout">
<tr>
<th class="callout-inner alert">
<p>
Ещё раз <b>обратите внимание</b> на то, что данную ссылку или письмо <b>ни в коем случае нельзя</b> передавать другим людям! Даже если они представляются службой поддержки.<br/>
Это письмо предназначено исключительно для одноразового, <b>непосредственного</b> использования владельцем аккаунта.
</p>
</th>
</tr>
</table>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<p class="text-right">
С уважением, овк-тян.
</p>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<hr/>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
<p class="text-left">
<small>
Вы получили это письмо так как кто-то или вы зарегистрировались на инстансе OpenVK. Это не рассылка и от неё нельзя отписаться. Если вы всё равно хотите перестать получать подобные письма, деактивируйте ваш аккаунт.
</small>
</p>
</td>
</tr>
</table>
</th>
</tr>
</table>
</td>
</tr>
</table>
<table class="spacer">
<tr>
<td>
&nbsp;
</td>
</tr>
</table>
</center>
</td>
</tr>
</table>
</body>
</html>

View file

@ -18,7 +18,6 @@ Updating the source code is done with this command: `git pull`
* **[openvk.su](https://openvk.su/)** * **[openvk.su](https://openvk.su/)**
* [social.fetbuk.ru](http://social.fetbuk.ru/) * [social.fetbuk.ru](http://social.fetbuk.ru/)
* [openvk.zavsc.pw](https://openvk.zavsc.pw/)
## Can I create my own OpenVK instance? ## Can I create my own OpenVK instance?
@ -48,7 +47,7 @@ ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/ext
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
``` ```
4. Import `install/init-static-db.sql` to **same database** you installed Chandler to 4. Import `install/init-static-db.sql` to **same database** you installed Chandler to and import all sqls from `install/sqls` to **same database**
5. Import `install/init-event-db.sql` to **separate database** 5. Import `install/init-event-db.sql` to **separate database**
6. Copy `openvk-example.yml` to `openvk.yml` and change options 6. Copy `openvk-example.yml` to `openvk.yml` and change options
7. Run `composer install` in OpenVK directory 7. Run `composer install` in OpenVK directory
@ -75,11 +74,12 @@ You may reach out to us via:
* [Bug-tracker](https://github.com/openvk/openvk/projects/1) * [Bug-tracker](https://github.com/openvk/openvk/projects/1)
* [Ticketing system](https://openvk.su/support?act=new) * [Ticketing system](https://openvk.su/support?act=new)
* Telegram chat: Go to [our channel](https://t.me/openvkch) and open discussion in our channel menu. * Telegram chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu.
* [Reddit](https://www.reddit.com/r/openvk/) * [Reddit](https://www.reddit.com/r/openvk/)
* [Discussions](https://github.com/openvk/openvk/discussions) * [Discussions](https://github.com/openvk/openvk/discussions)
* Matrix chat: #openvk:matrix.org
**Attention**: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com** **Attention**: bug tracker, telegram and matrix chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com**
<a href="https://codeberg.org/OpenVK/openvk"> <a href="https://codeberg.org/OpenVK/openvk">
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60"> <img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">

View file

@ -18,7 +18,6 @@ _[English](README.md)_
* **[openvk.su](https://openvk.su/)** * **[openvk.su](https://openvk.su/)**
* [social.fetbuk.ru](http://social.fetbuk.ru/) * [social.fetbuk.ru](http://social.fetbuk.ru/)
* [openvk.zavsc.pw](https://openvk.zavsc.pw/)
## Могу ли я создать свою собственную инстанцию OpenVK? ## Могу ли я создать свою собственную инстанцию OpenVK?
@ -48,7 +47,7 @@ ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/ext
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
``` ```
4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler 4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler, и импортируйте все SQL файлы из папки `install/sqls` в **ту же базу данных**
5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных** 5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных**
6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры 6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры
7. Запустите `composer install` в директории OpenVK 7. Запустите `composer install` в директории OpenVK
@ -78,8 +77,9 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvkch) и откройте обсуждение в меню нашего канала. * Telegram-чат: Перейдите на [наш канал](https://t.me/openvkch) и откройте обсуждение в меню нашего канала.
* [Reddit](https://www.reddit.com/r/openvk/) * [Reddit](https://www.reddit.com/r/openvk/)
* [Обсуждения](https://github.com/openvk/openvk/discussions) * [Обсуждения](https://github.com/openvk/openvk/discussions)
* Чат в Matrix: #ovk:matrix.org
**Внимание**: баг-трекер и телеграм-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [at] tutanota [dot] com**. **Внимание**: баг-трекер, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [at] tutanota [dot] com**.
<a href="https://codeberg.org/OpenVK/openvk"> <a href="https://codeberg.org/OpenVK/openvk">
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60"> <img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">

57
VKAPI/README.md Normal file
View file

@ -0,0 +1,57 @@
# VK API Compatability layer for OpenVK
This directory contains VK api handlers, structures and relared
exceptions. It is still a work-in-progress functionality.
**Note**: requests to api are routed through
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
## Implementing API methods
VK API methods have names like this: `example.test`. To implement a
method like this you will need to create a class `Example` in the
Handlers subdirectory. This class **must** extend VKAPIHandler and be
final.
Next step is to create test method. It **must** have a type hint that is
not void. Everything else is fine, the return value of method will be
authomatically converted to JSON and sent back to client.
### Parameters
Method arguments are parameters. To declare a parameter just create an
argument with the same name. You should also provide correct type hints
for them. Type conversion is done automatically if possible. If not
possible error №1 will be returned.
If parameter is not passed by client then router will pass default value
to argument. If there is no default value but argument accepts NULL then
NULL will be passed. If NULL is not acceptable, default value is
undefined and parameter is not passed, API will return missing parameter
error to client.
### Returning errors
To return an error, call fail method like this: `$this->fail(5,
"error")` (first argument is error code and second is error message).
You can also throw the exception manually: `throw new
APIErrorException("error", 5)` (class:
openvk.VKAPI.Exceptions.APIErrorException).
If you throw any exception that does not inherit APIErrorException then
API will return error №1 (unknown error) to client.
### Refering to user
To get user use `getUser` method: `$this->getUser()`. Keep in mind it
will return NULL if user is undefined (no access\_token passed or it is
invalid/expired or roaming authentification failed).
If you need to check whether user is defined use `userAuthorized`. This
method returns true if user is present and false if not.
If your method cant work without user context call `requireUser` and it
will automatically return unauthorized error.
### Working with data
You can use OpenVK models for that. However, **do not** return them
(either you will leak data or JSON conversion will fail). It is better
to create a response object and return it. It is also a good idea to
define a structure in Structures subdirectory.
Have a lot of fun <sup></sup>

View file

@ -1,31 +0,0 @@
h1. VK API Compatability layer for OpenVK
This directory contains VK api handlers, structures and relared exceptions. It is still a work-in-progress functionality.
**Note**: requests to api are routed through openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
h2. Implementing API methods
VK API methods have names like this: @example.test@. To implement a method like this you will need to create a class @Example@ in the Handlers subdirectory. This class **must** extend VKAPIHandler and be final.
Next step is to create %test% method. It **must** have a type hint that is not %void%. Everything else is fine, the return value of method will be authomatically converted to JSON and sent back to client.
h3. Parameters
Method arguments are parameters. To declare a parameter just create an argument with the same name. You should also provide correct type hints for them. Type conversion is done automatically if possible. If not possible error №1 will be returned.
If parameter is not passed by client then router will pass default value to argument. If there is no default value but argument accepts NULL then NULL will be passed. If NULL is not acceptable, default value is undefined and parameter is not passed, API will return missing parameter error to client.
h3. Returning errors
To return an error, call %fail% method like this: @$this->fail(5, "error")@ (first argument is error code and second is error message). You can also throw the exception manually: @throw new APIErrorException("error", 5)@ (class: openvk.VKAPI.Exceptions.APIErrorException).
If you throw any exception that does not inherit APIErrorException then API will return error №1 (unknown error) to client.
h3. Refering to user
To get user use @getUser@ method: @$this->getUser()@. Keep in mind it will return NULL if user is undefined (no access_token passed or it is invalid/expired or roaming authentification failed).
If you need to check whether user is defined use @userAuthorized@. This method returns true if user is present and false if not.
If your method can't work without user context call @requireUser@ and it will automatically return unauthorized error.
h3. Working with data
You can use OpenVK models for that. However, **do not** return them (either you will leak data or JSON conversion will fail). It is better to create a response object and return it. It is also a good idea to define a structure in Structures subdirectory.
Have a lot of fun ^^

View file

@ -40,7 +40,7 @@ class Club extends RowModel
function getAvatarUrl(): string function getAvatarUrl(): string
{ {
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"]; $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
$avPhoto = $this->getAvatarPhoto(); $avPhoto = $this->getAvatarPhoto();
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURL(); return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURL();
@ -64,16 +64,6 @@ class Club extends RowModel
else else
return "/club" . $this->getId(); return "/club" . $this->getId();
} }
/*
function getAvatarUrl(): string
{
$avAlbum = (new Albums)->getUserAvatarAlbum($this);
$avCount = $avAlbum->getPhotosCount();
$avPhotos = $avAlbum->getPhotos($avCount, 1);
$avPhoto = iterator_to_array($avPhotos)[0] ?? NULL;
return is_null($avPhoto) ? "/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURL();
} */
function getName(): string function getName(): string
{ {

View file

@ -0,0 +1,10 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
class EmailVerification extends PasswordReset
{
protected $tableName = "email_verifications";
}

View file

@ -1,6 +1,6 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications; namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Gift}; use openvk\Web\Models\Entities\User;
final class CoinsTransferNotification extends Notification final class CoinsTransferNotification extends Notification
{ {

View file

@ -0,0 +1,13 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\User;
final class RatingUpNotification extends Notification
{
protected $actionCode = 9603;
function __construct(User $receiver, User $sender, int $value, string $message)
{
parent::__construct($receiver, $receiver, $sender, time(), $value . " " . $message);
}
}

View file

@ -34,12 +34,15 @@ class PasswordReset extends RowModel
*/ */
function isNew(): bool function isNew(): bool
{ {
return $this->getRecord()->timestamp > (time() - 301); return $this->getRecord()->timestamp > (time() - (5 * MINUTE));
} }
/**
* Token is valid only for 3 days.
*/
function isStillValid(): bool function isStillValid(): bool
{ {
return $this->getRecord()->timestamp > (time() - 172801); return $this->getRecord()->timestamp > (time() - (3 * DAY));
} }
function verify(string $token): bool function verify(string $token): bool

View file

@ -64,7 +64,7 @@ trait TRichText
$text = preg_replace("%@([A-Za-z0-9]++) \(([\p{L} 0-9]+)\)%Xu", "[$1|$2]", $text); $text = preg_replace("%@([A-Za-z0-9]++) \(([\p{L} 0-9]+)\)%Xu", "[$1|$2]", $text);
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text); $text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
$text = preg_replace("%\[([A-Za-z0-9]++)\|([\p{L} 0-9@]+)\]%Xu", "<a href='/$1'>$2</a>", $text); $text = preg_replace("%\[([A-Za-z0-9]++)\|([\p{L} 0-9@]+)\]%Xu", "<a href='/$1'>$2</a>", $text);
$text = preg_replace("%(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "<a href='/feed/hashtag/$2'>$1</a>", $text); $text = preg_replace("%([\n\r\s]|^)(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "$1<a href='/feed/hashtag/$3'>$2</a>", $text);
$text = $this->formatEmojis($text); $text = $this->formatEmojis($text);
} }

View file

@ -5,6 +5,7 @@ use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel; use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift}; use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Gifts, Notifications}; use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Gifts, Notifications};
use openvk\Web\Models\Exceptions\InvalidUserNameException;
use Nette\Database\Table\ActiveRow; use Nette\Database\Table\ActiveRow;
use Chandler\Database\DatabaseConnection; use Chandler\Database\DatabaseConnection;
use Chandler\Security\User as ChandlerUser; use Chandler\Security\User as ChandlerUser;
@ -103,7 +104,7 @@ class User extends RowModel
function getAvatarUrl(): string function getAvatarUrl(): string
{ {
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"]; $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
if($this->getRecord()->deleted) if($this->getRecord()->deleted)
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png"; return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
@ -739,6 +740,27 @@ class User extends RowModel
return true; return true;
} }
function setFirst_Name(string $firstName): void
{
$firstName = mb_convert_case($firstName, MB_CASE_TITLE);
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?(?:[\p{L&}\p{Lo}]\p{Mn}?){1,16}$%u', $firstName))
throw new InvalidUserNameException;
$this->stateChanges("first_name", $firstName);
}
function setLast_Name(string $lastName): void
{
if(!empty($lastName))
{
$lastName = mb_convert_case($lastName, MB_CASE_TITLE);
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?([\p{L&}\p{Lo}]\p{Mn}?){1,16}(\-\g<1>+)?$%u', $lastName))
throw new InvalidUserNameException;
}
$this->stateChanges("last_name", $lastName);
}
function setNsfwTolerance(int $tolerance): void function setNsfwTolerance(int $tolerance): void
{ {
$this->stateChanges("nsfw_tolerance", $tolerance); $this->stateChanges("nsfw_tolerance", $tolerance);
@ -850,10 +872,10 @@ class User extends RowModel
function isDeleted(): bool function isDeleted(): bool
{ {
if ($this->getRecord()->deleted == 1) if ($this->getRecord()->deleted == 1)
return TRUE; return TRUE;
else else
return FALSE; return FALSE;
} }
/** /**
@ -882,6 +904,12 @@ class User extends RowModel
{ {
return $this->getRecord()->website; return $this->getRecord()->website;
} }
// ты устрица
function isActivated(): bool
{
return (bool) $this->getRecord()->activated;
}
use Traits\TSubscribable; use Traits\TSubscribable;
} }

View file

@ -0,0 +1,7 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Exceptions;
final class InvalidUserNameException extends \UnexpectedValueException
{
protected $message = "Invalid real name supplied";
}

View file

@ -37,7 +37,7 @@ class Notifications
if(!$count) { if(!$count) {
$query .= " ORDER BY timestamp DESC"; $query .= " ORDER BY timestamp DESC";
$query .= " LIMIT " . ($perPage ?? OPENVK_DEFAULT_PER_PAGE); $query .= " LIMIT " . ($perPage ?? OPENVK_DEFAULT_PER_PAGE);
$query .= " OFFSET " . ((($page - 1) * $perPage) ?? OPENVK_DEFAULT_PER_PAGE); $query .= " OFFSET " . (($page - 1) * ($perPage ?? OPENVK_DEFAULT_PER_PAGE));
} }
return $query; return $query;

View file

@ -39,7 +39,7 @@ class Users
function find(string $query): Util\EntityStream function find(string $query): Util\EntityStream
{ {
$query = "%$query%"; $query = "%$query%";
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name) LIKE ?", $query)->where("deleted", 0); $result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo) LIKE ?", $query)->where("deleted", 0);
return new Util\EntityStream("User", $result); return new Util\EntityStream("User", $result);
} }

View file

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Repositories;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Entities\EmailVerification;
use openvk\Web\Models\Entities\User;
use Nette\Database\Table\ActiveRow;
class Verifications
{
private $context;
private $verifications;
function __construct()
{
$this->context = DatabaseConnection::i()->getContext();
$this->verifications = $this->context->table("email_verifications");
}
function toEmailVerification(?ActiveRow $ar): ?EmailVerification
{
return is_null($ar) ? NULL : new EmailVerification($ar);
}
function getByToken(string $token): ?EmailVerification
{
return $this->toEmailVerification($this->verifications->where("key", $token)->fetch());
}
function getLatestByUser(User $user): ?EmailVerification
{
return $this->toEmailVerification($this->verifications->where("profile", $user->getId())->order("timestamp DESC")->fetch());
}
}

View file

@ -5,7 +5,7 @@ final class YouTubeVideoDriver extends VideoDriver
{ {
function getThumbnailURL(): string function getThumbnailURL(): string
{ {
return "https://img.youtube.com/vi/$this->id/mq3.jpg"; return "https://img.youtube.com/vi/$this->id/mqdefault.jpg";
} }
function getURL(): string function getURL(): string

View file

@ -8,6 +8,7 @@ use Chandler\Session\Session;
final class AboutPresenter extends OpenVKPresenter final class AboutPresenter extends OpenVKPresenter
{ {
protected $banTolerant = true; protected $banTolerant = true;
protected $activationTolerant = true;
function renderIndex(): void function renderIndex(): void
{ {

View file

@ -3,9 +3,11 @@ namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\IP; use openvk\Web\Models\Entities\IP;
use openvk\Web\Models\Entities\User; use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Entities\PasswordReset; use openvk\Web\Models\Entities\PasswordReset;
use openvk\Web\Models\Entities\EmailVerification;
use openvk\Web\Models\Repositories\IPs; use openvk\Web\Models\Repositories\IPs;
use openvk\Web\Models\Repositories\Users; use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\Repositories\Restores; use openvk\Web\Models\Repositories\Restores;
use openvk\Web\Models\Repositories\Verifications;
use openvk\Web\Util\Validator; use openvk\Web\Util\Validator;
use Chandler\Session\Session; use Chandler\Session\Session;
use Chandler\Security\User as ChandlerUser; use Chandler\Security\User as ChandlerUser;
@ -16,19 +18,22 @@ use lfkeitel\phptotp\{Base32, Totp};
final class AuthPresenter extends OpenVKPresenter final class AuthPresenter extends OpenVKPresenter
{ {
protected $banTolerant = true; protected $banTolerant = true;
protected $activationTolerant = true;
private $authenticator; private $authenticator;
private $db; private $db;
private $users; private $users;
private $restores; private $restores;
private $verifications;
function __construct(Users $users, Restores $restores) function __construct(Users $users, Restores $restores, Verifications $verifications)
{ {
$this->authenticator = Authenticator::i(); $this->authenticator = Authenticator::i();
$this->db = DatabaseConnection::i()->getContext(); $this->db = DatabaseConnection::i()->getContext();
$this->users = $users; $this->users = $users;
$this->restores = $restores; $this->restores = $restores;
$this->verifications = $verifications;
parent::__construct(); parent::__construct();
} }
@ -81,7 +86,7 @@ final class AuthPresenter extends OpenVKPresenter
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment")); $this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
if (strtotime($this->postParam("birthday")) > time()) if (strtotime($this->postParam("birthday")) > time())
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment")); $this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password")); $chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
if(!$chUser) if(!$chUser)
@ -96,12 +101,25 @@ final class AuthPresenter extends OpenVKPresenter
$user->setSince(date("Y-m-d H:i:s")); $user->setSince(date("Y-m-d H:i:s"));
$user->setRegistering_Ip(CONNECTING_IP); $user->setRegistering_Ip(CONNECTING_IP);
$user->setBirthday(strtotime($this->postParam("birthday"))); $user->setBirthday(strtotime($this->postParam("birthday")));
$user->setActivated((int) !OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']);
$user->save(); $user->save();
if(!is_null($referer)) { if(!is_null($referer)) {
$user->toggleSubscription($referer); $user->toggleSubscription($referer);
$referer->toggleSubscription($user); $referer->toggleSubscription($user);
} }
if (OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']) {
$verification = new EmailVerification;
$verification->setProfile($user->getId());
$verification->save();
$params = [
"key" => $verification->getKey(),
"name" => $user->getCanonicalName(),
];
$this->sendmail($user->getEmail(), "verify-email", $params); #Vulnerability possible
}
$this->authenticator->authenticate($chUser->getId()); $this->authenticator->authenticate($chUser->getId());
$this->redirect("/id" . $user->getId(), static::REDIRECT_TEMPORARY); $this->redirect("/id" . $user->getId(), static::REDIRECT_TEMPORARY);
@ -253,4 +271,48 @@ final class AuthPresenter extends OpenVKPresenter
$this->flashFail("succ", tr("information_-1"), tr("password_reset_email_sent")); $this->flashFail("succ", tr("information_-1"), tr("password_reset_email_sent"));
} }
} }
function renderResendEmail(): void
{
if(!is_null($this->user) && $this->user->identity->isActivated())
$this->redirect("/id" . $this->user->id, static::REDIRECT_TEMPORARY);
if($_SERVER["REQUEST_METHOD"] === "POST") {
$user = $this->user->identity;
if(!$user || $user->isDeleted() || $user->isActivated())
$this->flashFail("err", tr("error"), tr("email_error"));
$request = $this->verifications->getLatestByUser($user);
if(!is_null($request) && $request->isNew())
$this->flashFail("err", tr("forbidden"), tr("email_rate_limit_error"));
$verification = new EmailVerification;
$verification->setProfile($user->getId());
$verification->save();
$params = [
"key" => $verification->getKey(),
"name" => $user->getCanonicalName(),
];
$this->sendmail($user->getEmail(), "verify-email", $params); #Vulnerability possible
$this->flashFail("succ", tr("information_-1"), tr("email_sent"));
}
}
function renderVerifyEmail(): void
{
$request = $this->verifications->getByToken(str_replace(" ", "+", $this->queryParam("key")));
if(!$request || !$request->isStillValid()) {
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
$this->redirect("/");
} else {
$user = $request->getUser();
$user->setActivated(1);
$user->save();
$this->flash("success", tr("email_verify_success"));
$this->redirect("/");
}
}
} }

View file

@ -13,6 +13,7 @@ use WhichBrowser;
abstract class OpenVKPresenter extends SimplePresenter abstract class OpenVKPresenter extends SimplePresenter
{ {
protected $banTolerant = false; protected $banTolerant = false;
protected $activationTolerant = false;
protected $errorTemplate = "@error"; protected $errorTemplate = "@error";
protected $user = NULL; protected $user = NULL;
@ -212,6 +213,27 @@ abstract class OpenVKPresenter extends SimplePresenter
$this->template->userTainted = $user->isTainted(); $this->template->userTainted = $user->isTainted();
if($this->user->identity->isDeleted()) { if($this->user->identity->isDeleted()) {
/*
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⠶⠶⣶⠶⠶⠶⠶⠶⠶⠶⠶⠶⢶⠶⠶⠶⠤⠤⠤⠤⣄⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠊⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠒⠀⠀⠀⠀⠤⢤⣤⣄⠉⠉⠛⠛⠷⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣰⠟⠀⠀⠀⠀⠀⠐⠋⢑⣤⣶⣶⣤⡢⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣄⡂⠀⠀⠶⢄⠙⢷⣤⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣸⡿⠚⠉⡀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⢢⠀⠀⡀⣰⣿⣿⣿⣿⣦⡀⠀⠀⠡⡀⢹⡆⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢀⣴⠏⠀⣀⣀⣀⡤⢤⣄⣠⣿⣿⣿⣿⣻⣿⣿⣷⠀⢋⣾⠈⠙⣶⠒⢿⣿⣿⣿⣿⡿⠟⠃⠀⡀⠡⠼⣧⡀⠀⠀⠀⠀⠀⠀
⠀⠀⢀⣴⣿⢃⡴⢊⢽⣶⣤⣀⠀⠊⠉⠉⡛⢿⣿⣿⣿⠿⠋⢀⡀⠁⠀⠀⢸⣁⣀⣉⣉⣉⡉⠀⠩⡡⠀⣩⣦⠀⠈⠻⣦⡀⠀⠀⠀⠀
⠀⢠⡟⢡⠇⡞⢀⠆⠀⢻⣿⣿⣷⣄⠀⢀⠈⠂⠈⢁⡤⠚⡟⠉⠀⣀⣀⠀⠈⠳⣍⠓⢆⢀⡠⢀⣨⣴⣿⣿⡏⢀⡆⠀⢸⡇⠀⠀⠀⠀
⠀⣾⠁⢸⠀⠀⢸⠀⠀⠀⠹⣿⣿⣿⣿⣶⣬⣦⣤⡈⠀⠀⠇⠀⠛⠉⣩⣤⣤⣤⣿⣤⣤⣴⣾⣿⣿⣿⣿⣿⣧⠞⠀⠀⢸⡇⠀⠀⠀⠀
⠀⢹⣆⠸⠀⠀⢸⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣟⣛⠛⠛⣛⡛⠛⠛⣛⣋⡉⠉⣡⠶⢾⣿⣿⣿⣿⣿⣿⡇⠀⠀⢀⣾⠃⠀⠀⠀⠀
⠀⠀⠻⣆⡀⠀⠈⢂⠀⠀⠀⠠⡈⢻⣿⣿⣿⣿⡟⠁⠈⢧⡼⠉⠙⣆⡞⠁⠈⢹⣴⠃⠀⢸⣿⣿⣿⣿⣿⣿⠃⠀⡆⣾⠃⠀⠀⠀⠀⠀
⠀⠀⠀⠈⢻⣇⠀⠀⠀⠀⠀⠀⢡⠀⠹⣿⣿⣿⣷⡀⠀⣸⡇⠀⠀⣿⠁⠀⠀⠘⣿⠀⠀⠘⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠹⣇⠀⠠⠀⠀⠀⠀⠡⠐⢬⡻⣿⣿⣿⣿⣿⣷⣶⣶⣿⣦⣤⣤⣤⣿⣦⣶⣿⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠹⣧⡀⠡⡀⠀⠀⠀⠑⠄⠙⢎⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⢿⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠈⠳⣤⡐⡄⠀⠀⠀⠈⠂⠀⠱⣌⠻⣿⣿⣿⣿⣿⣿⣿⠿⣿⠟⢻⡏⢻⣿⣿⣿⣿⣿⣿⣿⠀⢸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢮⣦⡀⠂⠀⢀⠀⠀⠈⠳⣈⠻⣿⣿⣿⡇⠘⡄⢸⠀⠀⣇⠀⣻⣿⣿⣿⣿⣿⡏⠀⠸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢶⣤⣄⡑⠄⠀⠀⠈⠑⠢⠙⠻⢷⣶⣵⣞⣑⣒⣋⣉⣁⣻⣿⠿⠟⠱⠃⡸⠀⣧⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⣷⣄⡀⠐⠢⣄⣀⡀⠀⠉⠉⠉⠉⠛⠙⠭⠭⠄⠒⠈⠀⠐⠁⢀⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⢦⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣒⡠⠄⣠⡾⠃⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠷⠶⣦⣤⣭⣤⣬⣭⣭⣴⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀
*/
Authenticator::i()->logout(); Authenticator::i()->logout();
Session::i()->set("_su", NULL); Session::i()->set("_su", NULL);
$this->flashFail("err", tr("error"), tr("profile_not_found")); $this->flashFail("err", tr("error"), tr("profile_not_found"));
@ -227,6 +249,17 @@ abstract class OpenVKPresenter extends SimplePresenter
]); ]);
exit; exit;
} }
// ето для емейл уже надо (и по хорошему надо бы избавится от повторяющегося кода мда)
if(!$this->user->identity->isActivated() && !$this->activationTolerant) {
header("HTTP/1.1 403 Forbidden");
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@email.xml", [
"thisUser" => $this->user->identity,
"csrfToken" => $GLOBALS["csrfToken"],
"isTimezoned" => Session::i()->get("_timezoneOffset"),
]);
exit;
}
if ($this->user->identity->onlineStatus() == 0) { if ($this->user->identity->onlineStatus() == 0) {
$this->user->identity->setOnline(time()); $this->user->identity->setOnline(time());

View file

@ -9,8 +9,9 @@ use openvk\Web\Models\Repositories\Albums;
use openvk\Web\Models\Repositories\Videos; use openvk\Web\Models\Repositories\Videos;
use openvk\Web\Models\Repositories\Notes; use openvk\Web\Models\Repositories\Notes;
use openvk\Web\Models\Repositories\Vouchers; use openvk\Web\Models\Repositories\Vouchers;
use openvk\Web\Models\Exceptions\InvalidUserNameException;
use openvk\Web\Util\Validator; use openvk\Web\Util\Validator;
use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification; use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
use Chandler\Security\Authenticator; use Chandler\Security\Authenticator;
use lfkeitel\phptotp\{Base32, Totp}; use lfkeitel\phptotp\{Base32, Totp};
use chillerlan\QRCode\{QRCode, QROptions}; use chillerlan\QRCode\{QRCode, QROptions};
@ -137,10 +138,18 @@ final class UserPresenter extends OpenVKPresenter
$this->willExecuteWriteAction($_GET['act'] === "status"); $this->willExecuteWriteAction($_GET['act'] === "status");
if($_GET['act'] === "main" || $_GET['act'] == NULL) { if($_GET['act'] === "main" || $_GET['act'] == NULL) {
$user->setFirst_Name(empty($this->postParam("first_name")) ? $user->getFirstName() : $this->postParam("first_name")); try {
$user->setLast_Name(empty($this->postParam("last_name")) ? "" : $this->postParam("last_name")); $user->setFirst_Name(empty($this->postParam("first_name")) ? $user->getFirstName() : $this->postParam("first_name"));
$user->setLast_Name(empty($this->postParam("last_name")) ? "" : $this->postParam("last_name"));
} catch(InvalidUserNameException $ex) {
$this->flashFail("err", tr("error"), tr("invalid_real_name"));
}
$user->setPseudo(empty($this->postParam("pseudo")) ? NULL : $this->postParam("pseudo")); $user->setPseudo(empty($this->postParam("pseudo")) ? NULL : $this->postParam("pseudo"));
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status")); $user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
$user->setHometown(empty($this->postParam("hometown")) ? NULL : $this->postParam("hometown"));
if (strtotime($this->postParam("birthday")) < time()) if (strtotime($this->postParam("birthday")) < time())
$user->setBirthday(strtotime($this->postParam("birthday"))); $user->setBirthday(strtotime($this->postParam("birthday")));
@ -179,7 +188,7 @@ final class UserPresenter extends OpenVKPresenter
$user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city")); $user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city"));
$user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address")); $user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address"));
$website = $this->postParam("website") ?? ""; $website = $this->postParam("website") ?? "";
if(empty($website)) if(empty($website))
$user->setWebsite(NULL); $user->setWebsite(NULL);
@ -510,4 +519,44 @@ final class UserPresenter extends OpenVKPresenter
$this->flashFail("succ", tr("information_-1"), tr("points_transfer_successful", tr("points_amount", $value), $receiver->getURL(), htmlentities($receiver->getCanonicalName()))); $this->flashFail("succ", tr("information_-1"), tr("points_transfer_successful", tr("points_amount", $value), $receiver->getURL(), htmlentities($receiver->getCanonicalName())));
} }
function renderIncreaseRating(): void
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
$this->flashFail("err", tr("error"), tr("feature_disabled"));
$receiverAddress = $this->postParam("receiver");
$value = (int) $this->postParam("value");
$message = $this->postParam("message");
if(!$receiverAddress || !$value)
$this->flashFail("err", tr("failed_to_increase_rating"), tr("not_all_information_has_been_entered"));
if($value < 0)
$this->flashFail("err", tr("failed_to_increase_rating"), tr("negative_rating_value"));
if(iconv_strlen($message) > 255)
$this->flashFail("err", tr("failed_to_increase_rating"), tr("message_is_too_long"));
$receiver = $this->users->getByAddress($receiverAddress);
if(!$receiver)
$this->flashFail("err", tr("failed_to_increase_rating"), tr("receiver_not_found"));
if($this->user->identity->getCoins() < $value)
$this->flashFail("err", tr("failed_to_increase_rating"), tr("you_dont_have_enough_points"));
$this->user->identity->setCoins($this->user->identity->getCoins() - $value);
$this->user->identity->save();
$receiver->setRating($receiver->getRating() + $value);
$receiver->save();
if($this->user->id !== $receiver->getId())
(new RatingUpNotification($receiver, $this->user->identity, $value, $message))->emit();
$this->flashFail("succ", tr("information_-1"), tr("rating_increase_successful", $receiver->getURL(), htmlentities($receiver->getCanonicalName()), $value));
}
} }

View file

@ -5,6 +5,9 @@ use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotifi
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums}; use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
use Chandler\Database\DatabaseConnection; use Chandler\Database\DatabaseConnection;
use Nette\InvalidStateException as ISE; use Nette\InvalidStateException as ISE;
use Bhaktaraz\RSSGenerator\Item;
use Bhaktaraz\RSSGenerator\Feed;
use Bhaktaraz\RSSGenerator\Channel;
final class WallPresenter extends OpenVKPresenter final class WallPresenter extends OpenVKPresenter
{ {
@ -42,7 +45,7 @@ final class WallPresenter extends OpenVKPresenter
function renderWall(int $user, bool $embedded = false): void function renderWall(int $user, bool $embedded = false): void
{ {
if(false) if(false)
exit("Ошибка доступа: " . (string) random_int(0, 255)); exit(tr("forbidden") . ": " . (string) random_int(0, 255));
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user)); $owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
if(is_null($this->user)) { if(is_null($this->user)) {
@ -51,15 +54,15 @@ final class WallPresenter extends OpenVKPresenter
if(!$owner->isBanned()) if(!$owner->isBanned())
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity); $canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
else else
$this->flashFail("err", tr("error"), "Ошибка доступа"); $this->flashFail("err", tr("error"), tr("forbidden"));
} else if($user < 0) { } else if($user < 0) {
if($owner->canBeModifiedBy($this->user->identity)) if($owner->canBeModifiedBy($this->user->identity))
$canPost = true; $canPost = true;
else else
$canPost = $owner->canPost(); $canPost = $owner->canPost();
} else { } else {
$canPost = false; $canPost = false;
} }
if ($embedded == true) $this->template->_template = "components/wall.xml"; if ($embedded == true) $this->template->_template = "components/wall.xml";
$this->template->oObj = $owner; $this->template->oObj = $owner;
@ -82,6 +85,49 @@ final class WallPresenter extends OpenVKPresenter
{ {
$this->renderWall($user, true); $this->renderWall($user, true);
} }
function renderRSS(int $user): void
{
if(false)
exit(tr("forbidden") . ": " . (string) random_int(0, 255));
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
if(is_null($this->user)) {
$canPost = false;
} else if($user > 0) {
if(!$owner->isBanned())
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
else
$this->flashFail("err", tr("error"), tr("forbidden"));
} else if($user < 0) {
if($owner->canBeModifiedBy($this->user->identity))
$canPost = true;
else
$canPost = $owner->canPost();
} else {
$canPost = false;
}
$posts = iterator_to_array($this->posts->getPostsFromUsersWall($user));
$feed = new Feed();
$channel = new Channel();
$channel->title(OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["SERVER_NAME"])->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()}")
->pubDate($post->getPublicationTime()->timestamp())
->appendTo($channel);
}
header("Content-Type: application/rss+xml");
exit($feed);
}
function renderFeed(): void function renderFeed(): void
{ {
@ -167,12 +213,12 @@ final class WallPresenter extends OpenVKPresenter
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1)) $wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
?? $this->flashFail("err", "Не удалось опубликовать пост", "Такого пользователя не существует."); ?? $this->flashFail("err", tr("failed_to_publish_post"), tr("error_4"));
if($wall > 0) { if($wall > 0) {
if(!$wallOwner->isBanned()) if(!$wallOwner->isBanned())
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity); $canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
else else
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену."); $this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
} else if($wall < 0) { } else if($wall < 0) {
if($wallOwner->canBeModifiedBy($this->user->identity)) if($wallOwner->canBeModifiedBy($this->user->identity))
$canPost = true; $canPost = true;
@ -183,7 +229,7 @@ final class WallPresenter extends OpenVKPresenter
} }
if(!$canPost) if(!$canPost)
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену."); $this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]; $anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) { if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
@ -217,13 +263,13 @@ final class WallPresenter extends OpenVKPresenter
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon); $video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
} }
} catch(\DomainException $ex) { } catch(\DomainException $ex) {
$this->flashFail("err", "Не удалось опубликовать пост", "Файл медиаконтента повреждён."); $this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
} catch(ISE $ex) { } catch(ISE $ex) {
$this->flashFail("err", "Не удалось опубликовать пост", "Файл медиаконтента повреждён или слишком велик."); $this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted_or_too_large"));
} }
if(empty($this->postParam("text")) && !$photo && !$video) if(empty($this->postParam("text")) && !$photo && !$video)
$this->flashFail("err", "Не удалось опубликовать пост", "Пост пустой или слишком большой."); $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
try { try {
$post = new Post; $post = new Post;
@ -236,7 +282,7 @@ final class WallPresenter extends OpenVKPresenter
$post->setNsfw($this->postParam("nsfw") === "on"); $post->setNsfw($this->postParam("nsfw") === "on");
$post->save(); $post->save();
} catch (\LengthException $ex) { } catch (\LengthException $ex) {
$this->flashFail("err", "Не удалось опубликовать пост", "Пост слишком большой."); $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
} }
if(!is_null($photo)) if(!is_null($photo))
@ -270,7 +316,7 @@ final class WallPresenter extends OpenVKPresenter
$this->template->wallOwner = (new Users)->get($post->getTargetWall()); $this->template->wallOwner = (new Users)->get($post->getTargetWall());
$this->template->isWallOfGroup = false; $this->template->isWallOfGroup = false;
if($this->template->wallOwner->isBanned()) if($this->template->wallOwner->isBanned())
$this->flashFail("err", tr("error"), "Ошибка доступа"); $this->flashFail("err", tr("error"), tr("forbidden"));
} else { } else {
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall())); $this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
$this->template->isWallOfGroup = true; $this->template->isWallOfGroup = true;
@ -334,7 +380,7 @@ final class WallPresenter extends OpenVKPresenter
$user = $this->user->id; $user = $this->user->id;
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1)) $wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
?? $this->flashFail("err", "Не удалось удалить пост", "Такого пользователя не существует."); ?? $this->flashFail("err", tr("failed_to_delete_post"), tr("error_4"));
if($wall < 0) $canBeDeletedByOtherUser = $wallOwner->canBeModifiedBy($this->user->identity); if($wall < 0) $canBeDeletedByOtherUser = $wallOwner->canBeModifiedBy($this->user->identity);
else $canBeDeletedByOtherUser = false; else $canBeDeletedByOtherUser = false;
@ -345,7 +391,7 @@ final class WallPresenter extends OpenVKPresenter
$post->delete(); $post->delete();
} }
} else { } else {
$this->flashFail("err", "Не удалось удалить пост", "Вы не вошли в аккаунт."); $this->flashFail("err", tr("failed_to_delete_post"), tr("login_required_error_comment"));
} }
$this->redirect($wall < 0 ? "/club".($wall*-1) : "/id".$wall, static::REDIRECT_TEMPORARY); $this->redirect($wall < 0 ? "/club".($wall*-1) : "/id".$wall, static::REDIRECT_TEMPORARY);
@ -362,7 +408,7 @@ final class WallPresenter extends OpenVKPresenter
$this->notFound(); $this->notFound();
if(!$post->canBePinnedBy($this->user->identity)) if(!$post->canBePinnedBy($this->user->identity))
$this->flashFail("err", "Ошибка доступа", "Вам нельзя закреплять этот пост."); $this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
if(($this->queryParam("act") ?? "pin") === "pin") { if(($this->queryParam("act") ?? "pin") === "pin") {
$post->pin(); $post->pin();
@ -371,6 +417,6 @@ final class WallPresenter extends OpenVKPresenter
} }
// TODO localize message based on language and ?act=(un)pin // TODO localize message based on language and ?act=(un)pin
$this->flashFail("succ", "Операция успешна", "Операция успешна."); $this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
} }
} }

View file

@ -0,0 +1,19 @@
{extends "@layout.xml"}
{block title}{_ec_header}{/block}
{block header}
{_ec_header}
{/block}
{block content}
<div class="border-block center">
<h4>{_ec_title}</h4>
<p>{tr("ec_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/></p>
<p>{_ec_2}</p>
<p>
<form action="/reg/resend" method="post">
<input type="submit" class="button" value="{_ec_resend}">
</form>
</p>
</div>
{/block}

View file

@ -100,7 +100,7 @@
<div id="xhead" class="dm"></div> <div id="xhead" class="dm"></div>
<div class="page_header {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}page_custom_header{/if}"> <div class="page_header {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}page_custom_header{/if}">
<a href="/" class="home_button {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}home_button_custom{/if}" title="{$instance_name}">{$instance_name}</a> <a href="/" class="home_button {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}home_button_custom{/if}" title="{$instance_name}">{$instance_name}</a>
<div n:if="isset($thisUser) ? !$thisUser->isBanned() : true" class="header_navigation"> <div n:if="isset($thisUser) ? (!$thisUser->isBanned() XOR !$thisUser->isActivated()) : true" class="header_navigation">
{ifset $thisUser} {ifset $thisUser}
<div class="link"> <div class="link">
<a href="/">{_header_home}</a> <a href="/">{_header_home}</a>
@ -142,7 +142,7 @@
<div class="sidebar"> <div class="sidebar">
<div class="navigation"> <div class="navigation">
{ifset $thisUser} {ifset $thisUser}
{if !$thisUser->isBanned()} {if !$thisUser->isBanned() XOR !$thisUser->isActivated()}
<a href="/edit" class="link edit-button">{_edit_button}</a> <a href="/edit" class="link edit-button">{_edit_button}</a>
<a href="{$thisUser->getURL()}" class="link">{_my_page}</a> <a href="{$thisUser->getURL()}" class="link">{_my_page}</a>
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends} <a href="/friends{$thisUser->getId()}" class="link">{_my_friends}
@ -189,6 +189,8 @@
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable'] && $thisUser->getLeftMenuItemStatus('poster')" href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" > <a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable'] && $thisUser->getLeftMenuItemStatus('poster')" href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" >
<img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" /> <img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" />
</a> </a>
{elseif !$thisUser->isActivated()}
<a href="/logout?hash={urlencode($csrfToken)}" class="link">{_menu_logout}</a>
{else} {else}
<a href="/support" class="link">{_menu_support} <a href="/support" class="link">{_menu_support}
{if $ticketAnsweredCount > 0} {if $ticketAnsweredCount > 0}
@ -260,7 +262,7 @@
<a href="/privacy" class="link">{_footer_privacy}</a> <a href="/privacy" class="link">{_footer_privacy}</a>
</div> </div>
<p>OpenVK <a href="/about:openvk">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p> <p>OpenVK <a href="/about:openvk">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p>
<p n:ifcontent="ifcontent"> <p n:ifcontent>
{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]} {php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}
</p> </p>
</div> </div>

View file

@ -80,6 +80,14 @@
<input type="text" name="status" value="{$user->getStatus()}" /> <input type="text" name="status" value="{$user->getStatus()}" />
</td> </td>
</tr> </tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_"hometown"}: </span>
</td>
<td>
<input type="text" name="hometown" value="{$user->getHometown()}" />
</td>
</tr>
<tr> <tr>
<td width="120" valign="top"> <td width="120" valign="top">
<span class="nobold">{_"relationship"}: </span> <span class="nobold">{_"relationship"}: </span>

View file

@ -18,13 +18,13 @@
{block tabs} {block tabs}
{if !is_null($thisUser) && $user->getId() === $thisUser->getId()} {if !is_null($thisUser) && $user->getId() === $thisUser->getId()}
<div {if !$admin}id="activetabs"{/if} class="tab"> <div n:attr='id => ($admin ? false : "activetabs")' class="tab">
<a {if !$admin}id="act_tab_a"{/if} href="/groups{$user->getId()}"> <a n:attr='id => ($admin ? false : "act_tab_a")' href="/groups{$user->getId()}">
{_groups} {_groups}
</a> </a>
</div> </div>
<div {if $admin}id="activetabs"{/if} class="tab"> <div n:attr='id => (!$admin ? false : "activetabs")' class="tab">
<a {if $admin}id="act_tab_a"{/if} href="/groups{$user->getId()}?act=managed"> <a n:attr='id => (!$admin ? false : "act_tab_a")' href="/groups{$user->getId()}?act=managed">
{_managed} {_managed}
</a> </a>
</div> </div>
@ -51,15 +51,17 @@
<img src="{$x->getAvatarUrl()}" width="75" alt="Фотография группы" /> <img src="{$x->getAvatarUrl()}" width="75" alt="Фотография группы" />
{/block} {/block}
{block infotable} {block name}{/block}
{block infoTable}
<table id="basicInfo" class="ugc-table group_info" cellspacing="0" cellpadding="0" border="0"> <table id="basicInfo" class="ugc-table group_info" cellspacing="0" cellpadding="0" border="0">
<tbody> <tbody>
<tr> <tr>
<td class="label"><span class="nobold">Название: </span></td> <td class="label"><span class="nobold">{_name}: </span></td>
<td class="data"><a href="{$x->getURL()}">{$x->getName()}</a></td> <td class="data"><a href="{$x->getURL()}">{$x->getName()}</a></td>
</tr> </tr>
<tr> <tr>
<td class="label"><span class="nobold">Размер:</span></td> <td class="label"><span class="nobold">{_size}:</span></td>
<td class="data"><a href="/club{$x->getId()}/followers">{tr("participants", $x->getFollowersCount())}</a></td> <td class="data"><a href="/club{$x->getId()}/followers">{tr("participants", $x->getFollowersCount())}</a></td>
</tr> </tr>
</tbody> </tbody>
@ -99,7 +101,7 @@
{block bottom} {block bottom}
{if !is_null($thisUser) && $user->getId() === $thisUser->getId()} {if !is_null($thisUser) && $user->getId() === $thisUser->getId()}
<div class="groups_options"> <div class="groups_options">
<div id="gp_container" style="width: 200px;margin-right: 40px;"> <div id="gp_container" style="width: 200px; margin-right: 40px;">
<h4>{_open_new_group}</h4> <h4>{_open_new_group}</h4>
<span>{_open_group_desc}</span> <span>{_open_group_desc}</span>
<form action="/groups_create"> <form action="/groups_create">
@ -111,10 +113,10 @@
<span>{_search_group_desc}</span> <span>{_search_group_desc}</span>
<form action="/search"> <form action="/search">
<input name="type" type="hidden" value="groups"> <input name="type" type="hidden" value="groups">
<input name="query" class="header_search_input" value="" style="background: none;width: 155px;padding-left: 3px;"> <input name="query" class="header_search_input" value="" style="background: none; width: 155px; padding-left: 3px;">
<button class="button">{_search_by_groups}</button> <button class="button">{_search_by_groups}</button>
</form> </form>
</div> </div>
</div> </div>
{/if} {/if}
{/block} {/block}

View file

@ -407,7 +407,7 @@
</td> </td>
<td> <td>
<select name="style_avatar"> <select name="style_avatar">
<option value="0" {if $user->getStyleAvatar() == 0}selected{/if}>{_"default"}</option> <option value="0" {if $user->getStyleAvatar() == 0}selected{/if}>{_"arbitrary_avatars"} ({_"default"})</option>
<option value="1" {if $user->getStyleAvatar() == 1}selected{/if}>{_"cut"}</option> <option value="1" {if $user->getStyleAvatar() == 1}selected{/if}>{_"cut"}</option>
<option value="2" {if $user->getStyleAvatar() == 2}selected{/if}>{_"round_avatars"}</option> <option value="2" {if $user->getStyleAvatar() == 2}selected{/if}>{_"round_avatars"}</option>
</select> </select>

View file

@ -72,6 +72,9 @@
<div id="profile_link" style="width: 194px;"> <div id="profile_link" style="width: 194px;">
<a href="/edit" class="link">{_"edit_page"}</a> <a href="/edit" class="link">{_"edit_page"}</a>
</div> </div>
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce']" id="profile_link" style="width: 194px;">
<a onClick="showIncreaseRatingDialog({$thisUser->getCoins()}, {ltrim($thisUser->getUrl(), '/')}, {$csrfToken})" class="link">{_increase_rating}</a>
</div>
{else} {else}
{if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)} {if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)}
<a href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}" class="profile_link"> <a href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}" class="profile_link">

View file

@ -31,6 +31,5 @@
{/if} {/if}
<a n:if="$canDelete ?? false" class="profile_link" style="display:block;width:96%;" href="/wall{$post->getPrettyId()}/delete">{_delete}</a> <a n:if="$canDelete ?? false" class="profile_link" style="display:block;width:96%;" href="/wall{$post->getPrettyId()}/delete">{_delete}</a>
<a class="profile_link" style="display:block;width:96%;" href="/report.pl/{$post->getId()}?type=post">{_report}</a>
</div> </div>
{/block} {/block}

View file

@ -7,10 +7,12 @@
<tbody> <tbody>
<tr> <tr>
<td width="30" valign="top"> <td width="30" valign="top">
<img <a href="{$author->getURL()}">
src="{$author->getAvatarURL()}" <img
width="30" src="{$author->getAvatarURL()}"
class="cCompactAvatars" /> width="30"
class="cCompactAvatars" />
</a>
</td> </td>
<td width="100%" valign="top"> <td width="100%" valign="top">
<div class="post-author"> <div class="post-author">

View file

@ -0,0 +1,8 @@
{var sender = $notification->getModel(1)}
{var value = (int) explode(" ", $notification->getData(), 2)[0]}
{var message = explode(" ", $notification->getData(), 2)[1]}
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> {_increased_your_rating_by} {$value}%.
{if !empty($message)}
{_message}: "{$message}".
{/if}

View file

@ -8,15 +8,17 @@
<tbody> <tbody>
<tr> <tr>
<td width="54" valign="top"> <td width="54" valign="top">
<img <a href="{$author->getURL()}">
src="{$author->getAvatarURL()}" <img
width="{ifset $compact}25{else}50{/ifset}" src="{$author->getAvatarURL()}"
{ifset $compact}class="cCompactAvatars"{/ifset} /> width="{ifset $compact}25{else}50{/ifset}"
{if !$post->isPostedOnBehalfOfGroup() && !$compact} {ifset $compact}class="cCompactAvatars"{/ifset} />
<span n:if="$author->isOnline()" class="post-online"> {if !$post->isPostedOnBehalfOfGroup() && !$compact}
{_online} <span n:if="$author->isOnline()" class="post-online">
</span> {_online}
{/if} </span>
{/if}
</a>
</td> </td>
<td width="100%" valign="top"> <td width="100%" valign="top">
<div class="post-author"> <div class="post-author">

View file

@ -4,14 +4,17 @@
<tbody> <tbody>
<tr> <tr>
<td width="54" valign="top"> <td width="54" valign="top">
<img <a href="{$author->getURL()}">
src="{$author->getAvatarURL()}" <img
width="50" /> src="{$author->getAvatarURL()}"
width="50" />
</a>
{if !$post->isPostedOnBehalfOfGroup() && !($compact ?? false)} {if !$post->isPostedOnBehalfOfGroup() && !($compact ?? false)}
<span n:if="$author->isOnline()" class="post-online"> <span n:if="$author->isOnline()" class="post-online">
{_online} {_online}
</span> </span>
{/if} {/if}
</td> </td>
<td width="100%" valign="top"> <td width="100%" valign="top">
<div class="post-author"> <div class="post-author">

View file

@ -31,7 +31,8 @@ services:
- openvk\Web\Models\Repositories\Notes - openvk\Web\Models\Repositories\Notes
- openvk\Web\Models\Repositories\Tickets - openvk\Web\Models\Repositories\Tickets
- openvk\Web\Models\Repositories\Messages - openvk\Web\Models\Repositories\Messages
- openvk\Web\Models\Repositories\Restores - openvk\Web\Models\Repositories\Restores
- openvk\Web\Models\Repositories\Verifications
- openvk\Web\Models\Repositories\Notifications - openvk\Web\Models\Repositories\Notifications
- openvk\Web\Models\Repositories\TicketComments - openvk\Web\Models\Repositories\TicketComments
- openvk\Web\Models\Repositories\IPs - openvk\Web\Models\Repositories\IPs

View file

@ -59,6 +59,10 @@ routes:
handler: "Auth->restore" handler: "Auth->restore"
- url: "/restore/internal-finish" - url: "/restore/internal-finish"
handler: "Auth->finishRestoringPassword" handler: "Auth->finishRestoringPassword"
- url: "/reg/resend"
handler: "Auth->resendEmail"
- url: "/regFinish"
handler: "Auth->verifyEmail"
- url: "/setSID/{slug}" - url: "/setSID/{slug}"
handler: "Auth->su" handler: "Auth->su"
- url: "/settings" - url: "/settings"
@ -69,6 +73,8 @@ routes:
handler: "User->disableTwoFactorAuth" handler: "User->disableTwoFactorAuth"
- url: "/coins_transfer" - url: "/coins_transfer"
handler: "User->coinsTransfer" handler: "User->coinsTransfer"
- url: "/increase_social_credits"
handler: "User->increaseRating"
- url: "/id{num}" - url: "/id{num}"
handler: "User->view" handler: "User->view"
- url: "/friends{num}" - url: "/friends{num}"
@ -101,6 +107,8 @@ routes:
hashTag: ".++" hashTag: ".++"
- url: "/wall{num}" - url: "/wall{num}"
handler: "Wall->wall" handler: "Wall->wall"
- url: "/wall{num}/rss"
handler: "Wall->rss"
- url: "/wall{num}/makePost" - url: "/wall{num}/makePost"
handler: "Wall->makePost" handler: "Wall->makePost"
- url: "/wall{num}_{num}" - url: "/wall{num}_{num}"

View file

@ -38,4 +38,5 @@ div.content > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(1) > td:nth
.cCompactAvatars { .cCompactAvatars {
width: 30px !important; width: 30px !important;
height: 30px !important; height: 30px !important;
object-fit: cover;
} }

View file

@ -65,4 +65,5 @@ div.ovk-video > div > img
.cCompactAvatars { .cCompactAvatars {
width: 30px !important; width: 30px !important;
height: 30px !important; height: 30px !important;
object-fit: cover;
} }

View file

@ -1886,7 +1886,24 @@ html {
.mb_tab div { .mb_tab div {
padding: 5px 7px; padding: 5px 7px;
} }
#gp_container span {
display: block;
margin: 10px 0 15px;
}
#gp_container h4 {
font-size: 11px;
}
.container_gray .content:last-child {
margin-bottom: 0;
}
.group_info {
padding: 0 0 0 5px !important;
}
.mb_tab#active { .mb_tab#active {
background-color: #898989; background-color: #898989;
@ -1898,4 +1915,20 @@ html {
.mb_tab#active a { .mb_tab#active a {
color: white; color: white;
} }
.profile_thumb {
padding: 0px 10px 0px 0px;
width: 50px;
display: inline-block;
vertical-align: top;
}
.border-block {
box-shadow: inset 0px 0 0px 1px #b6bfca, inset 0px 0 0px 10px #d8dfe7;
width: 300px;
padding: 20px;
}
.center {
margin: 0 auto;
}

View file

@ -289,3 +289,78 @@ function supportFastAnswerDialogOnClick(answer) {
answerInput.value = answer; answerInput.value = answer;
answerInput.focus(); answerInput.focus();
} }
function ovk_proc_strtr(string, length = 0) {
const newString = string.substring(0, length);
return newString + (string !== newString ? "…" : "");
}
function showIncreaseRatingDialog(coinsCount, userUrl, hash) {
MessageBox(tr("increase_rating"), `
<div class="messagebox-content-header">
${tr("you_have_unused_votes", coinsCount)} <br />
<a href="/settings?act=finance.top-up">${tr("apply_voucher")} &raquo;</a>
</div>
<form action="/increase_social_credits" method="post" id="increase_rating_form" style="margin-top: 30px">
<table cellspacing="7" cellpadding="0" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr("to_whom")}:</span>
</td>
<td>
<input type="text" name="receiver" style="width: 100%;" value="${userUrl}" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr("increase_by")}:</span>
</td>
<td>
<input id="value_input" type="text" name="value" style="width: 100%;" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr("message")}:</span>
</td>
<td>
<textarea name="message" style="width: 100%;"></textarea>
</td>
</tr>
<tr>
<td colspan="2">
<div class="menu_divider"></div>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr("price")}:</span>
</td>
<td>
<span id="rating_price">${tr("points_amount", 0)}</span> <small class="nobold" style="float: right;">(1% = ${tr("points_amount_one", 1)})</small>
</td>
</tr>
</tbody>
</table>
<input type="hidden" name="hash" value="${hash}" />
</form>
`, [tr("increase_rating_button"), tr("cancel")], [
() => {
document.querySelector("#increase_rating_form").submit();
},
Function.noop
]);
document.querySelector("#value_input").oninput = function () {
let value = Number(this.value);
value = isNaN(value) ? "?" : ovk_proc_strtr(String(value), 7);
if(!value.endsWith("…") && value != "?")
value = Number(value);
if(typeof value === "number")
document.querySelector("#rating_price").innerHTML = tr("points_amount", value);
else
document.querySelector("#rating_price").innerHTML = value + " " + tr("points_amount_other").replace("$1 ", "");
};
}

View file

@ -231,7 +231,7 @@ return (function() {
if(is_dir($gitDir = OPENVK_ROOT . "/.git") && $showCommitHash) if(is_dir($gitDir = OPENVK_ROOT . "/.git") && $showCommitHash)
$ver = trim(`git --git-dir="$gitDir" log --pretty="%h" -n1 HEAD` ?? "Unknown version") . "-nightly"; $ver = trim(`git --git-dir="$gitDir" log --pretty="%h" -n1 HEAD` ?? "Unknown version") . "-nightly";
else else
$ver = "Build 15"; $ver = "Public Technical Preview 3";
// Unix time constants // Unix time constants
define('MINUTE', 60); define('MINUTE', 60);

View file

@ -13,7 +13,8 @@
"lfkeitel/phptotp": "dev-master", "lfkeitel/phptotp": "dev-master",
"chillerlan/php-qrcode": "dev-main", "chillerlan/php-qrcode": "dev-main",
"vearutop/php-obscene-censor-rus": "dev-master", "vearutop/php-obscene-censor-rus": "dev-master",
"erusev/parsedown": "dev-master" "erusev/parsedown": "dev-master",
"bhaktaraz/php-rss-generator": "dev-master"
}, },
"minimum-stability": "dev" "minimum-stability": "dev"
} }

92
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3816f5fbf2c78a3e252637476cd2ae08", "content-hash": "2c94032cae911ca438bbcfc46c346961",
"packages": [ "packages": [
{ {
"name": "al/emoji-detector", "name": "al/emoji-detector",
@ -52,6 +52,55 @@
}, },
"time": "2020-06-26T09:10:17+00:00" "time": "2020-06-26T09:10:17+00:00"
}, },
{
"name": "bhaktaraz/php-rss-generator",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/bhaktaraz/php-rss-generator.git",
"reference": "53cf11db18d87e65973e6df453fb8c1382e5a3bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bhaktaraz/php-rss-generator/zipball/53cf11db18d87e65973e6df453fb8c1382e5a3bd",
"reference": "53cf11db18d87e65973e6df453fb8c1382e5a3bd",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"Bhaktaraz\\RSSGenerator\\": "Source/Bhaktaraz/RSSGenerator/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bhaktaraz Bhatta",
"email": "bhattabhakta@gmail.com"
}
],
"description": "Simple RSS generator library for PHP 5.5 or later.",
"homepage": "https://github.com/bhaktaraz/php-rss-generator",
"keywords": [
"Facebook product feed generator",
"feed",
"generator",
"rss",
"writer"
],
"support": {
"issues": "https://github.com/bhaktaraz/php-rss-generator/issues",
"source": "https://github.com/bhaktaraz/php-rss-generator/tree/master"
},
"time": "2021-03-15T10:59:47+00:00"
},
{ {
"name": "chillerlan/php-qrcode", "name": "chillerlan/php-qrcode",
"version": "dev-main", "version": "dev-main",
@ -520,12 +569,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [ "files": [
"src/functions_include.php" "src/functions_include.php"
] ],
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -600,12 +649,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/JamesHeinrich/getID3.git", "url": "https://github.com/JamesHeinrich/getID3.git",
"reference": "46346ff3bea96a63f1a1d58ee4eabc79471d0ec8" "reference": "2279f7caca2d761dfc580dd02b401e7a1ff69dfe"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/46346ff3bea96a63f1a1d58ee4eabc79471d0ec8", "url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/2279f7caca2d761dfc580dd02b401e7a1ff69dfe",
"reference": "46346ff3bea96a63f1a1d58ee4eabc79471d0ec8", "reference": "2279f7caca2d761dfc580dd02b401e7a1ff69dfe",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -660,7 +709,7 @@
"issues": "https://github.com/JamesHeinrich/getID3/issues", "issues": "https://github.com/JamesHeinrich/getID3/issues",
"source": "https://github.com/JamesHeinrich/getID3/tree/master" "source": "https://github.com/JamesHeinrich/getID3/tree/master"
}, },
"time": "2022-01-03T16:59:52+00:00" "time": "2022-02-03T17:07:51+00:00"
}, },
{ {
"name": "komeiji-satori/curl", "name": "komeiji-satori/curl",
@ -1069,12 +1118,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
] ],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -1154,12 +1203,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
], ],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"classmap": [ "classmap": [
"Resources/stubs" "Resources/stubs"
] ]
@ -1235,12 +1284,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
] ],
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -1512,7 +1561,8 @@
"lfkeitel/phptotp": 20, "lfkeitel/phptotp": 20,
"chillerlan/php-qrcode": 20, "chillerlan/php-qrcode": 20,
"vearutop/php-obscene-censor-rus": 20, "vearutop/php-obscene-censor-rus": 20,
"erusev/parsedown": 20 "erusev/parsedown": 20,
"bhaktaraz/php-rss-generator": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,

View file

@ -0,0 +1,9 @@
ALTER TABLE `profiles` ADD `activated` tinyint(3) NULL DEFAULT '1' AFTER `2fa_secret`;
CREATE TABLE IF NOT EXISTS `email_verifications` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`profile` bigint(20) unsigned NOT NULL,
`key` char(64) COLLATE utf8mb4_general_nopad_ci NOT NULL,
`timestamp` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_nopad_ci;

View file

@ -37,6 +37,13 @@
"password_reset_error" = "Անկանխատեսելի սխալ գաղտնաբառի զրոյացման ժամանակ։"; "password_reset_error" = "Անկանխատեսելի սխալ գաղտնաբառի զրոյացման ժամանակ։";
"password_reset_rate_limit_error" = "Հազար ներողություն, բայց այդպես շատ հաճախ չի կարելի անել։"; "password_reset_rate_limit_error" = "Հազար ներողություն, բայց այդպես շատ հաճախ չի կարելի անել։";
"email_sent" = "Նամակը հաջողությամբ ուղարկվել է։";
"email_sent_desc" = "Եթե ձեր էլ․ հասցեն գոյություն ունի, Դուք կստանաք նամակը։";
"email_error" = "Անկանխատեսելի սխալ նամակ ուղարկելու ժամանակ։";
"email_rate_limit_error" = "Կներեք, չափից շատ նամակ ուղարկել չի կարելի։";
"email_verify_success" = "Ձեր E-Mail-ը հաստատված է։ Հաճելի՛ ժամանց։";
"registration_disabled_info" = "Գրանցումները անջատված են համակարգային ադմինիստրատորի կողմից։ Հնարավորության դեպքում, խնդրեք հրավերք արդեն գրանցված օգտատերերից։"; "registration_disabled_info" = "Գրանցումները անջատված են համակարգային ադմինիստրատորի կողմից։ Հնարավորության դեպքում, խնդրեք հրավերք արդեն գրանցված օգտատերերից։";
"registration_closed" = "Գրանցումը փակ է։"; "registration_closed" = "Գրանցումը փակ է։";
"invites_you_to" = "<strong>$1</strong> ձեզ հրավիրում է $2"; "invites_you_to" = "<strong>$1</strong> ձեզ հրավիրում է $2";
@ -137,6 +144,8 @@
/* Wall */ /* Wall */
"feed" = "Լուրեր";
"post_writes_m" = "գրել է"; "post_writes_m" = "գրել է";
"post_writes_f" = "գրել է"; "post_writes_f" = "գրել է";
"post_writes_g" = "հրապարակել են"; "post_writes_g" = "հրապարակել են";
@ -147,6 +156,9 @@
"delete" = "Ջնջել"; "delete" = "Ջնջել";
"comments" = "Մեկնաբանություններ"; "comments" = "Մեկնաբանություններ";
"share" = "Կիսվել"; "share" = "Կիսվել";
"pin" = "Ամրացնել";
"unpin" = "Հետ ամրացնել";
"pinned" = "ամրացված";
"comments_tip" = "Եղե՛ք առաջինը ով կթողնի իր կարծիքը։"; "comments_tip" = "Եղե՛ք առաջինը ով կթողնի իր կարծիքը։";
"your_comment" = "Ձեր մեկնաբանությունը"; "your_comment" = "Ձեր մեկնաբանությունը";
"comments" = "Մեկնաբանություններ"; "comments" = "Մեկնաբանություններ";
@ -157,7 +169,6 @@
"wall_few" = "$1 գրություն"; "wall_few" = "$1 գրություն";
"wall_many" = "$1 գրություն"; "wall_many" = "$1 գրություն";
"wall_other" = "$1 գրություն"; "wall_other" = "$1 գրություն";
"publish_post" = "Ավելացնել գրություն"; "publish_post" = "Ավելացնել գրություն";
"view_other_comments" = "Կարդալ մեկնաբանությունները"; "view_other_comments" = "Կարդալ մեկնաբանությունները";
@ -172,7 +183,10 @@
"contains_nsfw" = "Պարունակում է NSFW մատերիալ"; "contains_nsfw" = "Պարունակում է NSFW մատերիալ";
"nsfw_warning" = "Այս պոստը կարող է պարունակել 18+ մատերիալ։"; "nsfw_warning" = "Այս պոստը կարող է պարունակել 18+ մատերիալ։";
"report" = "Բողոքարկել"; "report" = "Բողոքարկել";
"attach" = "Ամրացնել";
"attach_photo" = "Ամրացնել նկար"; "attach_photo" = "Ամրացնել նկար";
"attach_video" = "Ամրացնել վիդեո";
"draw_graffiti" = "Նկարել գրաֆիտի";
"no_posts_abstract" = "Այստեղ ոչ ոք, ոչինչ․․․ դեռ չի գրել․․․"; "no_posts_abstract" = "Այստեղ ոչ ոք, ոչինչ․․․ դեռ չի գրել․․․";
"attach_no_longer_available" = "Այս հավելվածը այլևս հասանելի չէ"; "attach_no_longer_available" = "Այս հավելվածը այլևս հասանելի չէ";
"open_post" = "Բացել գրությունը"; "open_post" = "Բացել գրությունը";
@ -220,6 +234,7 @@
"subscriptions" = "Բաժանորդագրություններ"; "subscriptions" = "Բաժանորդագրություններ";
"join_community" = "Մտնել խումբ"; "join_community" = "Մտնել խումբ";
"leave_community" = "Լքել խումբը"; "leave_community" = "Լքել խումբը";
"check_community" = "Դիտել խումբը";
"min_6_community" = "Անվանումը չպետք է լինի 6 նշից պակաս"; "min_6_community" = "Անվանումը չպետք է լինի 6 նշից պակաս";
"participants" = "Անդամներ"; "participants" = "Անդամներ";
"groups" = "Խմբեր"; "groups" = "Խմբեր";
@ -237,6 +252,7 @@
"only_administrators" = "Միայն ադմինիստրատորները"; "only_administrators" = "Միայն ադմինիստրատորները";
"website" = "Վեբկայք"; "website" = "Վեբկայք";
"managed" = "Կառավարվում է"; "managed" = "Կառավարվում է";
"size" = "Չափ";
"administrators_one" = "$1 ադմինիստրատոր"; "administrators_one" = "$1 ադմինիստրատոր";
"administrators_few" = "$1 ադմինիստրատոր"; "administrators_few" = "$1 ադմինիստրատոր";
@ -245,12 +261,14 @@
"role" = "Դեր"; "role" = "Դեր";
"administrator" = "Ադմինիստրատոր"; "administrator" = "Ադմինիստրատոր";
"promote_to_admin" = "Բարձրացնել դեպի ադմինիստրատոր"; "promote_to_admin" = "Բարձրացնել դեպի ադմինիստրատոր";
"promote_to_owner" = "Դարձնել տեր";
"devote" = "Հետ բողոքարկել"; "devote" = "Հետ բողոքարկել";
"group_allow_post_for_everyone" = "Թույլ տար գրություններ թողնել բոլորին"; "group_allow_post_for_everyone" = "Թույլ տար գրություններ թողնել բոլորին";
"set_comment" = "Փոփոխել մեկնաբանությունը"; "set_comment" = "Փոփոխել մեկնաբանությունը";
"hidden_yes" = "Թաքցված է"; "hidden_yes" = "Թաքցված է";
"hidden_no" = "Թաքցված չէ"; "hidden_no" = "Թաքցված չէ";
"group_allow_post_for_everyone" = "Թույլատրել հրապարակել բոլորին"; "group_allow_post_for_everyone" = "Թույլատրել հրապարակել բոլորին";
"group_hide_from_global_feed" = "Չցույց տալ հրապարակությունները ընդհանուր լրահոսում։";
"statistics" = "Ստատիստիկա"; "statistics" = "Ստատիստիկա";
"group_administrators_list" = "Ադմինների ցուցակ"; "group_administrators_list" = "Ադմինների ցուցակ";
"group_display_only_creator" = "Ցույց տալ միայն խմբի ստեղծողին"; "group_display_only_creator" = "Ցույց տալ միայն խմբի ստեղծողին";
@ -273,12 +291,22 @@
"groups_many" = "$1 խումբ"; "groups_many" = "$1 խումբ";
"groups_other" = "$1 խումբ"; "groups_other" = "$1 խումբ";
"groups_list_zero" = "Դուք չեք կայանում ոչ մի խմբում";
"groups_list_one" = "Դուք կաք միայն մեկ խմբում";
"groups_list_other" = "Դուք կաք $1 խմբում";
"meetings_zero" = "Ոչ մի հանդիպում"; "meetings_zero" = "Ոչ մի հանդիպում";
"meetings_one" = "Մեկ հանդիպում"; "meetings_one" = "Մեկ հանդիպում";
"meetings_few" = "$1 հանդիպում"; "meetings_few" = "$1 հանդիպում";
"meetings_many" = "$1 հանդիպում"; "meetings_many" = "$1 հանդիպում";
"meetings_other" = "$1 հանդիպում"; "meetings_other" = "$1 հանդիպում";
"open_new_group" = "Նոր խումբ բացել";
"open_group_desc" = "Չե՞ք կարող խումբ գտնել, բացեք ձերը․․․";
"search_group" = "Խմբի որոնում";
"search_by_groups" = "Որոնում ըստ խմբերի";
"search_group_desc" = "Այստեղ դուք կարող եք փնտրել խմբեր և ընտրել ձեզ ամենահարմարը․․․";
/* Albums */ /* Albums */
"create" = "Ստեղծել"; "create" = "Ստեղծել";
@ -309,8 +337,8 @@
"text_note" = "Պարունակություն"; "text_note" = "Պարունակություն";
"create_note" = "Ստեղծել նշում"; "create_note" = "Ստեղծել նշում";
"actions" = "Գործողություններ"; "actions" = "Գործողություններ";
"feed" = "Լուրեր"; "edit_note" = "Խմբագրել նշումը";
"publish_post" = "Ավելացնել գրություն"; "edited" = "Խմբագրված է";
"notes_zero" = "Ոչ մի նշում չկա"; "notes_zero" = "Ոչ մի նշում չկա";
"notes_one" = "Մեկ նշում"; "notes_one" = "Մեկ նշում";
@ -337,9 +365,11 @@
"menu_registration" = "Գրանցում"; "menu_registration" = "Գրանցում";
"menu_help" = "Օգնություն"; "menu_help" = "Օգնություն";
"header_home" = "Գլխավոր"; "menu_logout" = "Դուրս գալ";
"menu_support" = "Օգնություն";
"header_home" = "գլխավոր";
"header_groups" = "խմբեր"; "header_groups" = "խմբեր";
"header_donate" = "օգնել";
"header_people" = "մարդիկ"; "header_people" = "մարդիկ";
"header_invite" = "հրավիրել"; "header_invite" = "հրավիրել";
"header_help" = "օգնություն"; "header_help" = "օգնություն";
@ -351,18 +381,14 @@
"left_menu_donate" = "Աջակցել"; "left_menu_donate" = "Աջակցել";
"footer_about_instance" = "հոսքի մասին";
"footer_blog" = "բլոգ"; "footer_blog" = "բլոգ";
"footer_help" = "օգնություն"; "footer_help" = "օգնություն";
"footer_developers" = "մշակողներին"; "footer_developers" = "մշակողներին";
"footer_choose_language" = "ընտրել լեզուն"; "footer_choose_language" = "ընտրել լեզուն";
"footer_privacy" = "գաղտնիություն"; "footer_privacy" = "գաղտնիություն";
"notes_zero" = "Ոչ մի նշում չկա";
"notes_one" = "Մեկ նշում";
"notes_few" = "$1 նշում";
"notes_many" = "$1 նշում";
"notes_other" = "$1 նշում";
/* Settings */ /* Settings */
"main" = "Հիմնական"; "main" = "Հիմնական";
@ -387,9 +413,12 @@
"cut" = "Կտրվածք"; "cut" = "Կտրվածք";
"round_avatars" = "Կլոր ավատար"; "round_avatars" = "Կլոր ավատար";
"apply_style_for_this_device" = "Հաստատել տեսքը միայն այս սարքի համար";
"search_for_groups" = "Խմբերի որոնում"; "search_for_groups" = "Խմբերի որոնում";
"search_for_people" = "Մարդկանց որոնում"; "search_for_people" = "Մարդկանց որոնում";
"search_button" = "Որոնել"; "search_button" = "Որոնել";
"search_placeholder" = "Գրեք ցանկացած անուն, անվանում կամ բառ";
"results_zero" = "Ոչ մի արդյունք"; "results_zero" = "Ոչ մի արդյունք";
"results_one" = "Մեկ արդյունք"; "results_one" = "Մեկ արդյունք";
"results_few" = "$1 արդյունք"; "results_few" = "$1 արդյունք";
@ -405,6 +434,7 @@
"privacy_setting_see_friends" = "Ում են երևում իմ ընկերները"; "privacy_setting_see_friends" = "Ում են երևում իմ ընկերները";
"privacy_setting_add_to_friends" = "Ով կարող է ինձ ընկեր կոչել"; "privacy_setting_add_to_friends" = "Ով կարող է ինձ ընկեր կոչել";
"privacy_setting_write_wall" = "Ով կարող է գրել իմ պատին"; "privacy_setting_write_wall" = "Ով կարող է գրել իմ պատին";
"privacy_setting_write_messages" = "Ով կարող է ինձ նամակներ գրել";
"privacy_value_anybody" = "Բոլոր ցանկացողները"; "privacy_value_anybody" = "Բոլոր ցանկացողները";
"privacy_value_anybody_dative" = "Բոլոր ցանկացողներին"; "privacy_value_anybody_dative" = "Բոլոր ցանկացողներին";
"privacy_value_users" = "OpenVKի օգտատերերին"; "privacy_value_users" = "OpenVKի օգտատերերին";
@ -429,6 +459,11 @@
"ui_settings_rating_show" = "Ցուցադրել"; "ui_settings_rating_show" = "Ցուցադրել";
"ui_settings_rating_hide" = "Թաքցնել"; "ui_settings_rating_hide" = "Թաքցնել";
"additional_links" = "Հավելյալ հղումներ";
"ad_poster" = "Գովազդային վահանակ";
/* Two-factor authentication */
"two_factor_authentication" = "Երկքայլ աուտենտիֆիկացիա"; "two_factor_authentication" = "Երկքայլ աուտենտիֆիկացիա";
"two_factor_authentication_disabled" = "Ապահովում է հուսալի պաշտպանում ջարդումից․ անհրաժեշտ է ներմուծել 2FA-ով ստացված կոդը։"; "two_factor_authentication_disabled" = "Ապահովում է հուսալի պաշտպանում ջարդումից․ անհրաժեշտ է ներմուծել 2FA-ով ստացված կոդը։";
"two_factor_authentication_enabled" = "Երկքայլ աուտենտիֆիկացիան միացված է։ Ձեր էջը պաշտպանված է։"; "two_factor_authentication_enabled" = "Երկքայլ աուտենտիֆիկացիան միացված է։ Ձեր էջը պաշտպանված է։";
@ -513,6 +548,7 @@
"nt_post_instrumental" = "պոստով"; "nt_post_instrumental" = "պոստով";
"nt_note_instrumental" = "նշումով"; "nt_note_instrumental" = "նշումով";
"nt_photo_instrumental" = "նկարով"; "nt_photo_instrumental" = "նկարով";
"nt_topic_instrumental" = "թեմայով";
/* Time */ /* Time */
@ -523,6 +559,10 @@
"time_today" = "այսօր"; "time_today" = "այսօր";
"time_yesterday" = "երեկ"; "time_yesterday" = "երեկ";
"points" = "Ձայն";
"points_count" = "ձայն";
"on_your_account" = "ձեր հաշվում";
"your_email_address" = "Ձեր էլեկտրոնային հասցեն"; "your_email_address" = "Ձեր էլեկտրոնային հասցեն";
"your_page_address" = "Ձեր էջի հասցեն"; "your_page_address" = "Ձեր էջի հասցեն";
"page_address" = "Էջի հասցեն"; "page_address" = "Էջի հասցեն";
@ -593,6 +633,11 @@
"gifts_few" = "$1 նվեր"; "gifts_few" = "$1 նվեր";
"gifts_many" = "$1 նվեր"; "gifts_many" = "$1 նվեր";
"gifts_other" = "$1 նվեր"; "gifts_other" = "$1 նվեր";
"gifts_left" = "Մնաց $1 նվեր";
"gifts_left_one" = "Մնաց մեկ նվեր";
"gifts_left_few" = "$1 նվեր մնաց";
"gifts_left_many" = "$1 նվեր մնաց";
"gifts_left_other" = "$1 նվեր մնաց";
"send_gift" = "Ուղարկել նվեր"; "send_gift" = "Ուղարկել նվեր";
@ -612,9 +657,6 @@
"users_gifts" = "Նվերներ"; "users_gifts" = "Նվերներ";
"comment" = "Մեկնաբանություն";
"sender" = "Ուղարկող";
/* Support */ /* Support */
"support_opened" = "Բաց"; "support_opened" = "Բաց";
@ -638,11 +680,29 @@
"support_new_title" = "Գրե՛ք ձեր դիմումի վերնագիրը"; "support_new_title" = "Գրե՛ք ձեր դիմումի վերնագիրը";
"support_new_content" = "Նկարագրե՛ք խնդիրը կամ առաջարկը"; "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_rated_good" = "Դուք թողել եք դրական ակնարկ պատասխանի մասին։";
"support_rated_bad" = "Դուք թողել եք բացասական ակնարկ պատասխանի մասին։";
"wrong_parameters" = "Հարցման սխալ կարգավորումներ";
"fast_answers" = "Արագ պատասխաններ";
"comment" = "Մեկնաբանություն"; "comment" = "Մեկնաբանություն";
"sender" = "Ուղարկող"; "sender" = "Ուղարկող";
"author" = "Հեղինակ"; "author" = "Հեղինակ";
"you_have_not_entered_text" = "Դուք չեք գրել տեքստ";
"you_have_not_entered_name_or_text" = "Դուք անուն կամ տեքստ չեք գրել";
"ticket_changed" = "Տոմսը փոփոխված է";
"ticket_changed_comment" = "Փոփոխությունները ուժի մեջ կմտնեն մի քանի վայրկյանից։";
/* Invite */ /* Invite */
"invite" = "Հրավիրել"; "invite" = "Հրավիրել";
@ -658,6 +718,14 @@
"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>։";
/* Registration confirm */
"ec_header" = "Գրանցման հաստատում";
"ec_title" = "Շնորհակալությո՛ւն";
"ec_1" = "<b>$1</b>, Ձեր գրանցումը համարյա ավարտվել է։ Մի քանի վայրկյանի ընթացքում ձեր էլ․ փոստին պետք է գա հաստատման հղում։";
"ec_2" = "Եթե ինչ-որ պատճառներով Ձեզ չի հասել նամակը, ստուգեք Սպամի պանակը։ Եթե այնտեղ էլ նամակը չգտնեք, դուք կարող եք նորից ուղարկել նամակը։";
"ec_resend" = "Վերաուղարկել նամակը";
/* Discussions */ /* Discussions */
"discussions" = "Քննարկումներ"; "discussions" = "Քննարկումներ";
@ -785,6 +853,36 @@
"about_openvk" = "OpenVK-ի մասին"; "about_openvk" = "OpenVK-ի մասին";
"about_this_instance" = "Այս հոսքի մասին";
"rules" = "Կանոններ";
"most_popular_groups" = "Ամենահայտնի խմբերը";
"on_this_instance_are" = "Այս հոսքում․";
"about_users_one" = "<b>Մեկ</b> օգտատեր";
"about_users_few" = "<b>$1</b> օգտատեր";
"about_users_many" = "<b>$1</b> օգտատեր";
"about_users_other" = "<b>$1</b> օգտատեր";
"about_online_users_one" = "<b>Մեկ</b> օգտատեր է ցանցի մեջ";
"about_online_users_few" = "<b>$1</b> օգտատեր է ցանցի մեջ";
"about_online_users_many" = "<b>$1</b> օգտատեր է ցանցի մեջ";
"about_online_users_other" = "<b>$1</b> օգտատեր է ցանցի մեջ";
"about_active_users_one" = "<b>Մեկ</b> ակտիվ օգտատեր";
"about_active_users_few" = "<b>$1</b> ակտիվ օգտատեր";
"about_active_users_many" = "<b>$1</b> ակտիվ օգտատեր";
"about_active_users_other" = "<b>$1</b> ակտիվ օգտատեր";
"about_groups_one" = "<b>Մեկ</b> խումբ";
"about_groups_few" = "<b>$1</b> խումբ";
"about_groups_many" = "<b>$1</b> խումբ";
"about_groups_other" = "<b>$1</b> խումբ";
"about_wall_posts_one" = "<b>Մեկ</b> գրություն պատերի վրա";
"about_wall_posts_few" = "<b>$1</b> գրություն պատերի վրա";
"about_wall_posts_many" = "<b>$1</b> գրություն պատերի վրա";
"about_wall_posts_other" = "<b>$1</b> գրություն պատերի վրա";
/* Dialogs */ /* Dialogs */
"ok" = "ОК"; "ok" = "ОК";

View file

@ -35,6 +35,13 @@
"password_reset_error" = "An unexpected error occurred while resetting the password."; "password_reset_error" = "An unexpected error occurred while resetting the password.";
"password_reset_rate_limit_error" = "You can't do it that often, sorry."; "password_reset_rate_limit_error" = "You can't do it that often, sorry.";
"email_sent" = "Mail has been successfully sent.";
"email_sent_desc" = "If your email addess exists, you will receive instructions.";
"email_error" = "An unexpected error occurred while sending the email.";
"email_rate_limit_error" = "You can't do it that often, sorry.";
"email_verify_success" = "Your Email has been verified. Have a great time!";
"registration_disabled_info" = "Registration has been disabled by the system administrator. If possible, ask for an invitation from your friend, if he is registered on this site."; "registration_disabled_info" = "Registration has been disabled by the system administrator. If possible, ask for an invitation from your friend, if he is registered on this site.";
"registration_closed" = "Registration is closed."; "registration_closed" = "Registration is closed.";
"invites_you_to" = "<strong>$1</strong> invites you to $2"; "invites_you_to" = "<strong>$1</strong> invites you to $2";
@ -216,6 +223,7 @@
"subscriptions" = "Subscriptions"; "subscriptions" = "Subscriptions";
"join_community" = "Join community"; "join_community" = "Join community";
"leave_community" = "Leave community"; "leave_community" = "Leave community";
"check_community" = "View community";
"min_6_community" = "Name of the group must have more that 6 characters"; "min_6_community" = "Name of the group must have more that 6 characters";
"participants" = "Participants"; "participants" = "Participants";
"groups" = "Groups"; "groups" = "Groups";
@ -233,6 +241,7 @@
"only_administrators" = "Only administrators"; "only_administrators" = "Only administrators";
"website" = "Website"; "website" = "Website";
"managed" = "Managed"; "managed" = "Managed";
"size" = "Size";
"administrators_one" = "$1 administrator"; "administrators_one" = "$1 administrator";
"administrators_other" = "$1 administrators"; "administrators_other" = "$1 administrators";
@ -265,6 +274,10 @@
"groups_one" = "$1 group"; "groups_one" = "$1 group";
"groups_other" = "$1 groups"; "groups_other" = "$1 groups";
"groups_list_zero" = "You are not a participant in any group";
"groups_list_one" = "You are participating in one group";
"groups_list_other" = "You are a participant of $1 groups";
"meetings_zero" = "No meetings"; "meetings_zero" = "No meetings";
"meetings_one" = "$1 meeting"; "meetings_one" = "$1 meeting";
"meetings_other" = "$1 meetings"; "meetings_other" = "$1 meetings";
@ -301,7 +314,7 @@
"note" = "Note"; "note" = "Note";
"name_note" = "Title"; "name_note" = "Title";
"text_note" = "Content"; "text_note" = "Content";
"create_note" = "Create note"; "create_note" = "Add note";
"edit_note" = "Edit note"; "edit_note" = "Edit note";
"actions" = "Actions"; "actions" = "Actions";
@ -312,6 +325,10 @@
"notes_other" = "$1 notes"; "notes_other" = "$1 notes";
"notes_start_screen" = "With notes, you can share your events with friends and see what's going on with them."; "notes_start_screen" = "With notes, you can share your events with friends and see what's going on with them.";
"notes_list_zero" = "No notes found";
"notes_list_one" = "$1 note found";
"notes_list_other" = "$1 notes found";
/* Menus */ /* Menus */
/* Note that is string need to fit into the "My Page" link */ /* Note that is string need to fit into the "My Page" link */
@ -376,9 +393,11 @@
"avatars_style" = "Avatar style"; "avatars_style" = "Avatar style";
"style" = "Style"; "style" = "Style";
"default" = "Default"; "default" = "default";
"cut" = "Cut";
"round_avatars" = "Round avatars"; "arbitrary_avatars" = "Arbitrary";
"cut" = "Square";
"round_avatars" = "Round";
"apply_style_for_this_device" = "Apply style only for this device"; "apply_style_for_this_device" = "Apply style only for this device";
@ -566,6 +585,21 @@
"receiver_not_found" = "The receiver was not found."; "receiver_not_found" = "The receiver was not found.";
"you_dont_have_enough_points" = "You don't have enough votes."; "you_dont_have_enough_points" = "You don't have enough votes.";
"increase_rating" = "Increase rating";
"increase_rating_button" = "Increase";
"to_whom" = "To whom";
"increase_by" = "Increase by";
"price" = "Price";
"you_have_unused_votes" = "You have $1 unused votes on your balance.";
"apply_voucher" = "Apply voucher";
"failed_to_increase_rating" = "Failed to increase rating";
"rating_increase_successful" = "You have successfully increased rating of <b><a href=\"$1\">$2</a></b> by <b>$3%</b>.";
"negative_rating_value" = "We cannot steal rating from another person, sorry.";
"increased_your_rating_by" = "increased your rating by";
/* Gifts */ /* Gifts */
"gift" = "Gift"; "gift" = "Gift";
@ -655,6 +689,14 @@
"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>. Unfortunately, this time we had to block you forever.";
"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>.";
/* Registration confirm */
"ec_header" = "Registration confirmation";
"ec_title" = "Thanks!";
"ec_1" = "<b>$1</b>, your registration is almost done. In a few minutes you should receive an mail with a link to confirm your email address.";
"ec_2" = "If for some reason you don't get the mail, check your spam folder. If you don't find the email there, you can resend it.";
"ec_resend" = "Resend mail";
/* Discussions */ /* Discussions */
"discussions" = "Discussions"; "discussions" = "Discussions";
@ -730,6 +772,8 @@
"invalid_email_address" = "Invalid Email address"; "invalid_email_address" = "Invalid Email address";
"invalid_email_address_comment" = "The Email you entered is not correct."; "invalid_email_address_comment" = "The Email you entered is not correct.";
"invalid_real_name" = "Please, enter your real name. It'll be easier for your friends to find you like this.";
"invalid_birth_date" = "Invalid date of birth"; "invalid_birth_date" = "Invalid date of birth";
"invalid_birth_date_comment" = "The date of birth you entered is not correct."; "invalid_birth_date_comment" = "The date of birth you entered is not correct.";
@ -758,6 +802,14 @@
"captcha_error" = "Incorrect characters entered"; "captcha_error" = "Incorrect characters entered";
"captcha_error_comment" = "Please make sure you fill in the captcha field correctly."; "captcha_error_comment" = "Please make sure you fill in the captcha field correctly.";
"failed_to_publish_post" = "Failed to publish post";
"failed_to_delete_post" = "Failed to delete post";
"media_file_corrupted" = "The media content file is corrupted.";
"media_file_corrupted_or_too_large" = "The media content file is corrupted or too large.";
"post_is_empty_or_too_big" = "The post is empty or too big.";
"post_is_too_big" = "The post is too big.";
/* Admin actions */ /* Admin actions */
"login_as" = "Login as $1"; "login_as" = "Login as $1";

View file

@ -9,6 +9,8 @@
"home" = "Басты бет"; "home" = "Басты бет";
"welcome" = "Қош келдіңіз!"; "welcome" = "Қош келдіңіз!";
"to_top" = "Жоғарыға";
/* Login */ /* Login */
"log_in" = "Логин"; "log_in" = "Логин";
@ -36,6 +38,13 @@
"password_reset_error" = "Құпиясөзді қалпына келтіру кезінде күтпеген қате орын алды."; "password_reset_error" = "Құпиясөзді қалпына келтіру кезінде күтпеген қате орын алды.";
"password_reset_rate_limit_error" = "Кешіріңіз, бірақ осы әрекетті сонай жиі жасай алмайсыз."; "password_reset_rate_limit_error" = "Кешіріңіз, бірақ осы әрекетті сонай жиі жасай алмайсыз.";
"email_sent" = "Хат сәтті жіберілді.";
"email_sent_desc" = "Сіз енгізген электрондық пошта бар болса оған нұсқаулар келеді.";
"email_error" = "Электрондық хатты жіберу кезінде күтпеген қате орын алды.";
"email_rate_limit_error" = "Бұл әрекетті жиі жасай алмайсыз.";
"email_verify_success" = "Электрондық поштаңыз расталды. Осы әлеуметтік желіде жақсы уақыт өткізуді тілейміз!";
"registration_disabled_info" = "Жүйе әкімшісі тіркеуді өшірді. Мүмкіндігінше, егер досыңыз сайтта тіркелген болса одан шақыру сілтемесін сұраңыз."; "registration_disabled_info" = "Жүйе әкімшісі тіркеуді өшірді. Мүмкіндігінше, егер досыңыз сайтта тіркелген болса одан шақыру сілтемесін сұраңыз.";
"registration_closed" = "Тіркелу жабық"; "registration_closed" = "Тіркелу жабық";
"invites_you_to" = "<strong>$1</strong> сізді $2 желіне шақырады."; "invites_you_to" = "<strong>$1</strong> сізді $2 желіне шақырады.";
@ -189,7 +198,8 @@
"follower" = "Жазылман"; "follower" = "Жазылман";
"login_as" = "Кім ретінде кіру:"; "login_as" = "Кім ретінде кіру:";
"friends_delete" = "Достарымнан жою"; "friends_delete" = "Достарымнан жою";
"friends_reject" = "Өтінімді қабылдамау"; "friends_add" = "Достарыма қосу";
"friends_reject" = "Өтінімнен бас тарту";
"friends_accept" = "Өтінімді қабылдау"; "friends_accept" = "Өтінімді қабылдау";
"send_message" = "Хабарлама жазу"; "send_message" = "Хабарлама жазу";
"incoming_req" = "Жазылушылар"; "incoming_req" = "Жазылушылар";
@ -216,6 +226,7 @@
"subscriptions" = "Жазылымдар"; "subscriptions" = "Жазылымдар";
"join_community" = "Топқа кіру"; "join_community" = "Топқа кіру";
"leave_community" = "Топтан шығу"; "leave_community" = "Топтан шығу";
"check_community" = "Топты қарау";
"min_6_community" = "Топтың аты тым болмаса 6 әріптен көп болу керек"; "min_6_community" = "Топтың аты тым болмаса 6 әріптен көп болу керек";
"participants" = "Қатысушылар"; "participants" = "Қатысушылар";
"groups" = "Топтар"; "groups" = "Топтар";
@ -232,6 +243,8 @@
"all_followers" = "Барлық жазылмандар"; "all_followers" = "Барлық жазылмандар";
"only_administrators" = "Тек әкімшілер"; "only_administrators" = "Тек әкімшілер";
"website" = "Сайт"; "website" = "Сайт";
"managed" = "Басқаратын";
"size" = "Көлемі";
"administrators_one" = "$1 әкімші"; "administrators_one" = "$1 әкімші";
"administrators_other" = "$1 әкімші"; "administrators_other" = "$1 әкімші";
@ -245,6 +258,7 @@
"hidden_yes" = "Жасырылған: Ия"; "hidden_yes" = "Жасырылған: Ия";
"hidden_no" = "Жасырылған: Жоқ"; "hidden_no" = "Жасырылған: Жоқ";
"group_allow_post_for_everyone" = "Бәріне жазба жазуға рұқсат ету"; "group_allow_post_for_everyone" = "Бәріне жазба жазуға рұқсат ету";
"group_hide_from_global_feed" = "Жаһандық арнада жазбаларды көрсетпеу";
"statistics" = "Статистика"; "statistics" = "Статистика";
"group_administrators_list" = "Әкімшілер тізімі"; "group_administrators_list" = "Әкімшілер тізімі";
"group_display_only_creator" = "Тек топты құрған кісіні көрсету"; "group_display_only_creator" = "Тек топты құрған кісіні көрсету";
@ -263,10 +277,20 @@
"groups_one" = "$1 топ"; "groups_one" = "$1 топ";
"groups_other" = "$1 топ"; "groups_other" = "$1 топ";
"groups_list_zero" = "Сіз ешқандай топқа қатыспайсыз.";
"groups_list_one" = "Сіз бір топқа қатысасыз";
"groups_list_other" = "Сіз $1 топтардың қатысушысысыз";
"meetings_zero" = "Кездесулер жоқ"; "meetings_zero" = "Кездесулер жоқ";
"meetings_one" = "$1 кездесу"; "meetings_one" = "$1 кездесу";
"meetings_other" = "$1 кездесу"; "meetings_other" = "$1 кездесу";
"open_new_group" = "Жаңа топ ашу";
"open_group_desc" = "Өзіңізге сай топ таба алмай жүрсіз бе? Өзіңіз ашып алыңыз!";
"search_group" = "Топ іздеу";
"search_by_groups" = "Топтардан іздеу";
"search_group_desc" = "Мұнда сіз бар топтарға шолу жасай аласыз және қажеттіліктеріңізге сай топты таңдай аласыз...";
/* Albums */ /* Albums */
"create" = "Жасау"; "create" = "Жасау";
@ -294,13 +318,21 @@
"name_note" = "Тақырыбы"; "name_note" = "Тақырыбы";
"text_note" = "Мазмұны"; "text_note" = "Мазмұны";
"create_note" = "Жолжазба жазу"; "create_note" = "Жолжазба жазу";
"edit_note" = "Жолжазбаны өндеу";
"actions" = "Әрекеттер"; "actions" = "Әрекеттер";
"feed" = "Жаңалықтар"; "feed" = "Жаңалықтар";
"publish_post" = "Жазба қосу"; "publish_post" = "Жазба қосу";
"edited" = "Өнделген";
"notes_zero" = "Жолжазбалар жоқ"; "notes_zero" = "Жолжазбалар жоқ";
"notes_one" = "$1 жолжазба"; "notes_one" = "$1 жолжазба";
"notes_other" = "$1 жолжазба"; "notes_other" = "$1 жолжазба";
"notes_start_screen" = "Жолжазбалар арқылы оқиғаларды достарыңызбен бөлісуге және олармен не болып жатқанын көруге болады.";
"notes_list_zero" = "Еш жолжазба табылған жоқ";
"notes_list_one" = "$1 жолжазба табылды";
"notes_list_other" = "$1 жолжазба табылды";
/* Menus */ /* Menus */
@ -337,6 +369,7 @@
"left_menu_donate" = "Қайырымдылық жасау"; "left_menu_donate" = "Қайырымдылық жасау";
"footer_about_instance" = "инстанция туралы";
"footer_blog" = "блог"; "footer_blog" = "блог";
"footer_help" = "көмек"; "footer_help" = "көмек";
"footer_developers" = "әзірлеушілерге"; "footer_developers" = "әзірлеушілерге";
@ -360,12 +393,16 @@
"new_password" = "Жаңа құпиясөз"; "new_password" = "Жаңа құпиясөз";
"repeat_password" = "Қайталап жазыңызшы"; "repeat_password" = "Қайталап жазыңызшы";
"avatars_style" = "Аватар стилі"; "avatars_style" = "Аватарлардың стилі";
"style" = "Стилі"; "style" = "Стилі";
"default" = "Әдепкі"; "default" = "әдепкі";
"cut" = "Кесілген";
"round_avatars" = "Дөңгелектелген"; "arbitrary_avatars" = "Ерікті"
"cut" = "Шаршы";
"round_avatars" = "Дөңгелек";
"apply_style_for_this_device" = "Стильді тек осы девайс үшін қосу";
"search_for_groups" = "Топтар іздеу"; "search_for_groups" = "Топтар іздеу";
"search_for_people" = "Кісілер іздеу"; "search_for_people" = "Кісілер іздеу";
@ -391,6 +428,7 @@
"ui_settings_rating_hide" = "Жасыру"; "ui_settings_rating_hide" = "Жасыру";
"additional_links" = "Қосымша сілтемелер"; "additional_links" = "Қосымша сілтемелер";
"ad_poster" = "Жарнама постері";
"two_factor_authentication" = "Екі факторлы аутентификация"; "two_factor_authentication" = "Екі факторлы аутентификация";
"two_factor_authentication_disabled" = "Бұзылудан сенімді қорғаныс қызметін қамтамасыз етеді: парақшаға кіру үшін 2FA қосымшасында алынған кодты енгізу керек."; "two_factor_authentication_disabled" = "Бұзылудан сенімді қорғаныс қызметін қамтамасыз етеді: парақшаға кіру үшін 2FA қосымшасында алынған кодты енгізу керек.";
@ -543,11 +581,26 @@
"points_transfer_successful" = "Сіз сәтті <b>$1</b> <b><a href=\"$2\">$3</a></b> аталатын пайдаланушыға аудардыңыз."; "points_transfer_successful" = "Сіз сәтті <b>$1</b> <b><a href=\"$2\">$3</a></b> аталатын пайдаланушыға аудардыңыз.";
"not_all_information_has_been_entered" = "Барлық мәлімет терілген жоқ"; "not_all_information_has_been_entered" = "Барлық мәлімет терілген жоқ";
"negative_transfer_value" = "Өкінішке орай біз басқа кісіден дауыс ұрлай алмаймыз."; "negative_transfer_value" = "Кешіріңіз, бірақ біз басқа адамнан дауыс ұрлай алмаймыз.";
"message_is_too_long" = "Хабар тым ұзын."; "message_is_too_long" = "Хабар тым ұзын.";
"receiver_not_found" = "Қабылдаушы табылған жоқ."; "receiver_not_found" = "Қабылдаушы табылған жоқ.";
"you_dont_have_enough_points" = "Сізде жеткілікті дауыс саны жоқ."; "you_dont_have_enough_points" = "Сізде жеткілікті дауыс саны жоқ.";
"increase_rating" = "Рейтингті көтеру";
"increase_rating_button" = "Көтеру";
"to_whom" = "Кімге";
"increase_by" = "Неше есе көтеру";
"price" = "Бағасы";
"you_have_unused_votes" = "Сіздің шотыңызда $1 қолданылмаған дауыс бар.";
"apply_voucher" = "Ваучерді қолдану";
"failed_to_increase_rating" = "Рейтингті көтеру мүмкін болмады";
"rating_increase_successful" = "Сіз <b><a href=\"$1\">$2</a></b> атты пайдаланушының рейтингің сәтті <b>$3%</b> есе көтердіңіз.";
"negative_rating_value" = "Кешіріңіз, бірақ біз басқа адамның рейтингің ұрлай алмаймыз.";
"increased_your_rating_by" = "сіздің рейтингіңізді көтерді";
/* Gifts */ /* Gifts */
"gift" = "Сыйлық"; "gift" = "Сыйлық";
@ -637,6 +690,14 @@
"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>.";
/* Registration confirm */
"ec_header" = "Тіркеуді растау";
"ec_title" = "Рахмет!";
"ec_1" = "<b>$1</b>, тіркелуіңіз аяқталуға жақын. Бірнеше минуттан кейін электрондық поштаңызға растау сілтемесі бар хат келуі керек.";
"ec_2" = "Егер қандай да бір себептермен хат келмесе, спам қалтасын тексеріңіз. Хатты ол жерден таппасаңыз, оны қайта жіберуге болады.";
"ec_resend" = "Қайта жіберу";
/* Discussions */ /* Discussions */
"discussions" = "Талқылаулар"; "discussions" = "Талқылаулар";
@ -718,7 +779,7 @@
"invalid_telegram_name" = "Telegram парақшаның аты қате"; "invalid_telegram_name" = "Telegram парақшаның аты қате";
"invalid_telegram_name_comment" = "Сіз енгізген Telegram-дағы парақшаның аты дұрыс емес."; "invalid_telegram_name_comment" = "Сіз енгізген Telegram-дағы парақшаның аты дұрыс емес.";
"token_manipulation_error" = "Токенді өңдеу қатесі"; "token_manipulation_error" = "Токенді өндеу қатесі";
"token_manipulation_error_comment" = "Токен жарамсыз немесе мерзімі өтіп кеткен"; "token_manipulation_error_comment" = "Токен жарамсыз немесе мерзімі өтіп кеткен";
"profile_changed" = "Парақша өзгерілді"; "profile_changed" = "Парақша өзгерілді";
@ -740,6 +801,14 @@
"captcha_error" = "Қате таңбалар енгізілді"; "captcha_error" = "Қате таңбалар енгізілді";
"captcha_error_comment" = "Капча өрісін дұрыс толтырғаныңызға көз жеткізіңіз."; "captcha_error_comment" = "Капча өрісін дұрыс толтырғаныңызға көз жеткізіңіз.";
"failed_to_publish_post" = "Постты жариялау мүмкін болмады";
"failed_to_delete_post" = "Постты жоюға мүмкін болмады";
"media_file_corrupted" = "Медиа мазмұн файлы бүлінген.";
"media_file_corrupted_or_too_large" = "Медиа мазмұн файлы бүлінген немесе тым үлкен.";
"post_is_empty_or_too_big" = "Жазбаңызда түк те жоқ немесе ол тым үлкен.";
"post_is_too_big" = "Жазбаңыз тым үлкен.";
/* Admin actions */ /* Admin actions */
"login_as" = "$1 ретінде кіру"; "login_as" = "$1 ретінде кіру";
@ -758,6 +827,26 @@
"about_openvk" = "OpenVK туралы"; "about_openvk" = "OpenVK туралы";
"about_this_instance" = "Инстанция туралы";
"rules" = "Ережелер";
"most_popular_groups" = "Ең танымал топтар";
"on_this_instance_are" = "Осы инстанцияда:";
"about_users_one" = "<b>1</b> қолданушы";
"about_users_other" = "<b>$1</b> қолданушы";
"about_online_users_one" = "<b>1</b> желідегі пайдаланушы";
"about_online_users_other" = "<b>$1</b> желідегі пайдаланушы";
"about_active_users_one" = "<b>1</b> белсенді пайдаланушы";
"about_active_users_other" = "<b>$1</b> белсенді пайдаланушы";
"about_groups_one" = "<b>1</b> топ";
"about_groups_other" = "<b>$1</b> топ";
"about_wall_posts_one" = "<b>1</b> жазба";
"about_wall_posts_other" = "<b>$1</b> жазба";
/* Dialogs */ /* Dialogs */
"ok" = "ОК"; "ok" = "ОК";

View file

@ -36,6 +36,13 @@
"password_reset_error" = "Непредвиденная ошибка при сбросе пароля."; "password_reset_error" = "Непредвиденная ошибка при сбросе пароля.";
"password_reset_rate_limit_error" = "Нельзя делать это так часто, извините."; "password_reset_rate_limit_error" = "Нельзя делать это так часто, извините.";
"email_sent" = "Письмо было успешно отправлено.";
"email_sent_desc" = "Если ваш электронный адрес существует, вы получите письмо.";
"email_error" = "Непредвиденная ошибка при отправке письма.";
"email_rate_limit_error" = "Нельзя делать это так часто, извините.";
"email_verify_success" = "Ваш Email был подтверждён. Приятного времяпрепровождения!";
"registration_disabled_info" = "Регистрация отключена системным администратором. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте."; "registration_disabled_info" = "Регистрация отключена системным администратором. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте.";
"registration_closed" = "Регистрация закрыта."; "registration_closed" = "Регистрация закрыта.";
"invites_you_to" = "<strong>$1</strong> приглашает вас в $2"; "invites_you_to" = "<strong>$1</strong> приглашает вас в $2";
@ -255,6 +262,7 @@
"only_administrators" = "Только администраторы"; "only_administrators" = "Только администраторы";
"website" = "Сайт"; "website" = "Сайт";
"managed" = "Управляемые"; "managed" = "Управляемые";
"size" = "Размер";
"administrators_one" = "$1 администратор"; "administrators_one" = "$1 администратор";
"administrators_few" = "$1 администратора"; "administrators_few" = "$1 администратора";
@ -420,12 +428,14 @@
"new_password" = "Новый пароль"; "new_password" = "Новый пароль";
"repeat_password" = "Повторите пароль"; "repeat_password" = "Повторите пароль";
"avatars_style" = "Отображение аватар"; "avatars_style" = "Отображение аватаров";
"style" = "Стиль"; "style" = "Стиль";
"default" = "По умолчанию"; "default" = "по умолчанию";
"cut" = "Обрезка";
"round_avatars" = "Круглый аватар"; "arbitrary_avatars" = "Произвольные";
"cut" = "Квадратные";
"round_avatars" = "Круглые";
"apply_style_for_this_device" = "Применить стиль только для этого устройства"; "apply_style_for_this_device" = "Применить стиль только для этого устройства";
@ -619,6 +629,21 @@
"receiver_not_found" = "Получатель не найден."; "receiver_not_found" = "Получатель не найден.";
"you_dont_have_enough_points" = "У вас недостаточно голосов."; "you_dont_have_enough_points" = "У вас недостаточно голосов.";
"increase_rating" = "Повысить рейтинг";
"increase_rating_button" = "Повысить";
"to_whom" = "Кому";
"increase_by" = "Повысить на";
"price" = "Стоимость";
"you_have_unused_votes" = "У Вас $1 неиспользованных голоса на балансе.";
"apply_voucher" = "Применить ваучер";
"failed_to_increase_rating" = "Не удалось повысить рейтинг";
"rating_increase_successful" = "Вы успешно повысыли рейтинг <b><a href=\"$1\">$2</a></b> на <b>$3%</b>.";
"negative_rating_value" = "Мы не можем украсть рейтинг у другого человека, извините.";
"increased_your_rating_by" = "повысил ваш рейтинг на";
/* Gifts */ /* Gifts */
"gift" = "Подарок"; "gift" = "Подарок";
@ -713,6 +738,14 @@
"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>.";
/* Registration confirm */
"ec_header" = "Подтверждение регистрации";
"ec_title" = "Спасибо!";
"ec_1" = "<b>$1</b>, ваша регистрация почти закончена. В течении нескольких минут на ваш адрес E-mail должно прийти письмо с ссылкой для подтверждения вашего адреса почты.";
"ec_2" = "Если по каким-то причинам вам не пришло письмо, то проверьте папку Спам. Если письма не окажется и там, то вы можете переотправить письмо.";
"ec_resend" = "Переотправить письмо";
/* Discussions */ /* Discussions */
"discussions" = "Обсуждения"; "discussions" = "Обсуждения";
@ -794,6 +827,8 @@
"invalid_email_address" = "Неверный Email адрес"; "invalid_email_address" = "Неверный Email адрес";
"invalid_email_address_comment" = "Email, который вы ввели, не является корректным."; "invalid_email_address_comment" = "Email, который вы ввели, не является корректным.";
"invalid_real_name" = "Пожалуйста, используйте реальные имена. Так вашим тульпам будет легче найти вас.";
"invalid_telegram_name" = "Неверное имя Telegram аккаунта"; "invalid_telegram_name" = "Неверное имя Telegram аккаунта";
"invalid_telegram_name_comment" = "Вы ввели неверное имя аккаунта Telegram."; "invalid_telegram_name_comment" = "Вы ввели неверное имя аккаунта Telegram.";
@ -822,6 +857,14 @@
"captcha_error" = "Неправильно введены символы"; "captcha_error" = "Неправильно введены символы";
"captcha_error_comment" = "Пожалуйста, убедитесь, что вы правильно заполнили поле с капчей."; "captcha_error_comment" = "Пожалуйста, убедитесь, что вы правильно заполнили поле с капчей.";
"failed_to_publish_post" = "Не удалось опубликовать пост";
"failed_to_delete_post" = "Не удалось удалить пост";
"media_file_corrupted" = "Файл медиаконтента повреждён.";
"media_file_corrupted_or_too_large" = "Файл медиаконтента повреждён или слишком велик.";
"post_is_empty_or_too_big" = "Пост пустой или слишком большой.";
"post_is_too_big" = "Пост слишком большой.";
/* Admin actions */ /* Admin actions */
"login_as" = "Войти как $1"; "login_as" = "Войти как $1";

View file

@ -364,9 +364,11 @@
"avatars_style" = "Отображеніе портрета"; "avatars_style" = "Отображеніе портрета";
"style" = "Стиль"; "style" = "Стиль";
"default" = "По умолчанію"; "default" = "по умолчанію";
"cut" = "Обрѣзка";
"round_avatars" = "Круглый портретъ"; "arbitrary_avatars" = "Произвольные​";
"cut" = "Квадратные​";
"round_avatars" = "Круглые";
"search_for_groups" = "Поискъ обществъ"; "search_for_groups" = "Поискъ обществъ";
"search_for_people" = "Поискъ людей"; "search_for_people" = "Поискъ людей";