Users: Add the ability to convert votes into a rating for yourself or other users

Closes #345
This commit is contained in:
Maxim Leshchenko 2022-02-07 23:20:55 +02:00
parent b7559dcfe8
commit 13b614c377
No known key found for this signature in database
GPG key ID: BB9C44A8733FBEEE
9 changed files with 173 additions and 2 deletions

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

@ -11,7 +11,7 @@ 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\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};
@ -519,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

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

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

@ -73,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}"

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

@ -583,6 +583,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";

View file

@ -609,6 +609,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" = "Подарок";