Profile deactivation function (#626)

This commit is contained in:
Ilya Prokopenko 2022-08-05 20:00:52 +00:00 committed by GitHub
parent 32233c34dd
commit b1ce106e4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 334 additions and 66 deletions

View file

@ -84,6 +84,11 @@ class Post extends Postable
{ {
return ($this->getRecord()->flags & 0b01000000) > 0; return ($this->getRecord()->flags & 0b01000000) > 0;
} }
function isDeactivationMessage(): bool
{
return ($this->getRecord()->flags & 0b00100000) > 0;
}
function isExplicit(): bool function isExplicit(): bool
{ {

View file

@ -147,7 +147,7 @@ class User extends RowModel
function getFirstName(bool $pristine = false): string function getFirstName(bool $pristine = false): string
{ {
$name = $this->getRecord()->deleted ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE); $name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE));
if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine) if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine)
return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE); return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE);
else else
@ -156,7 +156,7 @@ class User extends RowModel
function getLastName(bool $pristine = false): string function getLastName(bool $pristine = false): string
{ {
$name = $this->getRecord()->deleted ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE); $name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE));
if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine) if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine)
return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE); return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE);
else else
@ -165,12 +165,12 @@ class User extends RowModel
function getPseudo(): ?string function getPseudo(): ?string
{ {
return $this->getRecord()->deleted ? "DELETED" : $this->getRecord()->pseudo; return ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : $this->getRecord()->pseudo);
} }
function getFullName(): string function getFullName(): string
{ {
if($this->getRecord()->deleted) if($this->isDeleted() && !$this->isDeactivated())
return "DELETED"; return "DELETED";
$pseudo = $this->getPseudo(); $pseudo = $this->getPseudo();
@ -195,7 +195,7 @@ class User extends RowModel
function getCanonicalName(): string function getCanonicalName(): string
{ {
if($this->getRecord()->deleted) if($this->isDeleted() && !$this->isDeactivated())
return "DELETED"; return "DELETED";
else else
return $this->getFirstName() . " " . $this->getLastName(); return $this->getFirstName() . " " . $this->getLastName();
@ -785,6 +785,27 @@ class User extends RowModel
$this->save(); $this->save();
} }
function deactivate(?string $reason): void
{
$this->setDeleted(1);
$this->setDeact_Date(time() + (MONTH * 7));
$this->setDeact_Reason($reason);
$this->save();
}
function reactivate(): void
{
$this->setDeleted(0);
$this->setDeact_Date(0);
$this->setDeact_Reason("");
$this->save();
}
function getDeactivationDate(): DateTime
{
return new DateTime($this->getRecord()->deact_date);
}
function verifyNumber(string $code): bool function verifyNumber(string $code): bool
{ {
$ver = $this->getPendingPhoneVerification(); $ver = $this->getPendingPhoneVerification();
@ -956,6 +977,14 @@ class User extends RowModel
return FALSE; return FALSE;
} }
function isDeactivated(): bool
{
if ($this->getDeactivationDate()->timestamp() > time())
return TRUE;
else
return FALSE;
}
/** /**
* 0 - Default status * 0 - Default status
* 1 - Incognito online status * 1 - Incognito online status

View file

@ -9,6 +9,7 @@ final class AboutPresenter extends OpenVKPresenter
{ {
protected $banTolerant = true; protected $banTolerant = true;
protected $activationTolerant = true; protected $activationTolerant = true;
protected $deactivationTolerant = true;
function renderIndex(): void function renderIndex(): void
{ {

View file

@ -20,6 +20,7 @@ final class AuthPresenter extends OpenVKPresenter
{ {
protected $banTolerant = true; protected $banTolerant = true;
protected $activationTolerant = true; protected $activationTolerant = true;
protected $deactivationTolerant = true;
private $authenticator; private $authenticator;
private $db; private $db;
@ -151,7 +152,7 @@ final class AuthPresenter extends OpenVKPresenter
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password")); $this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
$ovkUser = new User($user->related("profiles.user")->fetch()); $ovkUser = new User($user->related("profiles.user")->fetch());
if($ovkUser->isDeleted()) if($ovkUser->isDeleted() && !$ovkUser->isDeactivated())
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password")); $this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
$secret = $user->related("profiles.user")->fetch()["2fa_secret"]; $secret = $user->related("profiles.user")->fetch()["2fa_secret"];
@ -321,4 +322,15 @@ final class AuthPresenter extends OpenVKPresenter
$this->redirect("/"); $this->redirect("/");
} }
} }
function renderReactivatePage(): void
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
$this->user->identity->reactivate();
$this->redirect("/", 2);
exit;
}
} }

View file

@ -14,6 +14,7 @@ abstract class OpenVKPresenter extends SimplePresenter
{ {
protected $banTolerant = false; protected $banTolerant = false;
protected $activationTolerant = false; protected $activationTolerant = false;
protected $deactivationTolerant = false;
protected $errorTemplate = "@error"; protected $errorTemplate = "@error";
protected $user = NULL; protected $user = NULL;
@ -214,39 +215,28 @@ abstract class OpenVKPresenter extends SimplePresenter
$this->template->thisUser = $this->user->identity; $this->template->thisUser = $this->user->identity;
$this->template->userTainted = $user->isTainted(); $this->template->userTainted = $user->isTainted();
if($this->user->identity->isDeleted()) { if($this->user->identity->isDeleted() && !$this->deactivationTolerant) {
/* if($this->user->identity->isDeactivated()) {
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⠶⠶⣶⠶⠶⠶⠶⠶⠶⠶⠶⠶⢶⠶⠶⠶⠤⠤⠤⠤⣄⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ header("HTTP/1.1 403 Forbidden");
⠀⠀⠀⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠊⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠒⠀⠀⠀⠀⠤⢤⣤⣄⠉⠉⠛⠛⠷⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ $this->getTemplatingEngine()->render(__DIR__ . "/templates/@deactivated.xml", [
⠀⠀⠀⠀⠀⠀⠀⣰⠟⠀⠀⠀⠀⠀⠐⠋⢑⣤⣶⣶⣤⡢⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣄⡂⠀⠀⠶⢄⠙⢷⣤⠀⠀⠀⠀⠀⠀⠀⠀ "thisUser" => $this->user->identity,
⠀⠀⠀⠀⠀⠀⣸⡿⠚⠉⡀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⢢⠀⠀⡀⣰⣿⣿⣿⣿⣦⡀⠀⠀⠡⡀⢹⡆⠀⠀⠀⠀⠀⠀⠀ "csrfToken" => $GLOBALS["csrfToken"],
⠀⠀⠀⠀⢀⣴⠏⠀⣀⣀⣀⡤⢤⣄⣠⣿⣿⣿⣿⣻⣿⣿⣷⠀⢋⣾⠈⠙⣶⠒⢿⣿⣿⣿⣿⡿⠟⠃⠀⡀⠡⠼⣧⡀⠀⠀⠀⠀⠀⠀ "isTimezoned" => Session::i()->get("_timezoneOffset"),
⠀⠀⢀⣴⣿⢃⡴⢊⢽⣶⣤⣀⠀⠊⠉⠉⡛⢿⣿⣿⣿⠿⠋⢀⡀⠁⠀⠀⢸⣁⣀⣉⣉⣉⡉⠀⠩⡡⠀⣩⣦⠀⠈⠻⣦⡀⠀⠀⠀⠀ ]);
⠀⢠⡟⢡⠇⡞⢀⠆⠀⢻⣿⣿⣷⣄⠀⢀⠈⠂⠈⢁⡤⠚⡟⠉⠀⣀⣀⠀⠈⠳⣍⠓⢆⢀⡠⢀⣨⣴⣿⣿⡏⢀⡆⠀⢸⡇⠀⠀⠀⠀ } else {
⠀⣾⠁⢸⠀⠀⢸⠀⠀⠀⠹⣿⣿⣿⣿⣶⣬⣦⣤⡈⠀⠀⠇⠀⠛⠉⣩⣤⣤⣤⣿⣤⣤⣴⣾⣿⣿⣿⣿⣿⣧⠞⠀⠀⢸⡇⠀⠀⠀⠀ Authenticator::i()->logout();
⠀⢹⣆⠸⠀⠀⢸⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣟⣛⠛⠛⣛⡛⠛⠛⣛⣋⡉⠉⣡⠶⢾⣿⣿⣿⣿⣿⣿⡇⠀⠀⢀⣾⠃⠀⠀⠀⠀ Session::i()->set("_su", NULL);
⠀⠀⠻⣆⡀⠀⠈⢂⠀⠀⠀⠠⡈⢻⣿⣿⣿⣿⡟⠁⠈⢧⡼⠉⠙⣆⡞⠁⠈⢹⣴⠃⠀⢸⣿⣿⣿⣿⣿⣿⠃⠀⡆⣾⠃⠀⠀⠀⠀⠀ $this->flashFail("err", tr("error"), tr("profile_not_found"));
⠀⠀⠀⠈⢻⣇⠀⠀⠀⠀⠀⠀⢡⠀⠹⣿⣿⣿⣷⡀⠀⣸⡇⠀⠀⣿⠁⠀⠀⠘⣿⠀⠀⠘⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀ $this->redirect("/", static::REDIRECT_TEMPORARY);
⠀⠀⠀⠀⠀⠹⣇⠀⠠⠀⠀⠀⠀⠡⠐⢬⡻⣿⣿⣿⣿⣿⣷⣶⣶⣿⣦⣤⣤⣤⣿⣦⣶⣿⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀ }
⠀⠀⠀⠀⠀⠀⠹⣧⡀⠡⡀⠀⠀⠀⠑⠄⠙⢎⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⢿⡇⠀⠀⠀⠀⠀⠀ exit;
⠀⠀⠀⠀⠀⠀⠀⠈⠳⣤⡐⡄⠀⠀⠀⠈⠂⠀⠱⣌⠻⣿⣿⣿⣿⣿⣿⣿⠿⣿⠟⢻⡏⢻⣿⣿⣿⣿⣿⣿⣿⠀⢸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢮⣦⡀⠂⠀⢀⠀⠀⠈⠳⣈⠻⣿⣿⣿⡇⠘⡄⢸⠀⠀⣇⠀⣻⣿⣿⣿⣿⣿⡏⠀⠸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢶⣤⣄⡑⠄⠀⠀⠈⠑⠢⠙⠻⢷⣶⣵⣞⣑⣒⣋⣉⣁⣻⣿⠿⠟⠱⠃⡸⠀⣧⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⣷⣄⡀⠐⠢⣄⣀⡀⠀⠉⠉⠉⠉⠛⠙⠭⠭⠄⠒⠈⠀⠐⠁⢀⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⢦⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣒⡠⠄⣠⡾⠃⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠷⠶⣦⣤⣭⣤⣬⣭⣭⣴⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀
*/
Authenticator::i()->logout();
Session::i()->set("_su", NULL);
$this->flashFail("err", tr("error"), tr("profile_not_found"));
$this->redirect("/", static::REDIRECT_TEMPORARY);
} }
if($this->user->identity->isBanned() && !$this->banTolerant) { if($this->user->identity->isBanned() && !$this->banTolerant) {
header("HTTP/1.1 403 Forbidden"); header("HTTP/1.1 403 Forbidden");
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [ $this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [
"thisUser" => $this->user->identity, "thisUser" => $this->user->identity,
"csrfToken" => $GLOBALS["csrfToken"], "csrfToken" => $GLOBALS["csrfToken"],
"isTimezoned" => Session::i()->get("_timezoneOffset"), "isTimezoned" => Session::i()->get("_timezoneOffset"),
]); ]);
exit; exit;
@ -256,8 +246,8 @@ abstract class OpenVKPresenter extends SimplePresenter
if(!$this->user->identity->isActivated() && !$this->activationTolerant) { if(!$this->user->identity->isActivated() && !$this->activationTolerant) {
header("HTTP/1.1 403 Forbidden"); header("HTTP/1.1 403 Forbidden");
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@email.xml", [ $this->getTemplatingEngine()->render(__DIR__ . "/templates/@email.xml", [
"thisUser" => $this->user->identity, "thisUser" => $this->user->identity,
"csrfToken" => $GLOBALS["csrfToken"], "csrfToken" => $GLOBALS["csrfToken"],
"isTimezoned" => Session::i()->get("_timezoneOffset"), "isTimezoned" => Session::i()->get("_timezoneOffset"),
]); ]);
exit; exit;

