first commit
This commit is contained in:
@@ -0,0 +1,591 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Auth;
|
||||
use App\Core\DB;
|
||||
use App\Core\View;
|
||||
|
||||
class AdminBoardController
|
||||
{
|
||||
private function requireAdmin(): void
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
header('Location: /login');
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
if ((int)($user['is_admin'] ?? 0) !== 1) {
|
||||
http_response_code(403);
|
||||
echo 'Доступ запрещен';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public function index(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->query("
|
||||
SELECT id, name, code, description, is_active, show_on_home, created_at
|
||||
FROM boards
|
||||
ORDER BY id DESC
|
||||
");
|
||||
$boards = $stmt->fetchAll();
|
||||
|
||||
View::render('admin/boards/index', [
|
||||
'boards' => $boards,
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
View::render('admin/boards/create', [
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$code = trim($_POST['code'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
||||
|
||||
if ($name === '' || $code === '') {
|
||||
$_SESSION['error'] = 'Заполните название и код доски';
|
||||
header('Location: /admin/boards/create');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO boards (name, code, description, is_active, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, NOW(), NOW())
|
||||
");
|
||||
$stmt->execute([
|
||||
$name,
|
||||
$code,
|
||||
$description !== '' ? $description : null,
|
||||
$isActive,
|
||||
]);
|
||||
$showOnHome = isset($_POST['show_on_home']) ? 1 : 0;
|
||||
header('Location: /admin/boards');
|
||||
exit;
|
||||
}
|
||||
public function showModal(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_GET['id'] ?? 0);
|
||||
|
||||
if ($boardId <= 0) {
|
||||
http_response_code(400);
|
||||
echo 'Board ID is required';
|
||||
return;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
b.*,
|
||||
COUNT(t.id) AS task_count,
|
||||
SUM(CASE WHEN t.status = 'NEW' THEN 1 ELSE 0 END) AS new_count,
|
||||
SUM(CASE WHEN t.status = 'IN_PROGRESS' THEN 1 ELSE 0 END) AS in_progress_count,
|
||||
SUM(CASE WHEN t.status = 'REVIEW' THEN 1 ELSE 0 END) AS review_count,
|
||||
SUM(CASE WHEN t.status = 'DONE' THEN 1 ELSE 0 END) AS done_count,
|
||||
SUM(CASE WHEN t.status = 'CANCELED' THEN 1 ELSE 0 END) AS canceled_count,
|
||||
SUM(CASE WHEN t.status = 'OVERDUE' THEN 1 ELSE 0 END) AS overdue_count
|
||||
FROM boards b
|
||||
LEFT JOIN tasks t ON t.board_id = b.id
|
||||
WHERE b.id = ?
|
||||
GROUP BY b.id
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$boardId]);
|
||||
$board = $stmt->fetch();
|
||||
|
||||
if (!$board) {
|
||||
http_response_code(404);
|
||||
echo 'Board not found';
|
||||
return;
|
||||
}
|
||||
|
||||
View::render('admin/boards/modal', [
|
||||
'board' => $board,
|
||||
'user' => Auth::user(),
|
||||
], null);
|
||||
}
|
||||
public function access(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_GET['id'] ?? 0);
|
||||
|
||||
if ($boardId <= 0) {
|
||||
http_response_code(400);
|
||||
echo 'Board ID is required';
|
||||
return;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, name, code, description
|
||||
FROM boards
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$boardId]);
|
||||
$board = $stmt->fetch();
|
||||
|
||||
if (!$board) {
|
||||
http_response_code(404);
|
||||
echo 'Доска не найдена';
|
||||
return;
|
||||
}
|
||||
|
||||
$groups = $pdo->query("
|
||||
SELECT id, name
|
||||
FROM ad_groups
|
||||
ORDER BY name ASC
|
||||
")->fetchAll();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT group_id
|
||||
FROM board_access_groups
|
||||
WHERE board_id = ?
|
||||
");
|
||||
$stmt->execute([$boardId]);
|
||||
$selectedGroupIds = array_map('intval', array_column($stmt->fetchAll(), 'group_id'));
|
||||
|
||||
View::render('admin/boards/access', [
|
||||
'board' => $board,
|
||||
'groups' => $groups,
|
||||
'selectedGroupIds' => $selectedGroupIds,
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function saveAccess(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_POST['board_id'] ?? 0);
|
||||
$groupIds = $_POST['group_ids'] ?? [];
|
||||
|
||||
if ($boardId <= 0) {
|
||||
$_SESSION['error'] = 'Некорректная доска';
|
||||
header('Location: /admin/boards');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("DELETE FROM board_access_groups WHERE board_id = ?");
|
||||
$stmt->execute([$boardId]);
|
||||
|
||||
if (is_array($groupIds)) {
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO board_access_groups (board_id, group_id, created_at)
|
||||
VALUES (?, ?, NOW())
|
||||
");
|
||||
|
||||
foreach ($groupIds as $groupId) {
|
||||
$groupId = (int)$groupId;
|
||||
if ($groupId > 0) {
|
||||
$stmt->execute([$boardId, $groupId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
$_SESSION['success'] = 'Права доступа к доске сохранены';
|
||||
} catch (\Throwable $e) {
|
||||
$pdo->rollBack();
|
||||
$_SESSION['error'] = 'Ошибка сохранения прав: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
header('Location: /admin/boards/access?id=' . $boardId);
|
||||
exit;
|
||||
}
|
||||
public function statuses(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_GET['id'] ?? 0);
|
||||
|
||||
if ($boardId <= 0) {
|
||||
http_response_code(400);
|
||||
echo 'Board ID is required';
|
||||
return;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT id, name, code FROM boards WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$boardId]);
|
||||
$board = $stmt->fetch();
|
||||
|
||||
if (!$board) {
|
||||
http_response_code(404);
|
||||
echo 'Доска не найдена';
|
||||
return;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM board_statuses
|
||||
WHERE board_id = ?
|
||||
ORDER BY sort_order ASC, id ASC
|
||||
");
|
||||
$stmt->execute([$boardId]);
|
||||
$statuses = $stmt->fetchAll();
|
||||
|
||||
View::render('admin/boards/statuses/index', [
|
||||
'board' => $board,
|
||||
'statuses' => $statuses,
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function createStatus(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_GET['board_id'] ?? 0);
|
||||
|
||||
$pdo = DB::connection();
|
||||
$stmt = $pdo->prepare("SELECT id, name, code FROM boards WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$boardId]);
|
||||
$board = $stmt->fetch();
|
||||
|
||||
if (!$board) {
|
||||
http_response_code(404);
|
||||
echo 'Доска не найдена';
|
||||
return;
|
||||
}
|
||||
|
||||
View::render('admin/boards/statuses/create', [
|
||||
'board' => $board,
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeStatus(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_POST['board_id'] ?? 0);
|
||||
$code = trim($_POST['code'] ?? '');
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$color = trim($_POST['color'] ?? 'secondary');
|
||||
$sortOrder = (int)($_POST['sort_order'] ?? 100);
|
||||
$isDone = isset($_POST['is_done']) ? 1 : 0;
|
||||
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
||||
|
||||
if ($boardId <= 0 || $code === '' || $name === '') {
|
||||
$_SESSION['error'] = 'Заполните обязательные поля статуса';
|
||||
header('Location: /admin/boards/statuses/create?board_id=' . $boardId);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO board_statuses (
|
||||
board_id, code, name, color, sort_order, is_done, is_active, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
");
|
||||
$stmt->execute([
|
||||
$boardId,
|
||||
$code,
|
||||
$name,
|
||||
$color,
|
||||
$sortOrder,
|
||||
$isDone,
|
||||
$isActive,
|
||||
]);
|
||||
|
||||
header('Location: /admin/boards/statuses?id=' . $boardId);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function editStatus(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$statusId = (int)($_GET['id'] ?? 0);
|
||||
|
||||
$pdo = DB::connection();
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT bs.*, b.name AS board_name, b.code AS board_code
|
||||
FROM board_statuses bs
|
||||
INNER JOIN boards b ON b.id = bs.board_id
|
||||
WHERE bs.id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$statusId]);
|
||||
$status = $stmt->fetch();
|
||||
|
||||
if (!$status) {
|
||||
http_response_code(404);
|
||||
echo 'Статус не найден';
|
||||
return;
|
||||
}
|
||||
|
||||
View::render('admin/boards/statuses/edit', [
|
||||
'status' => $status,
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateStatus(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$statusId = (int)($_POST['id'] ?? 0);
|
||||
$code = trim($_POST['code'] ?? '');
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$color = trim($_POST['color'] ?? 'secondary');
|
||||
$sortOrder = (int)($_POST['sort_order'] ?? 100);
|
||||
$isDone = isset($_POST['is_done']) ? 1 : 0;
|
||||
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
||||
|
||||
if ($statusId <= 0 || $code === '' || $name === '') {
|
||||
$_SESSION['error'] = 'Заполните обязательные поля';
|
||||
header('Location: /admin');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT board_id FROM board_statuses WHERE id = ? LIMIT 1");
|
||||
$stmt->execute([$statusId]);
|
||||
$boardId = (int)$stmt->fetchColumn();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE board_statuses
|
||||
SET
|
||||
code = ?,
|
||||
name = ?,
|
||||
color = ?,
|
||||
sort_order = ?,
|
||||
is_done = ?,
|
||||
is_active = ?,
|
||||
updated_at = NOW()
|
||||
WHERE id = ?
|
||||
");
|
||||
$stmt->execute([
|
||||
$code,
|
||||
$name,
|
||||
$color,
|
||||
$sortOrder,
|
||||
$isDone,
|
||||
$isActive,
|
||||
$statusId,
|
||||
]);
|
||||
|
||||
header('Location: /admin/boards/statuses?id=' . $boardId);
|
||||
exit;
|
||||
}
|
||||
public function delete(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_POST['id'] ?? 0);
|
||||
|
||||
if ($boardId <= 0) {
|
||||
$_SESSION['error'] = 'Доска не найдена';
|
||||
header('Location: /admin/boards');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, name, code
|
||||
FROM boards
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$boardId]);
|
||||
$board = $stmt->fetch();
|
||||
|
||||
if (!$board) {
|
||||
$_SESSION['error'] = 'Доска не найдена';
|
||||
header('Location: /admin/boards');
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM boards WHERE id = ?");
|
||||
$stmt->execute([$boardId]);
|
||||
|
||||
$_SESSION['success'] = 'Доска "' . ($board['name'] ?? '') . '" удалена вместе со связанными данными';
|
||||
header('Location: /admin/boards');
|
||||
exit;
|
||||
}
|
||||
public function edit(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_GET['id'] ?? 0);
|
||||
|
||||
if ($boardId <= 0) {
|
||||
http_response_code(400);
|
||||
echo 'Board ID is required';
|
||||
return;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, name, code, description, is_active, show_on_home
|
||||
FROM boards
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$boardId]);
|
||||
$board = $stmt->fetch();
|
||||
|
||||
if (!$board) {
|
||||
http_response_code(404);
|
||||
echo 'Доска не найдена';
|
||||
return;
|
||||
}
|
||||
|
||||
View::render('admin/boards/edit', [
|
||||
'board' => $board,
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(): void
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
$boardId = (int)($_POST['id'] ?? 0);
|
||||
$name = trim((string)($_POST['name'] ?? ''));
|
||||
$code = trim((string)($_POST['code'] ?? ''));
|
||||
$description = trim((string)($_POST['description'] ?? ''));
|
||||
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
||||
|
||||
if ($boardId <= 0 || $name === '' || $code === '') {
|
||||
$_SESSION['error'] = 'Заполните название и код доски';
|
||||
header('Location: /admin/boards/edit?id=' . $boardId);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = DB::connection();
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE boards
|
||||
SET
|
||||
name = ?,
|
||||
code = ?,
|
||||
description = ?,
|
||||
is_active = ?,
|
||||
updated_at = NOW()
|
||||
WHERE id = ?
|
||||
");
|
||||
$stmt->execute([
|
||||
$name,
|
||||
$code,
|
||||
$description !== '' ? $description : null,
|
||||
$isActive,
|
||||
$boardId,
|
||||
]);
|
||||
|
||||
$_SESSION['success'] = 'Доска обновлена';
|
||||
} catch (\PDOException $e) {
|
||||
$_SESSION['error'] = 'Не удалось обновить доску: ' . $e->getMessage();
|
||||
header('Location: /admin/boards/edit?id=' . $boardId);
|
||||
exit;
|
||||
}
|
||||
$showOnHome = isset($_POST['show_on_home']) ? 1 : 0;
|
||||
header('Location: /admin/boards');
|
||||
exit;
|
||||
}
|
||||
public function saveAll(): void
|
||||
{
|
||||
$pdo = DB::connection();
|
||||
|
||||
$boardId = (int)$_POST['board_id'];
|
||||
|
||||
// === ОБНОВЛЕНИЕ ДОСКИ ===
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE boards SET
|
||||
name = ?,
|
||||
code = ?,
|
||||
description = ?,
|
||||
is_active = ?,
|
||||
show_on_home = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
$_POST['name'],
|
||||
$_POST['code'],
|
||||
$_POST['description'] ?? null,
|
||||
isset($_POST['is_active']) ? 1 : 0,
|
||||
isset($_POST['show_on_home']) ? 1 : 0,
|
||||
$boardId
|
||||
]);
|
||||
|
||||
// === КАСТОМНЫЕ ПОЛЯ ===
|
||||
if (!empty($_POST['fields'])) {
|
||||
foreach ($_POST['fields'] as $field) {
|
||||
|
||||
if (!empty($field['delete'])) {
|
||||
$pdo->prepare("DELETE FROM board_fields WHERE id = ?")
|
||||
->execute([$field['id']]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$pdo->prepare("
|
||||
UPDATE board_fields
|
||||
SET name = ?, field_type = ?
|
||||
WHERE id = ?
|
||||
")->execute([
|
||||
$field['name'],
|
||||
$field['type'],
|
||||
$field['id']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// === НОВЫЕ ПОЛЯ ===
|
||||
if (!empty($_POST['new_fields'])) {
|
||||
foreach ($_POST['new_fields'] as $f) {
|
||||
if (empty($f['name'])) continue;
|
||||
|
||||
$pdo->prepare("
|
||||
INSERT INTO board_fields (board_id, code, name)
|
||||
VALUES (?, ?, ?)
|
||||
")->execute([
|
||||
$boardId,
|
||||
strtolower(preg_replace('/[^a-z0-9]/', '_', $f['name'])),
|
||||
$f['name']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: ' . $_SERVER['HTTP_REFERER']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user