Редактор Chandler (#721)

* Chandler Editor

* Features
This commit is contained in:
n1rwana 2022-11-02 13:45:49 +03:00 committed by GitHub
parent 403ee44b8e
commit f5b1890645
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 525 additions and 4 deletions

View file

@ -0,0 +1,48 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Repositories;
use Nette\Database\Table\ActiveRow;
use Chandler\Database\DatabaseConnection as DB;
use openvk\Web\Models\Entities\User;
use Chandler\Security\User as ChandlerUser;
class ChandlerGroups
{
private $context;
private $groups;
public function __construct()
{
$this->context = DB::i()->getContext();
$this->groups = $this->context->table("chandlergroups");
$this->members = $this->context->table("chandleraclrelations");
$this->perms = $this->context->table("chandleraclgroupspermissions");
}
function get(string $UUID): ?ActiveRow
{
return $this->groups->where("id", $UUID)->fetch();
}
function getList(): \Traversable
{
foreach($this->groups as $group) yield $group;
}
function getMembersById(string $UUID): \Traversable
{
foreach($this->members->where("group", $UUID) as $member)
yield (new Users)->getByChandlerUser(
new ChandlerUser($this->context->table("chandlerusers")->where("id", $member->user)->fetch())
);
}
function getUsersMemberships(string $UUID): \Traversable
{
foreach($this->members->where("user", $UUID) as $member) yield $member;
}
function getPermissionsById(string $UUID): \Traversable
{
foreach($this->perms->where("group", $UUID) as $perm) yield $perm;
}
}

View file

@ -0,0 +1,39 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Repositories;
use Nette\Database\Table\ActiveRow;
use Chandler\Database\DatabaseConnection as DB;
use openvk\Web\Models\Entities\User;
use Chandler\Security\User as ChandlerUser;
class ChandlerUsers
{
private $context;
private $users;
public function __construct()
{
$this->context = DB::i()->getContext();
$this->users = $this->context->table("chandlerusers");
}
private function toUser(?ActiveRow $ar): ?ChandlerUser
{
return is_null($ar) ? NULL : (new User($ar))->getChandlerUser();
}
function get(int $id): ?ChandlerUser
{
return (new Users)->get($id)->getChandlerUser();
}
function getById(string $UUID): ?ChandlerUser
{
return new ChandlerUser($this->users->where("id", $UUID)->fetch());
}
function getList(int $page = 1): \Traversable
{
foreach($this->users as $user)
yield new ChandlerUser($user);
}
}

View file

@ -1,7 +1,7 @@
<?php declare(strict_types=1);
namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts, BannedLinks};
use openvk\Web\Models\Repositories\{ChandlerGroups, ChandlerUsers, Users, Clubs, Vouchers, Gifts, BannedLinks};
use Chandler\Database\DatabaseConnection;
final class AdminPresenter extends OpenVKPresenter
@ -11,14 +11,16 @@ final class AdminPresenter extends OpenVKPresenter
private $vouchers;
private $gifts;
private $bannedLinks;
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks)
private $chandlerGroups;
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups)
{
$this->users = $users;
$this->clubs = $clubs;
$this->vouchers = $vouchers;
$this->gifts = $gifts;
$this->bannedLinks = $bannedLinks;
$this->chandlerGroups = $chandlerGroups;
parent::__construct();
}
@ -62,7 +64,9 @@ final class AdminPresenter extends OpenVKPresenter
$this->notFound();
$this->template->user = $user;
$this->template->c_groups_list = (new ChandlerGroups)->getList();
$this->template->c_memberships = $this->chandlerGroups->getUsersMemberships($user->getChandlerGUID());
if($_SERVER["REQUEST_METHOD"] !== "POST")
return;
@ -78,8 +82,13 @@ final class AdminPresenter extends OpenVKPresenter
$user->changeEmail($this->postParam("email"));
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
if($this->postParam("add-to-group")) {
$query = "INSERT INTO `chandleraclrelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
DatabaseConnection::i()->getConnection()->query($query);
}
$user->save();
break;
}
}
@ -447,4 +456,95 @@ final class AdminPresenter extends OpenVKPresenter
$this->redirect("/admin/bannedLinks");
}
function renderChandlerGroups(): void
{
$this->template->groups = (new ChandlerGroups)->getList();
if($_SERVER["REQUEST_METHOD"] !== "POST")
return;
$req = "INSERT INTO `chandlergroups` (`name`) VALUES ('" . $this->postParam("name") . "')";
DatabaseConnection::i()->getConnection()->query($req);
}
function renderChandlerGroup(string $UUID): void
{
$DB = DatabaseConnection::i()->getConnection();
if(is_null($DB->query("SELECT * FROM `chandlergroups` WHERE `id` = '$UUID'")->fetch()))
$this->flashFail("err", tr("error"), tr("c_group_not_found"));
$this->template->group = (new ChandlerGroups)->get($UUID);
$this->template->mode = in_array(
$this->queryParam("act"),
[
"main",
"members",
"permissions",
"removeMember",
"removePermission",
"delete"
]) ? $this->queryParam("act") : "main";
$this->template->members = (new ChandlerGroups)->getMembersById($UUID);
$this->template->perms = (new ChandlerGroups)->getPermissionsById($UUID);
if($this->template->mode == "removeMember") {
$where = "`user` = '" . $this->queryParam("uid") . "' AND `group` = '$UUID'";
if(is_null($DB->query("SELECT * FROM `chandleraclrelations` WHERE " . $where)->fetch()))
$this->flashFail("err", tr("error"), tr("c_user_is_not_in_group"));
$DB->query("DELETE FROM `chandleraclrelations` WHERE " . $where);
$this->flashFail("succ", tr("changes_saved"), tr("c_user_removed_from_group"));
} elseif($this->template->mode == "removePermission") {
$where = "`model` = '" . trim(addslashes($this->queryParam("model"))) . "' AND `permission` = '". $this->queryParam("perm") ."' AND `group` = '$UUID'";
if(is_null($DB->query("SELECT * FROM `chandleraclgroupspermissions WHERE $where`")))
$this->flashFail("err", tr("error"), tr("c_permission_not_found"));
$DB->query("DELETE FROM `chandleraclgroupspermissions` WHERE $where");
$this->flashFail("succ", tr("changes_saved"), tr("c_permission_removed_from_group"));
} elseif($this->template->mode == "delete") {
$DB->query("DELETE FROM `chandlergroups` WHERE `id` = '$UUID'");
$DB->query("DELETE FROM `chandleraclgroupspermissions` WHERE `group` = '$UUID'");
$DB->query("DELETE FROM `chandleraclrelations` WHERE `group` = '$UUID'");
$this->flashFail("succ", tr("changes_saved"), tr("c_group_removed"));
}
if ($_SERVER["REQUEST_METHOD"] !== "POST") return;
$req = "";
if($this->template->mode == "main")
if($this->postParam("delete"))
$req = "DELETE FROM `chandlergroups` WHERE `id`='$UUID'";
else
$req = "UPDATE `chandlergroups` SET `name`='". $this->postParam('name') ."' , `color`='". $this->postParam("color") ."' WHERE `id`='$UUID'";
if($this->template->mode == "members")
if($this->postParam("uid"))
if(!is_null($DB->query("SELECT * FROM `chandleraclrelations` WHERE `user` = '" . $this->postParam("uid") . "'")))
$this->flashFail("err", tr("error"), tr("c_user_is_already_in_group"));
$req = "INSERT INTO `chandleraclrelations` (`user`, `group`, `priority`) VALUES ('". $this->postParam("uid") ."', '$UUID', 32)";
if($this->template->mode == "permissions")
$req = "INSERT INTO `chandleraclgroupspermissions` (`group`, `model`, `permission`, `context`) VALUES ('$UUID', '". trim(addslashes($this->postParam("model"))) ."', '". $this->postParam("permission") ."', 0)";
$DB->query($req);
$this->flashFail("succ", tr("changes_saved"));
}
function renderChandlerUser(string $UUID): void
{
if(!$UUID) $this->notFound();
$c_user = (new ChandlerUsers())->getById($UUID);
$user = $this->users->getByChandlerUser($c_user);
if(!$user) $this->notFound();
$this->redirect("/admin/users/id" . $user->getId());
}
}

View file

@ -60,6 +60,14 @@
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
</li>
</ul>
<div class="aui-nav-heading">
<strong>Chandler</strong>
</div>
<ul class="aui-nav">
<li>
<a href="/admin/chandler/groups">{_c_groups}</a>
</li>
</ul>
<div class="aui-nav-heading">
<strong>{_admin_services}</strong>
</div>

View file

@ -0,0 +1,177 @@
{extends "@layout.xml"}
{block title}
{$group->name}
{/block}
{block heading}
<a href="/admin/chandler/groups">{_c_groups}</a>
» {$group->name}
{/block}
{block content}
{var $isMain = $mode === 'main'}
{var $isPermissions = $mode === 'permissions'}
{var $isMembers = $mode === 'members'}
{if $isMain}
<div class="aui-tabs horizontal-tabs">
<nav class="aui-navgroup aui-navgroup-horizontal">
<div class="aui-navgroup-inner">
<div class="aui-navgroup-primary">
<ul class="aui-nav">
<li class="aui-nav-selected"><a href="?act=main">{_admin_tab_main}</a></li>
<li><a href="?act=permissions">{_c_group_permissions}</a></li>
<li><a href="?act=members">{_c_group_members}</a></li>
</ul>
</div>
</div>
</nav>
<form class="aui" method="POST">
<div class="field-group">
<label for="id">ID</label>
<input class="text medium-field" type="text" id="id" disabled value="{$group->id}" />
</div>
<div class="field-group">
<label for="first_name">{_name}</label>
<input class="text medium-field" type="text" id="name" name="name" value="{$group->name}" />
</div>
<div class="field-group">
<label for="first_name">{_c_color}</label>
<input class="text medium-field" type="text" id="color" name="color" value="{$group->color}" />
</div>
<div class="buttons-container">
<div class="buttons">
<input type="hidden" name="hash" value="{$csrfToken}" />
<input class="button submit" type="submit" value="{_save}">
</div>
</div>
</form>
</div>
{elseif $isMembers}
<div class="aui-tabs horizontal-tabs">
<nav class="aui-navgroup aui-navgroup-horizontal">
<div class="aui-navgroup-inner">
<div class="aui-navgroup-primary">
<ul class="aui-nav">
<li><a href="?act=main">{_admin_tab_main}</a></li>
<li><a href="?act=permissions">{_c_group_permissions}</a></li>
<li class="aui-nav-selected"><a href="?act=members">{_c_group_members}</a></li>
<li>
<form class="aui" method="POST" style="display: flex;">
<div class="field-group">
<label for="uid">UID</label>
<input class="text" type="text" id="uid" name="uid" />
</div>
<div style="margin: 5px;">
<div class="buttons">
<input type="hidden" name="hash" value="{$csrfToken}" />
<input class="button submit" type="submit" value="{_add}">
</div>
</div>
</form>
</li>
</ul>
</div>
</div>
</nav>
<table class="aui aui-table-list">
<thead>
<tr>
<th>ID</th>
<th>UUID</th>
<th>{_admin_name}</th>
<th>{_gender}</th>
<th>{_admin_shortcode}</th>
<th>{_registration_date}</th>
<th>{_admin_actions}</th>
</tr>
</thead>
<tbody>
<tr n:foreach="$members as $user">
<td>{$user->getId()}</td>
<td>{$user->getChandlerGUID()}</td>
<td>
<span class="aui-avatar aui-avatar-xsmall">
<span class="aui-avatar-inner">
<img src="{$user->getAvatarUrl('miniscule')}" alt="{$user->getCanonicalName()}" style="object-fit: cover;" 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">{_admin_banned}</span>
</td>
<td>{$user->isFemale() ? tr("female") : tr("male")}</td>
<td>{$user->getShortCode() ?? "(" . tr("none") . ")"}</td>
<td>{$user->getRegistrationTime()}</td>
<td>
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}?act=removeMember&uid={$user->getChandlerGUID()}">
<span class="aui-icon aui-icon-small aui-iconfont-delete">{_delete}</span>
</a>
<a class="aui-button aui-button-primary" href="/admin/users/id{$user->getId()}">
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
</a>
{if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)}
<a class="aui-button" href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}">
<span class="aui-icon aui-icon-small aui-iconfont-sign-in">{_admin_loginas}</span>
</a>
{/if}
</td>
</tr>
</tbody>
</table>
</table>
</div>
{elseif $isPermissions}
<div class="aui-tabs horizontal-tabs">
<nav class="aui-navgroup aui-navgroup-horizontal">
<div class="aui-navgroup-inner">
<div class="aui-navgroup-primary">
<ul class="aui-nav">
<li><a href="?act=main">{_admin_tab_main}</a></li>
<li class="aui-nav-selected"><a href="?act=permissions">{_c_group_permissions}</a></li>
<li><a href="?act=members">{_c_group_members}</a></li>
<li>
<form class="aui" method="POST" style="display: flex;">
<div class="field-group">
<label for="model">{_c_model}</label>
<input class="text" type="text" id="model" name="model" />
<input class="text" type="text" id="permission" name="permission" />
<label for="action">{_c_permission}</label>
</div>
<div style="margin: 5px;">
<div class="buttons">
<input type="hidden" name="hash" value="{$csrfToken}" />
<input class="button submit" type="submit" value="{_add}">
</div>
</div>
</form>
</li>
</ul>
</div>
</div>
</nav>
<table class="aui aui-table-list">
<thead>
<tr>
<th>{_c_model}</th>
<th>{_c_permission}</th>
<th>{_admin_actions}</th>
</tr>
</thead>
<tbody>
<tr n:foreach="$perms as $perm">
<td>{$perm->model}</td>
<td>{$perm->permission}</td>
<td>
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$perm->group}?act=removePermission&model={$perm->model}&perm={$perm->permission}">
<span class="aui-icon aui-icon-small aui-iconfont-delete">{_edit}</span>
</a>
</td>
</tr>
</tbody>
</table>
</div>
{/if}
{/block}

View file

@ -0,0 +1,59 @@
{extends "@layout.xml"}
{block title}
{_c_groups}
{/block}
{block heading}
{_c_groups}
{/block}
{block content}
<form class="aui" method="POST">
<div class="field-group" style="margin-left: -65px;">
<label for="uid">{_admin_title}</label>
<div style="display: flex;">
<input class="text" type="text" id="name" name="name" />
<div class="buttons" style="margin-left: 5px;">
<input type="hidden" name="hash" value="{$csrfToken}" />
<input class="button submit" type="submit" value="{_add}">
</div>
</div>
</div>
</form>
<table class="aui aui-table-list">
<thead>
<tr>
<th>ID</th>
<th>{_admin_title}</th>
<th>{_admin_actions}</th>
</tr>
</thead>
<tbody>
<tr n:foreach="$groups as $group">
<td>
<a href="/admin/chandler/groups/{$group->id}">{$group->id}</a>
</td>
<td>
<span class="aui-lozenge aui-lozenge-subtle" style="text-transform: none;">
{$group->name}
</span>
</td>
<td>
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}">
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
</a>
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}?act=permissions">
<span class="aui-icon aui-icon-small aui-iconfont-book">{_c_permissions}</span>
</a>
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}?act=members">
<span class="aui-icon aui-icon-small aui-iconfont-group">{_members}</span>
</a>
<a class="aui-button aui-button-secondary" href="/admin/chandler/groups/{$group->id}?act=delete">
<span class="aui-icon aui-icon-small aui-iconfont-delete">{_delete}</span>
</a>
</td>
</tr>
</tbody>
</table>
{/block}

