Users: Add ability to transfer votes to another user

This commit is contained in:
Maxim Leshchenko 2021-12-19 14:34:33 +02:00
parent cb8c2f683b
commit 3b54c4b878
No known key found for this signature in database
GPG key ID: BB9C44A8733FBEEE
10 changed files with 184 additions and 0 deletions

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

@ -52,6 +52,23 @@ class Users
"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;
}

View file

@ -10,6 +10,7 @@ use openvk\Web\Models\Repositories\Videos;
use openvk\Web\Models\Repositories\Notes;
use openvk\Web\Models\Repositories\Vouchers;
use openvk\Web\Util\Validator;
use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification;
use Chandler\Security\Authenticator;
use lfkeitel\phptotp\{Base32, Totp};
use chillerlan\QRCode\{QRCode, QROptions};
@ -465,4 +466,42 @@ final class UserPresenter extends OpenVKPresenter
$this->user->identity->save();
$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

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

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

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

View file

@ -1687,3 +1687,9 @@ body.scrolled .toTop:hover {
border-bottom: #e6e6e6 solid 1px;
padding: 4px;
}
.messagebox-content-header {
background: #F7F7F7;
margin: -20px;
padding: 10px;
}

View file

@ -171,3 +171,49 @@ function setClubAdminComment(clubId, adminId, hash) {
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

@ -524,6 +524,31 @@
"usages_total" = "Number of uses";
"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 */
"gift" = "Gift";

View file

@ -546,6 +546,33 @@
"usages_total" = "Количество использований";
"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 */
"gift" = "Подарок";