mirror of
https://github.com/openvk/openvk
synced 2024-11-14 11:09:12 +03:00
Merge branch 'master' into feature-reports
This commit is contained in:
commit
47cfafc4c8
188 changed files with 18733 additions and 1158 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
||||||
[submodule "locales"]
|
[submodule "Web/static/img/oxygen-icons"]
|
||||||
path = locales
|
path = Web/static/img/oxygen-icons
|
||||||
url = https://github.com/openvk/locales
|
url = https://github.com/KDE/oxygen-icons5.git
|
||||||
|
|
|
@ -11,7 +11,7 @@ RUN dnf -y module enable php:remi-7.4 && \
|
||||||
dnf -y module enable nodejs:14
|
dnf -y module enable nodejs:14
|
||||||
|
|
||||||
#And install dependencies:
|
#And install dependencies:
|
||||||
RUN dnf -y install php php-cli php-common unzip php-zip php-yaml php-gd php-pdo_mysql nodejs git
|
RUN dnf -y --skip-broken install php php-cli php-common unzip php-zip php-yaml php-gd php-pdo_mysql nodejs git
|
||||||
|
|
||||||
#Don't forget about Yarn and Composer:
|
#Don't forget about Yarn and Composer:
|
||||||
RUN npm i -g yarn && \
|
RUN npm i -g yarn && \
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
h1. <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo.png" alt="openvk" title="openvk" width="15%">OpenVK
|
h1. <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||||
|
|
||||||
*OpenVK* is an attempt to create a simple CMS that -cosplays- imitates old VK. Code provided here is not stable yet.
|
*OpenVK* is an attempt to create a simple CMS that -cosplays- imitates old VK. Code provided here is not stable yet.
|
||||||
VKontakte belongs to Pavel Durov and mail.ru.
|
VKontakte belongs to Pavel Durov and VK Group.
|
||||||
To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports "in our bug-tracker":https://github.com/openvk/openvk/projects/1. You should also be able to submit them using "ticketing system":https://openvk.su/support?act=new (you will need an OVK account for this).
|
To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports "in our bug-tracker":https://github.com/openvk/openvk/projects/1. You should also be able to submit them using "ticketing system":https://openvk.su/support?act=new (you will need an OVK account for this).
|
||||||
|
|
||||||
h2. When's the release?
|
h2. When's the release?
|
||||||
|
|
||||||
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@
|
Updating the source code is done with this command: @git pull --recurse-submodules@
|
||||||
|
|
||||||
h2. Instances
|
h2. Instances
|
||||||
|
|
||||||
* *"openvk.su":https://openvk.su/*
|
* *"openvk.su":https://openvk.su/*
|
||||||
|
* "social.fetbuk.ru":http://social.fetbuk.ru/
|
||||||
|
|
||||||
h2. Can I create my own OpenVK instance?
|
h2. Can I create my own OpenVK instance?
|
||||||
|
|
||||||
|
@ -35,11 +36,13 @@ PHP 8 has *not* yet been tested, so you should not expect it to work.
|
||||||
@ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/@
|
@ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/@
|
||||||
# Import @install/init-static-db.sql@ to *same database* you installed Chandler to
|
# Import @install/init-static-db.sql@ to *same database* you installed Chandler to
|
||||||
# Import @install/init-event-db.sql@ to *separate database*
|
# Import @install/init-event-db.sql@ to *separate database*
|
||||||
# Copy openvk-example.yml to openvk.yml and change options
|
# Copy @openvk-example.yml@ to @openvk.yml@ and change options
|
||||||
# Run @composer install@ in OpenVK directory
|
# Run @composer install@ in OpenVK directory
|
||||||
# Move to @Web/static/js@ and execute @yarn install@
|
# Move to @Web/static/js@ and execute @yarn install@
|
||||||
# Set @openvk@ as your root app in @chandler.yml@
|
# 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
|
||||||
|
@ -62,3 +65,8 @@ You may reach out to us via:
|
||||||
*Attention*: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), *please use contact us directly*:
|
*Attention*: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), *please use contact us directly*:
|
||||||
* *Head of OpenVK Security Commitee*: stingray@jill.pl or "@id155":https://t.me/id155
|
* *Head of OpenVK Security Commitee*: stingray@jill.pl or "@id155":https://t.me/id155
|
||||||
* *Backend developer*: "@saddyteirusu":https://t.me/saddyteirusu
|
* *Backend developer*: "@saddyteirusu":https://t.me/saddyteirusu
|
||||||
|
|
||||||
|
Codeberg repository clone:
|
||||||
|
<a href="https://codeberg.org/OpenVK/openvk">
|
||||||
|
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||||
|
</a>
|
||||||
|
|
83
ServiceAPI/Notifications.php
Normal file
83
ServiceAPI/Notifications.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
use Latte\Engine as TemplatingEngine;
|
||||||
|
use RdKafka\{Conf as RDKConf, KafkaConsumer};
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\{Notifications as N};
|
||||||
|
|
||||||
|
class Notifications implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
protected $notifs;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->notifs = new N;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ack(callable $resolve, callable $reject): void
|
||||||
|
{
|
||||||
|
$this->user->updateNotificationOffset();
|
||||||
|
$this->user->save();
|
||||||
|
$resolve("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch(callable $resolve, callable $reject): void
|
||||||
|
{
|
||||||
|
$kafkaConf = OPENVK_ROOT_CONF["openvk"]["credentials"]["notificationsBroker"];
|
||||||
|
if(!$kafkaConf["enable"]) {
|
||||||
|
$reject(1999, "Disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$kafkaConf = $kafkaConf["kafka"];
|
||||||
|
$conf = new RDKConf();
|
||||||
|
$conf->set("metadata.broker.list", $kafkaConf["addr"] . ":" . $kafkaConf["port"]);
|
||||||
|
$conf->set("group.id", "UserFetch-" . $this->user->getId()); # Чтобы уведы приходили только на разные устройства одного чебупелика
|
||||||
|
$conf->set("auto.offset.reset", "latest");
|
||||||
|
|
||||||
|
set_time_limit(30);
|
||||||
|
$consumer = new KafkaConsumer($conf);
|
||||||
|
$consumer->subscribe([ $kafkaConf["topic"] ]);
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
$message = $consumer->consume(30*1000);
|
||||||
|
switch ($message->err) {
|
||||||
|
case RD_KAFKA_RESP_ERR_NO_ERROR:
|
||||||
|
$descriptor = $message->payload;
|
||||||
|
[,$user,] = explode(",", $descriptor);
|
||||||
|
if(((int) $user) === $this->user->getId()) {
|
||||||
|
$data = (object) [];
|
||||||
|
$notification = $this->notifs->fromDescriptor($descriptor, $data);
|
||||||
|
if(!$notification) {
|
||||||
|
$reject(1982, "Server Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tplDir = __DIR__ . "/../Web/Presenters/templates/components/notifications/";
|
||||||
|
$tplId = "$tplDir$data->actionCode/_$data->originModelType" . "_" . $data->targetModelType . "_.xml";
|
||||||
|
$latte = new TemplatingEngine;
|
||||||
|
$latte->setTempDirectory(CHANDLER_ROOT . "/tmp/cache/templates");
|
||||||
|
$latte->addFilter("translate", fn($trId) => tr($trId));
|
||||||
|
$resolve([
|
||||||
|
"title" => tr("notif_" . $data->actionCode . "_" . $data->originModelType . "_" . $data->targetModelType),
|
||||||
|
"body" => trim(preg_replace('%(\s){2,}%', "$1", $latte->renderToString($tplId, ["notification" => $notification]))),
|
||||||
|
"ava" => $notification->getModel(1)->getAvatarUrl(),
|
||||||
|
"priority" => 1,
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case RD_KAFKA_RESP_ERR__TIMED_OUT:
|
||||||
|
case RD_KAFKA_RESP_ERR__PARTITION_EOF:
|
||||||
|
$reject(1983, "Nothing to report");
|
||||||
|
break 2;
|
||||||
|
default:
|
||||||
|
$reject(1981, "Kafka Error: " . $message->errstr());
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ class Service implements Handler
|
||||||
|
|
||||||
function getTime(callable $resolve, callable $reject): void
|
function getTime(callable $resolve, callable $reject): void
|
||||||
{
|
{
|
||||||
$resolve((new DateTime)->format("%e %B %G" . tr("time_at_sp") . "%X"));
|
$resolve(trim((new DateTime)->format("%e %B %G" . tr("time_at_sp") . "%X")));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getServerVersion(callable $resolve, callable $reject): void
|
function getServerVersion(callable $resolve, callable $reject): void
|
||||||
|
|
58
ServiceAPI/Wall.php
Normal file
58
ServiceAPI/Wall.php
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\Posts;
|
||||||
|
|
||||||
|
class Wall implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
protected $posts;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->posts = new Posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPost(int $id, callable $resolve, callable $reject): void
|
||||||
|
{
|
||||||
|
$post = $this->posts->get($id);
|
||||||
|
if(!$post || $post->isDeleted())
|
||||||
|
$reject("No post with id=$id");
|
||||||
|
|
||||||
|
$res = (object) [];
|
||||||
|
$res->id = $post->getId();
|
||||||
|
$res->wall = $post->getTargetWall();
|
||||||
|
$res->author = (($owner = $post->getOwner())) instanceof User
|
||||||
|
? ($owner->getId())
|
||||||
|
: ($owner->getId() * -1);
|
||||||
|
|
||||||
|
if($post->isSigned())
|
||||||
|
$res->signedOffBy = $post->getOwnerPost();
|
||||||
|
|
||||||
|
$res->pinned = $post->isPinned();
|
||||||
|
$res->sponsored = $post->isAd();
|
||||||
|
$res->nsfw = $post->isExplicit();
|
||||||
|
$res->text = $post->getText();
|
||||||
|
|
||||||
|
$res->likes = [
|
||||||
|
"count" => $post->getLikesCount(),
|
||||||
|
"hasLike" => $post->hasLikeFrom($this->user),
|
||||||
|
"likedBy" => [],
|
||||||
|
];
|
||||||
|
foreach($post->getLikers() as $liker) {
|
||||||
|
$res->likes["likedBy"][] = [
|
||||||
|
"id" => $liker->getId(),
|
||||||
|
"url" => $liker->getURL(),
|
||||||
|
"name" => $liker->getCanonicalName(),
|
||||||
|
"avatar" => $liker->getAvatarURL(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$res->created = (string) $post->getPublicationTime();
|
||||||
|
$res->canPin = $post->canBePinnedBy($this->user);
|
||||||
|
$res->canEdit = $res->canDelete = $post->canBeDeletedBy($this->user);
|
||||||
|
|
||||||
|
$resolve((array) $res);
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ final class Account extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOnline(): object
|
function setOnline(): int
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
|
22
VKAPI/Handlers/Audio.php
Normal file
22
VKAPI/Handlers/Audio.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
|
||||||
|
final class Audio extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
function get(): object
|
||||||
|
{
|
||||||
|
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => 1,
|
||||||
|
"items" => [(object) [
|
||||||
|
"id" => 1,
|
||||||
|
"owner_id" => 1,
|
||||||
|
"artist" => "В ОВК ПОКА НЕТ МУЗЫКИ",
|
||||||
|
"title" => "ЖДИТЕ :)))",
|
||||||
|
"duration" => 22,
|
||||||
|
"url" => $serverUrl . "/assets/packages/static/openvk/audio/nomusic.mp3"
|
||||||
|
]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,19 +3,35 @@ namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
use openvk\Web\Models\Entities\Clubs;
|
use openvk\Web\Models\Entities\Clubs;
|
||||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
use openvk\Web\Models\Entities\Post;
|
use openvk\Web\Models\Entities\Post;
|
||||||
use openvk\Web\Models\Entities\Postable;
|
use openvk\Web\Models\Entities\Postable;
|
||||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||||
|
|
||||||
final class Groups extends VKAPIRequestHandler
|
final class Groups extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(string $group_ids, string $fields = "", int $offset = 0, int $count = 100, bool $online = false): array
|
function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 6, bool $online = false): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$clubs = new ClubsRepo;
|
if ($user_id == 0) {
|
||||||
$clbs = explode(',', $group_ids);
|
foreach($this->getUser()->getClubs($offset+1) as $club) {
|
||||||
$response;
|
$clbs[] = $club;
|
||||||
|
}
|
||||||
|
$clbsCount = $this->getUser()->getClubCount();
|
||||||
|
} else {
|
||||||
|
$users = new UsersRepo;
|
||||||
|
$user = $users->get($user_id);
|
||||||
|
if (is_null($user)) {
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
}
|
||||||
|
foreach($user->getClubs($offset+1) as $club) {
|
||||||
|
$clbs[] = $club;
|
||||||
|
}
|
||||||
|
$clbsCount = $user->getClubCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
$rClubs;
|
||||||
|
|
||||||
$ic = sizeof($clbs);
|
$ic = sizeof($clbs);
|
||||||
|
|
||||||
|
@ -24,22 +40,21 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$clbs = array_slice($clbs, $offset * $count);
|
$clbs = array_slice($clbs, $offset * $count);
|
||||||
|
|
||||||
for ($i=0; $i < $ic; $i++) {
|
for ($i=0; $i < $ic; $i++) {
|
||||||
$usr = $clubs->get((int) $clbs[$i]);
|
$usr = $clbs[$i];
|
||||||
if(is_null($usr))
|
if(is_null($usr))
|
||||||
{
|
{
|
||||||
$response[$i] = (object)[
|
$rClubs[$i] = (object)[
|
||||||
"id" => $clbs[$i],
|
"id" => $clbs[$i],
|
||||||
"first_name" => "DELETED",
|
"name" => "DELETED",
|
||||||
"last_name" => "",
|
|
||||||
"deactivated" => "deleted"
|
"deactivated" => "deleted"
|
||||||
];
|
];
|
||||||
}else if($clbs[$i] == null){
|
}else if($clbs[$i] == null){
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
$response[$i] = (object)[
|
$rClubs[$i] = (object)[
|
||||||
"id" => $usr->getId(),
|
"id" => $usr->getId(),
|
||||||
"first_name" => $usr->getFirstName(),
|
"name" => $usr->getName(),
|
||||||
"last_name" => $usr->getLastName(),
|
"screen_name" => $usr->getShortCode(),
|
||||||
"is_closed" => false,
|
"is_closed" => false,
|
||||||
"can_access_closed" => true,
|
"can_access_closed" => true,
|
||||||
];
|
];
|
||||||
|
@ -49,34 +64,28 @@ final class Groups extends VKAPIRequestHandler
|
||||||
foreach($flds as $field) {
|
foreach($flds as $field) {
|
||||||
switch ($field) {
|
switch ($field) {
|
||||||
case 'verified':
|
case 'verified':
|
||||||
$response[$i]->verified = intval($usr->isVerified());
|
$rClubs[$i]->verified = intval($usr->isVerified());
|
||||||
break;
|
|
||||||
case 'sex':
|
|
||||||
$response[$i]->sex = $this->getUser()->isFemale() ? 1 : 2;
|
|
||||||
break;
|
break;
|
||||||
case 'has_photo':
|
case 'has_photo':
|
||||||
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
$rClubs[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
||||||
break;
|
break;
|
||||||
case 'photo_max_orig':
|
case 'photo_max_orig':
|
||||||
$response[$i]->photo_max_orig = $usr->getAvatarURL();
|
$rClubs[$i]->photo_max_orig = $usr->getAvatarURL();
|
||||||
break;
|
break;
|
||||||
case 'photo_max':
|
case 'photo_max':
|
||||||
$response[$i]->photo_max = $usr->getAvatarURL();
|
$rClubs[$i]->photo_max = $usr->getAvatarURL();
|
||||||
break;
|
break;
|
||||||
|
case 'members_count':
|
||||||
|
$rClubs[$i]->members_count = $usr->getFollowersCount();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// НУЖЕН фикс - либо из-за моего дебилизма, либо из-за сегментации котлеток некоторые пользовали отображаются как онлайн, хотя лол, если зайти на страницу, то оный уже офлайн
|
return (object) [
|
||||||
if($online == true && $usr->getOnline()->timestamp() + 2505600 > time()) {
|
"count" => $clbsCount,
|
||||||
$response[$i]->online = 1;
|
"items" => $rClubs
|
||||||
}else{
|
];
|
||||||
$response[$i]->online = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,14 @@ use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
|
||||||
final class Users extends VKAPIRequestHandler
|
final class Users extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(string $user_ids, string $fields = "", int $offset = 0, int $count = 100): array
|
function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100): array
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
|
if($user_ids == "0")
|
||||||
|
$user_ids = (string) $this->getUser()->getId();
|
||||||
|
|
||||||
$usrs = explode(',', $user_ids);
|
$usrs = explode(',', $user_ids);
|
||||||
$response;
|
$response;
|
||||||
|
|
||||||
|
@ -60,11 +63,34 @@ final class Users extends VKAPIRequestHandler
|
||||||
$response[$i]->photo_max = $usr->getAvatarURL();
|
$response[$i]->photo_max = $usr->getAvatarURL();
|
||||||
break;
|
break;
|
||||||
case 'status':
|
case 'status':
|
||||||
|
if($usr->getStatus() != null)
|
||||||
$response[$i]->status = $usr->getStatus();
|
$response[$i]->status = $usr->getStatus();
|
||||||
break;
|
break;
|
||||||
case 'screen_name':
|
case 'screen_name':
|
||||||
|
if($usr->getShortCode() != null)
|
||||||
$response[$i]->screen_name = $usr->getShortCode();
|
$response[$i]->screen_name = $usr->getShortCode();
|
||||||
break;
|
break;
|
||||||
|
case 'friend_status':
|
||||||
|
switch($usr->getSubscriptionStatus($this->getUser())) {
|
||||||
|
case 3:
|
||||||
|
case 0:
|
||||||
|
$response[$i]->friend_status = $usr->getSubscriptionStatus($this->getUser());
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$response[$i]->friend_status = 2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$response[$i]->friend_status = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'last_seen':
|
||||||
|
if ($usr->onlineStatus() == 0) {
|
||||||
|
$response[$i]->last_seen = (object) [
|
||||||
|
"platform" => 1,
|
||||||
|
"time" => $usr->getOnline()->timestamp()
|
||||||
|
];
|
||||||
|
}
|
||||||
case 'music':
|
case 'music':
|
||||||
$response[$i]->music = $usr->getFavoriteMusic();
|
$response[$i]->music = $usr->getFavoriteMusic();
|
||||||
break;
|
break;
|
||||||
|
@ -86,8 +112,7 @@ final class Users extends VKAPIRequestHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// НУЖЕН фикс - либо из-за моего дебилизма, либо из-за сегментации котлеток некоторые пользовали отображаются как онлайн, хотя лол, если зайти на страницу, то оный уже офлайн
|
if($usr->getOnline()->timestamp() + 300 > time()) {
|
||||||
if($usr->getOnline()->timestamp() + 2505600 > time()) {
|
|
||||||
$response[$i]->online = 1;
|
$response[$i]->online = 1;
|
||||||
}else{
|
}else{
|
||||||
$response[$i]->online = 0;
|
$response[$i]->online = 0;
|
||||||
|
|
|
@ -16,20 +16,25 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$posts = new PostsRepo;
|
$posts = new PostsRepo;
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
|
$profiles = [];
|
||||||
|
$groups = [];
|
||||||
|
$count = $posts->getPostCountOnUserWall((int) $owner_id);
|
||||||
|
|
||||||
foreach ($posts->getPostsFromUsersWall((int)$owner_id) as $post) {
|
foreach ($posts->getPostsFromUsersWall((int)$owner_id, 1, $count, $offset) as $post) {
|
||||||
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
$items[] = (object)[
|
$items[] = (object)[
|
||||||
"id" => $post->getVirtualId(),
|
"id" => $post->getVirtualId(),
|
||||||
"from_id" => $post->getOwner()->getId(),
|
"from_id" => $from_id,
|
||||||
"owner_id" => $post->getTargetWall(),
|
"owner_id" => $post->getTargetWall(),
|
||||||
"date" => $post->getPublicationTime()->timestamp(),
|
"date" => $post->getPublicationTime()->timestamp(),
|
||||||
"post_type" => "post",
|
"post_type" => "post",
|
||||||
"text" => $post->getText(),
|
"text" => $post->getText(),
|
||||||
"can_edit" => 0, // TODO
|
"can_edit" => 0, // TODO
|
||||||
"can_delete" => $post->canBeDeletedBy($this->getUser()),
|
"can_delete" => $post->canBeDeletedBy($this->getUser()),
|
||||||
"can_pin" => 0, // TODO
|
"can_pin" => $post->canBePinnedBy($this->getUser()),
|
||||||
"can_archive" => false, // TODO MAYBE
|
"can_archive" => false, // TODO MAYBE
|
||||||
"is_archived" => false,
|
"is_archived" => false,
|
||||||
|
"is_pinned" => $post->isPinned(),
|
||||||
"post_source" => (object)["type" => "vk"],
|
"post_source" => (object)["type" => "vk"],
|
||||||
"comments" => (object)[
|
"comments" => (object)[
|
||||||
"count" => $post->getCommentsCount(),
|
"count" => $post->getCommentsCount(),
|
||||||
|
@ -46,26 +51,66 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"user_reposted" => 0
|
"user_reposted" => 0
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($from_id > 0)
|
||||||
|
$profiles[] = $from_id;
|
||||||
|
else
|
||||||
|
$groups[] = $from_id * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$profiles = [];
|
|
||||||
$groups = [];
|
|
||||||
|
|
||||||
$groups[0] = 'lol';
|
|
||||||
$groups[2] = 'cec';
|
|
||||||
|
|
||||||
if($extended == 1)
|
if($extended == 1)
|
||||||
return (object)[
|
{
|
||||||
"items" => (array)$items,
|
$profiles = array_unique($profiles);
|
||||||
"cock" => (array)$groups
|
$groups = array_unique($groups);
|
||||||
|
|
||||||
|
$profilesFormatted = [];
|
||||||
|
$groupsFormatted = [];
|
||||||
|
|
||||||
|
foreach ($profiles as $prof) {
|
||||||
|
$user = (new UsersRepo)->get($prof);
|
||||||
|
$profilesFormatted[] = (object)[
|
||||||
|
"first_name" => $user->getFirstName(),
|
||||||
|
"id" => $user->getId(),
|
||||||
|
"last_name" => $user->getLastName(),
|
||||||
|
"can_access_closed" => false,
|
||||||
|
"is_closed" => false,
|
||||||
|
"sex" => $user->isFemale() ? 1 : 2,
|
||||||
|
"screen_name" => $user->getShortCode(),
|
||||||
|
"photo_50" => $user->getAvatarUrl(),
|
||||||
|
"photo_100" => $user->getAvatarUrl(),
|
||||||
|
"online" => $user->isOnline()
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($groups as $g) {
|
||||||
|
$group = (new ClubsRepo)->get($g);
|
||||||
|
$groupsFormatted[] = (object)[
|
||||||
|
"id" => $group->getId(),
|
||||||
|
"name" => $group->getName(),
|
||||||
|
"screen_name" => $group->getShortCode(),
|
||||||
|
"is_closed" => 0,
|
||||||
|
"type" => "group",
|
||||||
|
"photo_50" => $group->getAvatarUrl(),
|
||||||
|
"photo_100" => $group->getAvatarUrl(),
|
||||||
|
"photo_200" => $group->getAvatarUrl(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object)[
|
||||||
|
"count" => $count,
|
||||||
|
"items" => (array)$items,
|
||||||
|
"profiles" => (array)$profilesFormatted,
|
||||||
|
"groups" => (array)$groupsFormatted
|
||||||
|
];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return (object)[
|
return (object)[
|
||||||
|
"count" => $count,
|
||||||
"items" => (array)$items
|
"items" => (array)$items
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function post(string $owner_id, string $message, int $from_group = 0): object
|
function post(string $owner_id, string $message, int $from_group = 0, int $signed = 0): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
@ -88,6 +133,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$flags = 0;
|
$flags = 0;
|
||||||
if($from_group == 1)
|
if($from_group == 1)
|
||||||
$flags |= 0b10000000;
|
$flags |= 0b10000000;
|
||||||
|
if($signed == 1)
|
||||||
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$post = new Post;
|
$post = new Post;
|
||||||
|
|
|
@ -90,6 +90,21 @@ class Club extends RowModel
|
||||||
return (new Users)->get($this->getRecord()->owner);
|
return (new Users)->get($this->getRecord()->owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOwnerComment(): string
|
||||||
|
{
|
||||||
|
return is_null($this->getRecord()->owner_comment) ? "" : $this->getRecord()->owner_comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOwnerHidden(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->owner_hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOwnerClubPinned(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->owner_club_pinned;
|
||||||
|
}
|
||||||
|
|
||||||
function getDescription(): ?string
|
function getDescription(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->about;
|
return $this->getRecord()->about;
|
||||||
|
@ -110,6 +125,11 @@ class Club extends RowModel
|
||||||
return $this->getRecord()->closed;
|
return $this->getRecord()->closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAdministratorsListDisplay(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->administrators_list_display;
|
||||||
|
}
|
||||||
|
|
||||||
function getType(): int
|
function getType(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->type;
|
return $this->getRecord()->type;
|
||||||
|
@ -259,21 +279,11 @@ class Club extends RowModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getManagers(int $page = 1): \Traversable
|
function getManagers(int $page = 1, bool $ignoreHidden = false): \Traversable
|
||||||
{
|
{
|
||||||
$rels = $this->getRecord()->related("group_coadmins.club")->page($page, 6);
|
$rels = $this->getRecord()->related("group_coadmins.club")->page($page, 6);
|
||||||
|
if($ignoreHidden)
|
||||||
foreach($rels as $rel) {
|
$rels = $rels->where("hidden", false);
|
||||||
$rel = (new Users)->get($rel->user);
|
|
||||||
if(!$rel) continue;
|
|
||||||
|
|
||||||
yield $rel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getManagersWithComment(int $page = 1): \Traversable
|
|
||||||
{
|
|
||||||
$rels = $this->getRecord()->related("group_coadmins.club")->where("comment IS NOT NULL")->page($page, 10);
|
|
||||||
|
|
||||||
foreach($rels as $rel) {
|
foreach($rels as $rel) {
|
||||||
$rel = (new Managers)->get($rel->id);
|
$rel = (new Managers)->get($rel->id);
|
||||||
|
@ -283,14 +293,22 @@ class Club extends RowModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getManagersCount(): int
|
function getManager(User $user, bool $ignoreHidden = false): ?Manager
|
||||||
{
|
{
|
||||||
return sizeof($this->getRecord()->related("group_coadmins.club")) + 1;
|
$manager = (new Managers)->getByUserAndClub($user->getId(), $this->getId());
|
||||||
|
|
||||||
|
if ($ignoreHidden && $manager !== null && $manager->isHidden())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getManagersCountWithComment(): int
|
function getManagersCount(bool $ignoreHidden = false): int
|
||||||
{
|
{
|
||||||
return sizeof($this->getRecord()->related("group_coadmins.club")->where("comment IS NOT NULL")) + 1;
|
if($ignoreHidden)
|
||||||
|
return sizeof($this->getRecord()->related("group_coadmins.club")->where("hidden", false)) + (int) !$this->isOwnerHidden();
|
||||||
|
|
||||||
|
return sizeof($this->getRecord()->related("group_coadmins.club")) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addManager(User $user, ?string $comment = NULL): void
|
function addManager(User $user, ?string $comment = NULL): void
|
||||||
|
@ -319,5 +337,10 @@ class Club extends RowModel
|
||||||
return !is_null($this->getRecord()->related("group_coadmins.club")->where("user", $id)->fetch());
|
return !is_null($this->getRecord()->related("group_coadmins.club")->where("user", $id)->fetch());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWebsite(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->website;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
|
||||||
class Comment extends Post
|
class Comment extends Post
|
||||||
{
|
{
|
||||||
|
@ -24,4 +26,19 @@ class Comment extends Post
|
||||||
|
|
||||||
return $entity;
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* May return fake owner (group), if flags are [1, (*)]
|
||||||
|
*
|
||||||
|
* @param bool $honourFlags - check flags
|
||||||
|
*/
|
||||||
|
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
|
||||||
|
{
|
||||||
|
if($honourFlags && $this->isPostedOnBehalfOfGroup()) {
|
||||||
|
if($this->getTarget() instanceof Post)
|
||||||
|
return (new Clubs)->get(abs($this->getTarget()->getTargetWall()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getOwner($honourFlags, $real);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
164
Web/Models/Entities/Gift.php
Normal file
164
Web/Models/Entities/Gift.php
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Util\DateTime;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use Nette\Utils\{Image, ImageException};
|
||||||
|
|
||||||
|
class Gift extends RowModel
|
||||||
|
{
|
||||||
|
const IMAGE_MAXSIZE = 131072;
|
||||||
|
const IMAGE_BINARY = 0;
|
||||||
|
const IMAGE_BASE64 = 1;
|
||||||
|
const IMAGE_URL = 2;
|
||||||
|
|
||||||
|
const PERIOD_IGNORE = 0;
|
||||||
|
const PERIOD_SET = 1;
|
||||||
|
const PERIOD_SET_IF_NONE = 2;
|
||||||
|
|
||||||
|
protected $tableName = "gifts";
|
||||||
|
|
||||||
|
function getName(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->internal_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrice(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->price;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsages(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->usages;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsagesBy(User $user, ?int $since = NULL): int
|
||||||
|
{
|
||||||
|
$sent = $this->getRecord()
|
||||||
|
->related("gift_user_relations.gift")
|
||||||
|
->where("sender", $user->getId())
|
||||||
|
->where("sent >= ?", $since ?? $this->getRecord()->limit_period ?? 0);
|
||||||
|
|
||||||
|
return sizeof($sent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsagesLeft(User $user): float
|
||||||
|
{
|
||||||
|
if($this->getLimit() === INF)
|
||||||
|
return INF;
|
||||||
|
|
||||||
|
return max(0, $this->getLimit() - $this->getUsagesBy($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImage(int $type = 0): /* ?binary */ string
|
||||||
|
{
|
||||||
|
switch($type) {
|
||||||
|
default:
|
||||||
|
case static::IMAGE_BINARY:
|
||||||
|
return $this->getRecord()->image ?? "";
|
||||||
|
break;
|
||||||
|
case static::IMAGE_BASE64:
|
||||||
|
return "data:image/png;base64," . base64_encode($this->getRecord()->image ?? "");
|
||||||
|
break;
|
||||||
|
case static::IMAGE_URL:
|
||||||
|
return "/gift" . $this->getId() . "_" . $this->getUpdateDate()->timestamp() . ".png";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLimit(): float
|
||||||
|
{
|
||||||
|
$limit = $this->getRecord()->limit;
|
||||||
|
|
||||||
|
return !$limit ? INF : (float) $limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLimitResetTime(): ?DateTime
|
||||||
|
{
|
||||||
|
return is_null($t = $this->getRecord()->limit_period) ? NULL : new DateTime($t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUpdateDate(): DateTime
|
||||||
|
{
|
||||||
|
return new DateTime($this->getRecord()->updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canUse(User $user): bool
|
||||||
|
{
|
||||||
|
return $this->getUsagesLeft($user) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFree(): bool
|
||||||
|
{
|
||||||
|
return $this->getPrice() === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function used(): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("usages", $this->getUsages() + 1);
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setName(string $name): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("internal_name", $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setImage(string $file): bool
|
||||||
|
{
|
||||||
|
$imgBlob;
|
||||||
|
try {
|
||||||
|
$image = Image::fromFile($file);
|
||||||
|
$image->resize(512, 512, Image::SHRINK_ONLY);
|
||||||
|
|
||||||
|
$imgBlob = $image->toString(Image::PNG);
|
||||||
|
} catch(ImageException $ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen($imgBlob) > (2**24 - 1)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$this->stateChanges("updated", time());
|
||||||
|
$this->stateChanges("image", $imgBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLimit(?float $limit = NULL, int $periodBehaviour = 0): void
|
||||||
|
{
|
||||||
|
$limit ??= $this->getLimit();
|
||||||
|
$limit = $limit === INF ? NULL : (int) $limit;
|
||||||
|
$this->stateChanges("limit", $limit);
|
||||||
|
|
||||||
|
if(!$limit) {
|
||||||
|
$this->stateChanges("limit_period", NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($periodBehaviour) {
|
||||||
|
default:
|
||||||
|
case static::PERIOD_IGNORE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::PERIOD_SET:
|
||||||
|
$this->stateChanges("limit_period", time());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::PERIOD_SET_IF_NONE:
|
||||||
|
if(is_null($this->getRecord()) || is_null($this->getRecord()->limit_period))
|
||||||
|
$this->stateChanges("limit_period", time());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(bool $softly = true): void
|
||||||
|
{
|
||||||
|
$this->getRecord()->related("gift_relations.gift")->delete();
|
||||||
|
|
||||||
|
parent::delete($softly);
|
||||||
|
}
|
||||||
|
}
|
156
Web/Models/Entities/GiftCategory.php
Normal file
156
Web/Models/Entities/GiftCategory.php
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
use openvk\Web\Models\Repositories\Gifts;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use Transliterator;
|
||||||
|
|
||||||
|
class GiftCategory extends RowModel
|
||||||
|
{
|
||||||
|
protected $tableName = "gift_categories";
|
||||||
|
|
||||||
|
private function getLocalization(string $language): object
|
||||||
|
{
|
||||||
|
return $this->getRecord()
|
||||||
|
->related("gift_categories_locales.category")
|
||||||
|
->where("language", $language);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createLocalizationIfNotExists(string $language): void
|
||||||
|
{
|
||||||
|
if(!is_null($this->getLocalization($language)->fetch()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DB::i()->getContext()->table("gift_categories_locales")->insert([
|
||||||
|
"category" => $this->getId(),
|
||||||
|
"language" => $language,
|
||||||
|
"name" => "Sample Text",
|
||||||
|
"description" => "Sample Text",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlug(): string
|
||||||
|
{
|
||||||
|
return str_replace("ʹ", "-", Transliterator::createFromRules(
|
||||||
|
":: Any-Latin;"
|
||||||
|
. ":: NFD;"
|
||||||
|
. ":: [:Nonspacing Mark:] Remove;"
|
||||||
|
. ":: NFC;"
|
||||||
|
. ":: [:Punctuation:] Remove;"
|
||||||
|
. ":: Lower();"
|
||||||
|
. "[:Separator:] > '-'"
|
||||||
|
)->transliterate($this->getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getThumbnailURL(): string
|
||||||
|
{
|
||||||
|
$primeGift = iterator_to_array($this->getGifts(1, 1))[0];
|
||||||
|
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
|
||||||
|
if(!$primeGift)
|
||||||
|
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
|
||||||
|
|
||||||
|
return $primeGift->getImage(Gift::IMAGE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(string $language = "_", bool $returnNull = false): ?string
|
||||||
|
{
|
||||||
|
$loc = $this->getLocalization($language)->fetch();
|
||||||
|
if(!$loc) {
|
||||||
|
if($returnNull)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return $language === "_" ? "Unlocalized" : $this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loc->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDescription(string $language = "_", bool $returnNull = false): ?string
|
||||||
|
{
|
||||||
|
$loc = $this->getLocalization($language)->fetch();
|
||||||
|
if(!$loc) {
|
||||||
|
if($returnNull)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return $language === "_" ? "Unlocalized" : $this->getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loc->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGifts(int $page = -1, ?int $perPage = NULL, &$count = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
$gifts = $this->getRecord()->related("gift_relations.category");
|
||||||
|
if($page !== -1) {
|
||||||
|
$count = $gifts->count();
|
||||||
|
$gifts = $gifts->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($gifts as $rel)
|
||||||
|
yield (new Gifts)->get($rel->gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMagical(): bool
|
||||||
|
{
|
||||||
|
return !is_null($this->getRecord()->autoquery);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasGift(Gift $gift): bool
|
||||||
|
{
|
||||||
|
$rels = $this->getRecord()->related("gift_relations.category");
|
||||||
|
|
||||||
|
return $rels->where("gift", $gift->getId())->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGift(Gift $gift): void
|
||||||
|
{
|
||||||
|
if($this->hasGift($gift))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DB::i()->getContext()->table("gift_relations")->insert([
|
||||||
|
"category" => $this->getId(),
|
||||||
|
"gift" => $gift->getId(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeGift(Gift $gift): void
|
||||||
|
{
|
||||||
|
if(!$this->hasGift($gift))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DB::i()->getContext()->table("gift_relations")->where([
|
||||||
|
"category" => $this->getId(),
|
||||||
|
"gift" => $gift->getId(),
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setName(string $language, string $name): void
|
||||||
|
{
|
||||||
|
$this->createLocalizationIfNotExists($language);
|
||||||
|
$this->getLocalization($language)->update([
|
||||||
|
"name" => $name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDescription(string $language, string $description): void
|
||||||
|
{
|
||||||
|
$this->createLocalizationIfNotExists($language);
|
||||||
|
$this->getLocalization($language)->update([
|
||||||
|
"description" => $description,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAutoQuery(?array $query = NULL): void
|
||||||
|
{
|
||||||
|
if(is_null($query)) {
|
||||||
|
$this->stateChanges("autoquery", NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedColumns = ["price", "usages"];
|
||||||
|
if(array_diff_key($query, array_flip($allowedColumns)))
|
||||||
|
throw new \LogicException("Invalid query");
|
||||||
|
|
||||||
|
$this->stateChanges("autoquery", serialize($query));
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ class Manager extends RowModel
|
||||||
return $this->getRecord()->id;
|
return $this->getRecord()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserId(): string
|
function getUserId(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->user;
|
return $this->getRecord()->user;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class Manager extends RowModel
|
||||||
return (new Users)->get($this->getRecord()->user);
|
return (new Users)->get($this->getRecord()->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClubId(): string
|
function getClubId(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->club;
|
return $this->getRecord()->club;
|
||||||
}
|
}
|
||||||
|
@ -42,5 +42,15 @@ class Manager extends RowModel
|
||||||
return is_null($this->getRecord()->comment) ? "" : $this->getRecord()->comment;
|
return is_null($this->getRecord()->comment) ? "" : $this->getRecord()->comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isHidden(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isClubPinned(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->club_pinned;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,4 +89,21 @@ abstract class Media extends Postable
|
||||||
|
|
||||||
$this->stateChanges("hash", $hash);
|
$this->stateChanges("hash", $hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delete(bool $softly = true): void
|
||||||
|
{
|
||||||
|
$deleteQuirk = ovkGetQuirk("blobs.erase-upon-deletion");
|
||||||
|
if($deleteQuirk === 2 || ($deleteQuirk === 1 && !$softly))
|
||||||
|
@unlink($this->getFileName());
|
||||||
|
|
||||||
|
parent::delete($softly);
|
||||||
|
}
|
||||||
|
|
||||||
|
function undelete(): void
|
||||||
|
{
|
||||||
|
if(ovkGetQuirk("blobs.erase-upon-deletion") === 2)
|
||||||
|
throw new \LogicException("Can't undelete model which is tied to blob, because of config constraint (quriks.yml:blobs.erase-upon-deletion)");
|
||||||
|
|
||||||
|
parent::undelete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
13
Web/Models/Entities/Notifications/GiftNotification.php
Normal file
13
Web/Models/Entities/Notifications/GiftNotification.php
Normal 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 GiftNotification extends Notification
|
||||||
|
{
|
||||||
|
protected $actionCode = 9601;
|
||||||
|
|
||||||
|
function __construct(User $receiver, User $sender, Gift $gift, ?string $comment)
|
||||||
|
{
|
||||||
|
parent::__construct($receiver, $gift, $sender, time(), $comment ?? "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ namespace openvk\Web\Models\Entities\Notifications;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\{User};
|
use openvk\Web\Models\Entities\{User};
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
|
use RdKafka\{Conf, Producer};
|
||||||
|
|
||||||
class Notification
|
class Notification
|
||||||
{
|
{
|
||||||
|
@ -80,14 +81,14 @@ class Notification
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
$this->recipient->getId(),
|
"recipient" => $this->recipient->getId(),
|
||||||
$this->encodeType($this->originModel),
|
"originModelType" => $this->encodeType($this->originModel),
|
||||||
$this->originModel->getId(),
|
"originModelId" => $this->originModel->getId(),
|
||||||
$this->encodeType($this->targetModel),
|
"targetModelType" => $this->encodeType($this->targetModel),
|
||||||
$this->targetModel->getId(),
|
"targetModelId" => $this->targetModel->getId(),
|
||||||
$this->actionCode,
|
"actionCode" => $this->actionCode,
|
||||||
$this->data,
|
"additionalPayload" => $this->data,
|
||||||
$this->time,
|
"timestamp" => $this->time,
|
||||||
];
|
];
|
||||||
|
|
||||||
$edb = $e->getConnection();
|
$edb = $e->getConnection();
|
||||||
|
@ -96,12 +97,33 @@ class Notification
|
||||||
$query = <<<'QUERY'
|
$query = <<<'QUERY'
|
||||||
SELECT * FROM `notifications` WHERE `recipientType`=0 AND `recipientId`=? AND `originModelType`=? AND `originModelId`=? AND `targetModelType`=? AND `targetModelId`=? AND `modelAction`=? AND `additionalData`=? AND `timestamp` > (? - ?)
|
SELECT * FROM `notifications` WHERE `recipientType`=0 AND `recipientId`=? AND `originModelType`=? AND `originModelId`=? AND `targetModelType`=? AND `targetModelId`=? AND `modelAction`=? AND `additionalData`=? AND `timestamp` > (? - ?)
|
||||||
QUERY;
|
QUERY;
|
||||||
$result = $edb->query($query, ...array_merge($data, [ $this->threshold ]));
|
$result = $edb->query($query, ...array_merge(array_values($data), [ $this->threshold ]));
|
||||||
if($result->getRowCount() > 0)
|
if($result->getRowCount() > 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$edb->query("INSERT INTO notifications VALUES (0, ?, ?, ?, ?, ?, ?, ?, ?)", ...$data);
|
$edb->query("INSERT INTO notifications VALUES (0, ?, ?, ?, ?, ?, ?, ?, ?)", ...array_values($data));
|
||||||
|
|
||||||
|
$kafkaConf = OPENVK_ROOT_CONF["openvk"]["credentials"]["notificationsBroker"];
|
||||||
|
if($kafkaConf["enable"]) {
|
||||||
|
$kafkaConf = $kafkaConf["kafka"];
|
||||||
|
$brokerConf = new Conf();
|
||||||
|
$brokerConf->set("log_level", (string) LOG_DEBUG);
|
||||||
|
$brokerConf->set("debug", "all");
|
||||||
|
|
||||||
|
$producer = new Producer($brokerConf);
|
||||||
|
$producer->addBrokers($kafkaConf["addr"] . ":" . $kafkaConf["port"]);
|
||||||
|
|
||||||
|
$descriptor = implode(",", [
|
||||||
|
str_replace("\\", ".", get_class($this)),
|
||||||
|
$this->recipient->getId(),
|
||||||
|
base64_encode(serialize((object) $data)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$notifTopic = $producer->newTopic($kafkaConf["topic"]);
|
||||||
|
$notifTopic->produce(RD_KAFKA_PARTITION_UA, RD_KAFKA_MSG_F_BLOCK, $descriptor);
|
||||||
|
$producer->flush(100);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,13 @@ class Photo extends Media
|
||||||
protected $tableName = "photos";
|
protected $tableName = "photos";
|
||||||
protected $fileExtension = "jpeg";
|
protected $fileExtension = "jpeg";
|
||||||
|
|
||||||
|
const ALLOWED_SIDE_MULTIPLIER = 7;
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
$image = Image::fromFile($filename);
|
$image = Image::fromFile($filename);
|
||||||
if(($image->height >= ($image->width * pi())) || ($image->width >= ($image->height * pi())))
|
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
||||||
throw new ISE("Invalid layout: expected layout that matches (x, ?!>3x)");
|
throw new ISE("Invalid layout: image is too wide/short");
|
||||||
|
|
||||||
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
||||||
|
|
||||||
|
@ -41,4 +43,20 @@ class Photo extends Media
|
||||||
|
|
||||||
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
|
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function fastMake(int $owner, string $description = "", array $file, ?Album $album = NULL, bool $anon = false): Photo
|
||||||
|
{
|
||||||
|
$photo = new static;
|
||||||
|
$photo->setOwner($owner);
|
||||||
|
$photo->setDescription(iconv_substr($description, 0, 36) . "...");
|
||||||
|
$photo->setAnonymous($anon);
|
||||||
|
$photo->setCreated(time());
|
||||||
|
$photo->setFile($file);
|
||||||
|
$photo->save();
|
||||||
|
|
||||||
|
if(!is_null($album))
|
||||||
|
$album->addPhoto($photo);
|
||||||
|
|
||||||
|
return $photo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,47 @@ namespace openvk\Web\Models\Entities;
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use openvk\Web\Models\Repositories\Clubs;
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Entities\Notifications\LikeNotification;
|
||||||
|
|
||||||
class Post extends Postable
|
class Post extends Postable
|
||||||
{
|
{
|
||||||
protected $tableName = "posts";
|
protected $tableName = "posts";
|
||||||
protected $upperNodeReferenceColumnName = "wall";
|
protected $upperNodeReferenceColumnName = "wall";
|
||||||
|
|
||||||
|
private function setLikeRecursively(bool $liked, User $user, int $depth): void
|
||||||
|
{
|
||||||
|
$searchData = [
|
||||||
|
"origin" => $user->getId(),
|
||||||
|
"model" => static::class,
|
||||||
|
"target" => $this->getRecord()->id,
|
||||||
|
];
|
||||||
|
|
||||||
|
if((sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) !== $liked) {
|
||||||
|
if($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment)
|
||||||
|
(new LikeNotification($this->getOwner(false), $this, $user))->emit();
|
||||||
|
|
||||||
|
parent::setLike($liked, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($depth < ovkGetQuirk("wall.repost-liking-recursion-limit"))
|
||||||
|
foreach($this->getChildren() as $attachment)
|
||||||
|
if($attachment instanceof Post)
|
||||||
|
$attachment->setLikeRecursively($liked, $user, $depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May return fake owner (group), if flags are [1, (*)]
|
* May return fake owner (group), if flags are [1, (*)]
|
||||||
*
|
*
|
||||||
* @param bool $honourFlags - check flags
|
* @param bool $honourFlags - check flags
|
||||||
*/
|
*/
|
||||||
function getOwner(bool $honourFlags = true): RowModel
|
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
|
||||||
{
|
{
|
||||||
if($honourFlags && ( ($this->getRecord()->flags & 0b10000000) > 0 )) {
|
if($honourFlags && $this->isPostedOnBehalfOfGroup()) {
|
||||||
if($this->getRecord()->wall < 0)
|
if($this->getRecord()->wall < 0)
|
||||||
return (new Clubs)->get(abs($this->getRecord()->wall));
|
return (new Clubs)->get(abs($this->getRecord()->wall));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::getOwner();
|
return parent::getOwner($real);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrettyId(): string
|
function getPrettyId(): string
|
||||||
|
@ -75,7 +97,7 @@ class Post extends Postable
|
||||||
|
|
||||||
function getOwnerPost(): int
|
function getOwnerPost(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->owner;
|
return $this->getOwner(false)->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
function pin(): void
|
function pin(): void
|
||||||
|
@ -122,6 +144,25 @@ class Post extends Postable
|
||||||
$this->stateChanges("content", $content);
|
$this->stateChanges("content", $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleLike(User $user): bool
|
||||||
|
{
|
||||||
|
$liked = parent::toggleLike($user);
|
||||||
|
|
||||||
|
if($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment)
|
||||||
|
(new LikeNotification($this->getOwner(false), $this, $user))->emit();
|
||||||
|
|
||||||
|
foreach($this->getChildren() as $attachment)
|
||||||
|
if($attachment instanceof Post)
|
||||||
|
$attachment->setLikeRecursively($liked, $user, 2);
|
||||||
|
|
||||||
|
return $liked;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLike(bool $liked, User $user): void
|
||||||
|
{
|
||||||
|
$this->setLikeRecursively($liked, $user, 1);
|
||||||
|
}
|
||||||
|
|
||||||
function deletePost(): void
|
function deletePost(): void
|
||||||
{
|
{
|
||||||
$this->setDeleted(1);
|
$this->setDeleted(1);
|
||||||
|
|
|
@ -29,9 +29,12 @@ abstract class Postable extends Attachable
|
||||||
return DB::i()->getContext()->table($this->tableName);
|
return DB::i()->getContext()->table($this->tableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOwner(): RowModel
|
function getOwner(bool $real = false): RowModel
|
||||||
{
|
{
|
||||||
$oid = (int) $this->getRecord()->owner;
|
$oid = (int) $this->getRecord()->owner;
|
||||||
|
if(!$real && $this->isAnonymous())
|
||||||
|
$oid = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
||||||
|
|
||||||
if($oid > 0)
|
if($oid > 0)
|
||||||
return (new Users)->get($oid);
|
return (new Users)->get($oid);
|
||||||
else
|
else
|
||||||
|
@ -71,9 +74,9 @@ abstract class Postable extends Attachable
|
||||||
return (new Comments)->getCommentsCountByTarget($this);
|
return (new Comments)->getCommentsCountByTarget($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLastComments()
|
function getLastComments(int $count): \Traversable
|
||||||
{
|
{
|
||||||
return (new Comments)->getLastCommentsByTarget($this);
|
return (new Comments)->getLastCommentsByTarget($this, $count);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLikesCount(): int
|
function getLikesCount(): int
|
||||||
|
@ -84,17 +87,52 @@ abstract class Postable extends Attachable
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleLike(User $user): void
|
// TODO add pagination
|
||||||
|
function getLikers(): \Traversable
|
||||||
|
{
|
||||||
|
$sel = DB::i()->getContext()->table("likes")->where([
|
||||||
|
"model" => static::class,
|
||||||
|
"target" => $this->getRecord()->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach($sel as $like)
|
||||||
|
yield (new Users)->get($like->origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnonymous(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->anonymous;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLike(User $user): bool
|
||||||
{
|
{
|
||||||
$searchData = [
|
$searchData = [
|
||||||
"origin" => $user->getId(),
|
"origin" => $user->getId(),
|
||||||
"model" => static::class,
|
"model" => static::class,
|
||||||
"target" => $this->getRecord()->id,
|
"target" => $this->getRecord()->id,
|
||||||
];
|
];
|
||||||
if(sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0)
|
|
||||||
|
if(sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) {
|
||||||
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
||||||
else
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
DB::i()->getContext()->table("likes")->insert($searchData);
|
DB::i()->getContext()->table("likes")->insert($searchData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLike(bool $liked, User $user): void
|
||||||
|
{
|
||||||
|
$searchData = [
|
||||||
|
"origin" => $user->getId(),
|
||||||
|
"model" => static::class,
|
||||||
|
"target" => $this->getRecord()->id,
|
||||||
|
];
|
||||||
|
|
||||||
|
if($liked)
|
||||||
|
DB::i()->getContext()->table("likes")->insert($searchData);
|
||||||
|
else
|
||||||
|
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasLikeFrom(User $user): bool
|
function hasLikeFrom(User $user): bool
|
||||||
|
|
39
Web/Models/Entities/SupportAlias.php
Normal file
39
Web/Models/Entities/SupportAlias.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Repositories\Users;
|
||||||
|
|
||||||
|
class SupportAlias extends RowModel
|
||||||
|
{
|
||||||
|
protected $tableName = "support_names";
|
||||||
|
|
||||||
|
function getUser(): User
|
||||||
|
{
|
||||||
|
return (new Users)->get($this->getRecord()->agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcon(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldAppendNumber(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->numerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAgent(User $agent): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("agent", $agent->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNumeration(bool $numerate): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("numerate", $numerate);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,9 +11,10 @@ use Nette\Database\Table\Selection;
|
||||||
|
|
||||||
class Ticket extends RowModel
|
class Ticket extends RowModel
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $tableName = "tickets";
|
protected $tableName = "tickets";
|
||||||
|
|
||||||
|
private $overrideContentColumn = "text";
|
||||||
|
|
||||||
function getId(): int
|
function getId(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->id;
|
return $this->getRecord()->id;
|
||||||
|
@ -23,11 +24,11 @@ class Ticket extends RowModel
|
||||||
{
|
{
|
||||||
if ($this->getRecord()->type === 0)
|
if ($this->getRecord()->type === 0)
|
||||||
{
|
{
|
||||||
return 'Вопрос находится на рассмотрении.';
|
return tr("support_status_0");
|
||||||
} elseif ($this->getRecord()->type === 1) {
|
} elseif ($this->getRecord()->type === 1) {
|
||||||
return 'Есть ответ.';
|
return tr("support_status_1");
|
||||||
} elseif ($this->getRecord()->type === 2) {
|
} elseif ($this->getRecord()->type === 2) {
|
||||||
return 'Закрыто.';
|
return tr("support_status_2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +44,11 @@ class Ticket extends RowModel
|
||||||
|
|
||||||
function getContext(): string
|
function getContext(): string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->text;
|
$text = $this->getRecord()->text;
|
||||||
|
$text = $this->formatLinks($text);
|
||||||
|
$text = $this->removeZalgo($text);
|
||||||
|
$text = nl2br($text);
|
||||||
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTime(): DateTime
|
function getTime(): DateTime
|
||||||
|
@ -70,4 +75,11 @@ class Ticket extends RowModel
|
||||||
{
|
{
|
||||||
return (new Users)->get($this->getRecord()->user_id);
|
return (new Users)->get($this->getRecord()->user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAd(): bool /* Эх, костыли... */
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
use Traits\TRichText;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,27 @@ use openvk\Web\Util\DateTime;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use openvk\Web\Models\Repositories\Users;
|
use openvk\Web\Models\Repositories\{Users, SupportAliases};
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
use Nette\Database\Table\Selection;
|
use Nette\Database\Table\Selection;
|
||||||
|
|
||||||
class TicketComment extends RowModel
|
class TicketComment extends RowModel
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $tableName = "tickets_comments";
|
protected $tableName = "tickets_comments";
|
||||||
|
|
||||||
|
private $overrideContentColumn = "text";
|
||||||
|
|
||||||
|
private function getSupportAlias(): ?SupportAlias
|
||||||
|
{
|
||||||
|
return (new SupportAliases)->get($this->getUser()->getId());
|
||||||
|
}
|
||||||
|
|
||||||
function getId(): int
|
function getId(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->id;
|
return $this->getRecord()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUType(): int
|
function getUType(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->user_type;
|
return $this->getRecord()->user_type;
|
||||||
|
@ -28,6 +35,33 @@ class TicketComment extends RowModel
|
||||||
return (new Users)->get($this->getRecord()->user_id);
|
return (new Users)->get($this->getRecord()->user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAuthorName(): string
|
||||||
|
{
|
||||||
|
if($this->getUType() === 0)
|
||||||
|
return $this->getUser()->getCanonicalName();
|
||||||
|
|
||||||
|
$alias = $this->getSupportAlias();
|
||||||
|
if(!$alias)
|
||||||
|
return OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["supportName"] . " №" . $this->getAgentNumber();
|
||||||
|
|
||||||
|
$name = $alias->getName();
|
||||||
|
if($alias->shouldAppendNumber())
|
||||||
|
$name .= " №" . $this->getAgentNumber();
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAvatar(): string
|
||||||
|
{
|
||||||
|
if($this->getUType() === 0)
|
||||||
|
return $this->getUser()->getAvatarUrl();
|
||||||
|
|
||||||
|
$default = "/assets/packages/static/openvk/img/support.jpeg";
|
||||||
|
$alias = $this->getSupportAlias();
|
||||||
|
|
||||||
|
return is_null($alias) ? $default : ($alias->getIcon() ?? $default);
|
||||||
|
}
|
||||||
|
|
||||||
function getAgentNumber(): ?string
|
function getAgentNumber(): ?string
|
||||||
{
|
{
|
||||||
if($this->getUType() === 0)
|
if($this->getUType() === 0)
|
||||||
|
@ -46,6 +80,9 @@ class TicketComment extends RowModel
|
||||||
if(is_null($agent = $this->getAgentNumber()))
|
if(is_null($agent = $this->getAgentNumber()))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if(!is_null($this->getSupportAlias()))
|
||||||
|
return 0;
|
||||||
|
|
||||||
$agent = (int) $agent;
|
$agent = (int) $agent;
|
||||||
$rotation = $agent > 500 ? ( ($agent * 360) / 999 ) : $agent; # cap at 360deg
|
$rotation = $agent > 500 ? ( ($agent * 360) / 999 ) : $agent; # cap at 360deg
|
||||||
$values = [0, 45, 160, 220, 310, 345]; # good looking colors
|
$values = [0, 45, 160, 220, 310, 345]; # good looking colors
|
||||||
|
@ -59,7 +96,11 @@ class TicketComment extends RowModel
|
||||||
|
|
||||||
function getContext(): string
|
function getContext(): string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->text;
|
$text = $this->getRecord()->text;
|
||||||
|
$text = $this->formatLinks($text);
|
||||||
|
$text = $this->removeZalgo($text);
|
||||||
|
$text = nl2br($text);
|
||||||
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTime(): DateTime
|
function getTime(): DateTime
|
||||||
|
@ -67,4 +108,10 @@ class TicketComment extends RowModel
|
||||||
return new DateTime($this->getRecord()->created);
|
return new DateTime($this->getRecord()->created);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAd(): bool
|
||||||
|
{
|
||||||
|
return false; # Кооостыыыль!!!
|
||||||
|
}
|
||||||
|
|
||||||
|
use Traits\TRichText;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ trait TRichText
|
||||||
{
|
{
|
||||||
private function formatEmojis(string $text): string
|
private function formatEmojis(string $text): string
|
||||||
{
|
{
|
||||||
if(iconv_strlen($this->getRecord()->content) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["emojiProcessingLimit"])
|
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
|
||||||
|
if(iconv_strlen($this->getRecord()->{$contentColumn}) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["emojiProcessingLimit"])
|
||||||
return $text;
|
return $text;
|
||||||
|
|
||||||
$emojis = \Emoji\detect_emoji($text);
|
$emojis = \Emoji\detect_emoji($text);
|
||||||
|
@ -49,8 +50,10 @@ trait TRichText
|
||||||
|
|
||||||
function getText(bool $html = true): string
|
function getText(bool $html = true): string
|
||||||
{
|
{
|
||||||
$text = htmlentities($this->getRecord()->content, ENT_DISALLOWED | ENT_XHTML);
|
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
|
||||||
$proc = iconv_strlen($this->getRecord()->content) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
|
|
||||||
|
$text = htmlentities($this->getRecord()->{$contentColumn}, ENT_DISALLOWED | ENT_XHTML);
|
||||||
|
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
|
||||||
if($html) {
|
if($html) {
|
||||||
if($proc) {
|
if($proc) {
|
||||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||||
|
|
|
@ -3,8 +3,8 @@ namespace openvk\Web\Models\Entities;
|
||||||
use openvk\Web\Themes\{Themepack, Themepacks};
|
use openvk\Web\Themes\{Themepack, Themepacks};
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\{Photo, Message, Correspondence};
|
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Notifications};
|
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Gifts, Notifications};
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use Chandler\Security\User as ChandlerUser;
|
use Chandler\Security\User as ChandlerUser;
|
||||||
|
@ -204,6 +204,11 @@ class User extends RowModel
|
||||||
return $this->getRecord()->shortcode;
|
return $this->getRecord()->shortcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAlert(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->alert;
|
||||||
|
}
|
||||||
|
|
||||||
function getBanReason(): ?string
|
function getBanReason(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->block_reason;
|
return $this->getRecord()->block_reason;
|
||||||
|
@ -214,11 +219,19 @@ class User extends RowModel
|
||||||
return $this->getRecord()->type;
|
return $this->getRecord()->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCoins(): int
|
function getCoins(): float
|
||||||
{
|
{
|
||||||
|
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
return $this->getRecord()->coins;
|
return $this->getRecord()->coins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRating(): int
|
||||||
|
{
|
||||||
|
return OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"] ? $this->getRecord()->rating : 0;
|
||||||
|
}
|
||||||
|
|
||||||
function getReputation(): int
|
function getReputation(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->reputation;
|
return $this->getRecord()->reputation;
|
||||||
|
@ -309,6 +322,21 @@ class User extends RowModel
|
||||||
return $this->getRecord()->birthday;
|
return $this->getRecord()->birthday;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAge(): ?int
|
||||||
|
{
|
||||||
|
return (int)floor((time() - $this->getBirthday()) / mktime(0, 0, 0, 1, 1, 1971));
|
||||||
|
}
|
||||||
|
|
||||||
|
function get2faSecret(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()["2fa_secret"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2faEnabled(): bool
|
||||||
|
{
|
||||||
|
return !is_null($this->get2faSecret());
|
||||||
|
}
|
||||||
|
|
||||||
function updateNotificationOffset(): void
|
function updateNotificationOffset(): void
|
||||||
{
|
{
|
||||||
$this->stateChanges("notification_offset", time());
|
$this->stateChanges("notification_offset", time());
|
||||||
|
@ -392,8 +420,16 @@ class User extends RowModel
|
||||||
$incompleteness += 20;
|
$incompleteness += 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$total = max(100 - $incompleteness + $this->getRating(), 0);
|
||||||
|
if(ovkGetQuirk("profile.rating-bar-behaviour") === 0)
|
||||||
|
if ($total >= 100)
|
||||||
|
$percent = round(($total / 10**strlen(strval($total))) * 100, 0);
|
||||||
|
else
|
||||||
|
$percent = min($total, 100);
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
"total" => 100 - $incompleteness,
|
"total" => $total,
|
||||||
|
"percent" => $percent,
|
||||||
"unfilled" => $unfilled,
|
"unfilled" => $unfilled,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -433,8 +469,21 @@ class User extends RowModel
|
||||||
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClubs(int $page = 1): \Traversable
|
function getClubs(int $page = 1, bool $admin = false): \Traversable
|
||||||
{
|
{
|
||||||
|
if($admin) {
|
||||||
|
$id = $this->getId();
|
||||||
|
$query = "SELECT `id` FROM `groups` WHERE `owner` = ? UNION SELECT `club` as `id` FROM `group_coadmins` WHERE `user` = ?";
|
||||||
|
$query .= " LIMIT " . OPENVK_DEFAULT_PER_PAGE . " OFFSET " . ($page - 1) * OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
|
||||||
|
$sel = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
||||||
|
foreach($sel as $target) {
|
||||||
|
$target = (new Clubs)->get($target->id);
|
||||||
|
if(!$target) continue;
|
||||||
|
|
||||||
|
yield $target;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$sel = $this->getRecord()->related("subscriptions.follower")->page($page, OPENVK_DEFAULT_PER_PAGE);
|
$sel = $this->getRecord()->related("subscriptions.follower")->page($page, OPENVK_DEFAULT_PER_PAGE);
|
||||||
foreach($sel->where("model", "openvk\\Web\\Models\\Entities\\Club") as $target) {
|
foreach($sel->where("model", "openvk\\Web\\Models\\Entities\\Club") as $target) {
|
||||||
$target = (new Clubs)->get($target->target);
|
$target = (new Clubs)->get($target->target);
|
||||||
|
@ -443,14 +492,54 @@ class User extends RowModel
|
||||||
yield $target;
|
yield $target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getClubCount(): int
|
function getClubCount(bool $admin = false): int
|
||||||
{
|
{
|
||||||
|
if($admin) {
|
||||||
|
$id = $this->getId();
|
||||||
|
$query = "SELECT COUNT(*) AS `cnt` FROM (SELECT `id` FROM `groups` WHERE `owner` = ? UNION SELECT `club` as `id` FROM `group_coadmins` WHERE `user` = ?) u0;";
|
||||||
|
|
||||||
|
return (int) DatabaseConnection::i()->getConnection()->query($query, $id, $id)->fetch()->cnt;
|
||||||
|
} else {
|
||||||
$sel = $this->getRecord()->related("subscriptions.follower");
|
$sel = $this->getRecord()->related("subscriptions.follower");
|
||||||
$sel = $sel->where("model", "openvk\\Web\\Models\\Entities\\Club");
|
$sel = $sel->where("model", "openvk\\Web\\Models\\Entities\\Club");
|
||||||
|
|
||||||
return sizeof($sel);
|
return sizeof($sel);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPinnedClubs(): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true) as $target) {
|
||||||
|
$target = (new Clubs)->get($target->id);
|
||||||
|
if(!$target) continue;
|
||||||
|
|
||||||
|
yield $target;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->getRecord()->related("group_coadmins.user")->where("club_pinned", true) as $target) {
|
||||||
|
$target = (new Clubs)->get($target->club);
|
||||||
|
if(!$target) continue;
|
||||||
|
|
||||||
|
yield $target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPinnedClubCount(): int
|
||||||
|
{
|
||||||
|
return sizeof($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true)) + sizeof($this->getRecord()->related("group_coadmins.user")->where("club_pinned", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isClubPinned(Club $club): bool
|
||||||
|
{
|
||||||
|
if($club->getOwner()->getId() === $this->getId())
|
||||||
|
return $club->isOwnerClubPinned();
|
||||||
|
|
||||||
|
$manager = $club->getManager($this);
|
||||||
|
if(!is_null($manager))
|
||||||
|
return $manager->isClubPinned();
|
||||||
|
}
|
||||||
|
|
||||||
function getMeetings(int $page = 1): \Traversable
|
function getMeetings(int $page = 1): \Traversable
|
||||||
{
|
{
|
||||||
|
@ -468,6 +557,57 @@ class User extends RowModel
|
||||||
return sizeof($this->getRecord()->related("event_turnouts.user"));
|
return sizeof($this->getRecord()->related("event_turnouts.user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGifts(int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$gifts = $this->getRecord()->related("gift_user_relations.receiver")->order("sent DESC")->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
foreach($gifts as $rel) {
|
||||||
|
yield (object) [
|
||||||
|
"sender" => (new Users)->get($rel->sender),
|
||||||
|
"gift" => (new Gifts)->get($rel->gift),
|
||||||
|
"caption" => $rel->comment,
|
||||||
|
"anon" => $rel->anonymous,
|
||||||
|
"sent" => new DateTime($rel->sent),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGiftCount(): int
|
||||||
|
{
|
||||||
|
return sizeof($this->getRecord()->related("gift_user_relations.receiver"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function get2faBackupCodes(): \Traversable
|
||||||
|
{
|
||||||
|
$sel = $this->getRecord()->related("2fa_backup_codes.owner");
|
||||||
|
foreach($sel as $target)
|
||||||
|
yield $target->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get2faBackupCodeCount(): int
|
||||||
|
{
|
||||||
|
return sizeof($this->getRecord()->related("2fa_backup_codes.owner"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate2faBackupCodes(): void
|
||||||
|
{
|
||||||
|
$codes = [];
|
||||||
|
|
||||||
|
for($i = 0; $i < 10 - $this->get2faBackupCodeCount(); $i++) {
|
||||||
|
$codes[] = [
|
||||||
|
owner => $this->getId(),
|
||||||
|
code => random_int(10000000, 99999999)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sizeof($codes) > 0)
|
||||||
|
DatabaseConnection::i()->getContext()->table("2fa_backup_codes")->insert($codes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function use2faBackupCode(int $code): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->related("2fa_backup_codes.owner")->where("code", $code)->delete();
|
||||||
|
}
|
||||||
|
|
||||||
function getSubscriptionStatus(User $user): int
|
function getSubscriptionStatus(User $user): int
|
||||||
{
|
{
|
||||||
$subbed = !is_null($this->getRecord()->related("subscriptions.follower")->where([
|
$subbed = !is_null($this->getRecord()->related("subscriptions.follower")->where([
|
||||||
|
@ -528,6 +668,11 @@ class User extends RowModel
|
||||||
return !is_null($this->getBanReason());
|
return !is_null($this->getBanReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isOnline(): bool
|
||||||
|
{
|
||||||
|
return time() - $this->getRecord()->online <= 300;
|
||||||
|
}
|
||||||
|
|
||||||
function prefersNotToSeeRating(): bool
|
function prefersNotToSeeRating(): bool
|
||||||
{
|
{
|
||||||
return !((bool) $this->getRecord()->show_rating);
|
return !((bool) $this->getRecord()->show_rating);
|
||||||
|
@ -538,6 +683,18 @@ class User extends RowModel
|
||||||
return !is_null($this->getPendingPhoneVerification());
|
return !is_null($this->getPendingPhoneVerification());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gift(User $sender, Gift $gift, ?string $comment = NULL, bool $anonymous = false): void
|
||||||
|
{
|
||||||
|
DatabaseConnection::i()->getContext()->table("gift_user_relations")->insert([
|
||||||
|
"sender" => $sender->getId(),
|
||||||
|
"receiver" => $this->getId(),
|
||||||
|
"gift" => $gift->getId(),
|
||||||
|
"comment" => $comment,
|
||||||
|
"anonymous" => $anonymous,
|
||||||
|
"sent" => time(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
function ban(string $reason): void
|
function ban(string $reason): void
|
||||||
{
|
{
|
||||||
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
||||||
|
@ -615,9 +772,11 @@ class User extends RowModel
|
||||||
$this->stateChanges("left_menu", $mask);
|
$this->stateChanges("left_menu", $mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShortCode(?string $code = NULL): ?bool
|
function setShortCode(?string $code = NULL, bool $force = false): ?bool
|
||||||
{
|
{
|
||||||
if(!is_null($code)) {
|
if(!is_null($code)) {
|
||||||
|
if(strlen($code) < OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["minLength"] && !$force)
|
||||||
|
return false;
|
||||||
if(!preg_match("%^[a-z][a-z0-9\\.\\_]{0,30}[a-z0-9]$%", $code))
|
if(!preg_match("%^[a-z][a-z0-9\\.\\_]{0,30}[a-z0-9]$%", $code))
|
||||||
return false;
|
return false;
|
||||||
if(in_array($code, OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["forbiddenNames"]))
|
if(in_array($code, OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["forbiddenNames"]))
|
||||||
|
@ -709,6 +868,10 @@ class User extends RowModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWebsite(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->website;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
use openvk\Web\Util\Shell\Shell;
|
use openvk\Web\Util\Shell\Shell;
|
||||||
use openvk\Web\Util\Shell\Shell\Exceptions\ShellUnavailableException;
|
use openvk\Web\Util\Shell\Shell\Exceptions\{ShellUnavailableException, UnknownCommandException};
|
||||||
use openvk\Web\Util\Shell\Shell\Exceptions\UnknownCommandException;
|
|
||||||
use openvk\Web\Models\VideoDrivers\VideoDriver;
|
use openvk\Web\Models\VideoDrivers\VideoDriver;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
|
|
||||||
|
@ -18,7 +17,24 @@ class Video extends Media
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
if(!Shell::commandAvailable("ffmpeg")) exit(VIDEOS_FRIENDLY_ERROR);
|
if(!Shell::commandAvailable("ffmpeg") || !Shell::commandAvailable("ffprobe"))
|
||||||
|
exit(VIDEOS_FRIENDLY_ERROR);
|
||||||
|
|
||||||
|
$error = NULL;
|
||||||
|
$streams = Shell::ffprobe("-i", $filename, "-show_streams", "-select_streams v", "-loglevel error")->execute($error);
|
||||||
|
if($error !== 0)
|
||||||
|
throw new \DomainException("$filename is not a valid video file");
|
||||||
|
else if(empty($streams) || ctype_space($streams))
|
||||||
|
throw new \DomainException("$filename does not contain any video streams");
|
||||||
|
|
||||||
|
$durations = [];
|
||||||
|
preg_match('%duration=([0-9\.]++)%', $streams, $durations);
|
||||||
|
if(sizeof($durations[1]) === 0)
|
||||||
|
throw new \DomainException("$filename does not contain any meaningful video streams");
|
||||||
|
|
||||||
|
foreach($durations[1] as $duration)
|
||||||
|
if(floatval($duration) < 1.0)
|
||||||
|
throw new \DomainException("$filename does not contain any meaningful video streams");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!is_dir($dirId = $this->pathFromHash($hash)))
|
if(!is_dir($dirId = $this->pathFromHash($hash)))
|
||||||
|
@ -105,4 +121,19 @@ class Video extends Media
|
||||||
$this->unwire();
|
$this->unwire();
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function fastMake(int $owner, string $description = "", array $file, bool $unlisted = true, bool $anon = false): Video
|
||||||
|
{
|
||||||
|
$video = new Video;
|
||||||
|
$video->setOwner($owner);
|
||||||
|
$video->setName("Unnamed Video.ogv");
|
||||||
|
$video->setDescription(ovk_proc_strtr($description, 300));
|
||||||
|
$video->setAnonymous($anon);
|
||||||
|
$video->setCreated(time());
|
||||||
|
$video->setFile($file);
|
||||||
|
$video->setUnlisted($unlisted);
|
||||||
|
$video->save();
|
||||||
|
|
||||||
|
return $video;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
85
Web/Models/Entities/Voucher.php
Normal file
85
Web/Models/Entities/Voucher.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
use openvk\Web\Models\Repositories\Users;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
|
||||||
|
class Voucher extends RowModel
|
||||||
|
{
|
||||||
|
protected $tableName = "coin_vouchers";
|
||||||
|
|
||||||
|
function getCoins(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRating(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->rating;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getToken(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormattedToken(): string
|
||||||
|
{
|
||||||
|
$fmtTok = "";
|
||||||
|
$token = $this->getRecord()->token;
|
||||||
|
foreach(array_chunk(str_split($token), 6) as $chunk)
|
||||||
|
$fmtTok .= implode("", $chunk) . "-";
|
||||||
|
|
||||||
|
return substr($fmtTok, 0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRemainingUsages(): float
|
||||||
|
{
|
||||||
|
return (float) ($this->getRecord()->usages_left ?? INF);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsers(int $page = -1, ?int $perPage = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$relations = $this->getRecord()->related("voucher_users.voucher");
|
||||||
|
if($page !== -1)
|
||||||
|
$relations = $relations->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
|
||||||
|
foreach($relations as $relation)
|
||||||
|
yield (new Users)->get($relation->user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExpired(): bool
|
||||||
|
{
|
||||||
|
return $this->getRemainingUsages() < 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wasUsedBy(User $user): bool
|
||||||
|
{
|
||||||
|
$record = $this->getRecord()->related("voucher_users.voucher")->where("user", $user->getId());
|
||||||
|
|
||||||
|
return sizeof($record) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function willUse(User $user): bool
|
||||||
|
{
|
||||||
|
if($this->wasUsedBy($user))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($this->isExpired())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$this->setRemainingUsages($this->getRemainingUsages() - 1);
|
||||||
|
DB::i()->getContext()->table("voucher_users")->insert([
|
||||||
|
"voucher" => $this->getId(),
|
||||||
|
"user" => $user->getId(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRemainingUsages(float $usages): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("usages_left", $usages === INF ? NULL : ((int) $usages));
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,19 @@ class Comments
|
||||||
yield $this->toComment($comment);
|
yield $this->toComment($comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLastCommentsByTarget(Postable $target, ?int $count = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$comments = $this->comments->where([
|
||||||
|
"model" => get_class($target),
|
||||||
|
"target" => $target->getId(),
|
||||||
|
"deleted" => false,
|
||||||
|
])->page(1, $count ?? OPENVK_DEFAULT_PER_PAGE)->order("created DESC");
|
||||||
|
|
||||||
|
$comments = array_reverse(iterator_to_array($comments));
|
||||||
|
foreach($comments as $comment)
|
||||||
|
yield $this->toComment($comment);
|
||||||
|
}
|
||||||
|
|
||||||
function getCommentsCountByTarget(Postable $target): int
|
function getCommentsCountByTarget(Postable $target): int
|
||||||
{
|
{
|
||||||
return sizeof($this->comments->where([
|
return sizeof($this->comments->where([
|
||||||
|
|
45
Web/Models/Repositories/Gifts.php
Normal file
45
Web/Models/Repositories/Gifts.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\{Gift, GiftCategory};
|
||||||
|
|
||||||
|
class Gifts
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $gifts;
|
||||||
|
private $cats;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DatabaseConnection::i()->getContext();
|
||||||
|
$this->gifts = $this->context->table("gifts");
|
||||||
|
$this->cats = $this->context->table("gift_categories");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?Gift
|
||||||
|
{
|
||||||
|
$gift = $this->gifts->get($id);
|
||||||
|
if(!$gift)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new Gift($gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCat(int $id): ?GiftCategory
|
||||||
|
{
|
||||||
|
$cat = $this->cats->get($id);
|
||||||
|
if(!$cat)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new GiftCategory($cat);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCategories(int $page, ?int $perPage = NULL, &$count = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
$cats = $this->cats->where("deleted", false);
|
||||||
|
$count = $cats->count();
|
||||||
|
$cats = $cats->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
foreach($cats as $cat)
|
||||||
|
yield new GiftCategory($cat);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,15 @@ class Notes
|
||||||
yield new Note($album);
|
yield new Note($album);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNoteById(int $owner, int $note): ?Note
|
||||||
|
{
|
||||||
|
$note = $this->notes->where(['owner' => $owner, 'virtual_id' => $note])->fetch();
|
||||||
|
if(!is_null($note))
|
||||||
|
return new Note($note);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function getUserNotesCount(User $user): int
|
function getUserNotesCount(User $user): int
|
||||||
{
|
{
|
||||||
return sizeof($this->notes->where("owner", $user->getId())->where("deleted", 0));
|
return sizeof($this->notes->where("owner", $user->getId())->where("deleted", 0));
|
||||||
|
|
|
@ -43,6 +43,19 @@ class Notifications
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assemble(int $act, int $originModelType, int $originModelId, int $targetModelType, int $targetModelId, int $recipientId, int $timestamp, $data, ?string $class = NULL): Notification
|
||||||
|
{
|
||||||
|
$class ??= 'openvk\Web\Models\Entities\Notifications\Notification';
|
||||||
|
|
||||||
|
$originModel = $this->getModel($originModelType, $originModelId);
|
||||||
|
$targetModel = $this->getModel($targetModelType, $targetModelId);
|
||||||
|
$recipient = (new Users)->get($recipientId);
|
||||||
|
|
||||||
|
$notification = new $class($recipient, $originModel, $targetModel, $timestamp, $data);
|
||||||
|
$notification->setActionCode($act);
|
||||||
|
return $notification;
|
||||||
|
}
|
||||||
|
|
||||||
function getNotificationCountByUser(User $user, int $offset, bool $archived = false): int
|
function getNotificationCountByUser(User $user, int $offset, bool $archived = false): int
|
||||||
{
|
{
|
||||||
$db = $this->getEDB(false);
|
$db = $this->getEDB(false);
|
||||||
|
@ -64,13 +77,38 @@ class Notifications
|
||||||
|
|
||||||
$results = $this->getEDB()->query($this->getQuery($user, false, $offset, $archived, $page, $perPage));
|
$results = $this->getEDB()->query($this->getQuery($user, false, $offset, $archived, $page, $perPage));
|
||||||
foreach($results->fetchAll() as $notif) {
|
foreach($results->fetchAll() as $notif) {
|
||||||
$originModel = $this->getModel($notif->originModelType, $notif->originModelId);
|
yield $this->assemble(
|
||||||
$targetModel = $this->getModel($notif->targetModelType, $notif->targetModelId);
|
$notif->modelAction,
|
||||||
$recipient = (new Users)->get($notif->recipientId);
|
$notif->originModelType,
|
||||||
|
$notif->originModelId,
|
||||||
|
|
||||||
$notification = new Notification($recipient, $originModel, $targetModel, $notif->timestamp, $notif->additionalData);
|
$notif->targetModelType,
|
||||||
$notification->setActionCode($notif->modelAction);
|
$notif->targetModelId,
|
||||||
yield $notification;
|
|
||||||
|
$notif->recipientId,
|
||||||
|
$notif->timestamp,
|
||||||
|
$notif->additionalData
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fromDescriptor(string $descriptor, ?object &$parsedData = nullptr)
|
||||||
|
{
|
||||||
|
[$class, $recv, $data] = explode(",", $descriptor);
|
||||||
|
$class = str_replace(".", "\\", $class);
|
||||||
|
|
||||||
|
$parsedData = unserialize(base64_decode($data));
|
||||||
|
return $this->assemble(
|
||||||
|
$parsedData->actionCode,
|
||||||
|
$parsedData->originModelType,
|
||||||
|
$parsedData->originModelId,
|
||||||
|
|
||||||
|
$parsedData->targetModelType,
|
||||||
|
$parsedData->targetModelId,
|
||||||
|
|
||||||
|
$parsedData->recipient,
|
||||||
|
$parsedData->timestamp,
|
||||||
|
$parsedData->additionalPayload,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,12 +37,13 @@ class Posts
|
||||||
return $this->toPost($post);
|
return $this->toPost($post);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPostsFromUsersWall(int $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
function getPostsFromUsersWall(int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||||
{
|
{
|
||||||
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
$offset = $perPage * ($page - 1);
|
$offset ??= $perPage * ($page - 1);
|
||||||
|
|
||||||
$pinPost = $this->getPinnedPost($user);
|
$pinPost = $this->getPinnedPost($user);
|
||||||
|
if(is_null($offset) || $offset == 0) {
|
||||||
if(!is_null($pinPost)) {
|
if(!is_null($pinPost)) {
|
||||||
if($page === 1) {
|
if($page === 1) {
|
||||||
$perPage--;
|
$perPage--;
|
||||||
|
@ -52,6 +53,9 @@ class Posts
|
||||||
$offset--;
|
$offset--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if(!is_null($offset)) {
|
||||||
|
$offset--;
|
||||||
|
}
|
||||||
|
|
||||||
$sel = $this->posts->where([
|
$sel = $this->posts->where([
|
||||||
"wall" => $user,
|
"wall" => $user,
|
||||||
|
|
|
@ -6,8 +6,8 @@ use Nette\Database\Table\ActiveRow;
|
||||||
|
|
||||||
abstract class Repository
|
abstract class Repository
|
||||||
{
|
{
|
||||||
private $context;
|
protected $context;
|
||||||
private $table;
|
protected $table;
|
||||||
|
|
||||||
protected $tableName;
|
protected $tableName;
|
||||||
protected $modelName;
|
protected $modelName;
|
||||||
|
@ -29,5 +29,18 @@ abstract class Repository
|
||||||
return $this->toEntity($this->table->get($id));
|
return $this->toEntity($this->table->get($id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function size(bool $withDeleted = false): int
|
||||||
|
{
|
||||||
|
return sizeof($this->table->where("deleted", $withDeleted));
|
||||||
|
}
|
||||||
|
|
||||||
|
function enumerate(int $page, ?int $perPage = NULL, bool $withDeleted = false): \Traversable
|
||||||
|
{
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
|
||||||
|
foreach($this->table->where("deleted", $withDeleted)->page($page, $perPage) as $entity)
|
||||||
|
yield $this->toEntity($entity);
|
||||||
|
}
|
||||||
|
|
||||||
use \Nette\SmartObject;
|
use \Nette\SmartObject;
|
||||||
}
|
}
|
||||||
|
|
13
Web/Models/Repositories/SupportAliases.php
Normal file
13
Web/Models/Repositories/SupportAliases.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
|
||||||
|
class SupportAliases extends Repository
|
||||||
|
{
|
||||||
|
protected $tableName = "support_names";
|
||||||
|
protected $modelName = "SupportAlias";
|
||||||
|
|
||||||
|
function get(int $agent)
|
||||||
|
{
|
||||||
|
return $this->toEntity($this->table->where("agent", $agent)->fetch());
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ class Tickets
|
||||||
|
|
||||||
function getTickets(int $state = 0, int $page = 1): \Traversable
|
function getTickets(int $state = 0, int $page = 1): \Traversable
|
||||||
{
|
{
|
||||||
foreach($this->tickets->where(["deleted" => 0, "type" => $state])->page($page, OPENVK_DEFAULT_PER_PAGE) as $t)
|
foreach($this->tickets->where(["deleted" => 0, "type" => $state])->order("created DESC")->page($page, OPENVK_DEFAULT_PER_PAGE) as $t)
|
||||||
yield new Ticket($t);
|
yield new Ticket($t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,12 @@ class Tickets
|
||||||
|
|
||||||
function getTicketsByuId(int $user_id): \Traversable
|
function getTicketsByuId(int $user_id): \Traversable
|
||||||
{
|
{
|
||||||
foreach($this->tickets->where(['user_id' => $user_id, 'deleted' => 0]) as $ticket) yield new Ticket($ticket);
|
foreach($this->tickets->where(['user_id' => $user_id, 'deleted' => 0])->order("created DESC") as $ticket) yield new Ticket($ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTicketsCountByuId(int $user_id, int $type = 0): int
|
||||||
|
{
|
||||||
|
return sizeof($this->tickets->where(['user_id' => $user_id, 'deleted' => 0, 'type' => $type]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRequestById(int $req_id): ?Ticket
|
function getRequestById(int $req_id): ?Ticket
|
||||||
|
|
|
@ -37,12 +37,12 @@ class Videos
|
||||||
function getByUser(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
function getByUser(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
{
|
{
|
||||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||||
foreach($this->videos->where("owner", $user->getId())->where("deleted", 0)->page($page, $perPage) as $video)
|
foreach($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->page($page, $perPage)->order("created DESC") as $video)
|
||||||
yield new Video($video);
|
yield new Video($video);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserVideosCount(User $user): int
|
function getUserVideosCount(User $user): int
|
||||||
{
|
{
|
||||||
return sizeof($this->videos->where("owner", $user->getId())->where("deleted", 0));
|
return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
Web/Models/Repositories/Vouchers.php
Normal file
19
Web/Models/Repositories/Vouchers.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use openvk\Web\Models\Entities\Voucher;
|
||||||
|
|
||||||
|
class Vouchers extends Repository
|
||||||
|
{
|
||||||
|
protected $tableName = "coin_vouchers";
|
||||||
|
protected $modelName = "Voucher";
|
||||||
|
|
||||||
|
function getByToken(string $token, bool $withDeleted = false)
|
||||||
|
{
|
||||||
|
$voucher = $this->table->where([
|
||||||
|
"token" => $token,
|
||||||
|
"deleted" => $withDeleted,
|
||||||
|
])->fetch();
|
||||||
|
|
||||||
|
return $this->toEntity($voucher);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Themes\Themepacks;
|
use openvk\Web\Themes\Themepacks;
|
||||||
use openvk\Web\Models\Repositories\{Users, Managers};
|
use openvk\Web\Models\Repositories\{Users, Managers};
|
||||||
|
use openvk\Web\Util\Localizator;
|
||||||
use Chandler\Session\Session;
|
use Chandler\Session\Session;
|
||||||
|
|
||||||
final class AboutPresenter extends OpenVKPresenter
|
final class AboutPresenter extends OpenVKPresenter
|
||||||
|
@ -62,10 +63,22 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
$this->template->languages = getLanguages();
|
$this->template->languages = getLanguages();
|
||||||
|
|
||||||
if(!is_null($_GET['lg'])){
|
if(!is_null($_GET['lg'])){
|
||||||
|
$this->assertNoCSRF();
|
||||||
setLanguage($_GET['lg']);
|
setLanguage($_GET['lg']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderExportJSLanguage($lg = NULL): void
|
||||||
|
{
|
||||||
|
$localizer = Localizator::i();
|
||||||
|
$lang = $lg;
|
||||||
|
if(is_null($lg))
|
||||||
|
$this->throwError(404, "Not found", "Language is not found");
|
||||||
|
header("Content-Type: application/javascript");
|
||||||
|
echo "window.lang = " . json_encode($localizer->export($lang)) . ";"; // привет хардкод :DDD
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
function renderSandbox(): void
|
function renderSandbox(): void
|
||||||
{
|
{
|
||||||
$this->template->languages = getLanguages();
|
$this->template->languages = getLanguages();
|
||||||
|
|
|
@ -1,21 +1,31 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs};
|
use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts};
|
||||||
|
|
||||||
final class AdminPresenter extends OpenVKPresenter
|
final class AdminPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $users;
|
private $users;
|
||||||
private $clubs;
|
private $clubs;
|
||||||
|
private $vouchers;
|
||||||
|
private $gifts;
|
||||||
|
|
||||||
function __construct(Users $users, Clubs $clubs)
|
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts)
|
||||||
{
|
{
|
||||||
$this->users = $users;
|
$this->users = $users;
|
||||||
$this->clubs = $clubs;
|
$this->clubs = $clubs;
|
||||||
|
$this->vouchers = $vouchers;
|
||||||
|
$this->gifts = $gifts;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function warnIfNoCommerce(): void
|
||||||
|
{
|
||||||
|
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||||
|
$this->flash("warn", "Коммерция отключена системным администратором", "Настройки ваучеров и подарков будут сохранены, но не будут оказывать никакого влияния.");
|
||||||
|
}
|
||||||
|
|
||||||
private function searchResults(object $repo, &$count)
|
private function searchResults(object $repo, &$count)
|
||||||
{
|
{
|
||||||
$query = $this->queryParam("q") ?? "";
|
$query = $this->queryParam("q") ?? "";
|
||||||
|
@ -106,6 +116,219 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderVouchers(): void
|
||||||
|
{
|
||||||
|
$this->warnIfNoCommerce();
|
||||||
|
|
||||||
|
$this->template->count = $this->vouchers->size();
|
||||||
|
$this->template->vouchers = iterator_to_array($this->vouchers->enumerate((int) ($this->queryParam("p") ?? 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVoucher(int $id): void
|
||||||
|
{
|
||||||
|
$this->warnIfNoCommerce();
|
||||||
|
|
||||||
|
$voucher = NULL;
|
||||||
|
$this->template->form = (object) [];
|
||||||
|
if($id === 0) {
|
||||||
|
$this->template->form->id = 0;
|
||||||
|
$this->template->form->token = NULL;
|
||||||
|
$this->template->form->coins = 0;
|
||||||
|
$this->template->form->rating = 0;
|
||||||
|
$this->template->form->usages = -1;
|
||||||
|
$this->template->form->users = [];
|
||||||
|
} else {
|
||||||
|
$voucher = $this->vouchers->get($id);
|
||||||
|
if(!$voucher)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->form->id = $voucher->getId();
|
||||||
|
$this->template->form->token = $voucher->getToken();
|
||||||
|
$this->template->form->coins = $voucher->getCoins();
|
||||||
|
$this->template->form->rating = $voucher->getRating();
|
||||||
|
$this->template->form->usages = $voucher->getRemainingUsages();
|
||||||
|
$this->template->form->users = iterator_to_array($voucher->getUsers());
|
||||||
|
|
||||||
|
if($this->template->form->usages === INF)
|
||||||
|
$this->template->form->usages = -1;
|
||||||
|
else
|
||||||
|
$this->template->form->usages = (int) $this->template->form->usages;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$voucher ??= new Voucher;
|
||||||
|
$voucher->setCoins((int) $this->postParam("coins"));
|
||||||
|
$voucher->setRating((int) $this->postParam("rating"));
|
||||||
|
$voucher->setRemainingUsages($this->postParam("usages") === '-1' ? INF : ((int) $this->postParam("usages")));
|
||||||
|
if(!empty($tok = $this->postParam("token")) && strlen($tok) === 24)
|
||||||
|
$voucher->setToken($tok);
|
||||||
|
|
||||||
|
$voucher->save();
|
||||||
|
|
||||||
|
$this->redirect("/admin/vouchers/id" . $voucher->getId(), static::REDIRECT_TEMPORARY);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGiftCategories(): void
|
||||||
|
{
|
||||||
|
$this->warnIfNoCommerce();
|
||||||
|
|
||||||
|
$this->template->act = $this->queryParam("act") ?? "list";
|
||||||
|
$this->template->categories = iterator_to_array($this->gifts->getCategories((int) ($this->queryParam("p") ?? 1), NULL, $this->template->count));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGiftCategory(string $slug, int $id): void
|
||||||
|
{
|
||||||
|
$this->warnIfNoCommerce();
|
||||||
|
|
||||||
|
$cat;
|
||||||
|
$gen = false;
|
||||||
|
if($id !== 0) {
|
||||||
|
$cat = $this->gifts->getCat($id);
|
||||||
|
if(!$cat)
|
||||||
|
$this->notFound();
|
||||||
|
else if($cat->getSlug() !== $slug)
|
||||||
|
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $id . ".meta", static::REDIRECT_TEMPORARY);
|
||||||
|
} else {
|
||||||
|
$gen = true;
|
||||||
|
$cat = new GiftCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->form = (object) [];
|
||||||
|
$this->template->form->id = $id;
|
||||||
|
$this->template->form->languages = [];
|
||||||
|
foreach(getLanguages() as $language) {
|
||||||
|
$language = (object) $language;
|
||||||
|
$this->template->form->languages[$language->code] = (object) [];
|
||||||
|
|
||||||
|
$this->template->form->languages[$language->code]->name = $gen ? "" : ($cat->getName($language->code, true) ?? "");
|
||||||
|
$this->template->form->languages[$language->code]->description = $gen ? "" : ($cat->getDescription($language->code, true) ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->form->languages["master"] = (object) [
|
||||||
|
"name" => $gen ? "Unknown Name" : $cat->getName(),
|
||||||
|
"description" => $gen ? "" : $cat->getDescription(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if($gen) {
|
||||||
|
$cat->setAutoQuery(NULL);
|
||||||
|
$cat->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cat->setName("_", $this->postParam("name_master"));
|
||||||
|
$cat->setDescription("_", $this->postParam("description_master"));
|
||||||
|
foreach(getLanguages() as $language) {
|
||||||
|
$code = $language["code"];
|
||||||
|
if(!empty($this->postParam("name_$code") ?? NULL))
|
||||||
|
$cat->setName($code, $this->postParam("name_$code"));
|
||||||
|
|
||||||
|
if(!empty($this->postParam("description_$code") ?? NULL))
|
||||||
|
$cat->setDescription($code, $this->postParam("description_$code"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $cat->getId() . ".meta", static::REDIRECT_TEMPORARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGifts(string $catSlug, int $catId): void
|
||||||
|
{
|
||||||
|
$this->warnIfNoCommerce();
|
||||||
|
|
||||||
|
$cat = $this->gifts->getCat($catId);
|
||||||
|
if(!$cat)
|
||||||
|
$this->notFound();
|
||||||
|
else if($cat->getSlug() !== $catSlug)
|
||||||
|
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $catId . "/", static::REDIRECT_TEMPORARY);
|
||||||
|
|
||||||
|
$this->template->cat = $cat;
|
||||||
|
$this->template->gifts = iterator_to_array($cat->getGifts((int) ($this->queryParam("p") ?? 1), NULL, $this->template->count));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGift(int $id): void
|
||||||
|
{
|
||||||
|
$this->warnIfNoCommerce();
|
||||||
|
|
||||||
|
$gift = $this->gifts->get($id);
|
||||||
|
$act = $this->queryParam("act") ?? "edit";
|
||||||
|
switch($act) {
|
||||||
|
case "delete":
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
if(!$gift)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$gift->delete();
|
||||||
|
$this->flashFail("succ", "Gift moved successfully", "This gift will now be in <b>Recycle Bin</b>.");
|
||||||
|
break;
|
||||||
|
case "copy":
|
||||||
|
case "move":
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
if(!$gift)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$catFrom = $this->gifts->getCat((int) ($this->queryParam("from") ?? 0));
|
||||||
|
$catTo = $this->gifts->getCat((int) ($this->queryParam("to") ?? 0));
|
||||||
|
if(!$catFrom || !$catTo || !$catFrom->hasGift($gift))
|
||||||
|
$this->badRequest();
|
||||||
|
|
||||||
|
if($act === "move")
|
||||||
|
$catFrom->removeGift($gift);
|
||||||
|
|
||||||
|
$catTo->addGift($gift);
|
||||||
|
|
||||||
|
$name = $catTo->getName();
|
||||||
|
$this->flash("succ", "Gift moved successfully", "This gift will now be in <b>$name</b>.");
|
||||||
|
$this->redirect("/admin/gifts/" . $catTo->getSlug() . "." . $catTo->getId() . "/", static::REDIRECT_TEMPORARY);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case "edit":
|
||||||
|
$gen = false;
|
||||||
|
if(!$gift) {
|
||||||
|
$gen = true;
|
||||||
|
$gift = new Gift;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->form = (object) [];
|
||||||
|
$this->template->form->id = $id;
|
||||||
|
$this->template->form->name = $gen ? "New Gift (1)" : $gift->getName();
|
||||||
|
$this->template->form->price = $gen ? 0 : $gift->getPrice();
|
||||||
|
$this->template->form->usages = $gen ? 0 : $gift->getUsages();
|
||||||
|
$this->template->form->limit = $gen ? -1 : ($gift->getLimit() === INF ? -1 : $gift->getLimit());
|
||||||
|
$this->template->form->pic = $gen ? NULL : $gift->getImage(Gift::IMAGE_URL);
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$limit = $this->postParam("limit") ?? $this->template->form->limit;
|
||||||
|
$limit = $limit == "-1" ? INF : (float) $limit;
|
||||||
|
$gift->setLimit($limit, is_null($this->postParam("reset_limit")) ? Gift::PERIOD_SET_IF_NONE : Gift::PERIOD_SET);
|
||||||
|
|
||||||
|
$gift->setName($this->postParam("name"));
|
||||||
|
$gift->setPrice((int) $this->postParam("price"));
|
||||||
|
$gift->setUsages((int) $this->postParam("usages"));
|
||||||
|
if(isset($_FILES["pic"]) && $_FILES["pic"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
if(!$gift->setImage($_FILES["pic"]["tmp_name"]))
|
||||||
|
$this->flashFail("err", "Не удалось сохранить подарок", "Изображение подарка кривое.");
|
||||||
|
} else if($gen) {
|
||||||
|
# If there's no gift pic but it's newly created
|
||||||
|
$this->flashFail("err", "Не удалось сохранить подарок", "Пожалуйста, загрузите изображение подарка.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$gift->save();
|
||||||
|
|
||||||
|
if($gen && !is_null($cat = $this->postParam("_cat"))) {
|
||||||
|
$cat = $this->gifts->getCat((int) $cat);
|
||||||
|
if(!is_null($cat))
|
||||||
|
$cat->addGift($gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/admin/gifts/id" . $gift->getId(), static::REDIRECT_TEMPORARY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderFiles(): void
|
function renderFiles(): void
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Chandler\Session\Session;
|
||||||
use Chandler\Security\User as ChandlerUser;
|
use Chandler\Security\User as ChandlerUser;
|
||||||
use Chandler\Security\Authenticator;
|
use Chandler\Security\Authenticator;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use lfkeitel\phptotp\{Base32, Totp};
|
||||||
|
|
||||||
final class AuthPresenter extends OpenVKPresenter
|
final class AuthPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
@ -89,6 +90,9 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
if(!$this->emailValid($this->postParam("email")))
|
if(!$this->emailValid($this->postParam("email")))
|
||||||
$this->flashFail("err", "Неверный email адрес", "Email, который вы ввели, не является корректным.");
|
$this->flashFail("err", "Неверный email адрес", "Email, который вы ввели, не является корректным.");
|
||||||
|
|
||||||
|
if (strtotime($this->postParam("birthday")) > time())
|
||||||
|
$this->flashFail("err", "Неверная дата рождения", "Дату рождения, которую вы ввели, не является корректным.");
|
||||||
|
|
||||||
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
|
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
|
||||||
if(!$chUser)
|
if(!$chUser)
|
||||||
$this->flashFail("err", "Не удалось зарегистрироваться", "Пользователь с таким email уже существует.");
|
$this->flashFail("err", "Не удалось зарегистрироваться", "Пользователь с таким email уже существует.");
|
||||||
|
@ -101,6 +105,7 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
$user->setEmail($this->postParam("email"));
|
$user->setEmail($this->postParam("email"));
|
||||||
$user->setSince(date("Y-m-d H:i:s"));
|
$user->setSince(date("Y-m-d H:i:s"));
|
||||||
$user->setRegistering_Ip(CONNECTING_IP);
|
$user->setRegistering_Ip(CONNECTING_IP);
|
||||||
|
$user->setBirthday(strtotime($this->postParam("birthday")));
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
if(!is_null($referer)) {
|
if(!is_null($referer)) {
|
||||||
|
@ -128,9 +133,27 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
if(!$user)
|
if(!$user)
|
||||||
$this->flashFail("err", "Не удалось войти", "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>");
|
$this->flashFail("err", "Не удалось войти", "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>");
|
||||||
|
|
||||||
if(!$this->authenticator->login($user->id, $this->postParam("password")))
|
if(!$this->authenticator->verifyCredentials($user->id, $this->postParam("password")))
|
||||||
$this->flashFail("err", "Не удалось войти", "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>");
|
$this->flashFail("err", "Не удалось войти", "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>");
|
||||||
|
|
||||||
|
$secret = $user->related("profiles.user")->fetch()["2fa_secret"];
|
||||||
|
$code = $this->postParam("code");
|
||||||
|
if(!is_null($secret)) {
|
||||||
|
$this->template->_template = "Auth/LoginSecondFactor.xml";
|
||||||
|
$this->template->login = $this->postParam("login");
|
||||||
|
$this->template->password = $this->postParam("password");
|
||||||
|
|
||||||
|
if(is_null($code))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$ovkUser = new User($user->related("profiles.user")->fetch());
|
||||||
|
if(!($code === (new Totp)->GenerateToken(Base32::decode($secret)) || $ovkUser->use2faBackupCode((int) $code))) {
|
||||||
|
$this->flash("err", "Не удалось войти", tr("incorrect_2fa_code"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->authenticator->authenticate($user->id);
|
||||||
$this->redirect($redirUrl ?? "/id" . $user->related("profiles.user")->fetch()->id, static::REDIRECT_TEMPORARY);
|
$this->redirect($redirUrl ?? "/id" . $user->related("profiles.user")->fetch()->id, static::REDIRECT_TEMPORARY);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
@ -159,6 +182,7 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
function renderLogout(): void
|
function renderLogout(): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
$this->assertNoCSRF();
|
||||||
$this->authenticator->logout();
|
$this->authenticator->logout();
|
||||||
Session::i()->set("_su", NULL);
|
Session::i()->set("_su", NULL);
|
||||||
|
|
||||||
|
@ -173,7 +197,19 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
$this->redirect("/");
|
$this->redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->template->is2faEnabled = $request->getUser()->is2faEnabled();
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
if($request->getUser()->is2faEnabled()) {
|
||||||
|
$user = $request->getUser();
|
||||||
|
$code = $this->postParam("code");
|
||||||
|
$secret = $user->get2faSecret();
|
||||||
|
if(!($code === (new Totp)->GenerateToken(Base32::decode($secret)) || $user->use2faBackupCode((int) $code))) {
|
||||||
|
$this->flash("err", tr("error"), tr("incorrect_2fa_code"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$user = $request->getUser()->getChandlerUser();
|
$user = $request->getUser()->getChandlerUser();
|
||||||
$this->db->table("ChandlerTokens")->where("user", $user->getId())->delete(); #Logout from everywhere
|
$this->db->table("ChandlerTokens")->where("user", $user->getId())->delete(); #Logout from everywhere
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Comment, User};
|
use openvk\Web\Models\Entities\{Comment, Photo, Video, User};
|
||||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||||
use openvk\Web\Models\Repositories\Comments;
|
use openvk\Web\Models\Repositories\Comments;
|
||||||
|
|
||||||
|
@ -38,6 +38,41 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
$entity = $repo->get($eId);
|
$entity = $repo->get($eId);
|
||||||
if(!$entity) $this->notFound();
|
if(!$entity) $this->notFound();
|
||||||
|
|
||||||
|
$flags = 0;
|
||||||
|
if($this->postParam("as_group") === "on")
|
||||||
|
$flags |= 0b10000000;
|
||||||
|
|
||||||
|
$photo = NULL;
|
||||||
|
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
try {
|
||||||
|
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"]);
|
||||||
|
} catch(ISE $ex) {
|
||||||
|
$this->flashFail("err", "Не удалось опубликовать пост", "Файл изображения повреждён, слишком велик или одна сторона изображения в разы больше другой.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move to trait
|
||||||
|
try {
|
||||||
|
$photo = NULL;
|
||||||
|
$video = NULL;
|
||||||
|
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
$album = NULL;
|
||||||
|
if($wall > 0 && $wall === $this->user->id)
|
||||||
|
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||||
|
|
||||||
|
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||||
|
}
|
||||||
|
} catch(ISE $ex) {
|
||||||
|
$this->flashFail("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($this->postParam("text")) && !$photo && !$video)
|
||||||
|
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий пустой или слишком большой.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$comment = new Comment;
|
$comment = new Comment;
|
||||||
$comment->setOwner($this->user->id);
|
$comment->setOwner($this->user->id);
|
||||||
|
@ -45,11 +80,18 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
$comment->setTarget($entity->getId());
|
$comment->setTarget($entity->getId());
|
||||||
$comment->setContent($this->postParam("text"));
|
$comment->setContent($this->postParam("text"));
|
||||||
$comment->setCreated(time());
|
$comment->setCreated(time());
|
||||||
|
$comment->setFlags($flags);
|
||||||
$comment->save();
|
$comment->save();
|
||||||
} catch(\LogicException $ex) {
|
} catch (\LengthException $ex) {
|
||||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Нельзя опубликовать пустой комментарий.");
|
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!is_null($photo))
|
||||||
|
$comment->attach($photo);
|
||||||
|
|
||||||
|
if(!is_null($video))
|
||||||
|
$comment->attach($video);
|
||||||
|
|
||||||
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
||||||
if(($owner = $entity->getOwner()) instanceof User)
|
if(($owner = $entity->getOwner()) instanceof User)
|
||||||
(new CommentNotification($owner, $comment, $entity, $this->user->identity))->emit();
|
(new CommentNotification($owner, $comment, $entity, $this->user->identity))->emit();
|
||||||
|
|
140
Web/Presenters/GiftsPresenter.php
Normal file
140
Web/Presenters/GiftsPresenter.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Presenters;
|
||||||
|
use openvk\Web\Models\Repositories\{Gifts, Users};
|
||||||
|
use openvk\Web\Models\Entities\Notifications\GiftNotification;
|
||||||
|
|
||||||
|
final class GiftsPresenter extends OpenVKPresenter
|
||||||
|
{
|
||||||
|
private $gifts;
|
||||||
|
private $users;
|
||||||
|
|
||||||
|
function __construct(Gifts $gifts, Users $users)
|
||||||
|
{
|
||||||
|
$this->gifts = $gifts;
|
||||||
|
$this->users = $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderUserGifts(int $user): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$user = $this->users->get($user);
|
||||||
|
if(!$user)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->user = $user;
|
||||||
|
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
$this->template->count = $user->getGiftCount();
|
||||||
|
$this->template->iterator = $user->getGifts($page);
|
||||||
|
$this->template->hideInfo = $this->user->id !== $user->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGiftMenu(): void
|
||||||
|
{
|
||||||
|
$user = $this->users->get((int) ($this->queryParam("user") ?? 0));
|
||||||
|
if(!$user)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
$cats = $this->gifts->getCategories($page, NULL, $this->template->count);
|
||||||
|
|
||||||
|
$this->template->user = $user;
|
||||||
|
$this->template->iterator = $cats;
|
||||||
|
$this->template->_template = "Gifts/Menu.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGiftList(): void
|
||||||
|
{
|
||||||
|
$user = $this->users->get((int) ($this->queryParam("user") ?? 0));
|
||||||
|
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||||
|
if(!$user || !$cat)
|
||||||
|
$this->flashFail("err", "Не удалось подарить", "Пользователь или набор не существуют.");
|
||||||
|
|
||||||
|
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
$gifts = $cat->getGifts($page, null, $this->template->count);
|
||||||
|
|
||||||
|
$this->template->user = $user;
|
||||||
|
$this->template->cat = $cat;
|
||||||
|
$this->template->gifts = iterator_to_array($gifts);
|
||||||
|
$this->template->_template = "Gifts/Pick.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderConfirmGift(): void
|
||||||
|
{
|
||||||
|
$user = $this->users->get((int) ($this->queryParam("user") ?? 0));
|
||||||
|
$gift = $this->gifts->get((int) ($this->queryParam("elid") ?? 0));
|
||||||
|
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||||
|
if(!$user || !$cat || !$gift || !$cat->hasGift($gift))
|
||||||
|
$this->flashFail("err", "Не удалось подарить", "Не удалось подтвердить права на подарок.");
|
||||||
|
|
||||||
|
if(!$gift->canUse($this->user->identity))
|
||||||
|
$this->flashFail("err", "Не удалось подарить", "У вас больше не осталось таких подарков.");
|
||||||
|
|
||||||
|
$coinsLeft = $this->user->identity->getCoins() - $gift->getPrice();
|
||||||
|
if($coinsLeft < 0)
|
||||||
|
$this->flashFail("err", "Не удалось подарить", "Ору нищ не пук.");
|
||||||
|
|
||||||
|
$this->template->_template = "Gifts/Confirm.xml";
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
$this->template->user = $user;
|
||||||
|
$this->template->cat = $cat;
|
||||||
|
$this->template->gift = $gift;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment = empty($c = $this->postParam("comment")) ? NULL : $c;
|
||||||
|
$notification = new GiftNotification($user, $this->user->identity, $gift, $comment);
|
||||||
|
$notification->emit();
|
||||||
|
$this->user->identity->setCoins($coinsLeft);
|
||||||
|
$this->user->identity->save();
|
||||||
|
$user->gift($this->user->identity, $gift, $comment, !is_null($this->postParam("anonymous")));
|
||||||
|
$gift->used();
|
||||||
|
|
||||||
|
$this->flash("succ", "Подарок отправлен", "Вы отправили подарок <b>" . $user->getFirstName() . "</b> за " . $gift->getPrice() . " голосов.");
|
||||||
|
$this->redirect($user->getURL(), static::REDIRECT_TEMPORARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderStub(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$act = $this->queryParam("act");
|
||||||
|
switch($act) {
|
||||||
|
case "pick":
|
||||||
|
$this->renderGiftMenu();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "menu":
|
||||||
|
$this->renderGiftList();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "confirm":
|
||||||
|
$this->renderConfirmGift();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->notFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGiftImage(int $id, int $timestamp): void
|
||||||
|
{
|
||||||
|
$gift = $this->gifts->get($id);
|
||||||
|
if(!$gift)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$image = $gift->getImage();
|
||||||
|
header("Cache-Control: no-transform, immutable");
|
||||||
|
header("Content-Length: " . strlen($image));
|
||||||
|
header("Content-Type: image/png");
|
||||||
|
exit($image);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStartup(): void
|
||||||
|
{
|
||||||
|
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||||
|
$this->flashFail("err", tr("error"), tr("feature_disabled"));
|
||||||
|
|
||||||
|
parent::onStartup();
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,8 +84,22 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
$this->template->club = $this->clubs->get($id);
|
$this->template->club = $this->clubs->get($id);
|
||||||
|
$this->template->onlyShowManagers = $this->queryParam("onlyAdmins") == "1";
|
||||||
|
if($this->template->onlyShowManagers) {
|
||||||
|
$this->template->followers = null;
|
||||||
|
|
||||||
|
$this->template->managers = $this->template->club->getManagers((int) ($this->queryParam("p") ?? 1), !$this->template->club->canBeModifiedBy($this->user->identity));
|
||||||
|
if($this->template->club->canBeModifiedBy($this->user->identity) || !$this->template->club->isOwnerHidden()) {
|
||||||
|
$this->template->managers = array_merge([$this->template->club->getOwner()], iterator_to_array($this->template->managers));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->count = $this->template->club->getManagersCount();
|
||||||
|
} else {
|
||||||
$this->template->followers = $this->template->club->getFollowers((int) ($this->queryParam("p") ?? 1));
|
$this->template->followers = $this->template->club->getFollowers((int) ($this->queryParam("p") ?? 1));
|
||||||
|
$this->template->managers = null;
|
||||||
$this->template->count = $this->template->club->getFollowersCount();
|
$this->template->count = $this->template->club->getFollowersCount();
|
||||||
|
}
|
||||||
|
|
||||||
$this->template->paginatorConf = (object) [
|
$this->template->paginatorConf = (object) [
|
||||||
"count" => $this->template->count,
|
"count" => $this->template->count,
|
||||||
"page" => $this->queryParam("p") ?? 1,
|
"page" => $this->queryParam("p") ?? 1,
|
||||||
|
@ -98,6 +112,8 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$user = is_null($this->queryParam("user")) ? $this->postParam("user") : $this->queryParam("user");
|
$user = is_null($this->queryParam("user")) ? $this->postParam("user") : $this->queryParam("user");
|
||||||
$comment = $this->postParam("comment");
|
$comment = $this->postParam("comment");
|
||||||
|
$removeComment = $this->postParam("removeComment") === "1";
|
||||||
|
$hidden = ["0" => false, "1" => true][$this->queryParam("hidden")] ?? null;
|
||||||
//$index = $this->queryParam("index");
|
//$index = $this->queryParam("index");
|
||||||
if(!$user)
|
if(!$user)
|
||||||
$this->badRequest();
|
$this->badRequest();
|
||||||
|
@ -107,19 +123,56 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
if(!$user || !$club)
|
if(!$user || !$club)
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL) && $club->getOwner()->getId() !== $user->getId())
|
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.");
|
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.");
|
||||||
|
|
||||||
/* if(!empty($index)){
|
if(!is_null($hidden)) {
|
||||||
$manager = (new Managers)->get($index);
|
if($club->getOwner()->getId() == $user->getId()) {
|
||||||
$manager->setComment($comment);
|
$club->setOwner_Hidden($hidden);
|
||||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору изменён");
|
$club->save();
|
||||||
}else{ */
|
} else {
|
||||||
if($comment) {
|
$manager = (new Managers)->getByUserAndClub($user->getId(), $club->getId());
|
||||||
|
$manager->setHidden($hidden);
|
||||||
|
$manager->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($club->getManagersCount(true) == 0) {
|
||||||
|
$club->setAdministrators_List_Display(2);
|
||||||
|
$club->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($hidden) {
|
||||||
|
$this->flashFail("succ", "Операция успешна", "Теперь " . $user->getCanonicalName() . " будет показываться как обычный подписчик всем кроме других администраторов");
|
||||||
|
} else {
|
||||||
|
$this->flashFail("succ", "Операция успешна", "Теперь все будут знать про то что " . $user->getCanonicalName() . " - администратор");
|
||||||
|
}
|
||||||
|
} elseif($removeComment) {
|
||||||
|
if($club->getOwner()->getId() == $user->getId()) {
|
||||||
|
$club->setOwner_Comment(null);
|
||||||
|
$club->save();
|
||||||
|
} else {
|
||||||
|
$manager = (new Managers)->getByUserAndClub($user->getId(), $club->getId());
|
||||||
|
$manager->setComment(null);
|
||||||
|
$manager->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору удален");
|
||||||
|
} elseif($comment) {
|
||||||
|
if(mb_strlen($comment) > 36) {
|
||||||
|
$commentLength = (string) mb_strlen($comment);
|
||||||
|
$this->flashFail("err", "Ошибка", "Комментарий слишком длинный ($commentLength символов вместо 36 символов)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if($club->getOwner()->getId() == $user->getId()) {
|
||||||
|
$club->setOwner_Comment($comment);
|
||||||
|
$club->save();
|
||||||
|
} else {
|
||||||
$manager = (new Managers)->getByUserAndClub($user->getId(), $club->getId());
|
$manager = (new Managers)->getByUserAndClub($user->getId(), $club->getId());
|
||||||
$manager->setComment($comment);
|
$manager->setComment($comment);
|
||||||
$manager->save();
|
$manager->save();
|
||||||
$this->flashFail("succ", "Операция успешна", ".");
|
}
|
||||||
|
|
||||||
|
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору изменён");
|
||||||
}else{
|
}else{
|
||||||
if($club->canBeModifiedBy($user)) {
|
if($club->canBeModifiedBy($user)) {
|
||||||
$club->removeManager($user);
|
$club->removeManager($user);
|
||||||
|
@ -150,6 +203,13 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
$club->setAbout(empty($this->postParam("about")) ? NULL : $this->postParam("about"));
|
$club->setAbout(empty($this->postParam("about")) ? NULL : $this->postParam("about"));
|
||||||
$club->setShortcode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode"));
|
$club->setShortcode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode"));
|
||||||
$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"));
|
||||||
|
|
||||||
|
$website = $this->postParam("website") ?? "";
|
||||||
|
if(empty($website))
|
||||||
|
$club->setWebsite(NULL);
|
||||||
|
else
|
||||||
|
$club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website);
|
||||||
|
|
||||||
if($_FILES["ava"]["error"] === UPLOAD_ERR_OK) {
|
if($_FILES["ava"]["error"] === UPLOAD_ERR_OK) {
|
||||||
$photo = new Photo;
|
$photo = new Photo;
|
||||||
|
|
|
@ -58,8 +58,8 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
||||||
try {
|
try {
|
||||||
$params = array_merge($input->params ?? [], [function($data) {
|
$params = array_merge($input->params ?? [], [function($data) {
|
||||||
$this->succ($data);
|
$this->succ($data);
|
||||||
}, function($data) {
|
}, function(int $errno, string $errstr) {
|
||||||
$this->fail($data);
|
$this->fail($errno, $errstr);
|
||||||
}]);
|
}]);
|
||||||
$handler->{$method}(...$params);
|
$handler->{$method}(...$params);
|
||||||
} catch(\TypeError $te) {
|
} catch(\TypeError $te) {
|
||||||
|
|
|
@ -31,10 +31,10 @@ final class NotesPresenter extends OpenVKPresenter
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderView(int $owner, int $id): void
|
function renderView(int $owner, int $note_id): void
|
||||||
{
|
{
|
||||||
$note = $this->notes->get($id);
|
$note = $this->notes->getNoteById($owner, $note_id);
|
||||||
if(!$note || $note->getOwner()->getId() !== $owner)
|
if(!$note || $note->getOwner()->getId() !== $owner || $note->isDeleted())
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
$this->template->cCount = $note->getCommentsCount();
|
$this->template->cCount = $note->getCommentsCount();
|
||||||
|
@ -65,7 +65,7 @@ final class NotesPresenter extends OpenVKPresenter
|
||||||
$note->setSource($this->postParam("html"));
|
$note->setSource($this->postParam("html"));
|
||||||
$note->save();
|
$note->save();
|
||||||
|
|
||||||
$this->redirect("/note" . $this->user->id . "_" . $note->getId());
|
$this->redirect("/note" . $this->user->id . "_" . $note->getVirtualId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
Web/Presenters/OpenVKPresenter.php
Normal file → Executable file
22
Web/Presenters/OpenVKPresenter.php
Normal file → Executable file
|
@ -6,7 +6,8 @@ use Chandler\Session\Session;
|
||||||
use Chandler\Security\Authenticator;
|
use Chandler\Security\Authenticator;
|
||||||
use Latte\Engine as TemplatingEngine;
|
use Latte\Engine as TemplatingEngine;
|
||||||
use openvk\Web\Models\Entities\IP;
|
use openvk\Web\Models\Entities\IP;
|
||||||
use openvk\Web\Models\Repositories\{IPs, Users, APITokens};
|
use openvk\Web\Themes\Themepacks;
|
||||||
|
use openvk\Web\Models\Repositories\{IPs, Users, APITokens, Tickets};
|
||||||
|
|
||||||
abstract class OpenVKPresenter extends SimplePresenter
|
abstract class OpenVKPresenter extends SimplePresenter
|
||||||
{
|
{
|
||||||
|
@ -33,6 +34,11 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setTempTheme(string $theme): void
|
||||||
|
{
|
||||||
|
Session::i()->set("_tempTheme", $theme);
|
||||||
|
}
|
||||||
|
|
||||||
protected function flashFail(string $type, string $title, ?string $message = NULL, ?int $code = NULL): void
|
protected function flashFail(string $type, string $title, ?string $message = NULL, ?int $code = NULL): void
|
||||||
{
|
{
|
||||||
$this->flash($type, $title, $message, $code);
|
$this->flash($type, $title, $message, $code);
|
||||||
|
@ -178,7 +184,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
{
|
{
|
||||||
$user = Authenticator::i()->getUser();
|
$user = Authenticator::i()->getUser();
|
||||||
|
|
||||||
$this->template->isXmas = intval(date('d')) >= 15 && date('m') == 12 || intval(date('d')) <= 15 && date('m') == 1 ? true : false;
|
$this->template->isXmas = intval(date('d')) >= 1 && date('m') == 12 || intval(date('d')) <= 15 && date('m') == 1 ? true : false;
|
||||||
|
|
||||||
if(!is_null($user)) {
|
if(!is_null($user)) {
|
||||||
$this->user = (object) [];
|
$this->user = (object) [];
|
||||||
|
@ -201,6 +207,9 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
$this->user->identity->save();
|
$this->user->identity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->template->ticketAnsweredCount = (new Tickets)->getTicketsCountByuId($this->user->id, 1);
|
||||||
|
if($user->can("write")->model("openvk\Web\Models\Entities\TicketReply")->whichBelongsTo(0))
|
||||||
|
$this->template->helpdeskTicketNotAnsweredCount = (new Tickets)->getTicketCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
||||||
|
@ -223,5 +232,14 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
$this->template->flashMessage = json_decode(Session::i()->get("_error"));
|
$this->template->flashMessage = json_decode(Session::i()->get("_error"));
|
||||||
Session::i()->set("_error", NULL);
|
Session::i()->set("_error", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(Session::i()->get("_tempTheme"))
|
||||||
|
$this->template->theme = Themepacks::i()[Session::i()->get("_tempTheme", "ovk")];
|
||||||
|
else if($this->requestParam("themePreview"))
|
||||||
|
$this->template->theme = Themepacks::i()[$this->requestParam("themePreview")];
|
||||||
|
else if($this->user->identity !== null && $this->user->identity->getTheme())
|
||||||
|
$this->template->theme = $this->user->identity->getTheme();
|
||||||
|
|
||||||
|
// Знаю, каша ебаная, целестора рефактор всё равно сделает :)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,6 +159,17 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
$this->template->comments = iterator_to_array($photo->getComments($this->template->cPage));
|
$this->template->comments = iterator_to_array($photo->getComments($this->template->cPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAbsolutePhoto($id): void
|
||||||
|
{
|
||||||
|
$id = (int) base_convert((string) $id, 32, 10);
|
||||||
|
$photo = $this->photos->get($id);
|
||||||
|
if(!$photo || $photo->isDeleted())
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->_template = "Photos/Photo.xml";
|
||||||
|
$this->renderPhoto($photo->getOwner(true)->getId(), $photo->getVirtualId());
|
||||||
|
}
|
||||||
|
|
||||||
function renderEditPhoto(int $ownerId, int $photoId): void
|
function renderEditPhoto(int $ownerId, int $photoId): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
|
@ -181,7 +181,7 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
$comment = new TicketComment;
|
$comment = new TicketComment;
|
||||||
$comment->setUser_id($this->user->id);
|
$comment->setUser_id($this->user->id);
|
||||||
$comment->setUser_type(1);
|
$comment->setUser_type(1);
|
||||||
$comment->setText('Здравствуйте, '.$ticket->getUser()->getFirstName().'!<br></br>'.$this->postParam("text").'<br></br>С уважением,<br/> Команда поддержки OpenVK.');
|
$comment->setText($this->postParam("text"));
|
||||||
$comment->setTicket_id($id);
|
$comment->setTicket_id($id);
|
||||||
$comment->setCreated(time());
|
$comment->setCreated(time());
|
||||||
$comment->save();
|
$comment->save();
|
||||||
|
|
|
@ -4,9 +4,14 @@ 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\Repositories\Users;
|
use openvk\Web\Models\Repositories\Users;
|
||||||
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
use openvk\Web\Models\Repositories\Albums;
|
use openvk\Web\Models\Repositories\Albums;
|
||||||
use openvk\Web\Models\Repositories\Videos;
|
use openvk\Web\Models\Repositories\Videos;
|
||||||
use openvk\Web\Models\Repositories\Notes;
|
use openvk\Web\Models\Repositories\Notes;
|
||||||
|
use openvk\Web\Models\Repositories\Vouchers;
|
||||||
|
use Chandler\Security\Authenticator;
|
||||||
|
use lfkeitel\phptotp\{Base32, Totp};
|
||||||
|
use chillerlan\QRCode\{QRCode, QROptions};
|
||||||
|
|
||||||
final class UserPresenter extends OpenVKPresenter
|
final class UserPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
@ -29,18 +34,14 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode())
|
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode())
|
||||||
$this->redirect("/" . $user->getShortCode(), static::REDIRECT_TEMPORARY_PRESISTENT);
|
$this->redirect("/" . $user->getShortCode(), static::REDIRECT_TEMPORARY_PRESISTENT);
|
||||||
|
|
||||||
$then = date_create("@" . $user->getOnline()->timestamp());
|
|
||||||
$now = date_create();
|
|
||||||
$diff = date_diff($now, $then);
|
|
||||||
|
|
||||||
$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);
|
||||||
$this->template->videos = (new Videos)->getByUser($user, 1, 2);
|
$this->template->videos = (new Videos)->getByUser($user, 1, 2);
|
||||||
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
||||||
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
|
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
|
||||||
$this->template->notesCount = (new Notes)->getUserNotesCount($user);
|
$this->template->notesCount = (new Notes)->getUserNotesCount($user);
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
$this->template->diff = $diff;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +82,42 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
} else {
|
} else {
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
$this->template->page = $this->queryParam("p") ?? 1;
|
$this->template->page = $this->queryParam("p") ?? 1;
|
||||||
|
$this->template->admin = $this->queryParam("act") == "managed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderPinClub(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$club = (new Clubs)->get((int) $this->queryParam("club"));
|
||||||
|
if(!$club)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||||
|
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.");
|
||||||
|
|
||||||
|
$isClubPinned = $this->user->identity->isClubPinned($club);
|
||||||
|
if(!$isClubPinned && $this->user->identity->getPinnedClubCount() > 10)
|
||||||
|
$this->flashFail("err", "Ошибка", "Находится в левом меню могут максимум 10 групп");
|
||||||
|
|
||||||
|
if($club->getOwner()->getId() === $this->user->identity->getId()) {
|
||||||
|
$club->setOwner_Club_Pinned(!$isClubPinned);
|
||||||
|
$club->save();
|
||||||
|
} else {
|
||||||
|
$manager = $club->getManager($this->user->identity);
|
||||||
|
if(!is_null($manager)) {
|
||||||
|
$manager->setClub_Pinned(!$isClubPinned);
|
||||||
|
$manager->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($isClubPinned)
|
||||||
|
$this->flashFail("succ", "Операция успешна", "Группа " . $club->getName() . " была успешно удалена из левого меню");
|
||||||
|
else
|
||||||
|
$this->flashFail("succ", "Операция успешна", "Группа " . $club->getName() . " была успешно добавлена в левое меню");
|
||||||
|
}
|
||||||
|
|
||||||
function renderEdit(): void
|
function renderEdit(): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
@ -108,7 +142,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
||||||
$user->setMarital_Status($this->postParam("marialstatus"));
|
$user->setMarital_Status($this->postParam("marialstatus"));
|
||||||
|
|
||||||
if ($this->postParam("politViews") <= 8 && $this->postParam("politViews") >= 0)
|
if ($this->postParam("politViews") <= 9 && $this->postParam("politViews") >= 0)
|
||||||
$user->setPolit_Views($this->postParam("politViews"));
|
$user->setPolit_Views($this->postParam("politViews"));
|
||||||
|
|
||||||
if ($this->postParam("gender") <= 1 && $this->postParam("gender") >= 0)
|
if ($this->postParam("gender") <= 1 && $this->postParam("gender") >= 0)
|
||||||
|
@ -125,9 +159,15 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
} elseif($_GET['act'] === "contacts") {
|
} elseif($_GET['act'] === "contacts") {
|
||||||
$user->setEmail_Contact(empty($this->postParam("email_contact")) ? NULL : $this->postParam("email_contact"));
|
$user->setEmail_Contact(empty($this->postParam("email_contact")) ? NULL : $this->postParam("email_contact"));
|
||||||
$user->setTelegram(empty($this->postParam("telegram")) ? NULL : $this->postParam("telegram"));
|
$user->setTelegram(empty($this->postParam("telegram")) ? NULL : ltrim($this->postParam("telegram"), "@"));
|
||||||
$user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city"));
|
$user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city"));
|
||||||
$user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address"));
|
$user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address"));
|
||||||
|
|
||||||
|
$website = $this->postParam("website") ?? "";
|
||||||
|
if(empty($website))
|
||||||
|
$user->setWebsite(NULL);
|
||||||
|
else
|
||||||
|
$user->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website);
|
||||||
} elseif($_GET['act'] === "interests") {
|
} elseif($_GET['act'] === "interests") {
|
||||||
$user->setInterests(empty($this->postParam("interests")) ? NULL : ovk_proc_strtr($this->postParam("interests"), 300));
|
$user->setInterests(empty($this->postParam("interests")) ? NULL : ovk_proc_strtr($this->postParam("interests"), 300));
|
||||||
$user->setFav_Music(empty($this->postParam("fav_music")) ? NULL : ovk_proc_strtr($this->postParam("fav_music"), 300));
|
$user->setFav_Music(empty($this->postParam("fav_music")) ? NULL : ovk_proc_strtr($this->postParam("fav_music"), 300));
|
||||||
|
@ -136,6 +176,18 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$user->setFav_Books(empty($this->postParam("fav_books")) ? NULL : ovk_proc_strtr($this->postParam("fav_books"), 300));
|
$user->setFav_Books(empty($this->postParam("fav_books")) ? NULL : ovk_proc_strtr($this->postParam("fav_books"), 300));
|
||||||
$user->setFav_Quote(empty($this->postParam("fav_quote")) ? NULL : ovk_proc_strtr($this->postParam("fav_quote"), 300));
|
$user->setFav_Quote(empty($this->postParam("fav_quote")) ? NULL : ovk_proc_strtr($this->postParam("fav_quote"), 300));
|
||||||
$user->setAbout(empty($this->postParam("about")) ? NULL : ovk_proc_strtr($this->postParam("about"), 300));
|
$user->setAbout(empty($this->postParam("about")) ? NULL : ovk_proc_strtr($this->postParam("about"), 300));
|
||||||
|
} elseif($_GET['act'] === "status") {
|
||||||
|
if(mb_strlen($this->postParam("status")) > 255) {
|
||||||
|
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||||
|
$this->flashFail("err", "Ошибка", "Статус слишком длинный ($statusLength символов вместо 255 символов)");
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
header("HTTP/1.1 302 Found");
|
||||||
|
header("Location: /id" . $user->getId());
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -224,6 +276,9 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
if(!$id)
|
if(!$id)
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
|
if(in_array($this->queryParam("act"), ["finance", "finance.top-up"]) && !OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||||
|
$this->flashFail("err", tr("error"), tr("feature_disabled"));
|
||||||
|
|
||||||
$user = $this->users->get($id);
|
$user = $this->users->get($id);
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
@ -231,6 +286,12 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
if($_GET['act'] === "main" || $_GET['act'] == NULL) {
|
if($_GET['act'] === "main" || $_GET['act'] == NULL) {
|
||||||
if($this->postParam("old_pass") && $this->postParam("new_pass") && $this->postParam("repeat_pass")) {
|
if($this->postParam("old_pass") && $this->postParam("new_pass") && $this->postParam("repeat_pass")) {
|
||||||
if($this->postParam("new_pass") === $this->postParam("repeat_pass")) {
|
if($this->postParam("new_pass") === $this->postParam("repeat_pass")) {
|
||||||
|
if($this->user->identity->is2faEnabled()) {
|
||||||
|
$code = $this->postParam("code");
|
||||||
|
if(!($code === (new Totp)->GenerateToken(Base32::decode($this->user->identity->get2faSecret())) || $this->user->identity->use2faBackupCode((int) $code)))
|
||||||
|
$this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
|
||||||
|
}
|
||||||
|
|
||||||
if(!$this->user->identity->getChandlerUser()->updatePassword($this->postParam("new_pass"), $this->postParam("old_pass")))
|
if(!$this->user->identity->getChandlerUser()->updatePassword($this->postParam("new_pass"), $this->postParam("old_pass")))
|
||||||
$this->flashFail("err", tr("error"), tr("error_old_password"));
|
$this->flashFail("err", tr("error"), tr("error_old_password"));
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,7 +301,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if(!$user->setShortCode(empty($this->postParam("sc")) ? NULL : $this->postParam("sc")))
|
if(!$user->setShortCode(empty($this->postParam("sc")) ? NULL : $this->postParam("sc")))
|
||||||
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||||
}elseif($_GET['act'] === "privacy") {
|
} else if($_GET['act'] === "privacy") {
|
||||||
$settings = [
|
$settings = [
|
||||||
"page.read",
|
"page.read",
|
||||||
"page.info.read",
|
"page.info.read",
|
||||||
|
@ -256,9 +317,27 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$input = $this->postParam(str_replace(".", "_", $setting));
|
$input = $this->postParam(str_replace(".", "_", $setting));
|
||||||
$user->setPrivacySetting($setting, min(3, abs($input ?? $user->getPrivacySetting($setting))));
|
$user->setPrivacySetting($setting, min(3, abs($input ?? $user->getPrivacySetting($setting))));
|
||||||
}
|
}
|
||||||
}elseif($_GET['act'] === "interface") {
|
} else if($_GET['act'] === "finance.top-up") {
|
||||||
|
$token = $this->postParam("key0") . $this->postParam("key1") . $this->postParam("key2") . $this->postParam("key3");
|
||||||
|
$voucher = (new Vouchers)->getByToken($token);
|
||||||
|
if(!$voucher)
|
||||||
|
$this->flashFail("err", tr("invalid_voucher"), tr("voucher_bad"));
|
||||||
|
|
||||||
|
$perm = $voucher->willUse($user);
|
||||||
|
if(!$perm)
|
||||||
|
$this->flashFail("err", tr("invalid_voucher"), tr("voucher_bad"));
|
||||||
|
|
||||||
|
$user->setCoins($user->getCoins() + $voucher->getCoins());
|
||||||
|
$user->setRating($user->getRating() + $voucher->getRating());
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$this->flashFail("succ", tr("voucher_good"), tr("voucher_redeemed"));
|
||||||
|
} else if($_GET['act'] === "interface") {
|
||||||
if (isset(Themepacks::i()[$this->postParam("style")]) || $this->postParam("style") === Themepacks::DEFAULT_THEME_ID)
|
if (isset(Themepacks::i()[$this->postParam("style")]) || $this->postParam("style") === Themepacks::DEFAULT_THEME_ID)
|
||||||
|
{
|
||||||
$user->setStyle($this->postParam("style"));
|
$user->setStyle($this->postParam("style"));
|
||||||
|
$this->setTempTheme($this->postParam("style"));
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->postParam("style_avatar") <= 2 && $this->postParam("style_avatar") >= 0)
|
if ($this->postParam("style_avatar") <= 2 && $this->postParam("style_avatar") >= 0)
|
||||||
$user->setStyle_Avatar((int)$this->postParam("style_avatar"));
|
$user->setStyle_Avatar((int)$this->postParam("style_avatar"));
|
||||||
|
@ -271,7 +350,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if(in_array($this->postParam("nsfw"), [0, 1, 2]))
|
if(in_array($this->postParam("nsfw"), [0, 1, 2]))
|
||||||
$user->setNsfwTolerance((int) $this->postParam("nsfw"));
|
$user->setNsfwTolerance((int) $this->postParam("nsfw"));
|
||||||
}elseif($_GET['act'] === "lMenu") {
|
} else if($_GET['act'] === "lMenu") {
|
||||||
$settings = [
|
$settings = [
|
||||||
"menu_bildoj" => "photos",
|
"menu_bildoj" => "photos",
|
||||||
"menu_filmetoj" => "videos",
|
"menu_filmetoj" => "videos",
|
||||||
|
@ -296,14 +375,76 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$this->flash(
|
$this->flash(
|
||||||
"succ",
|
"succ",
|
||||||
"Изменения сохранены",
|
"Изменения сохранены",
|
||||||
"Новые данные появятся на вашей странице.<br/>Если вы изменили стиль, перезагрузите страницу."
|
"Новые данные появятся на вашей странице."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->template->mode = in_array($this->queryParam("act"), [
|
$this->template->mode = in_array($this->queryParam("act"), [
|
||||||
"main", "privacy", "finance", "interface"
|
"main", "privacy", "finance", "finance.top-up", "interface"
|
||||||
]) ? $this->queryParam("act")
|
]) ? $this->queryParam("act")
|
||||||
: "main";
|
: "main";
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
$this->template->themes = Themepacks::i()->getThemeList();
|
$this->template->themes = Themepacks::i()->getThemeList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderTwoFactorAuthSettings(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
if($this->user->identity->is2faEnabled()) {
|
||||||
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
if(!Authenticator::verifyHash($this->postParam("password"), $this->user->identity->getChandlerUser()->getRaw()->passwordHash))
|
||||||
|
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||||
|
|
||||||
|
$this->user->identity->generate2faBackupCodes();
|
||||||
|
$this->template->_template = "User/TwoFactorAuthCodes.xml";
|
||||||
|
$this->template->codes = $this->user->identity->get2faBackupCodes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
$secret = Base32::encode(Totp::GenerateSecret(16));
|
||||||
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if(!Authenticator::verifyHash($this->postParam("password"), $this->user->identity->getChandlerUser()->getRaw()->passwordHash))
|
||||||
|
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||||
|
|
||||||
|
$secret = $this->postParam("secret");
|
||||||
|
$code = $this->postParam("code");
|
||||||
|
|
||||||
|
if($code === (new Totp)->GenerateToken(Base32::decode($secret))) {
|
||||||
|
$this->user->identity->set2fa_secret($secret);
|
||||||
|
$this->user->identity->save();
|
||||||
|
|
||||||
|
$this->flash("succ", tr("two_factor_authentication_enabled_message"), tr("two_factor_authentication_enabled_message_description"));
|
||||||
|
$this->redirect("/settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->secret = $secret;
|
||||||
|
$this->flash("err", tr("error"), tr("incorrect_code"));
|
||||||
|
} else {
|
||||||
|
$this->template->secret = $secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
$issuer = OPENVK_ROOT_CONF["openvk"]["appearance"]["name"];
|
||||||
|
$email = $this->user->identity->getEmail();
|
||||||
|
$this->template->qrCode = substr((new QRCode(new QROptions([
|
||||||
|
"imageTransparent" => false
|
||||||
|
])))->render("otpauth://totp/$issuer:$email?secret=$secret&issuer=$issuer"), 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDisableTwoFactorAuth(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if(!Authenticator::verifyHash($this->postParam("password"), $this->user->identity->getChandlerUser()->getRaw()->passwordHash))
|
||||||
|
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||||
|
|
||||||
|
$this->user->identity->set2fa_secret(NULL);
|
||||||
|
$this->user->identity->save();
|
||||||
|
$this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use Chandler\Database\DatabaseConnection as DB;
|
||||||
use openvk\VKAPI\Exceptions\APIErrorException;
|
use openvk\VKAPI\Exceptions\APIErrorException;
|
||||||
use openvk\Web\Models\Entities\{User, APIToken};
|
use openvk\Web\Models\Entities\{User, APIToken};
|
||||||
use openvk\Web\Models\Repositories\{Users, APITokens};
|
use openvk\Web\Models\Repositories\{Users, APITokens};
|
||||||
|
use lfkeitel\phptotp\{Base32, Totp};
|
||||||
|
|
||||||
final class VKAPIPresenter extends OpenVKPresenter
|
final class VKAPIPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
@ -161,6 +162,10 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
$uId = $chUser->related("profiles.user")->fetch()->id;
|
$uId = $chUser->related("profiles.user")->fetch()->id;
|
||||||
$user = (new Users)->get($uId);
|
$user = (new Users)->get($uId);
|
||||||
|
|
||||||
|
$code = $this->requestParam("code");
|
||||||
|
if($user->is2faEnabled() && !($code === (new Totp)->GenerateToken(Base32::decode($user->get2faSecret())) || $user->use2faBackupCode((int) $code)))
|
||||||
|
$this->fail(28, "Invalid 2FA code", "internal", "acquireToken");
|
||||||
|
|
||||||
$token = new APIToken;
|
$token = new APIToken;
|
||||||
$token->setUser($user);
|
$token->setUser($user);
|
||||||
$token->save();
|
$token->save();
|
||||||
|
|
|
@ -68,6 +68,8 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
$video->setLink($this->postParam("link"));
|
$video->setLink($this->postParam("link"));
|
||||||
else
|
else
|
||||||
$this->flashFail("err", "Нету видеозаписи", "Выберите файл или укажите ссылку.");
|
$this->flashFail("err", "Нету видеозаписи", "Выберите файл или укажите ссылку.");
|
||||||
|
} catch(\DomainException $ex) {
|
||||||
|
$this->flashFail("err", "Произошла ошибка", "Файл повреждён или не содержит видео." );
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
$this->flashFail("err", "Произошла ошибка", "Возможно, ссылка некорректна.");
|
$this->flashFail("err", "Произошла ошибка", "Возможно, ссылка некорректна.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Post, Photo, Club, User};
|
use openvk\Web\Models\Entities\{Post, Photo, Video, Club, User};
|
||||||
use openvk\Web\Models\Entities\Notifications\{LikeNotification, RepostNotification, WallPostNotification};
|
use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotification};
|
||||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
|
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
|
@ -45,17 +45,21 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
exit("Ошибка доступа: " . (string) random_int(0, 255));
|
exit("Ошибка доступа: " . (string) random_int(0, 255));
|
||||||
|
|
||||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||||
if(is_null($this->user))
|
if(is_null($this->user)) {
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
else if($user > 0)
|
} else if($user > 0) {
|
||||||
|
if(!$owner->isBanned())
|
||||||
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||||
else if($user < 0)
|
else
|
||||||
|
$this->flashFail("err", tr("error"), "Ошибка доступа");
|
||||||
|
} else if($user < 0) {
|
||||||
if($owner->canBeModifiedBy($this->user->identity))
|
if($owner->canBeModifiedBy($this->user->identity))
|
||||||
$canPost = true;
|
$canPost = true;
|
||||||
else
|
else
|
||||||
$canPost = $owner->canPost();
|
$canPost = $owner->canPost();
|
||||||
else
|
} else {
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ($embedded == true) $this->template->_template = "components/wall.xml";
|
if ($embedded == true) $this->template->_template = "components/wall.xml";
|
||||||
$this->template->oObj = $owner;
|
$this->template->oObj = $owner;
|
||||||
|
@ -164,21 +168,33 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
||||||
?? $this->flashFail("err", "Не удалось опубликовать пост", "Такого пользователя не существует.");
|
?? $this->flashFail("err", "Не удалось опубликовать пост", "Такого пользователя не существует.");
|
||||||
if($wall > 0)
|
if($wall > 0) {
|
||||||
|
if(!$wallOwner->isBanned())
|
||||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
|
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||||
else if($wall < 0)
|
else
|
||||||
|
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену.");
|
||||||
|
} else if($wall < 0) {
|
||||||
if($wallOwner->canBeModifiedBy($this->user->identity))
|
if($wallOwner->canBeModifiedBy($this->user->identity))
|
||||||
$canPost = true;
|
$canPost = true;
|
||||||
else
|
else
|
||||||
$canPost = $wallOwner->canPost();
|
$canPost = $wallOwner->canPost();
|
||||||
else
|
} else {
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!$canPost)
|
if(!$canPost)
|
||||||
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену.");
|
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену.");
|
||||||
|
|
||||||
if(false)
|
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Пост слишком большой.");
|
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
||||||
|
$manager = $wallOwner->getManager($this->user->identity);
|
||||||
|
if($manager)
|
||||||
|
$anon = $manager->isHidden();
|
||||||
|
elseif($this->user->identity->getId() === $wallOwner->getOwner()->getId())
|
||||||
|
$anon = $wallOwner->isOwnerHidden();
|
||||||
|
} else {
|
||||||
|
$anon = $anon && $this->postParam("anon") === "on";
|
||||||
|
}
|
||||||
|
|
||||||
$flags = 0;
|
$flags = 0;
|
||||||
if($this->postParam("as_group") === "on")
|
if($this->postParam("as_group") === "on")
|
||||||
|
@ -186,49 +202,49 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if($this->postParam("force_sign") === "on")
|
if($this->postParam("force_sign") === "on")
|
||||||
$flags |= 0b01000000;
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$photo = NULL;
|
||||||
|
$video = NULL;
|
||||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||||
try {
|
$album = NULL;
|
||||||
$photo = new Photo;
|
if(!$anon && $wall > 0 && $wall === $this->user->id)
|
||||||
$photo->setOwner($this->user->id);
|
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||||
$photo->setDescription(iconv_substr($this->postParam("text"), 0, 36) . "...");
|
|
||||||
$photo->setCreated(time());
|
|
||||||
$photo->setFile($_FILES["_pic_attachment"]);
|
|
||||||
$photo->save();
|
|
||||||
|
|
||||||
if($wall > 0 && $wall === $this->user->id) {
|
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon);
|
||||||
(new Albums)->getUserWallAlbum($wallOwner)->addPhoto($photo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
||||||
|
}
|
||||||
|
} catch(\DomainException $ex) {
|
||||||
|
$this->flashFail("err", "Не удалось опубликовать пост", "Файл медиаконтента повреждён.");
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Файл изображения повреждён, слишком велик или одна сторона изображения в разы больше другой.");
|
$this->flashFail("err", "Не удалось опубликовать пост", "Файл медиаконтента повреждён или слишком велик.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$post = new Post;
|
if(empty($this->postParam("text")) && !$photo && !$video)
|
||||||
$post->setOwner($this->user->id);
|
$this->flashFail("err", "Не удалось опубликовать пост", "Пост пустой или слишком большой.");
|
||||||
$post->setWall($wall);
|
|
||||||
$post->setCreated(time());
|
|
||||||
$post->setContent($this->postParam("text"));
|
|
||||||
$post->setFlags($flags);
|
|
||||||
$post->setNsfw($this->postParam("nsfw") === "on");
|
|
||||||
$post->save();
|
|
||||||
$post->attach($photo);
|
|
||||||
} elseif($this->postParam("text")) {
|
|
||||||
try {
|
try {
|
||||||
$post = new Post;
|
$post = new Post;
|
||||||
$post->setOwner($this->user->id);
|
$post->setOwner($this->user->id);
|
||||||
$post->setWall($wall);
|
$post->setWall($wall);
|
||||||
$post->setCreated(time());
|
$post->setCreated(time());
|
||||||
$post->setContent($this->postParam("text"));
|
$post->setContent($this->postParam("text"));
|
||||||
|
$post->setAnonymous($anon);
|
||||||
$post->setFlags($flags);
|
$post->setFlags($flags);
|
||||||
$post->setNsfw($this->postParam("nsfw") === "on");
|
$post->setNsfw($this->postParam("nsfw") === "on");
|
||||||
$post->save();
|
$post->save();
|
||||||
} catch(\LogicException $ex) {
|
} catch (\LengthException $ex) {
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Пост пустой или слишком большой.");
|
$this->flashFail("err", "Не удалось опубликовать пост", "Пост слишком большой.");
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Пост пустой или слишком большой.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!is_null($photo))
|
||||||
|
$post->attach($photo);
|
||||||
|
|
||||||
|
if(!is_null($video))
|
||||||
|
$post->attach($video);
|
||||||
|
|
||||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||||
|
|
||||||
|
@ -250,10 +266,11 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->logPostView($post, $wall);
|
$this->logPostView($post, $wall);
|
||||||
|
|
||||||
$this->template->post = $post;
|
$this->template->post = $post;
|
||||||
if ($post->getTargetWall() > 0)
|
if ($post->getTargetWall() > 0) {
|
||||||
{
|
|
||||||
$this->template->wallOwner = (new Users)->get($post->getTargetWall());
|
$this->template->wallOwner = (new Users)->get($post->getTargetWall());
|
||||||
$this->template->isWallOfGroup = false;
|
$this->template->isWallOfGroup = false;
|
||||||
|
if($this->template->wallOwner->isBanned())
|
||||||
|
$this->flashFail("err", tr("error"), "Ошибка доступа");
|
||||||
} else {
|
} else {
|
||||||
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
||||||
$this->template->isWallOfGroup = true;
|
$this->template->isWallOfGroup = true;
|
||||||
|
@ -274,9 +291,6 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if(!is_null($this->user)) {
|
if(!is_null($this->user)) {
|
||||||
$post->toggleLike($this->user->identity);
|
$post->toggleLike($this->user->identity);
|
||||||
|
|
||||||
if($post->getOwner(false)->getId() !== $this->user->identity->getId() && !($post->getOwner() instanceof Club))
|
|
||||||
(new LikeNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->redirect(
|
$this->redirect(
|
||||||
|
@ -298,7 +312,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$nPost = new Post;
|
$nPost = new Post;
|
||||||
$nPost->setOwner($this->user->id);
|
$nPost->setOwner($this->user->id);
|
||||||
$nPost->setWall($this->user->id);
|
$nPost->setWall($this->user->id);
|
||||||
$nPost->setContent("");
|
$nPost->setContent($this->postParam("text"));
|
||||||
$nPost->save();
|
$nPost->save();
|
||||||
$nPost->attach($post);
|
$nPost->attach($post);
|
||||||
|
|
||||||
|
@ -306,8 +320,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
(new RepostNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
(new RepostNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
||||||
};
|
};
|
||||||
|
|
||||||
$this->flash("succ", "Успешно", "Запись появится на вашей стене. <a href='/wall" . $wall . "_" . $post_id . "'>Вернуться к записи.</a>");
|
exit(json_encode(["wall_owner" => $this->user->identity->getId()]));
|
||||||
$this->redirect($this->user->identity->getURL());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDelete(int $wall, int $post_id): void
|
function renderDelete(int $wall, int $post_id): void
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
<div class="ovk-lw-container">
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block wrap}
|
||||||
|
<div class="ovk-lw-container">
|
||||||
<div class="ovk-lw--list">
|
<div class="ovk-lw--list">
|
||||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
|
|
||||||
|
@ -54,4 +57,5 @@
|
||||||
<a href="?C=M;O=A" class="profile_link">{_"sort_down"}</a>
|
<a href="?C=M;O=A" class="profile_link">{_"sort_down"}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/block}
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
|
{var instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||||
|
|
||||||
<html n:if="!isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'">
|
<html n:if="!isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'">
|
||||||
<head>
|
<head>
|
||||||
<title>
|
<title>
|
||||||
{ifset title}{include title} - {/ifset}OpenVK
|
{ifset title}{include title} - {/ifset}{$instance_name}
|
||||||
</title>
|
</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="shortcut icon" href="/assets/packages/static/openvk/img/icon.ico" />
|
<link rel="shortcut icon" href="/assets/packages/static/openvk/img/icon.ico" />
|
||||||
<meta name="application-name" content="OpenVK" />
|
<meta name="application-name" content="{$instance_name}" />
|
||||||
<meta n:ifset="$csrfToken" name="csrf" value="{$csrfToken}" />
|
<meta n:ifset="$csrfToken" name="csrf" value="{$csrfToken}" />
|
||||||
|
<script src="/language/{php echo getLanguage()}.js" crossorigin="anonymous"></script>
|
||||||
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
||||||
|
{script "js/l10n.js"}
|
||||||
{script "js/openvk.cls.js"}
|
{script "js/openvk.cls.js"}
|
||||||
|
|
||||||
{ifset $thisUser}
|
{ifset $thisUser}
|
||||||
|
@ -16,11 +20,11 @@
|
||||||
{css "css/nsfw-posts.css"}
|
{css "css/nsfw-posts.css"}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{if !is_null($thisUser->getTheme())}
|
{if $theme !== null}
|
||||||
{var theme = $thisUser->getTheme()}
|
|
||||||
{if $theme->inheritDefault()}
|
{if $theme->inheritDefault()}
|
||||||
{css "css/style.css"}
|
{css "css/style.css"}
|
||||||
{css "css/dialog.css"}
|
{css "css/dialog.css"}
|
||||||
|
{css "css/notifications.css"}
|
||||||
{if $isXmas}
|
{if $isXmas}
|
||||||
{css "css/xmas.css"}
|
{css "css/xmas.css"}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -28,11 +32,12 @@
|
||||||
|
|
||||||
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/stylesheet/styles.css" />
|
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/stylesheet/styles.css" />
|
||||||
{if $isXmas}
|
{if $isXmas}
|
||||||
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/stylesheet/xmas.css" />
|
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/resource/xmas.css" />
|
||||||
{/if}
|
{/if}
|
||||||
{else}
|
{else}
|
||||||
{css "css/style.css"}
|
{css "css/style.css"}
|
||||||
{css "css/dialog.css"}
|
{css "css/dialog.css"}
|
||||||
|
{css "css/notifications.css"}
|
||||||
{if $isXmas}
|
{if $isXmas}
|
||||||
{css "css/xmas.css"}
|
{css "css/xmas.css"}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -53,6 +58,7 @@
|
||||||
{css "css/style.css"}
|
{css "css/style.css"}
|
||||||
{css "css/dialog.css"}
|
{css "css/dialog.css"}
|
||||||
{css "css/nsfw-posts.css"}
|
{css "css/nsfw-posts.css"}
|
||||||
|
{css "css/notifications.css"}
|
||||||
|
|
||||||
{if $isXmas}
|
{if $isXmas}
|
||||||
{css "css/xmas.css"}
|
{css "css/xmas.css"}
|
||||||
|
@ -71,6 +77,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['testLabel']" id="test-label">FOR TESTING PURPOSES ONLY</div>
|
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['testLabel']" id="test-label">FOR TESTING PURPOSES ONLY</div>
|
||||||
|
<div class="notifications_global_wrap"></div>
|
||||||
<div class="dimmer"></div>
|
<div class="dimmer"></div>
|
||||||
<div class="toTop">
|
<div class="toTop">
|
||||||
⬆ Вверх
|
⬆ Вверх
|
||||||
|
@ -78,8 +85,8 @@
|
||||||
|
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<div id="xhead" class="dm"></div>
|
<div id="xhead" class="dm"></div>
|
||||||
<div class="page_header">
|
<div class="page_header {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}page_custom_header{/if}">
|
||||||
<a href="/" class="home_button" title="OpenVK">openvk</a>
|
<a href="/" class="home_button {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}home_button_custom{/if}" title="{$instance_name}">{$instance_name}</a>
|
||||||
<div n:if="isset($thisUser) ? !$thisUser->isBanned() : true" class="header_navigation">
|
<div n:if="isset($thisUser) ? !$thisUser->isBanned() : true" class="header_navigation">
|
||||||
{ifset $thisUser}
|
{ifset $thisUser}
|
||||||
<div class="link">
|
<div class="link">
|
||||||
|
@ -95,14 +102,17 @@
|
||||||
<a href="/search">{_"header_search"}</a>
|
<a href="/search">{_"header_search"}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<a href="/support">{_"header_help"}</a>
|
<a href="/support">
|
||||||
|
{_"header_help"}
|
||||||
|
<b n:if="$ticketAnsweredCount > 0">({$ticketAnsweredCount})</b>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<a href="/logout">{_"header_log_out"}</a>
|
<a href="/logout?hash={urlencode($csrfToken)}">{_"header_log_out"}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<form action="/search" method="get">
|
<form action="/search" method="get">
|
||||||
<input type="search" name="query" placeholder="{_"header_search"}" style="background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" />
|
<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;" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -154,12 +164,19 @@
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
<a href="/settings" class="link">{_"my_settings"}</a>
|
<a href="/settings" class="link">{_"my_settings"}</a>
|
||||||
<div style="height: 1px;background: #CCC;margin: 4px 0 2px;"></div>
|
{var canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||||
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
{var canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
|
{var menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0}
|
||||||
|
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
||||||
|
{if $canAccessAdminPanel}
|
||||||
<a href="/admin" class="link">Админ-панель</a>
|
<a href="/admin" class="link">Админ-панель</a>
|
||||||
{/if}
|
{/if}
|
||||||
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
{if $canAccessHelpdesk}
|
||||||
<a href="/support/tickets" class="link">Helpdesk</a>
|
<a href="/support/tickets" class="link">Helpdesk
|
||||||
|
{if $helpdeskTicketAnsweredCount > 0}
|
||||||
|
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
<a href="/admin/reports" class="link">Reports</a>
|
<a href="/admin/reports" class="link">Reports</a>
|
||||||
{/if}
|
{/if}
|
||||||
<a
|
<a
|
||||||
|
@ -167,7 +184,14 @@
|
||||||
href="{$menuItem['url']}"
|
href="{$menuItem['url']}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="link">{$menuItem["name"]}</a>
|
class="link">{$menuItem["name"]}</a>
|
||||||
|
<div id="_groupListPinnedGroups">
|
||||||
|
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
||||||
|
<a
|
||||||
|
n:foreach="$thisUser->getPinnedClubs() as $club"
|
||||||
|
href="{$club->getURL()}"
|
||||||
|
class="link group_link">{$club->getName()}</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
<a
|
<a
|
||||||
n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable']"
|
n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable']"
|
||||||
href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" >
|
href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" >
|
||||||
|
@ -177,7 +201,6 @@
|
||||||
class="psa-poster"
|
class="psa-poster"
|
||||||
style="max-width: 100%; margin-top: 50px;" />
|
style="max-width: 100%; margin-top: 50px;" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{else}
|
{else}
|
||||||
<a href="/support" class="link">Поддержка</a>
|
<a href="/support" class="link">Поддержка</a>
|
||||||
<a href="/logout" class="link">Выйти</a>
|
<a href="/logout" class="link">Выйти</a>
|
||||||
|
@ -248,19 +271,25 @@
|
||||||
<a href="/language" class="link">{_footer_choose_language}</a>
|
<a href="/language" class="link">{_footer_choose_language}</a>
|
||||||
<a href="/privacy" class="link">{_footer_privacy}</a>
|
<a href="/privacy" class="link">{_footer_privacy}</a>
|
||||||
</div>
|
</div>
|
||||||
<p>OpenVK <a href="/about:openvk2">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p>
|
<p>OpenVK <a href="/about:openvk">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p>
|
||||||
<p n:ifcontent>
|
<p n:ifcontent="ifcontent">
|
||||||
{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}
|
{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://rawgit.com/kawanet/msgpack-lite/master/dist/msgpack.min.js"></script>
|
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
||||||
|
{script "js/node_modules/soundjs/lib/soundjs.min.js"}
|
||||||
{script "js/node_modules/ky/umd.js"}
|
{script "js/node_modules/ky/umd.js"}
|
||||||
{script "js/messagebox.js"}
|
{script "js/messagebox.js"}
|
||||||
|
{script "js/notifications.js"}
|
||||||
{script "js/scroll.js"}
|
{script "js/scroll.js"}
|
||||||
{script "js/al_wall.js"}
|
{script "js/al_wall.js"}
|
||||||
{script "js/al_api.js"}
|
{script "js/al_api.js"}
|
||||||
|
|
||||||
|
{ifset $thisUser}
|
||||||
|
{script "js/al_notifs.js"}
|
||||||
|
{/ifset}
|
||||||
|
|
||||||
<script src="https://unpkg.com/fartscroll@1.0.0/fartscroll.js"></script>
|
<script src="https://unpkg.com/fartscroll@1.0.0/fartscroll.js"></script>
|
||||||
<script n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['fartscroll']">
|
<script n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['fartscroll']">
|
||||||
fartscroll(400);
|
fartscroll(400);
|
||||||
|
@ -269,6 +298,10 @@
|
||||||
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['enable']"
|
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['enable']"
|
||||||
async defer data-domain="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['domain']}"
|
async defer data-domain="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['domain']}"
|
||||||
src="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['server']}js/plausible.js"></script>
|
src="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['server']}js/plausible.js"></script>
|
||||||
|
|
||||||
|
{ifset bodyScripts}
|
||||||
|
{include bodyScripts}
|
||||||
|
{/ifset}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"count" => $count,
|
"count" => $count,
|
||||||
"amount" => sizeof($data),
|
"amount" => sizeof($data),
|
||||||
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
"atBottom" => true,
|
||||||
]}
|
]}
|
||||||
</div>
|
</div>
|
||||||
{else}
|
{else}
|
||||||
|
|
|
@ -6,21 +6,10 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<b>OpenVK - универсальное средство поиска коллег основанное на структуре ВКонтакте.</b><br>
|
{presenter "openvk!Support->knowledgeBaseArticle", "about"}
|
||||||
<p>Мы хотим, чтобы друзья, однокурсники, одноклассники, соседи и коллеги всегда могли быть в контакте.</p>
|
|
||||||
<p>
|
|
||||||
Нас уже <b>{$stats->all}</b> пользователя и <b>{$stats->online}</b> из
|
|
||||||
<b>{$stats->active}</b> активных сейчас в сети.
|
|
||||||
</p>
|
|
||||||
<p>С помощью этого сайта Вы можете:</p>
|
|
||||||
<ul>
|
|
||||||
<li><span>Найти людей, с которыми Вы когда-либо учились, работали или отдыхали.</span></li>
|
|
||||||
<li><span>Узнать больше о людях, которые Вас окружают, и найти новых друзей.</span></li>
|
|
||||||
<li><span>Всегда оставаться в контакте с теми, кто Вам дорог.</span></li>
|
|
||||||
<li><span>Продвигать своё творчество и/или мнение.</span></li>
|
|
||||||
</ul>
|
|
||||||
<center>
|
<center>
|
||||||
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_"log_in"}</a>
|
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_"log_in"}</a>
|
||||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable']" class="button" style="cursor: pointer;" href="/reg">{_"registration"}</a></center>
|
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable']" class="button" style="cursor: pointer;" href="/reg">{_"registration"}</a>
|
||||||
</div>
|
</center>
|
||||||
|
{* TO-DO: Add statistics about this instance as on mastodon.social *}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{block content}
|
{block content}
|
||||||
<div class="navigation">
|
<div class="navigation">
|
||||||
{foreach $languages as $language}
|
{foreach $languages as $language}
|
||||||
<a href="language?lg={$language['code']}" class="link"><img src="/assets/packages/static/openvk/img/flags/{$language['flag']}.gif"> {$language['native_name']}</a>
|
<a href="language?lg={$language['code']}&hash={urlencode($csrfToken)}" class="link"><img src="/assets/packages/static/openvk/img/flags/{$language['flag']}.gif"> {$language['native_name']}</a>
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
#ovkLogo {
|
#ovkLogo {
|
||||||
float: right;
|
float: right;
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 30px;
|
height: 30px;
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
<tr class="h">
|
<tr class="h">
|
||||||
<td>
|
<td>
|
||||||
<h1 class="p" style="float: left;">OpenVK {=OPENVK_VERSION}</h1>
|
<h1 class="p" style="float: left;">OpenVK {=OPENVK_VERSION}</h1>
|
||||||
<img id="ovkLogo" src="/assets/packages/static/openvk/img/logo.svg" alt="OpenVK Logo" />
|
<img id="ovkLogo" src="/assets/packages/static/openvk/img/logo_full.svg" alt="OpenVK Logo" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -397,8 +397,8 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="e">
|
<td class="e">
|
||||||
Vladimir Barinov (veselcraft), Alexandra Katunina (rem-pai), Konstantin Kichulkin (kosfurler),
|
Vladimir Barinov (veselcraft), Alexandra Katunina (rem-pai), Konstantin Kichulkin (kosfurler),
|
||||||
Nikita Volkov (sup_ban), Daniil Myslivets (myslivets), Alexander Kotov (l-lacker),
|
Nikita Volkov (sup_ban), Daniel Myslivets (myslivets), Alexander Kotov (l-lacker),
|
||||||
Alexey Assemblerov (BiosNod), Ilya Prokopenko (dsrev) and Vladimir Lapskiy (0x7d5)
|
Alexey Assemblerov (BiosNod), Ilya Prokopenko (dsrev) and Maxim Leshchenko (maksales / maksalees)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -412,7 +412,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="e">
|
<td class="e">
|
||||||
Vladimir Barinov (veselcraft) and Konstantin Kichulkin (kosfurler)<br/>
|
Vladimir Barinov (veselcraft) and Konstantin Kichulkin (kosfurler)<br/>
|
||||||
OpenVK is a free open-source software that "cosplays" (or imitates) older versions of russian website VKontakte. VKontakte belongs to Pavel Durov and mail.ru.
|
OpenVK is a free open-source software that "cosplays" (or imitates) older versions of russian website VKontakte. VKontakte belongs to Pavel Durov and VK Group.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -69,9 +69,19 @@
|
||||||
Группы
|
Группы
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="aui-nav-heading">
|
||||||
|
<strong>Платные услуги</strong>
|
||||||
|
</div>
|
||||||
|
<ul class="aui-nav">
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/files">
|
<a href="/admin/vouchers">
|
||||||
Загруженные файлы
|
{_vouchers}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/admin/gifts">
|
||||||
|
Подарки
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -120,10 +130,28 @@
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<section class="aui-page-panel-content">
|
<section class="aui-page-panel-content">
|
||||||
|
{ifset $flashMessage}
|
||||||
|
{var type = ["err" => "error", "warn" => "warning", "info" => "basic", "succ" => "success"][$flashMessage->type]}
|
||||||
|
<div class="aui-message aui-message-{$type}" style="margin-bottom: 15px;">
|
||||||
|
<p class="title">
|
||||||
|
<strong>{$flashMessage->title}</strong>
|
||||||
|
</p>
|
||||||
|
<p>{$flashMessage->msg|noescape}</p>
|
||||||
|
</div>
|
||||||
|
{/ifset}
|
||||||
|
|
||||||
|
{ifset preHeader}
|
||||||
|
{include preHeader}
|
||||||
|
{/ifset}
|
||||||
|
|
||||||
<header class="aui-page-header">
|
<header class="aui-page-header">
|
||||||
<div class="aui-page-header-inner">
|
<div class="aui-page-header-inner">
|
||||||
<div class="aui-page-header-main">
|
<div class="aui-page-header-main">
|
||||||
|
{ifset headingWrap}
|
||||||
|
{include headingWrap}
|
||||||
|
{else}
|
||||||
<h1>{include heading}</h1>
|
<h1>{include heading}</h1>
|
||||||
|
{/ifset}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
@ -141,5 +169,13 @@
|
||||||
</section>
|
</section>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
|
{script "js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.js"}
|
||||||
|
<script>AJS.tabs.setup();</script>
|
||||||
|
|
||||||
|
{ifset scripts}
|
||||||
|
{include scripts}
|
||||||
|
{/ifset}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
106
Web/Presenters/templates/Admin/Gift.xml
Normal file
106
Web/Presenters/templates/Admin/Gift.xml
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{if $form->id === 0}
|
||||||
|
Новый подарок
|
||||||
|
{else}
|
||||||
|
Подарок "{$form->name}"
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{include title}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<form class="aui" method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="avatar">
|
||||||
|
Изображение
|
||||||
|
<span n:if="$form->id === 0" class="aui-icon icon-required"></span>
|
||||||
|
</label>
|
||||||
|
{if $form->id === 0}
|
||||||
|
<input type="file" name="pic" accept="image/jpeg,image/png,image/gif,image/webp" required="required" />
|
||||||
|
{else}
|
||||||
|
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img id="pic" src="{$form->pic}" style="object-fit: cover;"></img>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<input style="display: none;" id="picInput" type="file" name="pic" accept="image/jpeg,image/png,image/gif,image/webp" />
|
||||||
|
<div class="description">
|
||||||
|
<a id="picChange" href="javascript:false">Заменить изображение?</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">
|
||||||
|
ID
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" id="id" disabled="disabled" value="{$form->id}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="putin">
|
||||||
|
Использований
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" id="putin" disabled="disabled" value="{$form->usages}" />
|
||||||
|
<div n:if="$form->usages > 0" class="description">
|
||||||
|
<a href="javascript:$('#putin').value(0);">Обнулить?</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="name">
|
||||||
|
Внутренее имя
|
||||||
|
<span class="aui-icon icon-required"></span>
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="text" id="name" name="name" value="{$form->name}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="price">
|
||||||
|
Цена
|
||||||
|
<span class="aui-icon icon-required"></span>
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" id="price" name="price" min="0" value="{$form->price}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="limit">
|
||||||
|
Ограничение
|
||||||
|
<span class="aui-icon icon-required"></span>
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" min="-1" id="limit" name="limit" value="{$form->limit}" />
|
||||||
|
</div>
|
||||||
|
<fieldset class="group">
|
||||||
|
<legend></legend>
|
||||||
|
<div class="checkbox" resolved="">
|
||||||
|
<input n:attr="disabled => $form->id === 0, checked => $form->id === 0" class="checkbox" type="checkbox" name="reset_limit" id="reset_limit" />
|
||||||
|
<span class="aui-form-glyph"></span>
|
||||||
|
|
||||||
|
<label for="reset_limit">Сбросить счётчик ограничений</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<input n:if="$form->id === 0" type="hidden" name="_cat" value="{$_GET['cat'] ?? 1}" />
|
||||||
|
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="aui-button aui-button-primary submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block scripts}
|
||||||
|
<script>
|
||||||
|
const TRANS_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
|
|
||||||
|
$("#picChange").click(_ => $("#picInput").click());
|
||||||
|
$("#picInput").bind("change", e => {
|
||||||
|
if(typeof e.target.files[0] === "undefined")
|
||||||
|
$("#pic").prop("src", URL.createObjectURL(TRANS_GIF));
|
||||||
|
|
||||||
|
$("#pic").prop("src", URL.createObjectURL(e.target.files[0]));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{/block}
|
56
Web/Presenters/templates/Admin/GiftCategories.xml
Normal file
56
Web/Presenters/templates/Admin/GiftCategories.xml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
Наборы подарков
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block headingWrap}
|
||||||
|
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/gifts/new.0.meta">
|
||||||
|
{_create}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<h1>Наборы подарков</h1>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div id="spacer" style="height: 8px;"></div>
|
||||||
|
{if sizeof($categories) > 0}
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$categories as $cat">
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-folder-filled">{$cat->getName()}</span>
|
||||||
|
{$cat->getName()}
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
{ovk_proc_strtr($cat->getDescription(), 128)}
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle; text-align: right;">
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}.meta">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="aui-button" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}/">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-gallery">Открыть</span>
|
||||||
|
Открыть
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{else}
|
||||||
|
<center>
|
||||||
|
<p>Наборов подарков нету. Чтобы создать подарок, создайте набор.</p>
|
||||||
|
</center>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div align="right">
|
||||||
|
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count}
|
||||||
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) - 1}">
|
||||||
|
⭁ туда
|
||||||
|
</a>
|
||||||
|
<a n:if="$isLast" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) + 1}">
|
||||||
|
⭇ сюда
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/block}
|
72
Web/Presenters/templates/Admin/GiftCategory.xml
Normal file
72
Web/Presenters/templates/Admin/GiftCategory.xml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{if $form->id === 0}
|
||||||
|
Создать набор подарков
|
||||||
|
{else}
|
||||||
|
{$form->languages["master"]->name}
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{include title}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<h3>Общие настройки</h3>
|
||||||
|
<fieldset>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">
|
||||||
|
ID
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="name_master">
|
||||||
|
Наименование
|
||||||
|
<span class="aui-icon icon-required"></span>
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="text" id="name_master" name="name_master" value="{$form->languages['master']->name}" />
|
||||||
|
<div class="description">Внутреннее название набора, которое будет использоваться, если не удаётся найти название на языке пользователя.</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="description_master">
|
||||||
|
Описание
|
||||||
|
<span class="aui-icon icon-required"></span>
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="text" id="description_master" name="description_master" value="{$form->languages['master']->description}" />
|
||||||
|
<div class="description">Внутреннее описание набора, которое будет использоваться, если не удаётся найти название на языке пользователя.</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<h3>Языко-зависимые настройки</h3>
|
||||||
|
<fieldset>
|
||||||
|
{foreach $form->languages as $locale => $data}
|
||||||
|
{continueIf $locale === "master"}
|
||||||
|
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="name_{$locale}">
|
||||||
|
Наименование
|
||||||
|
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="text" id="name_{$locale}" name="name_{$locale}" value="{$data->name}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="description_{$locale}">
|
||||||
|
Описание
|
||||||
|
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="text" id="description_{$locale}" name="description_{$locale}" value="{$data->description}" />
|
||||||
|
</div>
|
||||||
|
{/foreach}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="aui-button aui-button-primary submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{/block}
|
82
Web/Presenters/templates/Admin/Gifts.xml
Normal file
82
Web/Presenters/templates/Admin/Gifts.xml
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{$cat->getName()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block headingWrap}
|
||||||
|
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/gifts/id0?act=edit&cat={$cat->getId()}">
|
||||||
|
{_create}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<h1>Набор "{$cat->getName()}"</h1>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{if sizeof($gifts) > 0}
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<th>Подарок</th>
|
||||||
|
<th>Имя</th>
|
||||||
|
<th>Цена</th>
|
||||||
|
<th>Подарен</th>
|
||||||
|
<th>Ограничение</th>
|
||||||
|
<th>Сброс счётчика ограничений</th>
|
||||||
|
<th>Действия</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$gifts as $gift">
|
||||||
|
<td style="vertical-align: middle; width: 0px;">
|
||||||
|
<img style="max-width: 32px;" src="{$gift->getImage(2)}" alt="{$gift->getName()}" />
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
{$gift->getName()}
|
||||||
|
<span n:if="$gift->isFree()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">
|
||||||
|
бесплатный
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
{$gift->getPrice()} голосов
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
{$gift->getUsages()} раз
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
{if $gift->getLimit() === INF}
|
||||||
|
Отсутствует
|
||||||
|
{else}
|
||||||
|
Не более {$gift->getLimit()} дарений
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle;">
|
||||||
|
{if !$gift->getLimitResetTime()}
|
||||||
|
Никогда
|
||||||
|
{else}
|
||||||
|
Последний раз в
|
||||||
|
{$gift->getLimitResetTime()->format("%a, %d %B %G")}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle; text-align: right;">
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/gifts/id{$gift->getId()}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{else}
|
||||||
|
<center>
|
||||||
|
<p>Подарков нету. Нажмите на красивую кнопку вверху, чтобы создать первый.</p>
|
||||||
|
</center>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div align="right">
|
||||||
|
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count}
|
||||||
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
||||||
|
⭁ туда
|
||||||
|
</a>
|
||||||
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
||||||
|
⭇ сюда
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/block}
|
92
Web/Presenters/templates/Admin/Voucher.xml
Normal file
92
Web/Presenters/templates/Admin/Voucher.xml
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_edit}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{_edit} №{$form->token ?? "undefined"}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div style="margin: 8px -8px;" class="aui-tabs horizontal-tabs">
|
||||||
|
<ul class="tabs-menu">
|
||||||
|
<li class="menu-item active-tab">
|
||||||
|
<a href="#info">Информация</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a href="#activators">{_voucher_activators}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tabs-pane active-pane" id="info">
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">
|
||||||
|
ID
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="token">
|
||||||
|
Серийный номер
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="text" id="token" name="token" value="{$form->token}" />
|
||||||
|
<div class="description">Номер состоит из 24 символов, если формат неправильный или поле не заполнено, будет назначен автоматически.</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="coins">
|
||||||
|
Количество голосов
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" min="0" id="coins" name="coins" value="{$form->coins}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="rating">
|
||||||
|
Количество рейтинга
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" min="0" id="rating" name="rating" value="{$form->rating}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="usages">
|
||||||
|
{if $form->id === 0}
|
||||||
|
{_usages_total}
|
||||||
|
{else}
|
||||||
|
{_usages_left}
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
|
<input class="text long-field" type="number" min="-1" id="usages" name="usages" value="{$form->usages}" />
|
||||||
|
<div class="description">Количество аккаунтов, которые могут использовать ваучер. Если написать -1, будет Infinity.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="aui-button aui-button-primary submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs-pane" id="activators">
|
||||||
|
<table rules="none" class="aui aui-table-list">
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$form->users as $user">
|
||||||
|
<td>
|
||||||
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$user->getAvatarUrl()}" alt="{$user->getCanonicalName()}" role="presentation" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||||
|
|
||||||
|
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
||||||
|
заблокирован
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
61
Web/Presenters/templates/Admin/Vouchers.xml
Normal file
61
Web/Presenters/templates/Admin/Vouchers.xml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_vouchers}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block headingWrap}
|
||||||
|
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/vouchers/id0">
|
||||||
|
{_create}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<h1>{_vouchers}</h1>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Серийный номер</th>
|
||||||
|
<th>Голоса</th>
|
||||||
|
<th>Рейгтинг</th>
|
||||||
|
<th>Осталось использований</th>
|
||||||
|
<th>Состояние</th>
|
||||||
|
<th>Действия</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$vouchers as $voucher">
|
||||||
|
<td>{$voucher->getId()}</td>
|
||||||
|
<td>{$voucher->getFormattedToken()}</td>
|
||||||
|
<td>{$voucher->getCoins()}¢</td>
|
||||||
|
<td>{$voucher->getRating()}</td>
|
||||||
|
<td>{$voucher->getRemainingUsages() === INF ? "∞" : $voucher->getRemainingUsages()}</td>
|
||||||
|
<td>
|
||||||
|
{if $voucher->isExpired()}
|
||||||
|
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">закончился</span>
|
||||||
|
{else}
|
||||||
|
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">активен</span>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/vouchers/id{$voucher->getId()}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div align="right">
|
||||||
|
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count}
|
||||||
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
||||||
|
⭁ туда
|
||||||
|
</a>
|
||||||
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
||||||
|
⭇ сюда
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/block}
|
|
@ -17,6 +17,11 @@
|
||||||
<label for="password">Новый пароль: </label>
|
<label for="password">Новый пароль: </label>
|
||||||
<input id="password" type="password" name="password" required />
|
<input id="password" type="password" name="password" required />
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
{if $is2faEnabled}
|
||||||
|
<label for="code">Код двухфакторной аутентификации: </label>
|
||||||
|
<input id="code" type="text" name="code" required />
|
||||||
|
<br/><br/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<input type="submit" value="Сбросить пароль" class="button" style="float: right;" />
|
<input type="submit" value="Сбросить пароль" class="button" style="float: right;" />
|
||||||
|
|
38
Web/Presenters/templates/Auth/LoginSecondFactor.xml
Normal file
38
Web/Presenters/templates/Auth/LoginSecondFactor.xml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
{block title}{_"log_in"}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_"log_in"}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<p>
|
||||||
|
{_"two_factor_authentication_login"}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span>{_code}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="code" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="login" value="{$login}" />
|
||||||
|
<input type="hidden" name="password" value="{$password}" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_'log_in'}" class="button" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
{/block}
|
|
@ -24,7 +24,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
<table cellspacing="7" cellpadding="0" width="52%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
@ -54,6 +54,14 @@
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span>{_"birth_date"}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input max={date('Y-m-d')} name="birthday" type="date"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr></tr>
|
<tr></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
30
Web/Presenters/templates/Gifts/Confirm.xml
Normal file
30
Web/Presenters/templates/Gifts/Confirm.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_send_gift}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a> »
|
||||||
|
<a href="/gifts?act=pick&user={$user->getId()}">{_gift_select}</a> »
|
||||||
|
<a href="/gifts?act=pick&user={$user->getId()}">{_collections}</a> »
|
||||||
|
<a href="/gifts?act=menu&user={$user->getId()}&pack={$cat->getId()}">{$cat->getName(tr("__lang"))}</a> »
|
||||||
|
{_confirm}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<center>
|
||||||
|
<img class="gift_confirm_pic" style="max-width: 256px;" src="{$gift->getImage(2)}" alt="Подарок" />
|
||||||
|
|
||||||
|
<form style="width: 65%;" method="POST">
|
||||||
|
<textarea name="comment" style="resize: vertical; height: 65px;" placeholder="{_gift_your_message}"></textarea>
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_send}" class="button" />
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="anonymous"> {_as_anonymous}
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
</center>
|
||||||
|
{/block}
|
29
Web/Presenters/templates/Gifts/Menu.xml
Normal file
29
Web/Presenters/templates/Gifts/Menu.xml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{extends "../@listView.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_gift_select}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a> »
|
||||||
|
<a href="/gifts?act=pick&user={$user->getId()}">{_gift_select}</a> »
|
||||||
|
{_collections}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
|
{block link|strip|stripHtml}
|
||||||
|
/gifts?act=menu&user={$user->getId()}&pack={$x->getId()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block preview}
|
||||||
|
<img src="{$x->getThumbnailURL()}" width="75" alt="{$x->getName(tr('__lang'))}" />
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block name}
|
||||||
|
{$x->getName(tr("__lang"))}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block description}
|
||||||
|
{$x->getDescription(tr("__lang"))}
|
||||||
|
{/block}
|
58
Web/Presenters/templates/Gifts/Pick.xml
Normal file
58
Web/Presenters/templates/Gifts/Pick.xml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_gift_select}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a> »
|
||||||
|
<a href="/gifts?act=pick&user={$user->getId()}">{_gift_select}</a> »
|
||||||
|
<a href="/gifts?act=pick&user={$user->getId()}">{_collections}</a> »
|
||||||
|
{$cat->getName(tr("__lang"))}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="gift_grid">
|
||||||
|
<div n:foreach="$gifts as $gift" n:class="gift_sel, !$gift->canUse($thisUser) ? disabled" data-gift="{$gift->getId()}">
|
||||||
|
<img class="gift_pic" src="{$gift->getImage(2)}" alt="{_gift}" />
|
||||||
|
|
||||||
|
<strong class="gift_price">
|
||||||
|
{if $gift->isFree()}
|
||||||
|
{_free_gift}
|
||||||
|
{else}
|
||||||
|
{tr('coins', $gift->getPrice())}
|
||||||
|
{/if}
|
||||||
|
</strong>
|
||||||
|
|
||||||
|
<strong class="gift_limit">
|
||||||
|
{if $gift->getUsagesLeft($thisUser) !== INF}
|
||||||
|
{tr("gifts_left", $gift->getUsagesLeft($thisUser))}
|
||||||
|
{/if}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="padding: 8px;">
|
||||||
|
{include "../components/paginator.xml", conf => (object) [
|
||||||
|
"page" => $page,
|
||||||
|
"count" => $count,
|
||||||
|
"amount" => sizeof($gifts),
|
||||||
|
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
]}
|
||||||
|
</div>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block bodyScripts}
|
||||||
|
<script>
|
||||||
|
$(".gift_sel").click(function() {
|
||||||
|
let el = $(this);
|
||||||
|
if(el.hasClass("disabled"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let link = "/gifts?act=confirm&user={$user->getId()}&pack={$cat->getId()}&elid=";
|
||||||
|
let gift = el.data("gift");
|
||||||
|
|
||||||
|
window.location.assign(link + gift);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{/block}
|
43
Web/Presenters/templates/Gifts/UserGifts.xml
Normal file
43
Web/Presenters/templates/Gifts/UserGifts.xml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{extends "../@listView.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{tr("users_gifts", $user->getFirstName())}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a> »
|
||||||
|
{_gifts}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
|
{block link|strip|stripHtml}
|
||||||
|
javascript:false
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block preview}
|
||||||
|
<img src="{$x->gift->getImage(2)}" width="75" alt="{_gift}" />
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block name}
|
||||||
|
{_gift}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block description}
|
||||||
|
<table class="ugc-table" n:if="$hideInfo ? !$x->anon : true">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_sender}: </span></td>
|
||||||
|
<td>
|
||||||
|
<a href="{$x->sender->getURL()}">
|
||||||
|
{$x->sender->getFullName()}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr n:if="!empty($x->caption)">
|
||||||
|
<td><span class="nobold">{_comment}: </span></td>
|
||||||
|
<td>{$x->caption}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/block}
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="container_gray">
|
<div class="container_gray">
|
||||||
<h4>{_main_information}</h4>
|
<h4>{_main_information}</h4>
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top">
|
<td width="120" valign="top">
|
||||||
|
@ -53,6 +53,14 @@
|
||||||
<input type="text" name="shortcode" value="{$club->getShortcode()}" />
|
<input type="text" name="shortcode" value="{$club->getShortcode()}" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_website}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="website" value="{$club->getWebsite()}" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top">
|
<td width="120" valign="top">
|
||||||
<span class="nobold">{_avatar}: </span>
|
<span class="nobold">{_avatar}: </span>
|
||||||
|
@ -69,6 +77,17 @@
|
||||||
<input type="checkbox" name="wall" value="1" {if $club->canPost()}checked{/if}/> {_group_allow_post_for_everyone}
|
<input type="checkbox" name="wall" value="1" {if $club->canPost()}checked{/if}/> {_group_allow_post_for_everyone}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_group_administrators_list}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{var areAllAdminsHidden = $club->getManagersCount(true) == 0}
|
||||||
|
<input type="radio" name="administrators_list_display" value="0" n:attr="checked => $club->getAdministratorsListDisplay() == 0, disabled => $areAllAdminsHidden" /> {_group_display_only_creator}<br>
|
||||||
|
<input type="radio" name="administrators_list_display" value="1" n:attr="checked => $club->getAdministratorsListDisplay() == 1, disabled => $areAllAdminsHidden" /> {_group_display_all_administrators}<br>
|
||||||
|
<input type="radio" name="administrators_list_display" value="2" n:attr="checked => $club->getAdministratorsListDisplay() == 2" /> {_group_dont_display_administrators_list}<br>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = $followers}
|
{var $Manager = openvk\Web\Models\Entities\Manager::class}
|
||||||
|
{var iterator = $onlyShowManagers ? $managers : $followers}
|
||||||
{var count = $paginatorConf->count}
|
{var count = $paginatorConf->count}
|
||||||
{var page = $paginatorConf->page}
|
{var page = $paginatorConf->page}
|
||||||
{var perPage = 6}
|
{var perPage = 6}
|
||||||
|
@ -9,6 +10,8 @@
|
||||||
{block header}
|
{block header}
|
||||||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||||
» {_followers}
|
» {_followers}
|
||||||
|
<a n:if="!$onlyShowManagers" href="/club{$club->getId()}/followers?onlyAdmins=1" style="float: right;">{_all_followers}</a>
|
||||||
|
<a n:if="$onlyShowManagers" href="/club{$club->getId()}/followers" style="float: right;">{_only_administrators}</a>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block actions}
|
{block actions}
|
||||||
|
@ -17,45 +20,98 @@
|
||||||
|
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
|
{block tabs}
|
||||||
|
{if $club->canBeModifiedBy($thisUser)}
|
||||||
|
<div class="tab">
|
||||||
|
<a href="/club{$club->getId()}/edit">
|
||||||
|
{_main}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div id="activetabs" class="tab">
|
||||||
|
<a id="act_tab_a" href="/club{$club->getId()}/followers">
|
||||||
|
{_followers}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="tab">
|
||||||
|
<a href="/club{$club->getId()}/stats">
|
||||||
|
{_statistics}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
{block link|strip|stripHtml}
|
{block link|strip|stripHtml}
|
||||||
/id{$x->getId()}
|
/id{$x instanceof $Manager ? $x->getUserId() : $x->getId()}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x->getAvatarURL()}" alt="{$x->getCanonicalName()}" width=75 />
|
<img src="{$x instanceof $Manager ? $x->getUser()->getAvatarURL() : $x->getAvatarURL()}" alt="{$x instanceof $Manager ? $x->getUser()->getCanonicalName() : $x->getCanonicalName()}" width=75 />
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
{$x->getCanonicalName()}
|
{$x instanceof $Manager ? $x->getUser()->getCanonicalName() : $x->getCanonicalName()}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block description}
|
{block description}
|
||||||
|
{var user = $x instanceof $Manager ? $x->getUser() : $x}
|
||||||
|
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top"><span class="nobold">{_"gender"}: </span></td>
|
<td width="120" valign="top"><span class="nobold">{_"gender"}: </span></td>
|
||||||
<td>{$x->isFemale() ? "женский" : "мужской"}</td>
|
<td>{$user->isFemale() ? "женский" : "мужской"}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top"><span class="nobold">{_"registration_date"}: </span></td>
|
<td width="120" valign="top"><span class="nobold">{_"registration_date"}: </span></td>
|
||||||
<td>{$x->getRegistrationTime()}</td>
|
<td>{$user->getRegistrationTime()}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top"><span class="nobold">{_role}: </span></td>
|
<td width="120" valign="top"><span class="nobold">{_role}: </span></td>
|
||||||
<td>
|
<td>
|
||||||
{$club->canBeModifiedBy($x) ? tr("administrator") : tr("follower")}
|
{$club->getOwner()->getId() == $user->getId() ? !$club->isOwnerHidden() || $club->canBeModifiedBy($thisUser) : !is_null($manager) ? tr("administrator") : tr("follower")}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr n:if="$club->canBeModifiedBy($thisUser ?? NULL) && $club->getOwner()->getId() !== $x->getId()">
|
<tr n:if="$manager && !empty($manager->getComment()) || $club->getOwner()->getId() === $user->getId() && !empty($club->getOwnerComment()) && (!$club->isOwnerHidden() || $club->canBeModifiedBy($thisUser))">
|
||||||
|
<td width="120" valign="top"><span class="nobold">{_comment}: </span></td>
|
||||||
|
<td>
|
||||||
|
{if $club->getOwner()->getId() === $user->getId()}
|
||||||
|
{$club->getOwnerComment()}
|
||||||
|
{else}
|
||||||
|
{$manager->getComment()}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr n:if="$club->canBeModifiedBy($thisUser ?? NULL)">
|
||||||
<td width="120" valign="top"><span class="nobold">{_actions}: </span></td>
|
<td width="120" valign="top"><span class="nobold">{_actions}: </span></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/club{$club->getId()}/setAdmin.jsp?user={$x->getId()}&hash={rawurlencode($csrfToken)}">
|
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
|
||||||
{if $club->canBeModifiedBy($x)}
|
{if $manager}
|
||||||
{_devote}
|
{_devote}
|
||||||
{else}
|
{else}
|
||||||
{_promote_to_admin}
|
{_promote_to_admin}
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
|
{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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
{extends "../@layout.xml"}
|
{extends "../@layout.xml"}
|
||||||
{block title}Статистика группы{/block}
|
{block title}{$club->getName()} » {_statistics}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
<a href="{$club->getURL()}">{$club->getName()}</a> » Статистика
|
<a href="{$club->getURL()}">{$club->getName()}</a> » {_statistics}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<a href="/club{$club->getId()}/edit">
|
<a href="/club{$club->getId()}/edit">
|
||||||
Настройки
|
{_main}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="activetabs" class="tab">
|
<div id="activetabs" class="tab">
|
||||||
<a id="act_tab_a" href="javascript:void(0)">
|
<a id="act_tab_a" href="javascript:void(0)">
|
||||||
Статистика
|
{_statistics}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,6 +29,14 @@
|
||||||
<td><span class="nobold">{_"description"}:</span></td>
|
<td><span class="nobold">{_"description"}:</span></td>
|
||||||
<td>{$club->getDescription()}</td>
|
<td>{$club->getDescription()}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr n:if="!is_null($club->getWebsite())">
|
||||||
|
<td><span class="nobold">{_"website"}: </span></td>
|
||||||
|
<td>
|
||||||
|
<a href="{$club->getWebsite()}" rel="ugc" target="_blank">
|
||||||
|
{$club->getWebsite()}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -105,17 +113,66 @@
|
||||||
{_"group_type_open"}
|
{_"group_type_open"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div n:if="$club->getAdministratorsListDisplay() == 0">
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this);">
|
<div class="content_title_expanded" onclick="hidePanel(this);">
|
||||||
{_"creator"}
|
{_"creator"}
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:4px">
|
<div class="avatar-list-item" style="padding: 8px;">
|
||||||
{var author = $club->getOwner()}
|
{var author = $club->getOwner()}
|
||||||
<ul>
|
<div class="avatar">
|
||||||
<li>
|
<a href="{$author->getURL()}">
|
||||||
<a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a>
|
<img class="ava" src="{$author->getAvatarUrl()}" />
|
||||||
</li>
|
</a>
|
||||||
</ul>
|
</div>
|
||||||
|
{* Это наверное костыль, ну да ладно *}
|
||||||
|
<div n:class="info, mb_strlen($author->getCanonicalName()) < 22 ? info-centered" n:if="empty($club->getOwnerComment())">
|
||||||
|
<a href="{$author->getURL()}" class="title">{$author->getCanonicalName()}</a>
|
||||||
|
</div>
|
||||||
|
<div class="info" n:if="!empty($club->getOwnerComment())">
|
||||||
|
<a href="{$author->getURL()}" class="title">{$author->getCanonicalName()}</a>
|
||||||
|
<div class="subtitle">{$club->getOwnerComment()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div n:if="$club->getAdministratorsListDisplay() == 1">
|
||||||
|
{var managersCount = $club->getManagersCount(true)}
|
||||||
|
|
||||||
|
<div class="content_title_expanded" onclick="hidePanel(this, {$managersCount});">
|
||||||
|
{_"administrators"}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="content_subtitle">
|
||||||
|
{tr("administrators", $managersCount)}
|
||||||
|
<div style="float: right;">
|
||||||
|
<a href="/club{$club->getId()}/followers?onlyAdmins=1">{_"all_title"}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar-list">
|
||||||
|
<div class="avatar-list-item" n:if="!$club->isOwnerHidden()">
|
||||||
|
{var author = $club->getOwner()}
|
||||||
|
<div class="avatar">
|
||||||
|
<a href="{$author->getURL()}">
|
||||||
|
<img class="ava" src="{$author->getAvatarUrl()}" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<a href="{$author->getURL()}" class="title">{$author->getCanonicalName()}</a>
|
||||||
|
<div class="subtitle" n:if="!empty($club->getOwnerComment())">{$club->getOwnerComment()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar-list-item" n:foreach="$club->getManagers(1, true) as $manager">
|
||||||
|
{var user = $manager->getUser()}
|
||||||
|
<div class="avatar">
|
||||||
|
<a href="{$user->getURL()}">
|
||||||
|
<img height="32" class="ava" src="{$user->getAvatarUrl()}" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<a href="{$user->getURL()}" class="title">{$user->getCanonicalName()}</a>
|
||||||
|
<div class="subtitle" n:if="!empty($manager->getComment())">{$manager->getComment()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$albumsCount > 0">
|
<div n:if="$albumsCount > 0">
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
{block link|strip|stripHtml}
|
{block link|strip|stripHtml}
|
||||||
/note{$x->getOwner()->getId()}_{$x->getId()}
|
/note{$x->getPrettyId()}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{extends "../@layout.xml"}
|
{extends "../@layout.xml"}
|
||||||
{block title}Помощь{/block}
|
{block title}{_menu_help}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
{$ticket->getName()}
|
{$ticket->getName()}
|
||||||
|
@ -12,15 +12,15 @@
|
||||||
{$ticket->getName()}
|
{$ticket->getName()}
|
||||||
</b>
|
</b>
|
||||||
</a>
|
</a>
|
||||||
<br></b>Автор: <a href="/id{$ticket->getUser()->getId()}">{$ticket->getUser()->getFullName()}</a> | {$ticket->getUser()->getRegistrationIP()} | Статус: {$ticket->getStatus()}
|
<br></b>{_author}: <a href="/id{$ticket->getUser()->getId()}">{$ticket->getUser()->getFullName()}</a> | {$ticket->getUser()->getRegistrationIP()} | {_status}: {$ticket->getStatus()}.
|
||||||
</div>
|
</div>
|
||||||
<div class="text" style="padding-top: 10px;border-bottom: #ECECEC solid 1px;">
|
<div class="text" style="padding-top: 10px;border-bottom: #ECECEC solid 1px;">
|
||||||
{$ticket->getContext()}
|
{$ticket->getText()|noescape}
|
||||||
<br></br>
|
<br></br>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding-top: 5px;">
|
<div style="padding-top: 5px;">
|
||||||
{$ticket->getTime()} |
|
{$ticket->getTime()} |
|
||||||
<a href="/support/delete/{$id}?hash={$csrfToken}">Удалить</a>
|
<a href="/support/delete/{$id}?hash={$csrfToken}">{_delete}</a>
|
||||||
</div><br/>
|
</div><br/>
|
||||||
<div>
|
<div>
|
||||||
<form action="/al_comments.pl/create/support/reply/{$id}" method="post" style="margin:0;">
|
<form action="/al_comments.pl/create/support/reply/{$id}" method="post" style="margin:0;">
|
||||||
|
@ -30,16 +30,17 @@
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<br>
|
<br>
|
||||||
<input type="submit" value="Ответить" class="button">
|
<input type="submit" value="{_write}" class="button">
|
||||||
<select name="status" style="width: unset;">
|
<select name="status" style="width: unset;">
|
||||||
<option value="1">Есть ответ</option>
|
<option value="1">{_support_status_1}</option>
|
||||||
<option value="2">Закрыто</option>
|
<option value="2">{_support_status_2}</option>
|
||||||
<option value="0">Вопрос на рассмотрении</option>
|
<option value="0">{_support_status_0}</option>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<p n:if="!$comments">Комментарии отсутствуют</p>
|
<p n:if="!$comments">{_no_comments}</p>
|
||||||
|
{var $printedSupportGreeting = false}
|
||||||
<table n:foreach="$comments as $comment" border="0" style="font-size: 11px;" class="post">
|
<table n:foreach="$comments as $comment" border="0" style="font-size: 11px;" class="post">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -50,7 +51,7 @@
|
||||||
{else}
|
{else}
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
<img
|
<img
|
||||||
src="https://avatars.mds.yandex.net/get-zen_doc/164147/pub_5b60816e2dc67000a862253e_5b6081d4e9151400a9f48625/scale_1200"
|
src="{$comment->getAvatar()}"
|
||||||
style="max-width: 50px; filter: hue-rotate({$comment->getColorRotation()}deg);" />
|
style="max-width: 50px; filter: hue-rotate({$comment->getColorRotation()}deg);" />
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -59,14 +60,14 @@
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
<a href="{$comment->getUser()->getURL()}"><b>
|
<a href="{$comment->getUser()->getURL()}"><b>
|
||||||
{$comment->getUser()->getFullName()}
|
{$comment->getUser()->getFullName()}
|
||||||
</b></a> написал<br>
|
</b></a> {($comment->getUser()->isFemale() ? tr("post_writes_f") : tr("post_writes_m"))}<br>
|
||||||
<a href="#" class="date">{$comment->getTime()}</a>
|
<a href="#" class="date">{$comment->getTime()}</a>
|
||||||
</div>
|
</div>
|
||||||
{elseif ($comment->getUType() === 1)}
|
{elseif ($comment->getUType() === 1)}
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
<a href="javascript:false">
|
<a href="javascript:false">
|
||||||
<b>
|
<b>
|
||||||
{=OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["supportName"]} №{$comment->getAgentNumber()}
|
{$comment->getAuthorName()}
|
||||||
</b>
|
</b>
|
||||||
</a>
|
</a>
|
||||||
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
|
@ -76,17 +77,31 @@
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
написал<br>
|
{_post_writes_m}<br>
|
||||||
<a href="#" class="date">{$comment->getTime()}</a>
|
<a href="#" class="date">{$comment->getTime()}</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="post-content" id="{$comment->getId()}">
|
<div class="post-content" id="{$comment->getId()}">
|
||||||
<div class="text" id="text{$comment->getId()}">
|
<div class="text" id="text{$comment->getId()}">
|
||||||
{$comment->getContext()|noescape}
|
{if $comment->getUType() === 1 && !$printedSupportGreeting}
|
||||||
|
{var $printedSupportGreeting = true}
|
||||||
|
{tr("support_greeting_hi", $ticket->getUser()->getFullName())}
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
{$comment->getText()|noescape}
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
{tr("support_greeting_regards", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"])|noescape}
|
||||||
|
{else}
|
||||||
|
{$comment->getText()|noescape}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{if $comment->getUType() === 0}
|
{if $comment->getUType() === 0}
|
||||||
<div class="post-menu">
|
<div class="post-menu">
|
||||||
<a href="/support/comment/{$comment->getId()}/delete">Удалить</a>
|
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{extends "../@layout.xml"}
|
{extends "../@layout.xml"}
|
||||||
{block title}Помощь{/block}
|
{block title}{_menu_help}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
Помощь
|
{_menu_help}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
|
@ -14,13 +14,13 @@
|
||||||
{if $thisUser}
|
{if $thisUser}
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isMain ? 'act_tab_a' : 'ki')" href="/support">Часто задаваемые вопросы</a>
|
<a n:attr="id => ($isMain ? 'act_tab_a' : 'ki')" href="/support">{_support_faq}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($isList ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isList ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isList ? 'act_tab_a' : 'ki')" href="/support?act=list">Список обращений</a>
|
<a n:attr="id => ($isList ? 'act_tab_a' : 'ki')" href="/support?act=list">{_support_list}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($isNew ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isNew ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isNew ? 'act_tab_a' : 'ki')" href="/support?act=new">Новое обращение</a>
|
<a n:attr="id => ($isNew ? 'act_tab_a' : 'ki')" href="/support?act=new">{_support_new}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
@ -28,19 +28,19 @@
|
||||||
{if $isNew}
|
{if $isNew}
|
||||||
<div class="new">
|
<div class="new">
|
||||||
<form action="/support" method="post" style="margin:0;">
|
<form action="/support" method="post" style="margin:0;">
|
||||||
<center><input name="name" style="width: 80%;resize: vertical;" placeholder="Введите тему вашего обращения"></center><br>
|
<center><input name="name" style="width: 80%;resize: vertical;" placeholder="{_support_new_title}"></center><br>
|
||||||
<center><textarea name="text" style="width: 80%;resize: vertical;" placeholder="Опишите проблему или предложение"></textarea></center><br>
|
<center><textarea name="text" style="width: 80%;resize: vertical;" placeholder="{_support_new_content}"></textarea></center><br>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<center><input type="submit" value="Написать" class="button" style="margin-left:70%;"></center><br>
|
<center><input type="submit" value="{_write}" class="button" style="margin-left:70%;"></center><br>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{/if}{/if}
|
{/if}{/if}
|
||||||
|
|
||||||
{if $isMain}
|
{if $isMain}
|
||||||
<h4>Часто задаваемые вопросы</h4><br>
|
<h4>{_support_faq}</h4><br>
|
||||||
<div class="faq">
|
<div class="faq">
|
||||||
<div id="faqhead">Для кого этот сайт?</div>
|
<div id="faqhead">{_support_faq_title}</div>
|
||||||
<div id="faqcontent">Сайт предназначен для поиска друзей и знакомых, а также просмотр данных пользователя. Это как справочник города, с помощью которого люди могут быстро найти актуальную информацию о человеке. Также этот сайт подойдёт для ностальгираторов и тех, кто решил слезть с трубы "ВКонтакте", которого клон и является.<br></div>
|
<div id="faqcontent">{_support_faq_content}</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
<center><img src="/assets/packages/static/openvk/img/note_icon.png" alt="Заметка" style="margin-top: 17px;"></center>
|
<center><img src="/assets/packages/static/openvk/img/note_icon.png" alt="{_support_ticket}" style="margin-top: 17px;"></center>
|
||||||
</td>
|
</td>
|
||||||
<td width="345" valign="top">
|
<td width="345" valign="top">
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="post-content" style="padding: 4px;font-size: 11px;">
|
<div class="post-content" style="padding: 4px;font-size: 11px;">
|
||||||
Статус: {$ticket->getStatus()}
|
{_status}: {$ticket->getStatus()}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -5,18 +5,18 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
Helpdesk » Тикеты
|
Helpdesk » {_support_tickets}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block tabs}
|
{block tabs}
|
||||||
<div n:attr="id => ($act === 'open' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'open' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'open' ? 'act_tab_a' : 'ki')" href="?">Открытые</a>
|
<a n:attr="id => ($act === 'open' ? 'act_tab_a' : 'ki')" href="?">{_support_opened}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($act === 'answered' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'answered' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'answered' ? 'act_tab_a' : 'ki')" href="?act=answered">С ответом</a>
|
<a n:attr="id => ($act === 'answered' ? 'act_tab_a' : 'ki')" href="?act=answered">{_support_answered}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($act === 'closed' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'closed' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'closed' ? 'act_tab_a' : 'ki')" href="?act=closed">Закрытые</a>
|
<a n:attr="id => ($act === 'closed' ? 'act_tab_a' : 'ki')" href="?act=closed">{_support_closed}</a>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<center>
|
<center>
|
||||||
<img src="/assets/packages/static/openvk/img/note_icon.png" alt="Тикет" style="margin-top: 8px;">
|
<img src="/assets/packages/static/openvk/img/note_icon.png" alt="{_support_ticket}" style="margin-top: 8px;">
|
||||||
</center>
|
</center>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -40,5 +40,5 @@
|
||||||
{var author = $x->getUser()}
|
{var author = $x->getUser()}
|
||||||
|
|
||||||
{ovk_proc_strtr($x->getContext(), 50)}<br/>
|
{ovk_proc_strtr($x->getContext(), 50)}<br/>
|
||||||
<span class="nobold">Автор: </span> <a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
<span class="nobold">{_author}: </span> <a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{extends "../@layout.xml"}
|
{extends "../@layout.xml"}
|
||||||
{block title}Помощь{/block}
|
{block title}{_menu_help}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
{$ticket->getName()}
|
{$ticket->getName()}
|
||||||
|
@ -13,15 +13,15 @@
|
||||||
{$ticket->getName()}
|
{$ticket->getName()}
|
||||||
</b>
|
</b>
|
||||||
</a>
|
</a>
|
||||||
<br></b>Статус: {$ticket->getStatus()}
|
<br></b>{_status}: {$ticket->getStatus()}
|
||||||
</div>
|
</div>
|
||||||
<div class="text" style="padding-top: 10px;border-bottom: #ECECEC solid 1px;">
|
<div class="text" style="padding-top: 10px;border-bottom: #ECECEC solid 1px;">
|
||||||
{$ticket->getContext()}
|
{$ticket->getText()|noescape}
|
||||||
<br></br>
|
<br></br>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding-top: 5px;">
|
<div style="padding-top: 5px;">
|
||||||
{$ticket->getTime()} |
|
{$ticket->getTime()} |
|
||||||
<a href="/support/delete/{$id}?hash={$csrfToken}">Удалить</a>
|
<a href="/support/delete/{$id}?hash={$csrfToken}">{_delete}</a>
|
||||||
</div>
|
</div>
|
||||||
{if $ticket->getType() !== 2}
|
{if $ticket->getType() !== 2}
|
||||||
<br>
|
<br>
|
||||||
|
@ -33,12 +33,13 @@
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<br>
|
<br>
|
||||||
<input type="submit" value="Написать" class="button">
|
<input type="submit" value="{_write}" class="button">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</br>
|
</br>
|
||||||
<p n:if="!$comments">Комментарии отсутствуют</p>
|
<p n:if="!$comments">{_no_comments}</p>
|
||||||
|
{var $printedSupportGreeting = false}
|
||||||
<table n:foreach="$comments as $comment" border="0" style="font-size: 11px;" class="post">
|
<table n:foreach="$comments as $comment" border="0" style="font-size: 11px;" class="post">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -49,7 +50,7 @@
|
||||||
{else}
|
{else}
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
<img
|
<img
|
||||||
src="https://avatars.mds.yandex.net/get-zen_doc/164147/pub_5b60816e2dc67000a862253e_5b6081d4e9151400a9f48625/scale_1200"
|
src="{$comment->getAvatar()}"
|
||||||
style="max-width: 50px; filter: hue-rotate({$comment->getColorRotation()}deg);" />
|
style="max-width: 50px; filter: hue-rotate({$comment->getColorRotation()}deg);" />
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -58,24 +59,40 @@
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
<a href="{$comment->getUser()->getURL()}"><b>
|
<a href="{$comment->getUser()->getURL()}"><b>
|
||||||
{$comment->getUser()->getFullName()}
|
{$comment->getUser()->getFullName()}
|
||||||
</b></a> написал<br>
|
</b></a> {($comment->getUser()->isFemale() ? tr("post_writes_f") : tr("post_writes_m"))}<br>
|
||||||
<a href="#" class="date">{$comment->getTime()}</a>
|
<a href="#" class="date">{$comment->getTime()}</a>
|
||||||
</div>
|
</div>
|
||||||
{elseif ($comment->getUType() === 1)}
|
{elseif ($comment->getUType() === 1)}
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
<a href="#"><b>
|
<a href="javascript:false">
|
||||||
{=OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["supportName"]} №{$comment->getAgentNumber()}
|
<b>
|
||||||
</b></a> написал<br>
|
{$comment->getAuthorName()}
|
||||||
|
</b>
|
||||||
|
</a>
|
||||||
|
{_post_writes_m}<br>
|
||||||
<a href="#" class="date">{$comment->getTime()}</a>
|
<a href="#" class="date">{$comment->getTime()}</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="post-content" id="{$comment->getId()}">
|
<div class="post-content" id="{$comment->getId()}">
|
||||||
<div class="text" id="text{$comment->getId()}">
|
<div class="text" id="text{$comment->getId()}">
|
||||||
{$comment->getContext()|noescape}
|
{if $comment->getUType() === 1 && !$printedSupportGreeting}
|
||||||
|
{var $printedSupportGreeting = true}
|
||||||
|
{tr("support_greeting_hi", $ticket->getUser()->getFullName())}
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
{$comment->getText()|noescape}
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
{tr("support_greeting_regards", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"])|noescape}
|
||||||
|
{else}
|
||||||
|
{$comment->getText()|noescape}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{if $comment->getUType() === 0}
|
{if $comment->getUType() === 0}
|
||||||
<div class="post-menu">
|
<div class="post-menu">
|
||||||
<a href="/support/comment/{$comment->getId()}/delete">Удалить</a>
|
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -173,6 +173,14 @@
|
||||||
<input type="text" name="telegram" value="{$user->getTelegram()}" />
|
<input type="text" name="telegram" value="{$user->getTelegram()}" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_"personal_website"}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="website" value="{$user->getWebsite()}" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top">
|
<td width="120" valign="top">
|
||||||
<span class="nobold">{_"city"}: </span>
|
<span class="nobold">{_"city"}: </span>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = $user->getClubs($page)}
|
{var iterator = $user->getClubs($page, $admin)}
|
||||||
{var count = $user->getClubCount()}
|
{var count = $user->getClubCount($admin)}
|
||||||
|
|
||||||
{block title}{_"groups"}{/block}
|
{block title}{_"groups"}{/block}
|
||||||
|
|
||||||
|
@ -18,14 +18,23 @@
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block actions}
|
|
||||||
<div class="tile">
|
|
||||||
<a href="javascript:alert('Не запилил')" class="profile_link">Поиск групп</a>
|
|
||||||
</div>
|
|
||||||
{/block}
|
|
||||||
|
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
|
{block tabs}
|
||||||
|
{if !is_null($thisUser) && $user->getId() === $thisUser->getId()}
|
||||||
|
<div {if !$admin}id="activetabs"{/if} class="tab">
|
||||||
|
<a {if !$admin}id="act_tab_a"{/if} href="/groups{$user->getId()}">
|
||||||
|
{_groups}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div {if $admin}id="activetabs"{/if} class="tab">
|
||||||
|
<a {if $admin}id="act_tab_a"{/if} href="/groups{$user->getId()}?act=managed">
|
||||||
|
{_managed}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
{block link|strip|stripHtml}
|
{block link|strip|stripHtml}
|
||||||
{$x->getURL()}
|
{$x->getURL()}
|
||||||
{/block}
|
{/block}
|
||||||
|
@ -40,4 +49,23 @@
|
||||||
|
|
||||||
{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}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
{var isMain = $mode === 'main'}
|
{var isMain = $mode === 'main'}
|
||||||
{var isPrivacy = $mode === 'privacy'}
|
{var isPrivacy = $mode === 'privacy'}
|
||||||
{var isFinance = $mode === 'finance'}
|
{var isFinance = $mode === 'finance'}
|
||||||
|
{var isFinanceTU = $mode === 'finance.top-up'}
|
||||||
{var isInterface = $mode === 'interface'}
|
{var isInterface = $mode === 'interface'}
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
|
@ -19,8 +20,8 @@
|
||||||
<div n:attr="id => ($isPrivacy ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isPrivacy ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isPrivacy ? 'act_tab_a' : 'ki')" href="/settings?act=privacy">{_"privacy"}</a>
|
<a n:attr="id => ($isPrivacy ? 'act_tab_a' : 'ki')" href="/settings?act=privacy">{_"privacy"}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($isFinance ? 'activetabs' : 'ki')" class="tab">
|
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce']" n:attr="id => (($isFinance || $isFinanceTU) ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isFinance ? 'act_tab_a' : 'ki')" href="/settings?act=finance">{_points}</a>
|
<a n:attr="id => (($isFinance || $isFinanceTU) ? 'act_tab_a' : 'ki')" href="/settings?act=finance">{_points}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($isInterface ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isInterface ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isInterface ? 'act_tab_a' : 'ki')" href="/settings?act=interface">{_"interface"}</a>
|
<a n:attr="id => ($isInterface ? 'act_tab_a' : 'ki')" href="/settings?act=interface">{_"interface"}</a>
|
||||||
|
@ -58,6 +59,14 @@
|
||||||
<input type="password" name="repeat_pass" style="width: 100%;" />
|
<input type="password" name="repeat_pass" style="width: 100%;" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr n:if="$user->is2faEnabled()">
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_"2fa_code"}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="code" style="width: 100%;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
@ -70,6 +79,70 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
|
<h4>{_two_factor_authentication}</h4>
|
||||||
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
{if $user->is2faEnabled()}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="accent-box">
|
||||||
|
{_two_factor_authentication_enabled}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<a class="button" href="javascript:viewBackupCodes()">{_view_backup_codes}</a>
|
||||||
|
<a class="button" href="javascript:disableTwoFactorAuth()">{_disable}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function viewBackupCodes() {
|
||||||
|
MessageBox("Просмотр резервных кодов", `
|
||||||
|
<form id="back-codes-view-form" method="post" action="/settings/2fa">
|
||||||
|
<label for="password">Пароль</label>
|
||||||
|
<input type="password" id="password" name="password" required />
|
||||||
|
<input type="hidden" name="hash" value={$csrfToken} />
|
||||||
|
</form>
|
||||||
|
`, ["Просмотреть", "Отменить"], [
|
||||||
|
() => {
|
||||||
|
document.querySelector("#back-codes-view-form").submit();
|
||||||
|
}, Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableTwoFactorAuth() {
|
||||||
|
MessageBox("Отключить 2FA", `
|
||||||
|
<form id="two-factor-auth-disable-form" method="post" action="/settings/2fa/disable">
|
||||||
|
<label for="password">Пароль</label>
|
||||||
|
<input type="password" id="password" name="password" required />
|
||||||
|
<input type="hidden" name="hash" value={$csrfToken} />
|
||||||
|
</form>
|
||||||
|
`, ["Отключить", "Отменить"], [
|
||||||
|
() => {
|
||||||
|
document.querySelector("#two-factor-auth-disable-form").submit();
|
||||||
|
}, Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{else}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="accent-box">
|
||||||
|
{_two_factor_authentication_disabled}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<a class="button" href="/settings/2fa">{_connect}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
<h4>{_your_email_address}</h4>
|
<h4>{_your_email_address}</h4>
|
||||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -265,11 +338,26 @@
|
||||||
<b>
|
<b>
|
||||||
{_on_your_account}<br/>
|
{_on_your_account}<br/>
|
||||||
<span style="font-size: 50px;">{$thisUser->getCoins()}</span><br/>
|
<span style="font-size: 50px;">{$thisUser->getCoins()}</span><br/>
|
||||||
{_points_count}
|
{_points_count}<br/><br/>
|
||||||
|
<small><a href="?act=finance.top-up">[{_have_voucher}?]</a></small>
|
||||||
</b>
|
</b>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{elseif $isFinanceTU}
|
||||||
|
|
||||||
|
<p>{_voucher_explanation} {_voucher_explanation_ex}</p>
|
||||||
|
<form action="/settings?act=finance.top-up" method="POST" enctype="multipart/form-data">
|
||||||
|
<input type="text" name="key0" size="6" placeholder="123456" required="required" style="display: inline-block; width: 50px; text-align: center;" /> -
|
||||||
|
<input type="text" name="key1" size="6" placeholder="789012" required="required" style="display: inline-block; width: 50px; text-align: center;" /> -
|
||||||
|
<input type="text" name="key2" size="6" placeholder="345678" required="required" style="display: inline-block; width: 50px; text-align: center;" /> -
|
||||||
|
<input type="text" name="key3" size="6" placeholder="90ABCD" required="required" style="display: inline-block; width: 50px; text-align: center;" />
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_redeem}" class="button" />
|
||||||
|
</form>
|
||||||
|
|
||||||
{elseif $isInterface}
|
{elseif $isInterface}
|
||||||
|
|
||||||
<h4>{_ui_settings_interface}</h4>
|
<h4>{_ui_settings_interface}</h4>
|
||||||
|
|
18
Web/Presenters/templates/User/TwoFactorAuthCodes.xml
Normal file
18
Web/Presenters/templates/User/TwoFactorAuthCodes.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
{block title}{_"my_settings"} - {_"two_factor_authentication"}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="/settings">{_"my_settings"}</a> » {_"two_factor_authentication"}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<h4>{_"backup_codes"}</h4>
|
||||||
|
<p>{_"two_factor_authentication_backup_codes_1"}</p>
|
||||||
|
<p>{_"two_factor_authentication_backup_codes_2"|noescape}</p>
|
||||||
|
|
||||||
|
<ol style="columns: 2; text-align: center;">
|
||||||
|
<li n:foreach="$codes as $code">{$code}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p>{_"two_factor_authentication_backup_codes_3"}</p>
|
||||||
|
{/block}
|
48
Web/Presenters/templates/User/TwoFactorAuthSettings.xml
Normal file
48
Web/Presenters/templates/User/TwoFactorAuthSettings.xml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
{block title}{_"my_settings"} - {_"two_factor_authentication"}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="/settings">{_"my_settings"}</a> » {_"two_factor_authentication"}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{_"two_factor_authentication_settings_1"|noescape}
|
||||||
|
<p>{_"two_factor_authentication_settings_2"}</p>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<img src="data:image/png;base64,{$qrCode}">
|
||||||
|
</div>
|
||||||
|
<p>{tr("two_factor_authentication_settings_3", $secret)|noescape}</p>
|
||||||
|
<p>{_"two_factor_authentication_settings_4"}</p>
|
||||||
|
<form method="POST">
|
||||||
|
<table cellspacing="7" cellpadding="0" width="40%" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span>{_code}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="code" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span>{_password}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" name="password" required />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="secret" value="{$secret}" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_enable}" class="button" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
{/block}
|
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
<!-- DEBUG: ONLINE REPORT: static {$user->getOnline()->timestamp()}s adjusted {$user->getOnline()->timestamp() + 2505600}s real {time()}s -->
|
<!-- DEBUG: ONLINE REPORT: static {$user->getOnline()->timestamp()}s adjusted {$user->getOnline()->timestamp() + 2505600}s real {time()}s -->
|
||||||
<div n:if="$user->getOnline()->timestamp() + 2505600 > time()" style="float:right;">
|
<div n:if="$user->getOnline()->timestamp() + 2505600 > time()" style="float:right;">
|
||||||
{if $diff->i <= 5}
|
{if $user->isOnline()}
|
||||||
<span><b>{_online}</b></span>
|
<span><b>{_online}</b></span>
|
||||||
{else}
|
{else}
|
||||||
<span>{_was_online} {$user->getOnline()}</span>
|
<span>{_was_online} {$user->getOnline()}</span>
|
||||||
|
@ -76,6 +76,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
{if $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||||
|
<a href="/admin/users/id{$user->getId()}" class="profile_link">
|
||||||
|
{_manage_user_action}
|
||||||
|
</a>
|
||||||
<a href="javascript:banUser()" class="profile_link">
|
<a href="javascript:banUser()" class="profile_link">
|
||||||
{_ban_user_action}
|
{_ban_user_action}
|
||||||
</a>
|
</a>
|
||||||
|
@ -84,6 +87,8 @@
|
||||||
</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>
|
||||||
|
|
||||||
{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">
|
||||||
|
@ -120,8 +125,8 @@
|
||||||
<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()}
|
||||||
|
|
||||||
<div class="completeness-gauge">
|
<div n:class="completeness-gauge, $completeness->total >= 100 ? completeness-gauge-gold">
|
||||||
<div style="width: {$completeness->total}%"></div>
|
<div style="width: {$completeness->percent}%"></div>
|
||||||
<span>{$completeness->total}%</span>
|
<span>{$completeness->total}%</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -150,6 +155,30 @@
|
||||||
{/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()}
|
||||||
|
|
||||||
|
@ -227,7 +256,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$videosCount > 0 && $user->getPrivacyPermission('videos.read', $thisUser ?? NULL)">
|
<div n:if="$videosCount > 0 && $user->getPrivacyPermission('videos.read', $thisUser ?? NULL)">
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$albumsCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$videosCount});">
|
||||||
{_videos}
|
{_videos}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -238,21 +267,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 5px;">
|
<div style="padding: 5px;">
|
||||||
<div class="ovk-video" style="margin-bottom: 1rem; padding: 0 11px;" n:foreach="$videos as $video">
|
<div class="ovk-video" n:foreach="$videos as $video">
|
||||||
<div style="width: 170px;" align="center">
|
<a href="/video{$video->getPrettyId()}" class="preview" align="center">
|
||||||
<img
|
<img
|
||||||
src="{$video->getThumbnailURL()}"
|
src="{$video->getThumbnailURL()}"
|
||||||
style="max-width: 170px; margin: auto;" />
|
style="max-width: 170px; max-height: 127px; margin: auto;" />
|
||||||
</div>
|
</a>
|
||||||
<div>
|
<div>
|
||||||
<b><a href="/video{$video->getPrettyId()}">{ovk_proc_strtr($video->getName(), 30)}</a></b>
|
<b><a href="/video{$video->getPrettyId()}">{ovk_proc_strtr($video->getName(), 30)}</a></b><br>
|
||||||
|
<span style="font-size: 10px;">{$video->getPublicationTime()} | {_comments} ({$video->getCommentsCount()})</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$notesCount > 0 && $user->getPrivacyPermission('notes.read', $thisUser ?? NULL)">
|
<div n:if="$notesCount > 0 && $user->getPrivacyPermission('notes.read', $thisUser ?? NULL)">
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$albumsCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$notesCount});">
|
||||||
{_notes}
|
{_notes}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -266,13 +296,13 @@
|
||||||
<div style="padding: 5px 8px 15px 8px;">
|
<div style="padding: 5px 8px 15px 8px;">
|
||||||
<ul class="notes_titles" n:foreach="$notes as $note">
|
<ul class="notes_titles" n:foreach="$notes as $note">
|
||||||
<li class="written">
|
<li class="written">
|
||||||
<a href="/note{$user->getId()}_{$note->getId()}">
|
<a href="/note{$note->getPrettyId()}">
|
||||||
{$note->getName()}
|
{$note->getName()}
|
||||||
</a>
|
</a>
|
||||||
<small>
|
<small>
|
||||||
{$note->getPublicationTime()}
|
{$note->getPublicationTime()}
|
||||||
<span class="divide">|</span>
|
<span class="divide">|</span>
|
||||||
<a href="/note{$user->getId()}_{$note->getId()}">{_comments}</a>
|
<a href="/note{$note->getPrettyId()}">{_comments}</a>
|
||||||
</small>
|
</small>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -322,19 +352,30 @@
|
||||||
|
|
||||||
<div class="right_big_block">
|
<div class="right_big_block">
|
||||||
<div class="page_info">
|
<div class="page_info">
|
||||||
|
<div n:if="!is_null($alert = $user->getAlert())" class="user-alert">{$alert}</div>
|
||||||
|
{var thatIsThisUser = isset($thisUser) && $user->getId() == $thisUser->getId()}
|
||||||
|
<div n:if="$thatIsThisUser" class="page_status_popup" id="status_editor" style="display: none;">
|
||||||
|
<form method="post" action="/edit?act=status">
|
||||||
|
<div style="margin-bottom: 10px;">
|
||||||
|
<input type="text" name="status" size="50" value="{$user->getStatus()}" />
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" class="button" value="{_'save'}" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<div class="accountInfo clearFix">
|
<div class="accountInfo clearFix">
|
||||||
<div class="profileName">
|
<div class="profileName">
|
||||||
<h2>{$user->getFullName()}</h2>
|
<h2>{$user->getFullName()}</h2>
|
||||||
{if !is_null($user->getStatus())}
|
{if !is_null($user->getStatus())}
|
||||||
<div class="page_status">{$user->getStatus()}</div>
|
<div n:class="page_status, $thatIsThisUser ? page_status_edit_button" n:attr="id => $thatIsThisUser ? page_status_text : NULL">{$user->getStatus()}</div>
|
||||||
{elseif isset($thisUser) && $user->getId() == $thisUser->getId()}
|
{elseif $thatIsThisUser}
|
||||||
<div class="page_status">
|
<div class="page_status">
|
||||||
<a href="/edit" class="edit_link">[ {_"change_status"} ]</a>
|
<div n:class="edit_link, $thatIsThisUser ? page_status_edit_button" id="page_status_text">[ {_"change_status"} ]</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div><div>
|
</div>
|
||||||
|
<div>
|
||||||
<table id="basicInfo" class="ugc-table" border="0" cellspacing="0" cellpadding="0" border="0" cellspacing="0" cellpadding="0" n:if=" $user->getPrivacyPermission('page.info.read', $thisUser ?? NULL)">
|
<table id="basicInfo" class="ugc-table" border="0" cellspacing="0" cellpadding="0" border="0" cellspacing="0" cellpadding="0" n:if=" $user->getPrivacyPermission('page.info.read', $thisUser ?? NULL)">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -357,17 +398,18 @@
|
||||||
<td class="label"><span class="nobold">{_"politViews"}:</span></td>
|
<td class="label"><span class="nobold">{_"politViews"}:</span></td>
|
||||||
<td class="data">{var $pviews = $user->getPoliticalViews()}{_"politViews_$pviews"}</td>
|
<td class="data">{var $pviews = $user->getPoliticalViews()}{_"politViews_$pviews"}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{if $user->getBirthday() != 0}
|
{if $user->getBirthday() > 0}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label"><span class="nobold">{_"birth_date"}:</span></td>
|
<td class="label"><span class="nobold">{_"birth_date"}:</span></td>
|
||||||
<td class="data">{date('d F Y',$user->getBirthday())}, {date('Y') - date('Y', $user->getBirthday())} {_"years"}</td>
|
<td class="data">{date('d F Y',$user->getBirthday())},
|
||||||
|
{tr("years", $user->getAge())}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/if}
|
{/if}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if=" $user->getPrivacyPermission('page.info.read', $thisUser ?? NULL)">
|
<div n:if="$user->getPrivacyPermission('page.info.read', $thisUser ?? NULL)">
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this);">
|
<div class="content_title_expanded" onclick="hidePanel(this);">
|
||||||
{_"information"}
|
{_"information"}
|
||||||
</div>
|
</div>
|
||||||
|
@ -375,7 +417,6 @@
|
||||||
{capture $contactInfo_Tmp}
|
{capture $contactInfo_Tmp}
|
||||||
<table class="ugc-table" border="0" cellspacing="0" cellpadding="0" border="0" cellspacing="0" cellpadding="0" n:ifcontent>
|
<table class="ugc-table" border="0" cellspacing="0" cellpadding="0" border="0" cellspacing="0" cellpadding="0" n:ifcontent>
|
||||||
<tbody n:ifcontent>
|
<tbody n:ifcontent>
|
||||||
<!--sse-->
|
|
||||||
<tr n:if="!is_null($user->getContactEmail())">
|
<tr n:if="!is_null($user->getContactEmail())">
|
||||||
<td class="label"><span class="nobold">{_"email"}: </span></td>
|
<td class="label"><span class="nobold">{_"email"}: </span></td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -392,7 +433,14 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!--/sse-->
|
<tr n:if="!is_null($user->getWebsite())">
|
||||||
|
<td class="label"><span class="nobold">{_"personal_website"}: </span></td>
|
||||||
|
<td>
|
||||||
|
<a href="{$user->getWebsite()}" rel="ugc" target="_blank">
|
||||||
|
{$user->getWebsite()}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr n:if="!is_null($user->getCity())">
|
<tr n:if="!is_null($user->getCity())">
|
||||||
<td class="label"><span class="nobold">{_"city"}:</span></td>
|
<td class="label"><span class="nobold">{_"city"}:</span></td>
|
||||||
<td class="data">{$user->getCity()}</td>
|
<td class="data">{$user->getCity()}</td>
|
||||||
|
@ -440,7 +488,6 @@
|
||||||
{/capture}
|
{/capture}
|
||||||
<div>
|
<div>
|
||||||
<div style="padding: 10px 8px 15px 8px;" n:ifcontent>
|
<div style="padding: 10px 8px 15px 8px;" n:ifcontent>
|
||||||
{if !empty($contactInfo_Tmp)}
|
|
||||||
<h4 style="border-bottom: none; font-size: 11px; padding: 0; display: inline-block;">{_"contact_information"} {ifset $thisUser}{if $thisUser->getId() == $user->getId()}<a href="/edit?act=contacts" class="edit_link">[ {_"edit"} ]</a>{/if}{/ifset}</h4>
|
<h4 style="border-bottom: none; font-size: 11px; padding: 0; display: inline-block;">{_"contact_information"} {ifset $thisUser}{if $thisUser->getId() == $user->getId()}<a href="/edit?act=contacts" class="edit_link">[ {_"edit"} ]</a>{/if}{/ifset}</h4>
|
||||||
{if !empty($contactInfo_Tmp)}
|
{if !empty($contactInfo_Tmp)}
|
||||||
{$contactInfo_Tmp|noescape}
|
{$contactInfo_Tmp|noescape}
|
||||||
|
@ -448,7 +495,6 @@
|
||||||
<div style="padding: 15px;color:gray;text-align: center;">{_no_information_provided}</div>
|
<div style="padding: 15px;color:gray;text-align: center;">{_no_information_provided}</div>
|
||||||
{/if}
|
{/if}
|
||||||
<br>
|
<br>
|
||||||
{/if}
|
|
||||||
<h4 style="border-bottom: none; font-size: 11px; padding: 0; display: inline-block;">{_"personal_information"} {ifset $thisUser}{if $thisUser->getId() == $user->getId()}<a href="/edit?act=interests" class="edit_link">[ {_"edit"} ]</a>{/if}{/ifset}</h4>
|
<h4 style="border-bottom: none; font-size: 11px; padding: 0; display: inline-block;">{_"personal_information"} {ifset $thisUser}{if $thisUser->getId() == $user->getId()}<a href="/edit?act=interests" class="edit_link">[ {_"edit"} ]</a>{/if}{/ifset}</h4>
|
||||||
{if !empty($uInfo_Tmp)}
|
{if !empty($uInfo_Tmp)}
|
||||||
{$uInfo_Tmp|noescape}
|
{$uInfo_Tmp|noescape}
|
||||||
|
@ -457,9 +503,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p n:if="empty($contactInfo_Tmp) && empty($uInfo_Tmp)">
|
|
||||||
Пользователь предпочёл оставить о себе только воздух тайны.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{presenter "openvk!Wall->wallEmbedded", $user->getId()}
|
{presenter "openvk!Wall->wallEmbedded", $user->getId()}
|
||||||
|
@ -509,6 +552,19 @@
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script n:if="isset($thisUser) && $user->getId() == $thisUser->getId()">
|
||||||
|
function setStatusEditorShown(shown) {
|
||||||
|
document.getElementById("status_editor").style.display = shown ? "block" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("click", event => {
|
||||||
|
if(!event.target.closest("#status_editor") && !event.target.closest("#page_status_text"))
|
||||||
|
setStatusEditorShown(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("page_status_text").onclick = setStatusEditorShown.bind(this, true);
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -14,26 +14,8 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<div>
|
<div class="postFeedWrapper">
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this);">
|
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost"}
|
||||||
{_"publish_post"}
|
|
||||||
</div>
|
|
||||||
<div style="margin: 0 5px;"><br>
|
|
||||||
<form action="/wall{$thisUser->getId()}/makePost" method="POST" enctype="multipart/form-data" style="margin:0;">
|
|
||||||
<textarea id="wall-post-input" name="text" style="width: 100%;resize: none;"></textarea>
|
|
||||||
<input type="file" name="_pic_attachment" accept="image/*" style="display:none;" />
|
|
||||||
<input type="hidden" name="type" value="1" />
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<div></div>
|
|
||||||
<br/>
|
|
||||||
<input type="submit" value="{_'write'}" class="button" />
|
|
||||||
<div style="float: right;">
|
|
||||||
<a href="javascript:void(document.querySelector(`input[name=_pic_attachment]`).click());">
|
|
||||||
{_"attach_photo"}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
@ -41,7 +23,7 @@
|
||||||
{foreach $posts as $post}
|
{foreach $posts as $post}
|
||||||
<a name="postGarter={$post->getId()}"></a>
|
<a name="postGarter={$post->getId()}"></a>
|
||||||
|
|
||||||
{include "../components/post.xml", post => $post, onWallOf => true}
|
{include "../components/post.xml", post => $post, onWallOf => true, commentSection => true}
|
||||||
{/foreach}
|
{/foreach}
|
||||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -15,52 +15,8 @@
|
||||||
{block content}
|
{block content}
|
||||||
<div class="content_divider">
|
<div class="content_divider">
|
||||||
<div>
|
<div>
|
||||||
<!-- TODO: Move the creating post form to dedicated file -->
|
|
||||||
<div n:if="$canPost" class="content_subtitle">
|
<div n:if="$canPost" class="content_subtitle">
|
||||||
<div id="write" style="padding: 5px 0;" >
|
{include "../components/textArea.xml", route => "/wall$owner/makePost"}
|
||||||
<form action="/wall{$owner}/makePost" method="post" enctype="multipart/form-data" style="margin:0;">
|
|
||||||
<textarea id="wall-post-input" placeholder="{_write}" name="text" style="width: 100%;resize: none;" class="expanded-textarea"></textarea>
|
|
||||||
<div>
|
|
||||||
<!-- padding to fix <br/> bug -->
|
|
||||||
</div>
|
|
||||||
<div id="post-buttons">
|
|
||||||
<div class="post-upload">
|
|
||||||
Вложение: <span>(unknown)</span>
|
|
||||||
</div>
|
|
||||||
<div class="post-opts">
|
|
||||||
{if !is_null($thisUser) && $owner < 0 && $club->canBeModifiedBy($thisUser)}
|
|
||||||
<script>
|
|
||||||
function onWallAsGroupClick(el) {
|
|
||||||
_display = el.checked ? "block" : "none";
|
|
||||||
document.querySelector("#forceSignOpt").style.display = _display;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="as_group" onchange="onWallAsGroupClick(this)" /> От имени сообщества
|
|
||||||
</label>
|
|
||||||
<label id="forceSignOpt" style="display: none;">
|
|
||||||
<input type="checkbox" name="force_sign" /> Подпись автора
|
|
||||||
</label>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="nsfw" /> Содержит NSFW-контент
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<input type="file" name="_pic_attachment" accept="image/*" style="display:none;" />
|
|
||||||
<input type="hidden" name="type" value="1" />
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<br/>
|
|
||||||
<input type="submit" value="{_'write'}" class="button" />
|
|
||||||
<div style="float: right;">
|
|
||||||
<a href="javascript:void(document.querySelector(`input[name=_pic_attachment]`).click());">
|
|
||||||
{_attach_photo}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -68,7 +24,7 @@
|
||||||
{foreach $posts as $post}
|
{foreach $posts as $post}
|
||||||
<a name="postGarter={$post->getId()}"></a>
|
<a name="postGarter={$post->getId()}"></a>
|
||||||
|
|
||||||
{include "../components/post.xml", post => $post}
|
{include "../components/post.xml", post => $post, commentSection => true}
|
||||||
{/foreach}
|
{/foreach}
|
||||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||||
{else}
|
{else}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{if $attachment instanceof \openvk\Web\Models\Entities\Photo}
|
{if $attachment instanceof \openvk\Web\Models\Entities\Photo}
|
||||||
{if !$attachment->isDeleted()}
|
{if !$attachment->isDeleted()}
|
||||||
<a href="/photo{$attachment->getPrettyId()}">
|
{var link = "/photo" . ($attachment->isAnonymous() ? ("s/" . base_convert((string) $attachment->getId(), 10, 32)) : $attachment->getPrettyId())}
|
||||||
|
<a href="{$link}">
|
||||||
<img class="media" src="{$attachment->getURL()}" alt="{$attachment->getDescription()}" />
|
<img class="media" src="{$attachment->getURL()}" alt="{$attachment->getDescription()}" />
|
||||||
</a>
|
</a>
|
||||||
{else}
|
{else}
|
||||||
|
@ -8,6 +9,8 @@
|
||||||
<img class="media" src="/assets/packages/static/openvk/img/camera_200.png" alt="{_"attach_no_longer_available"}" />
|
<img class="media" src="/assets/packages/static/openvk/img/camera_200.png" alt="{_"attach_no_longer_available"}" />
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
{elseif $attachment instanceof \openvk\Web\Models\Entities\Video}
|
||||||
|
<video class="media" src="{$attachment->getURL()}" controls="controls"></video>
|
||||||
{elseif $attachment instanceof \openvk\Web\Models\Entities\Post}
|
{elseif $attachment instanceof \openvk\Web\Models\Entities\Post}
|
||||||
{php $GLOBALS["_nesAttGloCou"] = (isset($GLOBALS["_nesAttGloCou"]) ? $GLOBALS["_nesAttGloCou"] : 0) + 1}
|
{php $GLOBALS["_nesAttGloCou"] = (isset($GLOBALS["_nesAttGloCou"]) ? $GLOBALS["_nesAttGloCou"] : 0) + 1}
|
||||||
{if $GLOBALS["_nesAttGloCou"] > 2}
|
{if $GLOBALS["_nesAttGloCou"] > 2}
|
||||||
|
|
|
@ -1,36 +1,44 @@
|
||||||
{var author = $comment->getOwner()}
|
{var author = $comment->getOwner()}
|
||||||
|
{var $Club = openvk\Web\Models\Entities\Club::class}
|
||||||
|
|
||||||
<a name="cid={$comment->getId()}"></a>
|
<a name="cid={$comment->getId()}"></a>
|
||||||
<table border="0" style="font-size: 11px;" class="post">
|
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$comment->getOwner() instanceof $Club}">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="54" valign="top">
|
<td width="30" valign="top">
|
||||||
<img
|
<img
|
||||||
src="{$author->getAvatarURL()}"
|
src="{$author->getAvatarURL()}"
|
||||||
width="50" />
|
width="30"
|
||||||
|
class="cCompactAvatars" />
|
||||||
</td>
|
</td>
|
||||||
<td width="345" valign="top">
|
<td width="100%" valign="top">
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
<a href="{$author->getURL()}"><b>
|
<a href="{$author->getURL()}"><b>
|
||||||
{$author->getCanonicalName()}
|
{$author->getCanonicalName()}
|
||||||
</b></a> {$author->isFemale() ? tr("post_writes_f") : tr("post_writes_m")}<br/>
|
</b></a><br/>
|
||||||
<a href="/comment{$comment->getId()}" class="date">{$comment->getPublicationTime()}</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="post-content" id="{$comment->getId()}">
|
<div class="post-content" id="{$comment->getId()}">
|
||||||
<div class="text" id="text{$comment->getId()}">
|
<div class="text" id="text{$comment->getId()}">
|
||||||
{$comment->getText()|noescape}
|
{$comment->getText()|noescape}
|
||||||
|
|
||||||
|
<div n:ifcontent class="attachments_b">
|
||||||
|
<div class="attachment" n:foreach="$comment->getChildren() as $attachment" data-localized-nsfw-text="{_nsfw_warning}">
|
||||||
|
{include "attachment.xml", attachment => $attachment}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="isset($thisUser) &&! ($compact ?? false)" class="post-menu">
|
<div n:if="isset($thisUser) &&! ($compact ?? false)" class="post-menu">
|
||||||
|
<a href="#_comment{$comment->getId()}" class="date">{$comment->getPublicationTime()}</a> |
|
||||||
{var canDelete = $comment->getOwner()->getId() == $thisUser->getId()}
|
{var canDelete = $comment->getOwner()->getId() == $thisUser->getId()}
|
||||||
{var canDelete = $canDelete || $comment->getTarget()->getOwner()->getId() == $thisUser->getId()}
|
{var canDelete = $canDelete || $comment->getTarget()->getOwner()->getId() == $thisUser->getId()}
|
||||||
{if $canDelete}
|
{if $canDelete}
|
||||||
<a href="/comment{$comment->getId()}/delete">{_"delete"}</a>
|
<a href="/comment{$comment->getId()}/delete">{_"delete"}</a> |
|
||||||
{/if}
|
{/if}
|
||||||
|
<a class="comment-reply">Ответить</a>
|
||||||
<div style="float: right; font-size: .7rem;">
|
<div style="float: right; font-size: .7rem;">
|
||||||
<a 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>
|
||||||
<span class="likeCnt">{$comment->getLikesCount()}</span>
|
<span class="likeCnt">{if $comment->getLikesCount() > 0}{$comment->getLikesCount()}{/if}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
<h4>{_"comments"} ({$count})</h4>
|
<h4>{_"comments"} ({$count})</h4>
|
||||||
|
|
||||||
<div n:ifset="$thisUser">
|
<div n:ifset="$thisUser">
|
||||||
<form action="/al_comments.pl/create/{$model}/{$parent->getId()}" method="POST" style="margin-top: 2pt;">
|
{var commentsURL = "/al_comments.pl/create/$model/" . $parent->getId()}
|
||||||
<textarea name="text" style="width: 411px; margin: 0px; height: 53px; resize: none;"></textarea>
|
{var club = $parent instanceof \openvk\Web\Models\Entities\Post && $parent->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($parent->getTargetWall())) : NULL}
|
||||||
<br/>
|
{include "textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), club => $club}
|
||||||
<input type="hidden" value="{$csrfToken}" name="hash" />
|
|
||||||
<input type="submit" value="{_'write'}" class="button" style="bottom-right: 50px;" />
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{if sizeof($comments) > 0}
|
{if sizeof($comments) > 0}
|
||||||
|
@ -28,3 +25,5 @@
|
||||||
{/if} -->
|
{/if} -->
|
||||||
{_"comments_tip"}
|
{_"comments_tip"}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{script "js/al_comments.js"}
|
||||||
|
|
|
@ -4,8 +4,3 @@
|
||||||
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
||||||
{$notification->getDateTime()} {_nt_liked_yours}
|
{$notification->getDateTime()} {_nt_liked_yours}
|
||||||
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_nominative}</b></a> {_nt_from} {$post->getPublicationTime()}.
|
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_nominative}</b></a> {_nt_from} {$post->getPublicationTime()}.
|
||||||
|
|
||||||
<?php
|
|
||||||
// костыльно скрыл лол, сами исправите проблему - гфх
|
|
||||||
//{tr('notifications_like', '<a href="'.$user->getURL().'"><b>'.$user->getCanonicalName().'</b></a>', '<a href="/wall'.$post->getPrettyId().'"><b>', '</b></a>', $post->getPublicationTime())}
|
|
||||||
?>
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{var gift = $notification->getModel(0)}
|
||||||
|
{var sender = $notification->getModel(1)}
|
||||||
|
|
||||||
|
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> отправил вам {$notification->getDateTime()} подарок.
|
|
@ -1,14 +1,16 @@
|
||||||
<div n:if="!($conf->page === 1 && $conf->count <= $conf->perPage)" class="paginator">
|
{var $space = 2}
|
||||||
<br/>
|
{var $pageCount = ceil($conf->count / $conf->perPage)}
|
||||||
<center>
|
|
||||||
<a n:if="$conf->page != 1"
|
|
||||||
href="?{http_build_query(array_merge($_GET, ['p' => ($conf->page - 1)]), 'k', '&', PHP_QUERY_RFC3986)}"
|
|
||||||
style="float: left;"><< {_paginator_back}</a>
|
|
||||||
|
|
||||||
{tr("paginator_page", $conf->page)}
|
<div n:if="!($conf->page === 1 && $conf->count <= $conf->perPage)" n:class="paginator, $conf->atBottom ? paginator-at-bottom">
|
||||||
|
{if $conf->page > $space}
|
||||||
<a n:if="$conf->count > (($conf->page - 1) * $conf->perPage + $conf->amount) && $conf->amount > 0"
|
<a n:attr="class => ($conf->page === 1 ? 'active')" href="?{http_build_query(array_merge($_GET, ['p' => 1]), 'k', '&', PHP_QUERY_RFC3986)}">«</a>
|
||||||
href="?{http_build_query(array_merge($_GET, ['p' => ($conf->page + 1)]), 'k', '&', PHP_QUERY_RFC3986)}"
|
{/if}
|
||||||
style="float: right;">{_paginator_next} >></a>
|
{for $j = $conf->page - ($space-1); $j <= $conf->page + ($space-1); $j++}
|
||||||
</center>
|
{if $j > 0 && $j <= $pageCount}
|
||||||
|
<a n:attr="class => ($conf->page === $j ? 'active')" href="?{http_build_query(array_merge($_GET, ['p' => $j]), 'k', '&', PHP_QUERY_RFC3986)}">{$j}</a>
|
||||||
|
{/if}
|
||||||
|
{/for}
|
||||||
|
{if $conf->page <= $pageCount-$space}
|
||||||
|
<a n:attr="class => ($conf->page === $pageCount ? 'active')" href="?{http_build_query(array_merge($_GET, ['p' => $pageCount]), 'k', '&', PHP_QUERY_RFC3986)}">»</a>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
{var microblogEnabled = isset($thisUser) ? $thisUser->hasMicroblogEnabled() : false}
|
{var microblogEnabled = isset($thisUser) ? $thisUser->hasMicroblogEnabled() : false}
|
||||||
|
{if !$post->isPostedOnBehalfOfGroup()}
|
||||||
|
{var then = date_create("@" . $post->getOwner()->getOnline()->timestamp())}
|
||||||
|
{var now = date_create()}
|
||||||
|
{var diff = date_diff($now, $then)}
|
||||||
|
{/if}
|
||||||
|
|
||||||
{if $microblogEnabled}
|
{if $microblogEnabled}
|
||||||
{include "post/microblogpost.xml", post => $post}
|
{include "post/microblogpost.xml", post => $post, diff => $diff, commentSection => $commentSection}
|
||||||
{else}
|
{else}
|
||||||
{include "post/oldpost.xml", post => $post}
|
{include "post/oldpost.xml", post => $post, diff => $diff}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
{var author = $post->getOwner()}
|
{var author = $post->getOwner()}
|
||||||
|
{var comments = $post->getLastComments(3)}
|
||||||
|
{var commentsCount = $post->getCommentsCount()}
|
||||||
|
|
||||||
|
{var commentTextAreaId = $post === null ? rand(1,300) : $post->getId()}
|
||||||
|
|
||||||
<table border="0" style="font-size: 11px;" n:class="post, !$compact ? post-divider, $post->isExplicit() ? post-nsfw">
|
<table border="0" style="font-size: 11px;" n:class="post, !$compact ? post-divider, $post->isExplicit() ? post-nsfw">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -7,6 +11,11 @@
|
||||||
<img
|
<img
|
||||||
src="{$author->getAvatarURL()}"
|
src="{$author->getAvatarURL()}"
|
||||||
width="{ifset $compact}25{else}50{/ifset}" />
|
width="{ifset $compact}25{else}50{/ifset}" />
|
||||||
|
{if !$post->isPostedOnBehalfOfGroup() && !$compact}
|
||||||
|
<span n:if="$author->isOnline()" class="post-online">
|
||||||
|
{_online}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td width="100%" valign="top">
|
<td width="100%" valign="top">
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
|
@ -18,24 +27,22 @@
|
||||||
{if $author->isVerified()}<img class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">{/if}
|
{if $author->isVerified()}<img class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">{/if}
|
||||||
{ifset $compact}<br>
|
{ifset $compact}<br>
|
||||||
<a href="/wall{$post->getPrettyId()}" class="date">
|
<a href="/wall{$post->getPrettyId()}" class="date">
|
||||||
{if $post->isPinned()}
|
|
||||||
{$post->getPublicationTime()},
|
|
||||||
{_pinned}
|
|
||||||
{else}
|
|
||||||
{$post->getPublicationTime()}
|
{$post->getPublicationTime()}
|
||||||
{/if}
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
{if $post->isPinned()}
|
||||||
|
<span class="nobold">{_pinned}</span>
|
||||||
|
{/if}
|
||||||
{if $post->canBeDeletedBy($thisUser) && !($forceNoDeleteLink ?? false) && !isset($compact)}
|
{if $post->canBeDeletedBy($thisUser) && !($forceNoDeleteLink ?? false) && !isset($compact)}
|
||||||
<a class="delete" href="/wall{$post->getPrettyId()}/delete"></a>
|
<a class="delete" href="/wall{$post->getPrettyId()}/delete"></a>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{if $post->canBePinnedBy($thisUser) && !($forceNoPinLink ?? false) && !isset($compact)}
|
{if $post->canBePinnedBy($thisUser) && !($forceNoPinLink ?? false) && !isset($compact)}
|
||||||
{if $post->isPinned()}
|
{if $post->isPinned()}
|
||||||
<a class="delete" href="/wall{$post->getPrettyId()}/pin?act=unpin&hash={rawurlencode($csrfToken)}"></a>
|
<a class="pin" href="/wall{$post->getPrettyId()}/pin?act=unpin&hash={rawurlencode($csrfToken)}"></a>
|
||||||
{else}
|
{else}
|
||||||
<a class="delete" href="/wall{$post->getPrettyId()}/pin?act=pin&hash={rawurlencode($csrfToken)}"></a>
|
<a class="pin" href="/wall{$post->getPrettyId()}/pin?act=pin&hash={rawurlencode($csrfToken)}"></a>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,23 +76,15 @@
|
||||||
|
|
||||||
|
|
||||||
{if !($forceNoCommentsLink ?? false)}
|
{if !($forceNoCommentsLink ?? false)}
|
||||||
<a href="/wall{$post->getPrettyId()}#comments">
|
<a n:if="$commentsCount == 0" href="javascript:expand_comment_textarea({$commentTextAreaId})">
|
||||||
{if $post->getCommentsCount() > 0}
|
{_"comment"}
|
||||||
{_"comments"} (<b>{$post->getCommentsCount()}</b>)
|
|
||||||
{else}
|
|
||||||
{_"comments"}
|
|
||||||
{/if}
|
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="like_wrap">
|
<div class="like_wrap">
|
||||||
<a class="post-share-button" href="/wall{$post->getPrettyId()}/repost?hash={rawurlencode($csrfToken)}"
|
<a class="post-share-button" href="javascript:repostPost('{$post->getPrettyId()}', '{rawurlencode($csrfToken)}')">
|
||||||
class="post-like-button">
|
|
||||||
<div class="repost-icon" style="opacity: 0.4;"></div>
|
<div class="repost-icon" style="opacity: 0.4;"></div>
|
||||||
<span class="likeCnt">{$post->getRepostCount()}</span>
|
<span class="likeCnt">{if $post->getRepostCount() > 0}{$post->getRepostCount()}{/if}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{var liked = $post->hasLikeFrom($thisUser)}
|
{var liked = $post->hasLikeFrom($thisUser)}
|
||||||
|
@ -93,14 +92,23 @@
|
||||||
class="post-like-button"
|
class="post-like-button"
|
||||||
data-liked="{(int) $liked}"
|
data-liked="{(int) $liked}"
|
||||||
data-likes="{$post->getLikesCount()}">
|
data-likes="{$post->getLikesCount()}">
|
||||||
<div class="heart" style="{if $liked}opacity: 1;{else}opacity: 0.4;{/if}"></div>
|
<div class="heart" id="{if $liked}liked{/if}"></div>
|
||||||
<span class="likeCnt">{$post->getLikesCount()}</span>
|
<span class="likeCnt">{if $post->getLikesCount() > 0}{$post->getLikesCount()}{/if}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div n:if="isset($thisUser) &&! ($compact ?? false)" class="post-menu-s">
|
<div n:if="$commentSection == true && $compact == false" class="post-menu-s">
|
||||||
<!-- kosfurler -->
|
{if $commentsCount > 3}
|
||||||
|
<a href="/wall{$post->getPrettyId()}" class="expand_button">{_view_other_comments}</a>
|
||||||
|
{/if}
|
||||||
|
{foreach $comments as $comment}
|
||||||
|
{include "../comment.xml", comment => $comment, $compact => true}
|
||||||
|
{/foreach}
|
||||||
|
<div n:ifset="$thisUser" id="commentTextArea{$commentTextAreaId}" n:attr="style => ($commentsCount == 0 ? 'display: none;')" class="commentsTextFieldWrap">
|
||||||
|
{var commentsURL = "/al_comments.pl/create/posts/" . $post->getId()}
|
||||||
|
{include "../textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), post => $post}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue