Merge branch 'openvk:master' into master

This commit is contained in:
Dmitry 2021-12-24 19:29:56 +07:00 committed by GitHub
commit 8ea6204e4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 540 additions and 231 deletions

0
.gitmodules vendored
View file

View file

@ -10,7 +10,7 @@ To be honest, we don't even know whether it even works. However, this version is
Please use the master branch, as it has the most changes. Please use the master branch, as it has the most changes.
Updating the source code is done with this command: `git pull --recurse-submodules` Updating the source code is done with this command: `git pull`
## Instances ## Instances
@ -29,7 +29,7 @@ If you want, you can add your instance to the list above so that people can regi
* PHP 8 has **not** yet been tested, so you should not expect it to work. * PHP 8 has **not** yet been tested, so you should not expect it to work.
2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this: 2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
``` ```
git clone --recursive https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
``` ```
3. And enable them: 3. And enable them:
@ -44,8 +44,6 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
8. Move to `Web/static/js` and execute `yarn install` 8. Move to `Web/static/js` and execute `yarn install`
9. Set `openvk` as your root app in `chandler.yml` 9. Set `openvk` as your root app in `chandler.yml`
**Note**: If OVK submodules were not downloaded beforehand (i.e. `--recursive` was not used during cloning), this command *must be* executed in the `openvk` folder: `git submodule update --init`
Once you are done, you can login as a system administrator on the network itself (no registration required): Once you are done, you can login as a system administrator on the network itself (no registration required):
* **Login**: `admin@localhost.localdomain6` * **Login**: `admin@localhost.localdomain6`
* **Password**: `admin` * **Password**: `admin`

View file

@ -135,6 +135,11 @@ class Club extends RowModel
return (bool) $this->getRecord()->everyone_can_create_topics; return (bool) $this->getRecord()->everyone_can_create_topics;
} }
function isDisplayTopicsAboveWallEnabled(): bool
{
return (bool) $this->getRecord()->display_topics_above_wall;
}
function getType(): int function getType(): int
{ {
return $this->getRecord()->type; return $this->getRecord()->type;

View file

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

View file

@ -60,7 +60,7 @@ trait TRichText
$rel = $this->isAd() ? "sponsored" : "ugc"; $rel = $this->isAd() ? "sponsored" : "ugc";
$text = $this->formatLinks($text); $text = $this->formatLinks($text);
$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("%@([A-Za-z0-9]++)%Xu", "[$1|@$1]", $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("%(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "<a href='/feed/hashtag/$2'>$1</a>", $text);
$text = $this->formatEmojis($text); $text = $this->formatEmojis($text);

View file

@ -52,6 +52,23 @@ class Users
"online" => sizeof((clone $this->users)->where("online >= ?", time() - 900)), "online" => sizeof((clone $this->users)->where("online >= ?", time() - 900)),
]; ];
} }
function getByAddress(string $address): ?User
{
if(substr_compare($address, "/", -1) === 0)
$address = substr($address, 0, iconv_strlen($address) - 1);
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
if(strpos($address, $serverUrl . "/") === 0)
$address = substr($address, iconv_strlen($serverUrl) + 1);
if(strpos($address, "id") === 0) {
$user = $this->get((int) substr($address, 2));
if($user) return $user;
}
return $this->getByShortUrl($address);
}
use \Nette\SmartObject; use \Nette\SmartObject;
} }

View file

@ -208,6 +208,7 @@ final class GroupPresenter extends OpenVKPresenter
$club->setWall(empty($this->postParam("wall")) ? 0 : 1); $club->setWall(empty($this->postParam("wall")) ? 0 : 1);
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display")); $club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1); $club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
$club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);;
$website = $this->postParam("website") ?? ""; $website = $this->postParam("website") ?? "";
if(empty($website)) if(empty($website))

View file

@ -138,12 +138,13 @@ final class PhotosPresenter extends OpenVKPresenter
} }
$this->template->album = $album; $this->template->album = $album;
$this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1) ) ); $this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1), 20) );
$this->template->paginatorConf = (object) [ $this->template->paginatorConf = (object) [
"count" => $album->getPhotosCount(), "count" => $album->getPhotosCount(),
"page" => $this->queryParam("p") ?? 1, "page" => $this->queryParam("p") ?? 1,
"amount" => sizeof($this->template->photos), "amount" => sizeof($this->template->photos),
"perPage" => OPENVK_DEFAULT_PER_PAGE, "perPage" => 20,
"atBottom" => true
]; ];
} }

View file

@ -10,6 +10,7 @@ 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\Util\Validator; use openvk\Web\Util\Validator;
use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification;
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};
@ -465,4 +466,42 @@ final class UserPresenter extends OpenVKPresenter
$this->user->identity->save(); $this->user->identity->save();
$this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message")); $this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message"));
} }
function renderCoinsTransfer(): void
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
$receiverAddress = $this->postParam("receiver");
$value = (int) $this->postParam("value");
$message = $this->postParam("message");
if(!$receiverAddress || !$value)
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("not_all_information_has_been_entered"));
if($value < 0)
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("negative_transfer_value"));
if(iconv_strlen($message) > 255)
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("message_is_too_long"));
$receiver = $this->users->getByAddress($receiverAddress);
if(!$receiver)
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("receiver_not_found"));
if($this->user->identity->getCoins() < $value)
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("you_dont_have_enough_points"));
if($this->user->id !== $receiver->getId()) {
$this->user->identity->setCoins($this->user->identity->getCoins() - $value);
$this->user->identity->save();
$receiver->setCoins($receiver->getCoins() + $value);
$receiver->save();
(new CoinsTransferNotification($receiver, $this->user->identity, $value, $message))->emit();
}
$this->flashFail("succ", tr("information_-1"), tr("points_transfer_successful", tr("points_amount", $value), $receiver->getURL(), htmlentities($receiver->getCanonicalName())));
}
} }

View file

@ -29,6 +29,9 @@
{include description, x => $dat} {include description, x => $dat}
</td> </td>
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px">
{include actions, x => $dat}
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -1,9 +1,9 @@
{extends "../@layout.xml"} {extends "../@layout.xml"}
{block title}Об OpenVK{/block} {block title}{_about_openvk}{/block}
{block header} {block header}
Об OpenVK {_about_openvk}
{/block} {/block}
{block content} {block content}
@ -343,7 +343,7 @@
<tbody> <tbody>
<tr class="h"> <tr class="h">
<th>Name</th> <th>Name</th>
<th>Status</th> <th style="width: 50px;">Status</th>
<th>Version</th> <th>Version</th>
<th>Description</th> <th>Description</th>
<th>Author</th> <th>Author</th>

View file

@ -24,7 +24,7 @@
{/block} {/block}
{block description} {block description}
<table class="ugc-table" n:if="$hideInfo ? !$x->anon : true"> <table class="ugc-table" n:if="$hideInfo ? (!$x->anon || $x->sender->getId() === $thisUser->getId()) : true">
<tbody> <tbody>
<tr> <tr>
<td><span class="nobold">{_sender}: </span></td> <td><span class="nobold">{_sender}: </span></td>

View file

@ -82,7 +82,8 @@
<span class="nobold">{_discussions}: </span> <span class="nobold">{_discussions}: </span>
</td> </td>
<td> <td>
<input type="checkbox" name="everyone_can_create_topics" value="1" n:attr="checked => $club->isEveryoneCanCreateTopics()" /> {_everyone_can_create_topics} <input type="checkbox" name="everyone_can_create_topics" value="1" n:attr="checked => $club->isEveryoneCanCreateTopics()" /> {_everyone_can_create_topics}<br>
<input type="checkbox" name="display_topics_above_wall" value="1" n:attr="checked => $club->isDisplayTopicsAboveWallEnabled()" /> {_display_list_of_topics_above_wall}
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -14,10 +14,6 @@
<a n:if="$onlyShowManagers" href="/club{$club->getId()}/followers" style="float: right;">{_only_administrators}</a> <a n:if="$onlyShowManagers" href="/club{$club->getId()}/followers" style="float: right;">{_only_administrators}</a>
{/block} {/block}
{block actions}
{/block}
{* BEGIN ELEMENTS DESCRIPTION *} {* BEGIN ELEMENTS DESCRIPTION *}
{block tabs} {block tabs}
@ -81,50 +77,13 @@
{/if} {/if}
</td> </td>
</tr> </tr>
<tr n:if="$club->canBeModifiedBy($thisUser ?? NULL)">
<td width="120" valign="top"><span class="nobold">{_actions}: </span></td>
<td>
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
{if $manager}
{_devote}
{else}
{_promote_to_admin}
{/if}
</a>
{if $club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()}
|
<a href="javascript:changeOwner({$club->getId()}, {$user->getId()})">
{_promote_to_owner}
</a>
{/if}
{if $manager}
|
<a href="javascript:setClubAdminComment('{$club->getId()}', '{$manager->getUserId()}', '{rawurlencode($csrfToken)}')">
{_set_comment}
</a>
{/if}
<a n:if="$club->getOwner()->getId() === $user->getId()" href="javascript:setClubAdminComment('{$club->getId()}', '{$club->getOwner()->getId()}', '{rawurlencode($csrfToken)}')">
{_set_comment}
</a>
{if $manager}
|
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$manager->isHidden()}&hash={rawurlencode($csrfToken)}">
{if $manager->isHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
</a>
{/if}
{if $club->getOwner()->getId() == $user->getId()}
|
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$club->isOwnerHidden()}&hash={rawurlencode($csrfToken)}">
{if $club->isOwnerHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
</a>
{/if}
</td>
</tr>
</tbody> </tbody>
</table> </table>
<script n:if="$club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()"> <script n:if="$club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()">
console.log("gayshit") console.log("gayshit");
console.log("сам такой");
function changeOwner(club, newOwner) { function changeOwner(club, newOwner) {
const action = "/groups/" + club + "/setNewOwner/" + newOwner; const action = "/groups/" + club + "/setNewOwner/" + newOwner;
@ -145,3 +104,40 @@
} }
</script> </script>
{/block} {/block}
{block actions}
{var user = $x instanceof $Manager ? $x->getUser() : $x}
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
{if $club->canBeModifiedBy($thisUser ?? NULL)}
<a class="profile_link" href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
{if $manager}
{_devote}
{else}
{_promote_to_admin}
{/if}
</a>
{if $club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()}
<a class="profile_link" href="javascript:changeOwner({$club->getId()}, {$user->getId()})">
{_promote_to_owner}
</a>
{/if}
{if $manager}
<a class="profile_link" href="javascript:setClubAdminComment('{$club->getId()}', '{$manager->getUserId()}', '{rawurlencode($csrfToken)}')">
{_set_comment}
</a>
{/if}
<a class="profile_link" n:if="$club->getOwner()->getId() === $user->getId()" href="javascript:setClubAdminComment('{$club->getId()}', '{$club->getOwner()->getId()}', '{rawurlencode($csrfToken)}')">
{_set_comment}
</a>
{if $manager}
<a class="profile_link" href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$manager->isHidden()}&hash={rawurlencode($csrfToken)}">
{if $manager->isHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
</a>
{/if}
{if $club->getOwner()->getId() == $user->getId()}
<a class="profile_link" href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$club->isOwnerHidden()}&hash={rawurlencode($csrfToken)}">
{if $club->isOwnerHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
</a>
{/if}
{/if}
{/block}

View file

@ -41,45 +41,54 @@
</table> </table>
</div> </div>
<div n:if="$club->getFollowersCount() > 0"> <div n:if="$club->getFollowersCount() > 0">
{var followersCount = $club->getFollowersCount()} {var followersCount = $club->getFollowersCount()}
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});"> <div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
{_participants} {_participants}
</div> </div>
<div> <div>
<div class="content_subtitle"> <div class="content_subtitle">
{tr("participants", $followersCount)} {tr("participants", $followersCount)}
<div style="float:right;"> <div style="float:right;">
<a href="/club{$club->getId()}/followers">{_all_title}</a> <a href="/club{$club->getId()}/followers">{_all_title}</a>
</div>
</div> </div>
<div style="padding-left: 5px;"> </div>
<table <div style="padding-left: 5px;" class="content_list long">
n:foreach="$club->getFollowers(1) as $follower" <div class="cl_element" n:foreach="$club->getFollowers(1) as $follower">
n:class="User" <div class="cl_avatar">
style="text-align:center;display:inline-block;width:62px" <a href="{$follower->getURL()}">
cellspacing=4> <img class="ava" src="{$follower->getAvatarUrl()}" />
<tbody> </a>
<tr> </div>
<td> <a href="{$follower->getURL()}" class="cl_name">
<a href="{$follower->getURL()}"> <text class="cl_fname">{$follower->getFirstName()}</text>
<img src="{$follower->getAvatarUrl()}" width="50" /> <text class="cl_lname">{$follower->getLastName()}</text>
</a> </a>
</td>
</tr>
<tr>
<td>
<a href="{$follower->getURL()}">{$follower->getFirstName()}</a>
</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</div>
<div n:if="($topicsCount > 0 || $club->isEveryoneCanCreateTopics() || ($thisUser && $club->canBeModifiedBy($thisUser))) && $club->isDisplayTopicsAboveWallEnabled()">
<div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});">
{_discussions}
</div>
<div>
<div class="content_subtitle">
{tr("topics", $topicsCount)}
<div style="float: right;">
<a href="/board{$club->getId()}">{_"all_title"}</a>
</div>
</div>
<div>
<div n:foreach="$topics as $topic" class="topic-list-item" style="padding: 8px;">
<b><a href="/topic{$topic->getPrettyId()}">{$topic->getTitle()}</a></b><br>
<span class="nobold">{tr("updated_at", $topic->getUpdateTime())}</span>
</div>
</div>
</div>
</div>
{presenter "openvk!Wall->wallEmbedded", -$club->getId()} {presenter "openvk!Wall->wallEmbedded", -$club->getId()}
</div> </div>
<div class="right_small_block"> <div class="right_small_block">
<a href="{$club->getAvatarLink()|nocheck}"> <a href="{$club->getAvatarLink()|nocheck}">
@ -203,7 +212,7 @@
</div> </div>
</div> </div>
</div> </div>
<div n:if="$topicsCount > 0 || $club->isEveryoneCanCreateTopics() || $club->canBeModifiedBy($thisUser)"> <div n:if="($topicsCount > 0 || $club->isEveryoneCanCreateTopics() || ($thisUser && $club->canBeModifiedBy($thisUser))) && !$club->isDisplayTopicsAboveWallEnabled()">
<div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});"> <div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});">
{_discussions} {_discussions}
</div> </div>

View file

@ -19,13 +19,6 @@
</div> </div>
{/block} {/block}
{block actions}
<div class="tile">
<a class="profile_link" href="?">{_unreaded}</a>
<a class="profile_link" href="?act=archived">{_archive}</a>
</div>
{/block}
{* BEGIN ELEMENTS DESCRIPTION *} {* BEGIN ELEMENTS DESCRIPTION *}
{block link|strip|stripHtml} {block link|strip|stripHtml}

View file

@ -27,23 +27,24 @@
&nbsp;|&nbsp; &nbsp;|&nbsp;
<a href="/album{$album->getPrettyId()}/edit">{_"edit_album"}</a> <a href="/album{$album->getPrettyId()}/edit">{_"edit_album"}</a>
{/if} {/if}
<br/> <br/><br/>
{if $album->getPhotosCount() > 0} {if $album->getPhotosCount() > 0}
{foreach $photos as $photo} <div class="container_gray album-flex">
{php if($photo->isDeleted()) continue; } {foreach $photos as $photo}
<div class="album-photo" style="display: inline-table;"> {php if($photo->isDeleted()) continue; }
<a <div class="album-photo">
n:if="!is_null($thisUser) && $album->canBeModifiedBy($thisUser)" <a
href="/album{$album->getPrettyId()}/remove_photo.pl/{$photo->getId()}" class="album-photo--delete"> n:if="!is_null($thisUser) && $album->canBeModifiedBy($thisUser)"
&times; href="/album{$album->getPrettyId()}/remove_photo.pl/{$photo->getId()}" class="album-photo--delete">
</a> &times;
</a>
<a href="/photo{$photo->getPrettyId()}?from=album{$album->getId()}">
<img class="album-photo--image" src="{$photo->getURL()}" alt="{$photo->getDescription()}" style="width:unset;max-height:unset;max-width: 188px;" /> <a href="/photo{$photo->getPrettyId()}?from=album{$album->getId()}">
</a> <img class="album-photo--image" src="{$photo->getURL()}" alt="{$photo->getDescription()}" />
</div> </a>
{/foreach} </div>
{/foreach}
</div>
{include "../components/paginator.xml", conf => $paginatorConf} {include "../components/paginator.xml", conf => $paginatorConf}
{else} {else}
{include "../components/nothing.xml"} {include "../components/nothing.xml"}