View file

@ -68,6 +68,43 @@
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>{_admin_user_online_deceased}</option>
</select>
</div>
<hr/>
<h2>{_c_groups}</h2>
<div>
<div class="field-group">
<label for="add-to-group">{_c_add_to_group}</label>
<select class="select" name="add-to-group">
<option n:foreach="$c_groups_list as $group" value="{$group->id}">
{$group->name}
</option>
</select>
<table class="aui aui-table-list">
<thead>
<tr>
<th>ID</th>
<th>{_admin_actions}</th>
</tr>
</thead>
<tbody>
<tr n:foreach="$c_memberships as $membership">
<td>
<a href="/admin/chandler/groups/{$membership->group}?act=members">{$membership->group}</a>
</td>
<td>
<a
class="aui-icon aui-icon-small aui-iconfont-cross"
href="/admin/chandler/groups/{$membership->group}?act=removeMember&uid={$user->getChandlerGUID()}"
style="margin: 0 50%;"
>
{_c_remove_from_group}
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="buttons-container">
<div class="buttons">
<input type="hidden" name="hash" value="{$csrfToken}" />

View file

@ -21,6 +21,7 @@
<thead>
<tr>
<th>ID</th>
<th>UUID</th>
<th>{_admin_name}</th>
<th>{_gender}</th>
<th>{_admin_shortcode}</th>
@ -31,6 +32,7 @@
<tbody>
<tr n:foreach="$users as $user">
<td>{$user->getId()}</td>
<td>{$user->getChandlerGUID()}</td>
<td>
<span class="aui-avatar aui-avatar-xsmall">
<span class="aui-avatar-inner">

