591 lines
16 KiB
PHP
591 lines
16 KiB
PHP
<?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']);
|
|
}
|
|
} |