1. Создание личных проектов 2. Управление командой 3. Приглашение участников 4. Уведомления и многое другое...
541 lines
24 KiB
PHP
541 lines
24 KiB
PHP
<?php
|
||
|
||
class ProjectAccess {
|
||
|
||
// Список всех доступных прав
|
||
const PERMISSIONS = [
|
||
'create_task', // создание задач
|
||
'edit_task', // редактирование любых задач
|
||
'edit_own_task_only', // редактирование только назначенных на себя
|
||
'delete_task', // удаление задач
|
||
'move_task', // перемещение любых задач между колонками
|
||
'move_own_task_only', // перемещение только назначенных на себя
|
||
'archive_task', // архивирование задач
|
||
'create_column', // создание колонок
|
||
'edit_column', // редактирование колонок
|
||
'delete_column', // удаление колонок
|
||
'manage_departments', // управление отделами
|
||
'remove_members', // удаление участников (приглашать/редактировать могут только админы)
|
||
'create_comment', // создание комментариев в любых задачах
|
||
'create_comment_own_task_only', // создание комментариев только в назначенных на себя
|
||
'delete_own_comments', // удаление своих комментариев
|
||
'delete_all_comments', // удаление любых комментариев в проекте
|
||
'upload_files', // загрузка файлов
|
||
'upload_images' // загрузка картинок
|
||
];
|
||
|
||
// Права по умолчанию для нового участника
|
||
const DEFAULT_PERMISSIONS = [
|
||
'create_task' => true, // создание задач
|
||
'edit_task' => false, // редактирование любых задач
|
||
'edit_own_task_only' => true, // редактирование только назначенных на себя
|
||
'delete_task' => false, // удаление задач
|
||
'move_task' => false, // перемещение любых задач между колонками
|
||
'move_own_task_only' => true, // перемещение только назначенных на себя
|
||
'archive_task' => true, // архивирование задач
|
||
'create_column' => false, // создание колонок
|
||
'edit_column' => false, // редактирование колонок
|
||
'delete_column' => false, // удаление колонок
|
||
'manage_departments' => false, // управление отделами
|
||
'remove_members' => false, // удаление участников
|
||
'create_comment' => false, // создание комментариев в любых задачах
|
||
'create_comment_own_task_only' => true, // создание комментариев только в назначенных на себя
|
||
'delete_own_comments' => true, // удаление своих комментариев
|
||
'delete_all_comments' => false, // удаление любых комментариев
|
||
'upload_files' => true, // загрузка файлов
|
||
'upload_images' => true // загрузка картинок
|
||
];
|
||
|
||
// ==================== ПРОВЕРКИ ДОСТУПА ====================
|
||
|
||
// Проверить, является ли пользователь владельцем проекта (в id_admin таблицы projects)
|
||
public static function isOwner(int $project_id, int $user_id): bool {
|
||
$project = Database::get('project', ['id_admin'], ['id' => $project_id]);
|
||
if (!$project || !$project['id_admin']) {
|
||
return false;
|
||
}
|
||
$owners = json_decode($project['id_admin'], true);
|
||
return is_array($owners) && in_array($user_id, $owners);
|
||
}
|
||
|
||
// Проверить, является ли пользователь участником проекта
|
||
public static function isMember(int $project_id, int $user_id): bool {
|
||
// Владелец всегда имеет доступ
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
return true;
|
||
}
|
||
return Database::has('project_members', [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
}
|
||
|
||
// Проверить, является ли пользователь админом проекта
|
||
public static function isAdmin(int $project_id, int $user_id): bool {
|
||
// Владелец = безоговорочный админ
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
return true;
|
||
}
|
||
return Database::has('project_members', [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id,
|
||
'is_admin' => 1
|
||
]);
|
||
}
|
||
|
||
// Проверить конкретное право (владелец/админ имеет все права)
|
||
public static function can(int $project_id, int $user_id, string $permission): bool {
|
||
// Владелец может всё
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
return true;
|
||
}
|
||
|
||
$member = Database::get('project_members', ['is_admin', 'permissions'], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
if (!$member) {
|
||
return false;
|
||
}
|
||
|
||
// Админ может всё
|
||
if ((int)$member['is_admin'] === 1) {
|
||
return true;
|
||
}
|
||
|
||
$permissions = $member['permissions'] ? json_decode($member['permissions'], true) : [];
|
||
return isset($permissions[$permission]) && $permissions[$permission] === true;
|
||
}
|
||
|
||
// Проверить право на редактирование задачи (с учётом edit_own_task_only)
|
||
// "Своя задача" = назначен на пользователя ИЛИ создана пользователем
|
||
public static function canEditTask(int $project_id, int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): bool {
|
||
if (self::isAdmin($project_id, $user_id)) {
|
||
return true;
|
||
}
|
||
|
||
if (self::can($project_id, $user_id, 'edit_task')) {
|
||
return true;
|
||
}
|
||
|
||
// Создатель + право создания → может редактировать свои созданные
|
||
if ($task_creator_id === $user_id && self::can($project_id, $user_id, 'create_task')) {
|
||
return true;
|
||
}
|
||
|
||
// Назначена на себя + право edit_own_task_only
|
||
if ($task_assignee_id === $user_id && self::can($project_id, $user_id, 'edit_own_task_only')) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Проверить право на перемещение задачи (с учётом move_own_task_only)
|
||
// "Своя задача" = назначен на пользователя ИЛИ создана пользователем
|
||
public static function canMoveTask(int $project_id, int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): bool {
|
||
if (self::isAdmin($project_id, $user_id)) {
|
||
return true;
|
||
}
|
||
|
||
if (self::can($project_id, $user_id, 'move_task')) {
|
||
return true;
|
||
}
|
||
|
||
// Создатель + право создания → может перемещать свои созданные
|
||
if ($task_creator_id === $user_id && self::can($project_id, $user_id, 'create_task')) {
|
||
return true;
|
||
}
|
||
|
||
// Назначена на себя + право move_own_task_only
|
||
if ($task_assignee_id === $user_id && self::can($project_id, $user_id, 'move_own_task_only')) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Проверить право на создание комментария в задаче
|
||
public static function canCreateComment(int $project_id, int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): bool {
|
||
if (self::isAdmin($project_id, $user_id)) {
|
||
return true;
|
||
}
|
||
|
||
// Полное право — комментировать любые задачи
|
||
if (self::can($project_id, $user_id, 'create_comment')) {
|
||
return true;
|
||
}
|
||
|
||
// Создатель + право создания → может комментировать свои созданные
|
||
if ($task_creator_id === $user_id && self::can($project_id, $user_id, 'create_task')) {
|
||
return true;
|
||
}
|
||
|
||
// Назначена на себя + право create_comment_own_task_only
|
||
if ($task_assignee_id === $user_id && self::can($project_id, $user_id, 'create_comment_own_task_only')) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// ==================== ПРОВЕРКИ С EXIT ====================
|
||
|
||
// Требовать доступ к проекту (при отказе — 403 и exit)
|
||
public static function requireAccess(int $project_id, ?int $user_id): void {
|
||
if (!$user_id) {
|
||
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
|
||
}
|
||
|
||
if (!self::isMember($project_id, $user_id)) {
|
||
RestApi::response(['success' => false, 'errors' => ['access' => 'Нет доступа к проекту']], 403);
|
||
}
|
||
}
|
||
|
||
// Требовать права админа
|
||
public static function requireAdmin(int $project_id, ?int $user_id): void {
|
||
if (!$user_id) {
|
||
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
|
||
}
|
||
|
||
if (!self::isAdmin($project_id, $user_id)) {
|
||
RestApi::response(['success' => false, 'errors' => ['access' => 'Требуются права администратора']], 403);
|
||
}
|
||
}
|
||
|
||
// Требовать конкретное право
|
||
public static function requirePermission(int $project_id, ?int $user_id, string $permission): void {
|
||
if (!$user_id) {
|
||
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
|
||
}
|
||
|
||
if (!self::can($project_id, $user_id, $permission)) {
|
||
RestApi::response(['success' => false, 'errors' => ['access' => 'Недостаточно прав']], 403);
|
||
}
|
||
}
|
||
|
||
// Требовать право на редактирование задачи
|
||
public static function requireEditTask(int $project_id, ?int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): void {
|
||
if (!$user_id) {
|
||
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
|
||
}
|
||
|
||
if (!self::canEditTask($project_id, $user_id, $task_assignee_id, $task_creator_id)) {
|
||
RestApi::response(['success' => false, 'errors' => ['access' => 'Нет прав на редактирование задачи']], 403);
|
||
}
|
||
}
|
||
|
||
// Требовать право на перемещение задачи
|
||
public static function requireMoveTask(int $project_id, ?int $user_id, ?int $task_assignee_id, ?int $task_creator_id = null): void {
|
||
if (!$user_id) {
|
||
RestApi::response(['success' => false, 'errors' => ['auth' => 'Требуется авторизация']], 401);
|
||
}
|
||
|
||
if (!self::canMoveTask($project_id, $user_id, $task_assignee_id, $task_creator_id)) {
|
||
RestApi::response(['success' => false, 'errors' => ['access' => 'Нет прав на перемещение задачи']], 403);
|
||
}
|
||
}
|
||
|
||
// ==================== УПРАВЛЕНИЕ УЧАСТНИКАМИ ====================
|
||
|
||
// Добавить участника в проект
|
||
public static function addMember(int $project_id, int $user_id, int $current_user_id, bool $is_admin = false, ?array $permissions = null): array {
|
||
// Нельзя пригласить себя
|
||
if ($user_id === $current_user_id) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя пригласить себя в проект']];
|
||
}
|
||
|
||
if (self::isMember($project_id, $user_id)) {
|
||
return ['success' => false, 'errors' => ['member' => 'Пользователь уже участник проекта']];
|
||
}
|
||
|
||
$perms = $permissions ?? self::DEFAULT_PERMISSIONS;
|
||
|
||
Database::insert('project_members', [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id,
|
||
'is_admin' => $is_admin ? 1 : 0,
|
||
'permissions' => json_encode($perms),
|
||
'created_at' => date('Y-m-d H:i:s')
|
||
]);
|
||
|
||
return ['success' => true, 'id' => Database::id()];
|
||
}
|
||
|
||
// Удалить участника из проекта (владельца удалить нельзя, админ не может удалить себя)
|
||
public static function removeMember(int $project_id, int $user_id, int $current_user_id): array {
|
||
// Владельца удалить нельзя
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя удалить владельца проекта']];
|
||
}
|
||
|
||
$member = Database::get('project_members', ['is_admin'], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
if (!$member) {
|
||
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
|
||
}
|
||
|
||
// Админ может выйти сам, если есть другие админы или владелец
|
||
// (владелец всегда есть в id_admin проекта, так что это безопасно)
|
||
|
||
Database::delete('project_members', [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
return ['success' => true];
|
||
}
|
||
|
||
// Получить всех участников проекта (включая владельцев из id_admin)
|
||
public static function getMembers(int $project_id): array {
|
||
$result = [];
|
||
$addedUserIds = [];
|
||
|
||
// Получаем владельцев из id_admin проекта
|
||
$project = Database::get('project', ['id_admin'], ['id' => $project_id]);
|
||
if ($project && $project['id_admin']) {
|
||
$ownerIds = json_decode($project['id_admin'], true);
|
||
if (is_array($ownerIds) && count($ownerIds) > 0) {
|
||
$owners = Database::select('accounts', ['id', 'name', 'username', 'avatar_url', 'telegram'], ['id' => $ownerIds]);
|
||
foreach ($owners as $owner) {
|
||
$result[] = [
|
||
'id' => null,
|
||
'id_user' => (int)$owner['id'],
|
||
'is_admin' => true,
|
||
'is_owner' => true,
|
||
'permissions' => [],
|
||
'created_at' => null,
|
||
'name' => $owner['name'],
|
||
'username' => $owner['username'],
|
||
'avatar_url' => $owner['avatar_url'],
|
||
'telegram' => $owner['telegram']
|
||
];
|
||
$addedUserIds[] = (int)$owner['id'];
|
||
}
|
||
}
|
||
}
|
||
|
||
// Получаем участников из project_members
|
||
$members = Database::select('project_members', [
|
||
'[>]accounts' => ['id_user' => 'id']
|
||
], [
|
||
'project_members.id',
|
||
'project_members.id_user',
|
||
'project_members.is_admin',
|
||
'project_members.permissions',
|
||
'project_members.created_at',
|
||
'accounts.name',
|
||
'accounts.username',
|
||
'accounts.avatar_url',
|
||
'accounts.telegram'
|
||
], [
|
||
'project_members.id_project' => $project_id
|
||
]);
|
||
|
||
foreach ($members as $member) {
|
||
$userId = (int)$member['id_user'];
|
||
// Пропускаем если уже добавлен как владелец
|
||
if (in_array($userId, $addedUserIds)) {
|
||
continue;
|
||
}
|
||
$result[] = [
|
||
'id' => $member['id'],
|
||
'id_user' => $userId,
|
||
'is_admin' => (int)$member['is_admin'] === 1,
|
||
'is_owner' => false,
|
||
'permissions' => $member['permissions'] ? json_decode($member['permissions'], true) : [],
|
||
'created_at' => $member['created_at'],
|
||
'name' => $member['name'],
|
||
'username' => $member['username'],
|
||
'avatar_url' => $member['avatar_url'],
|
||
'telegram' => $member['telegram']
|
||
];
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
// Получить ID всех проектов пользователя (включая где он владелец)
|
||
public static function getUserProjectIds(int $user_id): array {
|
||
$projectIds = [];
|
||
|
||
// Проекты из project_members
|
||
$rows = Database::select('project_members', ['id_project'], [
|
||
'id_user' => $user_id
|
||
]);
|
||
foreach ($rows as $row) {
|
||
$projectIds[] = (int)$row['id_project'];
|
||
}
|
||
|
||
// Проекты где пользователь в id_admin
|
||
$allProjects = Database::select('project', ['id', 'id_admin']);
|
||
foreach ($allProjects as $project) {
|
||
if ($project['id_admin']) {
|
||
$owners = json_decode($project['id_admin'], true);
|
||
if (is_array($owners) && in_array($user_id, $owners)) {
|
||
$pid = (int)$project['id'];
|
||
if (!in_array($pid, $projectIds)) {
|
||
$projectIds[] = $pid;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return $projectIds;
|
||
}
|
||
|
||
// ==================== УПРАВЛЕНИЕ ПРАВАМИ ====================
|
||
|
||
// Установить конкретное право
|
||
public static function setPermission(int $project_id, int $user_id, string $permission, bool $value, int $current_user_id): array {
|
||
// Нельзя редактировать свои права
|
||
if ($user_id === $current_user_id) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять свои права']];
|
||
}
|
||
|
||
if (!in_array($permission, self::PERMISSIONS)) {
|
||
return ['success' => false, 'errors' => ['permission' => 'Неизвестное право: ' . $permission]];
|
||
}
|
||
|
||
$member = Database::get('project_members', ['permissions'], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
if (!$member) {
|
||
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
|
||
}
|
||
|
||
$permissions = $member['permissions'] ? json_decode($member['permissions'], true) : [];
|
||
$permissions[$permission] = $value;
|
||
|
||
Database::update('project_members', [
|
||
'permissions' => json_encode($permissions)
|
||
], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
return ['success' => true, 'permissions' => $permissions];
|
||
}
|
||
|
||
// Установить несколько прав сразу
|
||
public static function setPermissions(int $project_id, int $user_id, array $permissions, int $current_user_id): array {
|
||
// Нельзя редактировать свои права
|
||
if ($user_id === $current_user_id) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять свои права']];
|
||
}
|
||
|
||
// Нельзя редактировать права владельца
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять права владельца проекта']];
|
||
}
|
||
|
||
foreach (array_keys($permissions) as $perm) {
|
||
if (!in_array($perm, self::PERMISSIONS)) {
|
||
return ['success' => false, 'errors' => ['permission' => 'Неизвестное право: ' . $perm]];
|
||
}
|
||
}
|
||
|
||
$member = Database::get('project_members', ['permissions'], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
if (!$member) {
|
||
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
|
||
}
|
||
|
||
$currentPerms = $member['permissions'] ? json_decode($member['permissions'], true) : [];
|
||
$mergedPerms = array_merge($currentPerms, $permissions);
|
||
|
||
Database::update('project_members', [
|
||
'permissions' => json_encode($mergedPerms)
|
||
], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
return ['success' => true, 'permissions' => $mergedPerms];
|
||
}
|
||
|
||
// Получить права участника
|
||
public static function getPermissions(int $project_id, int $user_id): ?array {
|
||
// Владелец имеет все права
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
$allPerms = [];
|
||
foreach (self::PERMISSIONS as $perm) {
|
||
$allPerms[$perm] = true;
|
||
}
|
||
return $allPerms;
|
||
}
|
||
|
||
$member = Database::get('project_members', ['is_admin', 'permissions'], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
if (!$member) {
|
||
return null;
|
||
}
|
||
|
||
// Админ имеет все права
|
||
if ((int)$member['is_admin'] === 1) {
|
||
$allPerms = [];
|
||
foreach (self::PERMISSIONS as $perm) {
|
||
$allPerms[$perm] = true;
|
||
}
|
||
return $allPerms;
|
||
}
|
||
|
||
return $member['permissions'] ? json_decode($member['permissions'], true) : [];
|
||
}
|
||
|
||
// Назначить/снять админа
|
||
public static function setAdmin(int $project_id, int $user_id, bool $is_admin, int $current_user_id): array {
|
||
// Нельзя редактировать свои права
|
||
if ($user_id === $current_user_id) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять свои права']];
|
||
}
|
||
|
||
// Нельзя изменять статус владельца
|
||
if (self::isOwner($project_id, $user_id)) {
|
||
return ['success' => false, 'errors' => ['member' => 'Нельзя изменять статус владельца проекта']];
|
||
}
|
||
|
||
$member = Database::get('project_members', ['id'], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
if (!$member) {
|
||
return ['success' => false, 'errors' => ['member' => 'Участник не найден']];
|
||
}
|
||
|
||
Database::update('project_members', [
|
||
'is_admin' => $is_admin ? 1 : 0
|
||
], [
|
||
'id_project' => $project_id,
|
||
'id_user' => $user_id
|
||
]);
|
||
|
||
return ['success' => true];
|
||
}
|
||
|
||
// Получить список всех доступных прав (для UI)
|
||
public static function getAvailablePermissions(): array {
|
||
return self::PERMISSIONS;
|
||
}
|
||
|
||
// Получить дефолтные права (для UI)
|
||
public static function getDefaultPermissions(): array {
|
||
return self::DEFAULT_PERMISSIONS;
|
||
}
|
||
}
|
||
|
||
?>
|