View file

@ -110,7 +110,7 @@
</div> </div>
{/if} {/if}
{if $comment->getUType() === 1} {if $comment->getUType() === 1 && !is_null($comment->isLikedByUser())}
<div class="post-menu"> <div class="post-menu">
<strong> <strong>
{if $comment->isLikedByUser()} {if $comment->isLikedByUser()}

View file

@ -79,3 +79,39 @@
</tbody> </tbody>
</table> </table>
{/block} {/block}
{block actions}
{if $x->getId() !== $thisUser->getId()}
{var subStatus = $x->getSubscriptionStatus($thisUser)}
{if $subStatus === 0}
<form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="add" />
<input type="hidden" name="id" value="{$x->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" class="profile_link" value="{_"friends_add"}" />
</form>
{elseif $subStatus === 1}
<form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="add" />
<input type="hidden" name="id" value="{$x->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" class="profile_link" value="{_"friends_accept"}" />
</form>
{elseif $subStatus === 2}
<form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="rem" />
<input type="hidden" name="id" value="{$x->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" class="profile_link" value="{_"friends_reject"}" />
</form>
{elseif $subStatus === 3}
<a href="/im?sel={$x->getId()}" class="profile_link">{_"send_message"}</a>
<form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="rem" />
<input type="hidden" name="id" value="{$x->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" class="profile_link" value="{_"friends_delete"}" />
</form>
{/if}
{/if}
{/block}

View file

@ -49,23 +49,17 @@
{block description} {block description}
{$x->getDescription()} {$x->getDescription()}
{if $x->canBeModifiedBy($thisUser ?? NULL)}
{var clubPinned = $thisUser->isClubPinned($x)}
<table n:if="$clubPinned || $thisUser->getPinnedClubCount() <= 10">
<tbody>
<tr>
<td width="120" valign="top"><span class="nobold">{_actions}: </span></td>
<td>
<a href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
{if $clubPinned}
{_remove_from_left_menu}
{else}
{_add_to_left_menu}
{/if}
</a>
</td>
</tr>
</tbody>
</table>
{/if}
{/block} {/block}
{var clubPinned = $thisUser->isClubPinned($x)}
{if $x->canBeModifiedBy($thisUser ?? NULL) || $clubPinned || $thisUser->getPinnedClubCount() <= 10}
{block actions}
<a class="profile_link" href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
{if $clubPinned}
{_remove_from_left_menu}
{else}
{_add_to_left_menu}
{/if}
</a>
{/block}
{/if}

View file

@ -332,6 +332,7 @@
<div style="width: 75%; display: inline-block;"> <div style="width: 75%; display: inline-block;">
{presenter "openvk!Support->knowledgeBaseArticle", "points"} {presenter "openvk!Support->knowledgeBaseArticle", "points"}
<center>{tr("also_you_can_transfer_points", $thisUser->getCoins(), rawurlencode($csrfToken))|noescape}</center>
</div> </div>
<div style="width: 22%; float: right;"> <div style="width: 22%; float: right;">
<p style="margin: 0; font-size: medium; text-align: center;"> <p style="margin: 0; font-size: medium; text-align: center;">

View file

@ -91,25 +91,25 @@
</a> </a>
{/if} {/if}
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce']" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a> <a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
{var subStatus = $user->getSubscriptionStatus($thisUser)} {var subStatus = $user->getSubscriptionStatus($thisUser)}
{if $subStatus === 0} {if $subStatus === 0}
<form action="/setSub/user" method="post"> <form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="add" /> <input type="hidden" name="act" value="add" />
<input type="hidden" name="id" value="{$user->getId()}" /> <input type="hidden" name="id" value="{$user->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" class="profile_link" value="{_"friends_add"}" /> <input type="submit" class="profile_link" value="{_"friends_add"}" />
</form> </form>
{elseif $subStatus === 1} {elseif $subStatus === 1}
<form action="/setSub/user" method="post"> <form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="add" /> <input type="hidden" name="act" value="add" />
<input type="hidden" name="id" value="{$user->getId()}" /> <input type="hidden" name="id" value="{$user->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
<input type="submit" class="profile_link" value="{_"friends_accept"}" /> <input type="submit" class="profile_link" value="{_"friends_accept"}" />
</form> </form>
{elseif $subStatus === 2} {elseif $subStatus === 2}
<form action="/setSub/user" method="post"> <form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="rem" /> <input type="hidden" name="act" value="rem" />
<input type="hidden" name="id" value="{$user->getId()}" /> <input type="hidden" name="id" value="{$user->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
@ -117,7 +117,7 @@
</form> </form>
{elseif $subStatus === 3} {elseif $subStatus === 3}
<a href="/im?sel={$user->getId()}" class="profile_link">{_"send_message"}</a> <a href="/im?sel={$user->getId()}" class="profile_link">{_"send_message"}</a>
<form action="/setSub/user" method="post"> <form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="rem" /> <input type="hidden" name="act" value="rem" />
<input type="hidden" name="id" value="{$user->getId()}" /> <input type="hidden" name="id" value="{$user->getId()}" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
@ -125,6 +125,7 @@
</form> </form>
{/if} {/if}
{/if} {/if}
<a n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a>
</div> </div>
<div n:if="isset($thisUser) && !$thisUser->prefersNotToSeeRating()" class="profile-hints"> <div n:if="isset($thisUser) && !$thisUser->prefersNotToSeeRating()" class="profile-hints">
{var completeness = $user->getProfileCompletenessReport()} {var completeness = $user->getProfileCompletenessReport()}
@ -150,39 +151,15 @@
</a> </a>
<a n:if="in_array('telegram', $completeness->unfilled)" href="/edit?act=contacts"> <a n:if="in_array('telegram', $completeness->unfilled)" href="/edit?act=contacts">
<img src="/assets/packages/static/openvk/img/icon2.gif" /> <img src="/assets/packages/static/openvk/img/icon2.gif" />
Telegram (+10%) Telegram (+15%)
</a> </a>
<a n:if="in_array('status', $completeness->unfilled)" href="/edit"> <a n:if="in_array('status', $completeness->unfilled)" href="/edit">
<img src="/assets/packages/static/openvk/img/icon3.gif" /> <img src="/assets/packages/static/openvk/img/icon3.gif" />
{_status} (+10%) {_status} (+15%)
</a> </a>
{/if} {/if}
</div> </div>
<br /> <br />
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && ($giftCount = $user->getGiftCount()) > 0">
<div class="content_title_expanded" onclick="hidePanel(this, {$giftCount});">
{_gifts}
</div>
<div>
<div class="content_subtitle">
{tr("gifts", $giftCount)}
<div style="float:right;">
<a href="/gifts{$user->getId()}">{_all_title}</a>
</div>
</div>
<div class="ovk-avView">
<div class="ovk-avView--el" n:foreach="$user->getGifts(1, 3) as $giftDescriptor">
{var hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
<img class="ava"
src="{$giftDescriptor->gift->getImage(2)}"
alt="{$hideInfo ? tr('gift') : ($giftDescriptor->caption ?? tr('gift'))}" />
</a>
</div>
</div>
</div>
</div>
<div n:if="$user->getFriendsCount() > 0 && $user->getPrivacyPermission('friends.read', $thisUser ?? NULL)"> <div n:if="$user->getFriendsCount() > 0 && $user->getPrivacyPermission('friends.read', $thisUser ?? NULL)">
{var friendCount = $user->getFriendsCount()} {var friendCount = $user->getFriendsCount()}
@ -196,37 +173,17 @@
<a href="/friends{$user->getId()}">{_"all_title"}</a> <a href="/friends{$user->getId()}">{_"all_title"}</a>
</div> </div>
</div> </div>
<div class="ovk-avView"> <div class="content_list">
<div class="ovk-avView--el" n:foreach="$user->getFriends(1) as $friend"> <div class="cl_element" n:foreach="$user->getFriends(1) as $friend">
<a href="{$friend->getURL()}"> <div class="cl_avatar">
<img class="ava" src="{$friend->getAvatarUrl()}" /> <a href="{$friend->getURL()}">
</a> <img class="ava" src="{$friend->getAvatarUrl()}" />
<br/> </a>
<a href="{$friend->getURL()}">{$friend->getFirstName()}</a> </div>
</div> <a href="{$friend->getURL()}" class="cl_name">
</div> <text class="cl_fname">{$friend->getFirstName()}</text>
</div> <text class="cl_lname">{$friend->getLastName()}</text>
</div> </a>
<div n:if="$user->getFollowersCount() > 0">
{var followersCount = $user->getFollowersCount()}
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
{_followers}
</div>
<div>
<div class="content_subtitle">
{tr("followers", $followersCount)}
<div style="float:right;">
<a href="/friends{$user->getId()}?act=incoming">{_"all_title"}</a>
</div>
</div>
<div class="ovk-avView">
<div class="ovk-avView--el" n:foreach="$user->getFollowers(1) as $follower">
<a href="{$follower->getURL()}">
<img class="ava" src="{$follower->getAvatarUrl()}" />
</a>
<br/>
<a href="{$follower->getURL()}">{$follower->getFirstName()}</a>
</div> </div>
</div> </div>
</div> </div>
@ -508,6 +465,35 @@
</div> </div>
</div> </div>
</div> </div>
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && ($giftCount = $user->getGiftCount()) > 0">
<div class="content_title_expanded" onclick="hidePanel(this, {$giftCount});">
{_gifts}
</div>
<div>
<div class="content_subtitle">
{tr("gifts", $giftCount)}
<div style="float:right;">
{if OPENVK_ROOT_CONF['openvk']['preferences']['commerce']}
<a href="/gifts?act=pick&user={$user->getId()}">{_send_gift}</a> |
{/if}
<a href="/gifts{$user->getId()}">{_all_title}</a>
</div>
</div>
<div class="content_list long">
<div class="cl_element" style="width: 25%;" n:foreach="$user->getGifts(1, 4) as $giftDescriptor">
{var hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
<div class="cl_avatar">
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
<img style="width: 70px; max-height: 70px;"
src="{$giftDescriptor->gift->getImage(2)}"
alt="{$hideInfo ? tr('gift') : ($giftDescriptor->caption ?? tr('gift'))}" />
</a>
</div>
</div>
</div>
</div>
</div>
{presenter "openvk!Wall->wallEmbedded", $user->getId()} {presenter "openvk!Wall->wallEmbedded", $user->getId()}

View file

@ -33,7 +33,7 @@
{if $comment->canBeDeletedBy($thisUser)} {if $comment->canBeDeletedBy($thisUser)}
<a href="/comment{$comment->getId()}/delete">{_"delete"}</a>&nbsp;| <a href="/comment{$comment->getId()}/delete">{_"delete"}</a>&nbsp;|
{/if} {/if}
<a class="comment-reply">Ответить</a> <a class="comment-reply">{_"reply"}</a>
<div style="float: right; font-size: .7rem;"> <div style="float: right; font-size: .7rem;">
<a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}"> <a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}">
<div class="heart" style="{if $comment->hasLikeFrom($thisUser)}opacity: 1;{else}opacity: 0.4;{/if}"></div> <div class="heart" style="{if $comment->hasLikeFrom($thisUser)}opacity: 1;{else}opacity: 0.4;{/if}"></div>

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> {_transferred_to_you} {tr("points_amount", $value)}.
{if !empty($message)}
{_message}: "{$message}".
{/if}

View file

@ -10,7 +10,8 @@
<td width="54" valign="top"> <td width="54" valign="top">
<img <img
src="{$author->getAvatarURL()}" src="{$author->getAvatarURL()}"
width="{ifset $compact}25{else}50{/ifset}" /> width="{ifset $compact}25{else}50{/ifset}"
{ifset $compact}class="cCompactAvatars"{/ifset} />
{if !$post->isPostedOnBehalfOfGroup() && !$compact} {if !$post->isPostedOnBehalfOfGroup() && !$compact}
<span n:if="$author->isOnline()" class="post-online"> <span n:if="$author->isOnline()" class="post-online">
{_online} {_online}

View file

@ -57,7 +57,7 @@ class Localizator
$lang = is_null($lang) ? static::DEFAULT_LANG : $lang; $lang = is_null($lang) ? static::DEFAULT_LANG : $lang;
$array = @self::parse(dirname(__FILE__) . "/../../locales/$lang.strings"); $array = @self::parse(dirname(__FILE__) . "/../../locales/$lang.strings");
return $array[$id] ?? (!empty($array["__fallback"]) ? $this->_($id, $array["__fallback"]) : "@$id"); return $array[$id] ?? "@$id";
} }
function export($lang = NULL): ?array function export($lang = NULL): ?array

View file

@ -59,6 +59,8 @@ routes:
handler: "User->twoFactorAuthSettings" handler: "User->twoFactorAuthSettings"
- url: "/settings/2fa/disable" - url: "/settings/2fa/disable"
handler: "User->disableTwoFactorAuth" handler: "User->disableTwoFactorAuth"
- url: "/coins_transfer"
handler: "User->coinsTransfer"
- url: "/id{num}" - url: "/id{num}"
handler: "User->view" handler: "User->view"
- url: "/friends{num}" - url: "/friends{num}"

View file

@ -257,23 +257,29 @@ a {
.album-photo { .album-photo {
position: relative; position: relative;
background-color: darkgrey; width: 25%;
margin: 4pt; max-height: 140px;
width: calc(33% - 10pt); margin-bottom: 8px;
height: 82px;
text-align: center; text-align: center;
vertical-align: text-top; display: flex;
align-items: center;
justify-content: center;
} }
.album-photo img { .album-photo img {
width: 100%; width: unset;
max-height: 82px; max-height: 120px !important;
max-width: 83%;
vertical-align: top; vertical-align: top;
border: 1px #ccc solid;
padding: 8px;
background-color: #fff;
} }
.album-photo > .album-photo--delete { .album-photo > .album-photo--delete {
position: absolute; position: absolute;
right: 0; right: 0;
top: 0;
padding: 5px; padding: 5px;
margin: 4px; margin: 4px;
color: #fff; color: #fff;
@ -289,6 +295,11 @@ a {
opacity: 1; opacity: 1;
} }
.album-flex {
display: flex;
flex-wrap: wrap;
}
.name-checkmark { .name-checkmark {
margin-left: 2pt; margin-left: 2pt;
} }
@ -307,6 +318,10 @@ a {
cursor: pointer; cursor: pointer;
} }
.profile_link_form {
margin-bottom: 0;
}
#profile_links { #profile_links {
margin: 10px 0; margin: 10px 0;
} }
@ -315,6 +330,10 @@ a {
background: #ECECEC; background: #ECECEC;
} }
.action_links > .profile_link, .action_links > .profile_link_form > .profile_link {
width: 150px;
}
.page_footer { .page_footer {
margin-left: 95px; margin-left: 95px;
padding-top: 5px; padding-top: 5px;
@ -683,6 +702,44 @@ span {
max-height: 63px; max-height: 63px;
} }
.content_list {
display: flex;
width: 200px;
flex-wrap: wrap;
}
.content_list.long {
width: 397px;
}
.content_list .cl_element {
width: 33%;
}
.content_list.long .cl_element {
width: 16.5%;
}
.content_list .cl_element .cl_avatar {
padding: 7px 7px 0 7px;
text-align: center;
}
.content_list .cl_element .cl_name {
padding: 0 3px;
text-align: center;
display: flex;
flex-direction: column;
}
.content_list .cl_element .cl_name .cl_lname {
font-size: 7pt;
}
.ava {
width: 45px;
}
table.User { table.User {
vertical-align: text-top; vertical-align: text-top;
} }
@ -696,6 +753,10 @@ table.User {
margin-bottom: -12px; margin-bottom: -12px;
} }
.container_gray.bottom {
border-bottom: #ebebeb solid 1px;
}
#auth .container_gray { #auth .container_gray {
margin-left: -10px; margin-left: -10px;
margin-bottom: -10px; margin-bottom: -10px;
@ -1687,3 +1748,9 @@ body.scrolled .toTop:hover {
border-bottom: #e6e6e6 solid 1px; border-bottom: #e6e6e6 solid 1px;
padding: 4px; padding: 4px;
} }
.messagebox-content-header {
background: #F7F7F7;
margin: -20px;
padding: 10px;
}

View file

@ -1,36 +1,37 @@
function tr(string, ...arg) { function tr(string, ...args) {
let output = window.lang[string]; let output = window.lang[string];
if(arg.length > 0) { if(args.length > 0) {
if(typeof arg[0] == 'number') { if(typeof args[0] === "number") {
let numberedStringId; const cardinal = args[0];
let cardinal = arg[0]; let numberedString;
switch(cardinal) { switch(cardinal) {
case 0: case 0:
numberedString = string + '_zero'; numberedString = string + "_zero";
break; break;
case 1: case 1:
numberedString = string + '_one'; numberedString = string + "_one";
break; break;
default: default:
numberedString = string + (cardinal < 5 ? '_few' : '_other'); numberedString = string + (cardinal < 5 ? "_few" : "_other");
} }
let newoutput = window.lang[numberedString]; let newOutput = window.lang[numberedString];
if(newoutput === null) { if(newOutput === null)
newoutput = window.lang[string + '_other']; newOutput = window.lang[string + "_other"];
if(newoutput === null) {
newoutput = output;
}
}
output = newoutput; if(newOutput === null)
newOutput = output;
output = newOutput;
} }
} }
let i = 1; if(output == null)
arg.forEach(element => { return "@" + string;
output = output.replace(RegExp('(\\$' + i + ')'), element);
i++; for(const [ i, element ] of Object.entries(args))
}); output = output.replace(RegExp("(\\$" + (Number(i) + 1) + ")"), element);
return output; return output;
} }

View file

@ -171,3 +171,49 @@ function setClubAdminComment(clubId, adminId, hash) {
Function.noop Function.noop
]); ]);
} }
function showCoinsTransferDialog(coinsCount, hash) {
MessageBox(tr("transfer_poins"), `
<div class="messagebox-content-header">
${tr("points_transfer_dialog_header_1")}
${tr("points_transfer_dialog_header_2")} <b>${tr("points_amount", coinsCount)}</b>
</div>
<form action="/coins_transfer" method="post" id="coins_transfer_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("receiver_address")}:</span>
</td>
<td>
<input type="text" name="receiver" style="width: 100%;" />
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr("coins_count")}:</span>
</td>
<td>
<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>
</tbody>
</table>
<input type="hidden" name="hash" value="${hash}" />
</form>
`, [tr("transfer_poins_button"), tr("cancel")], [
() => {
document.querySelector("#coins_transfer_form").submit();
},
Function.noop
]);
}

View file

@ -154,7 +154,7 @@ composer2 install
```bash ```bash
cd .. cd ..
git clone --recursive https://github.com/openvk/openvk.git git clone https://github.com/openvk/openvk.git
cd openvk/ cd openvk/
composer2 install composer2 install
cd Web/static/js cd Web/static/js

View file

@ -0,0 +1 @@
ALTER TABLE `groups` ADD COLUMN `display_topics_above_wall` BOOLEAN NOT NULL DEFAULT FALSE AFTER `everyone_can_create_topics`;

View file

@ -1,3 +1,5 @@
#include <en>
"__locale" = "hy_AM.utf8;hy_AM.UTF-8;Arm"; "__locale" = "hy_AM.utf8;hy_AM.UTF-8;Arm";
"__WinEncoding" = "Windows-1251"; "__WinEncoding" = "Windows-1251";

View file

@ -1,3 +1,5 @@
#include <ru>
"__locale" = "be_BY.UTF-8;Bel"; "__locale" = "be_BY.UTF-8;Bel";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <ru>
"__locale" = "be_BY@latin;Bel_Lat"; "__locale" = "be_BY@latin;Bel_Lat";
"__WinEncoding" = "Windows-1251"; "__WinEncoding" = "Windows-1251";

View file

@ -1,3 +1,5 @@
#include <en>
"__locale" = "de_DE.UTF-8;Deu"; "__locale" = "de_DE.UTF-8;Deu";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -179,6 +179,8 @@
"open_post" = "Open post"; "open_post" = "Open post";
"version_incompatibility" = "This attachment could not be displayed. Probably the database is incompatible with the current version of OpenVK."; "version_incompatibility" = "This attachment could not be displayed. Probably the database is incompatible with the current version of OpenVK.";
"reply" = "Reply";
/* Friends */ /* Friends */
"friends" = "Friends"; "friends" = "Friends";
@ -522,6 +524,31 @@
"usages_total" = "Number of uses"; "usages_total" = "Number of uses";
"usages_left" = "Uses left"; "usages_left" = "Uses left";
"points_transfer_dialog_header_1" = "You can send as a gift or transfer part of the votes to another person.";
"points_transfer_dialog_header_2" = "Your current balance:";
"points_amount_one" = "1 vote";
"points_amount_other" = "$1 votes";
"transfer_poins" = "Transfer votes";
"transfer_poins_button" = "Transfer votes";
"also_you_can_transfer_points" = "You can also <a href=\"javascript:showCoinsTransferDialog($1, '$2')\">transfer votes</a> to another person.";
"transferred_to_you" = "transferred to you";
"receiver_address" = "Receiver address";
"coins_count" = "Number of votes";
"message" = "Message";
"failed_to_tranfer_points" = "Failed to transfer votes";
"points_transfer_successful" = "You have successfully transferred <b>$1</b> to <b><a href=\"$2\">$3</a></b>.";
"not_all_information_has_been_entered" = "Not all information has been entered.";
"negative_transfer_value" = "We cannot steal votes from another person, sorry.";
"message_is_too_long" = "The message is too long.";
"receiver_not_found" = "The receiver was not found.";
"you_dont_have_enough_points" = "You don't have enough votes.";
/* Gifts */ /* Gifts */
"gift" = "Gift"; "gift" = "Gift";
@ -641,6 +668,7 @@
"created" = "Created"; "created" = "Created";
"everyone_can_create_topics" = "Everyone can create topics"; "everyone_can_create_topics" = "Everyone can create topics";
"display_list_of_topics_above_wall" = "Display a list of topics above the wall";
"topic_changes_saved_comment" = "The updated title and settings will appear on the topic page."; "topic_changes_saved_comment" = "The updated title and settings will appear on the topic page.";
@ -724,6 +752,10 @@
"paginator_page" = "Page $1"; "paginator_page" = "Page $1";
"paginator_next" = "Next"; "paginator_next" = "Next";
/* About */
"about_openvk" = "About OpenVK";
/* Dialogs */ /* Dialogs */
"ok" = "OK"; "ok" = "OK";

