Files
ave-cms/adminx/modules/Users/Controller.php
T
2026-07-30 11:56:32 +03:00

259 lines
7.8 KiB
PHP

<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file adminx/modules/Users/Controller.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Adminx\Users;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
use App\Common\AdminAssets;
use App\Common\AuditLog;
use App\Common\Auth;
use App\Common\Controller as BaseController;
use App\Common\Permission;
use App\Adminx\Support\BulkActionExecutor;
use App\Adminx\Support\Roles;
use App\Helpers\Request;
/**
* CRUD пользователей. Список — server-render (таблица AdminKit), создание/правка —
* ajax из drawer-формы по единому контракту success/error (§3).
*/
class Controller extends BaseController
{
/** GET /users */
public function index(array $params = [])
{
AdminAssets::addStyle($this->base() . '/modules/Users/assets/users.css', 50);
AdminAssets::addScript($this->base() . '/modules/Users/assets/users.js', 50);
$search = Request::getStr('q');
$role = Request::getStr('role');
return $this->render('@users/list.twig', [
'users' => Model::all($search, $role),
'roles' => Roles::map(),
'stats' => Model::stats(),
'q' => $search,
'role' => $role,
'can_manage' => Permission::check('manage_users'),
'current_id' => Auth::id(),
]);
}
/** GET /users/{id} — данные пользователя (JSON, для формы правки). */
public function show(array $params = [])
{
$user = Model::find($params['id'] ?? 0);
if (!$user) {
return $this->error('Пользователь не найден', [], 404);
}
return $this->success('', ['data' => [
'id' => (int) $user->id,
'name' => $user->name,
'email' => $user->email,
'login' => $user->login,
'phone' => $user->phone,
'role' => $user->role,
'is_active' => (int) $user->is_active,
'created_at' => $user->created_at,
'updated_at' => $user->updated_at,
]]);
}
/** POST /users — создать. */
public function store(array $params = [])
{
if (($resp = $this->guard()) !== null) {
return $resp;
}
$data = $this->input();
$errors = $this->validate($data, 0, true);
if ($errors) {
return $this->error('Проверьте поля формы', $errors);
}
$id = Model::create($data);
return $this->success('Пользователь создан', ['redirect' => $this->base() . '/users']);
}
/** POST /users/{id} — обновить. */
public function update(array $params = [])
{
if (($resp = $this->guard()) !== null) {
return $resp;
}
$id = (int) ($params['id'] ?? 0);
$user = Model::find($id);
if (!$user) {
return $this->error('Пользователь не найден', [], 404);
}
$data = $this->input();
if ($id === Auth::id()) {
$data['role'] = (string) $user->role;
$data['is_active'] = true;
}
$errors = $this->validate($data, $id, false);
if ($errors) {
return $this->error('Проверьте поля формы', $errors);
}
Model::update($id, $data);
return $this->success('Изменения сохранены', ['redirect' => $this->base() . '/users']);
}
/** POST /users/{id}/toggle — вкл/выкл активность. */
public function toggle(array $params = [])
{
if (($resp = $this->guard()) !== null) {
return $resp;
}
$id = (int) ($params['id'] ?? 0);
if (!Model::find($id)) {
return $this->error('Пользователь не найден', [], 404);
}
if ($id === Auth::id()) {
return $this->error('Нельзя отключить собственную учётную запись', [], 422);
}
$active = Model::toggle($id);
return $this->success($active ? 'Пользователь включён' : 'Пользователь отключён', [
'data' => ['is_active' => $active],
]);
}
/** POST /users/{id}/delete — удалить. */
public function destroy(array $params = [])
{
if (($resp = $this->guard()) !== null) {
return $resp;
}
$id = (int) ($params['id'] ?? 0);
if (!Model::find($id)) {
return $this->error('Пользователь не найден', [], 404);
}
if ($id === Auth::id()) {
return $this->error('Нельзя удалить самого себя', [], 422);
}
Model::delete($id);
return $this->success('Пользователь удалён', ['redirect' => $this->base() . '/users']);
}
public function bulk(array $params = array())
{
if (($response = $this->guard()) !== null) {
return $response;
}
$action = Request::postStr('action', '');
$currentId = (int) Auth::id();
$handler = function ($id) use ($action, $currentId) {
if ((int) $id === $currentId || !Model::find($id)) {
return false;
}
Model::setActive($id, $action === 'activate');
return true;
};
try {
$result = BulkActionExecutor::execute($action, Request::post('ids', array()), array(
'activate' => $handler,
'deactivate' => $handler,
), 200);
} catch (\InvalidArgumentException $e) {
return $this->error($e->getMessage(), array(), 422);
}
AuditLog::record('user.bulk_status_updated', array(
'actor_id' => Auth::id(),
'target_type' => 'user',
'meta' => array(
'action' => $action,
'requested' => $result['requested'],
'done' => $result['done'],
'skipped' => $result['skipped'],
),
));
return $this->success('Состояние пользователей изменено', array('data' => $result));
}
// ------------------------------------------------------------------ //
/** CSRF + право на изменение. */
protected function guard()
{
return $this->guardPermission('manage_users');
}
protected function input()
{
return [
'name' => trim(Request::postStr('name')),
'email' => trim(Request::postStr('email')),
'login' => trim(Request::postStr('login')),
'phone' => trim(Request::postStr('phone')),
'role' => Request::postStr('role'),
'password' => (string) Request::post('password', ''),
'is_active' => Request::postBool('is_active', false),
];
}
/** @return array field => message */
protected function validate(array $data, $id, $isCreate)
{
$errors = [];
if ($data['name'] === '') {
$errors['name'] = 'Укажите имя.';
}
if ($data['email'] === '') {
$errors['email'] = 'Укажите email.';
} elseif (!preg_match('/^[^@\s]+@[^@\s]+$/', $data['email'])) {
//-- Допускаем локальные адреса вида user@local (демо/дев), которые
//-- FILTER_VALIDATE_EMAIL отвергает из-за отсутствия TLD.
$errors['email'] = 'Некорректный email.';
} elseif (Model::emailExists($data['email'], (int) $id)) {
$errors['email'] = 'Этот email уже занят.';
}
if ($data['login'] !== '' && Model::loginExists($data['login'], (int) $id)) {
$errors['login'] = 'Этот логин уже занят.';
}
if (!in_array($data['role'], Roles::codes(), true)) {
$errors['role'] = 'Выберите роль.';
}
if ($isCreate && strlen($data['password']) < 6) {
$errors['password'] = 'Пароль не короче 6 символов.';
} elseif (!$isCreate && $data['password'] !== '' && strlen($data['password']) < 6) {
$errors['password'] = 'Пароль не короче 6 символов.';
}
return $errors;
}
}