View file

@ -47,4 +47,5 @@ services:
- openvk\Web\Models\Repositories\ContentSearchRepository
- openvk\Web\Models\Repositories\Aliases
- openvk\Web\Models\Repositories\BannedLinks
- openvk\Web\Models\Repositories\ChandlerGroups
- openvk\Web\Presenters\MaintenancePresenter

View file

@ -323,6 +323,12 @@ routes:
handler: "VKAPI->tokenLogin"
- url: "/admin/sandbox"
handler: "About->sandbox"
- url: "/admin/chandler/groups"
handler: "Admin->chandlerGroups"
- url: "/admin/chandler/groups/{slug}"
handler: "Admin->chandlerGroup"
- url: "/admin/chandler/users/{slug}"
handler: "Admin->chandlerUser"
- url: "/internal/wall{num}"
handler: "Wall->wallEmbedded"
- url: "/robots.txt"

View file

@ -1186,6 +1186,28 @@
"url_is_banned_title" = "Link to a suspicious site";
"url_is_banned_proceed" = "Follow the link";
/* Chandler */
"c_user_removed_from_group" = "The user has been removed from the group";
"c_permission_removed_from_group" = "The permission has been removed from the group";
"c_group_removed" = "The group has been deleted.";
"c_groups" = "Chandler Groups";
"c_users" = "Chandler Users";
"c_group_permissions" = "Permissions";
"c_group_members" = "Members";
"c_model" = "Model";
"c_permission" = "Permission";
"c_permissions" = "Permissions";
"c_color" = "Color";
"add" = "Add";
"c_edit_groups" = "Edit Groups";
"c_user_is_not_in_group" = "The relationship between the user and the group was not found.";
"c_permission_not_found" = "The relationship between the permission and the group was not found.";
"c_group_not_found" = "The group was not found.";
"c_user_is_already_in_group" = "This user is already a member of this group.";
"c_add_to_group" = "Add to group";
"c_remove_from_group" = "Remove from group";
/* Maintenance */
"global_maintenance" = "Undergoing maintenance";

View file

@ -1244,6 +1244,28 @@
"url_is_banned_title" = "Ссылка на подозрительный сайт";
"url_is_banned_proceed" = "Перейти по ссылке";
/* Chandler */
"c_user_removed_from_group" = "Пользователь был удалён из группы";
"c_permission_removed_from_group" = "Право было удалено из группы";
"c_group_removed" = "Группа была удалена.";
"c_groups" = "Группы Chandler";
"c_users" = "Пользователи Chandler";
"c_group_permissions" = "Права";
"c_group_members" = "Участники";
"c_model" = "Модель";
"c_permission" = "Право";
"c_permissions" = "Права";
"c_color" = "Цвет";
"add" = "Добавить";
"c_edit_groups" = "Редактировать группы";
"c_user_is_not_in_group" = "Связь пользователя и группы не найдена.";
"c_permission_not_found" = "Связь права и группы не найдена.";
"c_group_not_found" = "Группа не найдена.";
"c_user_is_already_in_group" = "Этот пользователь уже включён в эту группу.";
"c_add_to_group" = "Добавить в группу";
"c_remove_from_group" = "Исключить из группы";
/* Maintenance */
"global_maintenance" = "Технические работы";