View file

@ -1,3 +1,5 @@
#include <en>
"__locale" = "eo.utf8"; "__locale" = "eo.utf8";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <ru>
"__locale" = "kk_KZ.UTF-8;Kaz"; "__locale" = "kk_KZ.UTF-8;Kaz";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <en>
"__locale" = "pl_PL.utf8;pl_PL.UTF-8;Pol"; "__locale" = "pl_PL.utf8;pl_PL.UTF-8;Pol";
"__WinEncoding" = "Windows-1251"; "__WinEncoding" = "Windows-1251";

View file

@ -182,6 +182,8 @@
"open_post" = "Открыть запись"; "open_post" = "Открыть запись";
"version_incompatibility" = "Не удалось отобразить это вложение. Возможно, база данных несовместима с текущей версией OpenVK."; "version_incompatibility" = "Не удалось отобразить это вложение. Возможно, база данных несовместима с текущей версией OpenVK.";
"reply" = "Ответить";
/* Friends */ /* Friends */
"friends" = "Друзья"; "friends" = "Друзья";
@ -544,6 +546,33 @@
"usages_total" = "Количество использований"; "usages_total" = "Количество использований";
"usages_left" = "Осталось использований"; "usages_left" = "Осталось использований";
"points_transfer_dialog_header_1" = "Вы можете отправить в подарок или передать часть голосов другому человеку.";
"points_transfer_dialog_header_2" = "Ваш текущий баланс:";
"points_amount_one" = "1 голос";
"points_amount_few" = "$1 голоса";
"points_amount_many" = "$1 голосов";
"points_amount_other" = "$1 голосов";
"transfer_poins" = "Передача голосов";
"transfer_poins_button" = "Передать голоса";
"also_you_can_transfer_points" = "Также вы можете <a href=\"javascript:showCoinsTransferDialog($1, '$2')\">передать голоса</a> другому человеку.";
"transferred_to_you" = "передал вам";
"receiver_address" = "Адрес получателя";
"coins_count" = "Количество голосов";
"message" = "Сообщение";
"failed_to_tranfer_points" = "Не удалось передать голоса";
"points_transfer_successful" = "Вы успешно передали <b>$1 <a href=\"$2\">$3</a></b>.";
"not_all_information_has_been_entered" = "Введена не вся информация.";
"negative_transfer_value" = "Мы не можем украсть голоса у другого человека, извините.";
"message_is_too_long" = "Сообщение слишком длинное.";
"receiver_not_found" = "Получатель не найден.";
"you_dont_have_enough_points" = "У вас недостаточно голосов.";
/* Gifts */ /* Gifts */
"gift" = "Подарок"; "gift" = "Подарок";
@ -674,6 +703,7 @@
"created" = "Создано"; "created" = "Создано";
"everyone_can_create_topics" = "Все могут создавать темы"; "everyone_can_create_topics" = "Все могут создавать темы";
"display_list_of_topics_above_wall" = "Отображать список тем над стеной";
"topic_changes_saved_comment" = "Обновлённый заголовок и настройки появятся на странице с темой."; "topic_changes_saved_comment" = "Обновлённый заголовок и настройки появятся на странице с темой.";
@ -757,6 +787,10 @@
"paginator_page" = "Страница $1"; "paginator_page" = "Страница $1";
"paginator_next" = "Дальше"; "paginator_next" = "Дальше";
/* About */
"about_openvk" = "Об OpenVK";
/* Dialogs */ /* Dialogs */
"ok" = "ОК"; "ok" = "ОК";

View file

@ -1,6 +1,7 @@
#include <ru>
"__locale" = "ru_UA.utf8;ru_RU.UTF-8;Rus"; "__locale" = "ru_UA.utf8;ru_RU.UTF-8;Rus";
"__WinEncoding" = "Windows-1251"; "__WinEncoding" = "Windows-1251";
"__fallback" = "ru";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <ru>
"__locale" = "sr_CS.UTF-8;Srb"; "__locale" = "sr_CS.UTF-8;Srb";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <en>
"__locale" = "sr_CS.UTF-8;Srb_Latin"; "__locale" = "sr_CS.UTF-8;Srb_Latin";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <en>
"__locale" = "tr_TR.UTF-8;Tur"; "__locale" = "tr_TR.UTF-8;Tur";
/* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /* Check for https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */

View file

@ -1,3 +1,5 @@
#include <ru>
"__locale" = "uk_UA.utf8;Ukr"; "__locale" = "uk_UA.utf8;Ukr";
"__WinEncoding" = "Windows-1251"; "__WinEncoding" = "Windows-1251";