View file

@ -12,6 +12,7 @@ use Parsedown;
final class SupportPresenter extends OpenVKPresenter final class SupportPresenter extends OpenVKPresenter
{ {
protected $banTolerant = true; protected $banTolerant = true;
protected $deactivationTolerant = true;
private $tickets; private $tickets;
private $comments; private $comments;

View file

@ -3,6 +3,7 @@ namespace openvk\Web\Presenters;
use openvk\Web\Util\Sms; use openvk\Web\Util\Sms;
use openvk\Web\Themes\Themepacks; use openvk\Web\Themes\Themepacks;
use openvk\Web\Models\Entities\Photo; use openvk\Web\Models\Entities\Photo;
use openvk\Web\Models\Entities\Post;
use openvk\Web\Models\Repositories\Users; use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\Repositories\Clubs; use openvk\Web\Models\Repositories\Clubs;
use openvk\Web\Models\Repositories\Albums; use openvk\Web\Models\Repositories\Albums;
@ -22,6 +23,8 @@ use Nette\Database\UniqueConstraintViolationException;
final class UserPresenter extends OpenVKPresenter final class UserPresenter extends OpenVKPresenter
{ {
private $users; private $users;
public $deactivationTolerant = false;
function __construct(Users $users) function __construct(Users $users)
{ {
@ -34,7 +37,13 @@ final class UserPresenter extends OpenVKPresenter
{ {
$user = $this->users->get($id); $user = $this->users->get($id);
if(!$user || $user->isDeleted()) { if(!$user || $user->isDeleted()) {
$this->template->_template = "User/deleted.xml"; if($user->isDeactivated()) {
$this->template->_template = "User/deactivated.xml";
$this->template->user = $user;
} else {
$this->template->_template = "User/deleted.xml";
}
} else { } else {
$this->template->albums = (new Albums)->getUserAlbums($user); $this->template->albums = (new Albums)->getUserAlbums($user);
$this->template->albumsCount = (new Albums)->getUserAlbumsCount($user); $this->template->albumsCount = (new Albums)->getUserAlbumsCount($user);
@ -272,8 +281,7 @@ final class UserPresenter extends OpenVKPresenter
$user->toggleSubscription($this->user->identity); $user->toggleSubscription($this->user->identity);
header("HTTP/1.1 302 Found"); $this->redirect("/id" . $user->getId());
header("Location: /id" . $user->getId());
exit; exit;
} }
@ -474,6 +482,33 @@ final class UserPresenter extends OpenVKPresenter
$this->template->themes = Themepacks::i()->getThemeList(); $this->template->themes = Themepacks::i()->getThemeList();
} }
function renderDeactivate(): void
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
$flags = 0;
$reason = $this->postParam("deactivate_reason");
$share = $this->postParam("deactivate_share");
if($share) {
$flags |= 0b00100000;
$post = new Post;
$post->setOwner($this->user->id);
$post->setWall($this->user->id);
$post->setCreated(time());
$post->setContent($reason);
$post->setFlags($flags);
$post->save();
}
$this->user->identity->deactivate($reason);
$this->redirect("/");
exit;
}
function renderTwoFactorAuthSettings(): void function renderTwoFactorAuthSettings(): void
{ {
$this->assertUserLoggedIn(); $this->assertUserLoggedIn();

View file

@ -0,0 +1,34 @@
{extends "@layout.xml"}
{block title}{$thisUser->getCanonicalName()}{/block}
{block header}
{$thisUser->getCanonicalName()}
{/block}
{block content}
<div class="container_gray bottom" style="margin: -10px -10px 10px;">
{tr("profile_deactivated_msg", $thisUser->getDeactivationDate()->format("%e %B %G" . tr("time_at_sp") . "%R"))|noescape}
</div>
<div class="left_small_block">
<div>
<img src="{$thisUser->getAvatarUrl('normal')}"
alt="{$thisUser->getCanonicalName()}"
style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
</div>
</div>
<div class="right_big_block">
<div class="page_info">
<div class="accountInfo clearFix">
<div class="profileName">
<h2>{$thisUser->getFullName()}</h2>
<div class="page_status" style="color: #AAA;">{_profile_deactivated_status}</div>
</div>
</div>
<center style="color: #AAA;margin: 40px 0;font-size: 13px;">
{_profile_deactivated_info|noescape}
</center>
</div>
</div>
{/block}

View file

@ -102,29 +102,35 @@
<a href="/" class="home_button{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME} home_button_custom{/if}" title="{$instance_name}">{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}{$instance_name}{/if}</a> <a href="/" class="home_button{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME} home_button_custom{/if}" title="{$instance_name}">{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}{$instance_name}{/if}</a>
<div n:if="isset($thisUser) ? (!$thisUser->isBanned() XOR !$thisUser->isActivated()) : 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"> {if $thisUser->isDeactivated()}
<a href="/">{_header_home}</a> <div class="link">
</div> <a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
<div class="link"> </div>
<a href="/search?type=groups">{_header_groups}</a> {else}
</div> <div class="link">
<div class="link"> <a href="/">{_header_home}</a>
<a href="/search">{_header_search}</a> </div>
</div> <div class="link">
<div class="link"> <a href="/search?type=groups">{_header_groups}</a>
<a href="/invite">{_header_invite}</a> </div>
</div> <div class="link">
<div class="link"> <a href="/search">{_header_search}</a>
<a href="/support">{_header_help} <b n:if="$ticketAnsweredCount > 0">({$ticketAnsweredCount})</b></a> </div>
</div> <div class="link">
<div class="link"> <a href="/invite">{_header_invite}</a>
<a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a> </div>
</div> <div class="link">
<div class="link"> <a href="/support">{_header_help} <b n:if="$ticketAnsweredCount > 0">({$ticketAnsweredCount})</b></a>
<form action="/search" method="get"> </div>
<input type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" /> <div class="link">
</form> <a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
</div> </div>
<div class="link">
<form action="/search" method="get">
<input type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
</form>
</div>
{/if}
{else} {else}
<div class="link"> <div class="link">
<a href="/login">{_header_login}</a> <a href="/login">{_header_login}</a>
@ -142,7 +148,7 @@
<div class="sidebar"> <div class="sidebar">
<div class="navigation"> <div class="navigation">
{ifset $thisUser} {ifset $thisUser}
{if !$thisUser->isBanned() XOR !$thisUser->isActivated()} {if !$thisUser->isBanned() XOR !$thisUser->isActivated() XOR $thisUser->isDeactivated()}
<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" title="{_my_page} [Alt+Shift+.]" accesskey=".">{_my_page}</a> <a href="{$thisUser->getURL()}" class="link" title="{_my_page} [Alt+Shift+.]" accesskey=".">{_my_page}</a>
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends} <a href="/friends{$thisUser->getId()}" class="link">{_my_friends}

View file

@ -220,9 +220,14 @@
</tbody> </tbody>
</table> </table>
</form> </form>
<br/>
<div class="settings_delete">
{_you_can_also} <a onClick="showProfileDeactivateDialog({$csrfToken})">{_delete_your_page}</a>.
</div>
{elseif $isPrivacy} {elseif $isPrivacy}
<form action="/settings?act=privacy" method="POST" enctype="multipart/form-data"> <form action="/settings?act=privacy" method="POST" enctype="multipart/form-data">
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center"> <table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
<tr> <tr>

View file

@ -0,0 +1,30 @@
{extends "../@layout.xml"}
{block title}{$user->getCanonicalName()}{/block}
{block header}
{$user->getCanonicalName()}
{/block}
{block content}
<div class="left_small_block">
<div>
<img src="{$user->getAvatarUrl('normal')}"
alt="{$user->getCanonicalName()}"
style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
</div>
</div>
<div class="right_big_block">
<div class="page_info">
<div class="accountInfo clearFix">
<div class="profileName">
<h2>{$user->getFullName()}</h2>
<div class="page_status" style="color: #AAA;">{_profile_deactivated_status}</div>
</div>
</div>
<center style="color: #AAA;margin: 40px 0;font-size: 13px;">
{_profile_deactivated_info|noescape}
</center>
</div>
</div>
{/block}

View file

@ -1,6 +1,11 @@
{var $author = $post->getOwner()} {var $author = $post->getOwner()}
{var $comments = $post->getLastComments(3)} {var $comments = $post->getLastComments(3)}
{var $commentsCount = $post->getCommentsCount()} {var $commentsCount = $post->getCommentsCount()}
{if $post->isDeactivationMessage() && $post->getText()}
{var $deac = "post_deact"}
{else}
{var $deac = "post_deact_silent"}
{/if}
{var $commentTextAreaId = $post === NULL ? rand(1,300) : $post->getId()} {var $commentTextAreaId = $post === NULL ? rand(1,300) : $post->getId()}
@ -17,6 +22,7 @@
<div class="post-author"> <div class="post-author">
<a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a> <a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a>
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png"> <img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">
{$post->isDeactivationMessage() ? ($author->isFemale() ? tr($deac . "_f") : tr($deac . "_m"))}
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()} {if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
{var $wallId = $post->getTargetWall()} {var $wallId = $post->getTargetWall()}
{var $wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)} {var $wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}

View file

@ -1,4 +1,9 @@
{var $author = $post->getOwner()} {var $author = $post->getOwner()}
{if $post->isDeactivationMessage() && $post->getText()}
{var $deac = "post_deact"}
{else}
{var $deac = "post_deact_silent"}
{/if}
<table border="0" style="font-size: 11px;" n:class="post, $post->isExplicit() ? post-nsfw"> <table border="0" style="font-size: 11px;" n:class="post, $post->isExplicit() ? post-nsfw">
<tbody> <tbody>
@ -13,7 +18,7 @@
<div class="post-author"> <div class="post-author">
<a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a> <a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a>
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png"> <img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">
{$post->isPostedOnBehalfOfGroup() ? tr("post_writes_g") : ($author->isFemale() ? tr("post_writes_f") : tr("post_writes_m"))} {$post->isDeactivationMessage() ? ($author->isFemale() ? tr($deac . "_f") : tr($deac . "_m")) : ($post->isPostedOnBehalfOfGroup() ? tr("post_writes_g") : ($author->isFemale() ? tr("post_writes_f") : tr("post_writes_m")))}
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()} {if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
{var $wallId = $post->getTargetWall()} {var $wallId = $post->getTargetWall()}
{var $wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)} {var $wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}

View file

@ -75,6 +75,10 @@ routes:
handler: "User->resetThemepack" handler: "User->resetThemepack"
- url: "/settings/change_email" - url: "/settings/change_email"
handler: "User->emailChangeFinish" handler: "User->emailChangeFinish"
- url: "/settings/deactivate"
handler: "User->deactivate"
- url: "/settings/reactivate"
handler: "Auth->reactivatePage"
- url: "/coins_transfer" - url: "/coins_transfer"
handler: "User->coinsTransfer" handler: "User->coinsTransfer"
- url: "/increase_social_credits" - url: "/increase_social_credits"

View file

@ -306,6 +306,58 @@ function ovk_proc_strtr(string, length = 0) {
return newString + (string !== newString ? "…" : ""); return newString + (string !== newString ? "…" : "");
} }
function showProfileDeactivateDialog(hash) {
MessageBox(tr("profile_deactivate"), `
<div class="messagebox-content-header">
${tr("profile_deactivate_header")}
</div>
<form action="/settings/deactivate" method="post" id="profile_deactivate_dialog" style="margin-top: 30px">
<h4>${tr("profile_deactivate_reason_header")}</h4>
<table>
<tbody>
<tr>
<td><input type="radio" name="deactivate_type" id="deactivate_r_1" data-text="${tr("profile_deactivate_reason_1_text")}"></td>
<td><label for="deactivate_r_1">${tr("profile_deactivate_reason_1")}</label></td>
</tr>
<tr>
<td><input type="radio" name="deactivate_type" id="deactivate_r_2" data-text="${tr("profile_deactivate_reason_2_text")}"></td>
<td><label for="deactivate_r_2">${tr("profile_deactivate_reason_2")}</label></td>
</tr>
<tr>
<td><input type="radio" name="deactivate_type" id="deactivate_r_3" data-text="${tr("profile_deactivate_reason_3_text")}"></td>
<td><label for="deactivate_r_3">${tr("profile_deactivate_reason_3")}</label></td>
</tr>
<tr>
<td><input type="radio" name="deactivate_type" id="deactivate_r_4" data-text="${tr("profile_deactivate_reason_4_text")}"></td>
<td><label for="deactivate_r_4">${tr("profile_deactivate_reason_4")}</label></td>
</tr>
<tr>
<td><input type="radio" name="deactivate_type" id="deactivate_r_5" data-text="${tr("profile_deactivate_reason_5_text")}"></td>
<td><label for="deactivate_r_5">${tr("profile_deactivate_reason_5")}</label></td>
</tr>
<tr>
<td><input type="radio" name="deactivate_type" id="deactivate_r_6" data-text=""></td>
<td><label for="deactivate_r_6">${tr("profile_deactivate_reason_6")}</label></td>
</tr>
</tbody>
</table>
<textarea name="deactivate_reason" id="deactivate_reason" placeholder="${tr("gift_your_message")}"></textarea><br><br>
<input type="checkbox" name="deactivate_share" id="deactivate_share" checked>
<label for="deactivate_share">${tr("share_with_friends")}</label>
<input type="hidden" name="hash" value="${hash}" />
</form>
`, [tr("profile_deactivate_button"), tr("cancel")], [
() => {
$("#profile_deactivate_dialog").submit();
},
Function.noop
]);
$('[id^="deactivate_r_"]').on("click", function () {
$('#deactivate_reason').val($(this).data("text"));
});
}
function showIncreaseRatingDialog(coinsCount, userUrl, hash) { function showIncreaseRatingDialog(coinsCount, userUrl, hash) {
MessageBox(tr("increase_rating"), ` MessageBox(tr("increase_rating"), `
<div class="messagebox-content-header"> <div class="messagebox-content-header">

View file

@ -0,0 +1 @@
ALTER TABLE `profiles` ADD `deact_date` bigint(20) unsigned NOT NULL DEFAULT '0' AFTER `deleted`, ADD `deact_reason` text NULL AFTER `deact_date`;

View file

@ -149,6 +149,10 @@
"post_writes_m" = "wrote"; "post_writes_m" = "wrote";
"post_writes_f" = "wrote"; "post_writes_f" = "wrote";
"post_writes_g" = "published"; "post_writes_g" = "published";
"post_deact_m" = "deleted his profile saying:";
"post_deact_f" = "deleted her profile saying:";
"post_deact_silent_m" = "silently deleted his profile.";
"post_deact_silent_f" = "silently deleted her profile.";
"wall" = "Wall"; "wall" = "Wall";
"post" = "Post"; "post" = "Post";
"write" = "Write"; "write" = "Write";
@ -487,6 +491,28 @@
"email_change_confirm_message" = "Please confirm your new email address for the change to take effect. We have sent instructions to it."; "email_change_confirm_message" = "Please confirm your new email address for the change to take effect. We have sent instructions to it.";
"profile_deactivate" = "Delete account";
"profile_deactivate_button" = "Delete account";
"profile_deactivate_header" = "We are sorry that you want to delete your page. Therefore, you can specify the reason for deletion and your message about it. We read your feedback and try to make the site better!";
"profile_deactivate_reason_header" = "Please select a reason why you are leaving";
"profile_deactivate_reason_1" = "I have another profile and don't need this one";
"profile_deactivate_reason_1_text" = "I created a new page and now I want to wipe my past.";
"profile_deactivate_reason_2" = "Website takes away too much of my time";
"profile_deactivate_reason_2_text" = "Even though this site is nice and beautiful, it takes away my time that I need for work and life.";
"profile_deactivate_reason_3" = "Website harbors too much inappropriate content";
"profile_deactivate_reason_3_text" = "I have found enough porn and pirated content to last me a lifetime, I'm leaving now.";
"profile_deactivate_reason_4" = "I am worried about the safety of my data";
"profile_deactivate_reason_4_text" = "I'm being watched and I'm scared to be here. I'm sorry, I have to leave.";
"profile_deactivate_reason_5" = "No one comments on my posts";
"profile_deactivate_reason_5_text" = "No one watches me here and it's sad. You will regret that I left.";
"profile_deactivate_reason_6" = "Other reason";
"profile_deactivated_msg" = "Your account has been <b>deleted</b>.<br/><br/>If you would like to start using the site again, you can <a href='/settings/reactivate'>restore your account</a> until $1.";
"profile_deactivated_status" = "Account deleted";
"profile_deactivated_info" = "The account has been deleted.<br/>The information is not available.";
"share_with_friends" = "Share with friends";
/* Two-factor authentication */ /* Two-factor authentication */
"two_factor_authentication" = "Two-factor authentication"; "two_factor_authentication" = "Two-factor authentication";

View file

@ -155,6 +155,10 @@
"post_writes_m" = "написал"; "post_writes_m" = "написал";
"post_writes_f" = "написала"; "post_writes_f" = "написала";
"post_writes_g" = "опубликовали"; "post_writes_g" = "опубликовали";
"post_deact_m" = "удалил страницу со словами:";
"post_deact_f" = "удалила страницу со словами:";
"post_deact_silent_m" = "молча удалил свою страницу.";
"post_deact_silent_f" = "молча удалила свою страницу.";
"wall" = "Стена"; "wall" = "Стена";
"post" = "Запись"; "post" = "Запись";
"write" = "Написать"; "write" = "Написать";
@ -523,6 +527,28 @@
"email_change_confirm_message" = "Чтобы изменение вступило в силу, подтвердите ваш новый адрес электронной почты. Мы отправили инструкции на него."; "email_change_confirm_message" = "Чтобы изменение вступило в силу, подтвердите ваш новый адрес электронной почты. Мы отправили инструкции на него.";
"profile_deactivate" = "Удаление страницы";
"profile_deactivate_button" = "Удалить страницу";
"profile_deactivate_header" = "Мы сожалеем, что Вы хотите удалить свою страницу. Поэтому, Вы можете указать причину удаления и Ваше сообщение по этому поводу. Мы читаем Ваши отзывы и пытаемся сделать сайт лучше!";
"profile_deactivate_reason_header" = "Пожалуйста, укажите причину удаления Вашей страницы";
"profile_deactivate_reason_1" = "У меня есть другая страница на сайте";
"profile_deactivate_reason_1_text" = "Я создал новую страницу и теперь хочу подтереть своё прошлое.";
"profile_deactivate_reason_2" = "Сайт отнимает у меня слишком много времени";
"profile_deactivate_reason_2_text" = "Пусть этот сайт хорош и прекрасен, но он отнимает у меня время, которое мне нужно для работы и жизни.";
"profile_deactivate_reason_3" = "Сайт содержит слишком много неприемлемых материалов";
"profile_deactivate_reason_3_text" = "Я нашёл достаточно порнографии и пиратского контента - хватит на всю жизнь. Теперь я ухожу.";
"profile_deactivate_reason_4" = "Меня беспокоит безопасность моих данных";
"profile_deactivate_reason_4_text" = "За мной следят и мне страшно здесь находится. Извините, я вынужден уйти.";
"profile_deactivate_reason_5" = "Мою страницу не комментируют";
"profile_deactivate_reason_5_text" = "Меня никто не смотрит здесь и это грустно. Вы пожалеете о том, что я ушёл.";
"profile_deactivate_reason_6" = "Другая причина";
"profile_deactivated_msg" = "Ваша страница <b>удалена</b>.<br/><br/>Если Вы захотите снова начать пользоваться сайтом, Вы можете <a href='/settings/reactivate'>восстановить свою страницу</a> до $1.";
"profile_deactivated_status" = "Страница удалена";
"profile_deactivated_info" = "Страница пользователя удалена.<br/>Информация недоступна.";
"share_with_friends" = "Рассказать друзьям";
/* Two-factor authentication */ /* Two-factor authentication */
"two_factor_authentication" = "Двухфакторная аутентификация"; "two_factor_authentication" = "Двухфакторная аутентификация";