user($request); $folders = $this->accessService->navigationFolders($user); $selectedFolder = $request->integer('folder') ? DocumentationFolder::query()->with('permissionRules')->findOrFail($request->integer('folder')) : $folders->first(fn (DocumentationFolder $folder): bool => (bool) $folder->getAttribute('can_read')); if ($selectedFolder) { abort_unless($this->accessService->canRead($user, $selectedFolder), 403); } $search = trim((string) $request->query('search', '')); $documents = $selectedFolder ? DocumentationDocument::query() ->where('folder_id', $selectedFolder->id) ->when($search !== '', fn ($query) => $query->where('name', 'like', "%{$search}%")) ->with(['currentVersion.file', 'currentVersion.creator', 'versions.file', 'versions.creator']) ->orderBy('name') ->paginate(20) ->withQueryString() : null; $canWrite = $selectedFolder && $this->accessService->canWrite($user, $selectedFolder); $canManagePermissions = $selectedFolder && $this->accessService->canManagePermissions($user, $selectedFolder); $selectedFolder?->loadMissing('permissionRules'); $rules = $selectedFolder?->permissionRules ?? collect(); return view('documents.index', [ 'active' => 'documents', 'folders' => $folders, 'foldersByParent' => $folders->groupBy(fn (DocumentationFolder $folder): int => $folder->parent_id ?? 0), 'selectedFolder' => $selectedFolder, 'documents' => $documents, 'search' => $search, 'canCreateRoot' => $this->accessService->isAdministrator($user), 'canWrite' => (bool) $canWrite, 'canManagePermissions' => (bool) $canManagePermissions, 'roles' => $canManagePermissions ? Role::query()->where('is_active', true)->orderBy('name')->get() : collect(), 'users' => $canManagePermissions ? User::query()->with('roleModel')->orderBy('name')->get() : collect(), 'permissionValues' => $this->permissionValues($rules), ]); } public function storeFolder(StoreDocumentationFolderRequest $request): RedirectResponse { $user = $this->user($request); $parentId = $request->validated('parent_id'); $parent = $parentId ? DocumentationFolder::query()->findOrFail((int) $parentId) : null; if ($parent) { abort_unless($this->accessService->canWrite($user, $parent), 403); } else { abort_unless($this->accessService->isAdministrator($user), 403); } $folder = $this->documentationService->createFolder($parent, (string) $request->validated('name'), $user); return redirect() ->route('documents.index', ['folder' => $folder->id]) ->with('success', 'Папка создана.'); } public function updateFolder( RenameDocumentationItemRequest $request, DocumentationFolder $folder, ): RedirectResponse { abort_unless($this->accessService->canWrite($this->user($request), $folder), 403); $this->documentationService->renameFolder($folder, (string) $request->validated('name')); return $this->folderRedirect($folder, 'Папка переименована.'); } public function destroyFolder(Request $request, DocumentationFolder $folder): RedirectResponse { abort_unless($this->accessService->canWrite($this->user($request), $folder), 403); $parentId = $folder->parent_id; $this->documentationService->deleteFolder($folder); return redirect() ->route('documents.index', $parentId ? ['folder' => $parentId] : []) ->with('success', 'Папка и всё её содержимое удалены.'); } public function updatePermissions( UpdateDocumentationPermissionsRequest $request, DocumentationFolder $folder, ): RedirectResponse { abort_unless($this->accessService->canManagePermissions($this->user($request), $folder), 403); $this->documentationService->updatePermissions($folder, $request->validated()); return $this->folderRedirect($folder, 'Права папки обновлены.'); } public function storeDocument( StoreDocumentationFileRequest $request, DocumentationFolder $folder, ): RedirectResponse { $user = $this->user($request); abort_unless($this->accessService->canWrite($user, $folder), 403); /** @var UploadedFile $file */ $file = $request->file('file'); $this->documentationService->createDocument( $folder, $request->validated('name'), $file, $user, ); return $this->folderRedirect($folder, 'Документ загружен.'); } public function updateDocument( RenameDocumentationItemRequest $request, DocumentationDocument $document, ): RedirectResponse { $document->loadMissing('folder'); abort_unless($this->accessService->canWrite($this->user($request), $document->folder), 403); $this->documentationService->renameDocument($document, (string) $request->validated('name')); return $this->folderRedirect($document->folder, 'Документ переименован.'); } public function destroyDocument(Request $request, DocumentationDocument $document): RedirectResponse { $document->loadMissing('folder'); abort_unless($this->accessService->canWrite($this->user($request), $document->folder), 403); $folder = $document->folder; $this->documentationService->deleteDocument($document); return $this->folderRedirect($folder, 'Документ и все его версии удалены.'); } public function storeVersion( StoreDocumentationFileRequest $request, DocumentationDocument $document, ): RedirectResponse { $document->loadMissing('folder'); $user = $this->user($request); abort_unless($this->accessService->canWrite($user, $document->folder), 403); /** @var UploadedFile $file */ $file = $request->file('file'); $this->documentationService->addVersion($document, $file, $user); return $this->folderRedirect($document->folder, 'Новая версия загружена.'); } public function downloadVersion(Request $request, DocumentationDocumentVersion $version): StreamedResponse { $version->loadMissing(['document.folder', 'file']); abort_unless($this->accessService->canRead($this->user($request), $version->document->folder), 403); abort_unless($version->file && Storage::disk('local')->exists($version->file->path), 404); return Storage::disk('local')->download($version->file->path, $version->file->original_name); } public function previewVersion(Request $request, DocumentationDocumentVersion $version): BinaryFileResponse { $version->loadMissing(['document.folder', 'file']); abort_unless($this->accessService->canRead($this->user($request), $version->document->folder), 403); abort_unless($version->file && Storage::disk('local')->exists($version->file->path), 404); $mimeType = Storage::disk('local')->mimeType($version->file->path); abort_unless(is_string($mimeType) && str_starts_with(strtolower($mimeType), 'image/'), 404); return response()->file(Storage::disk('local')->path($version->file->path), [ 'Content-Type' => $mimeType, 'Content-Security-Policy' => "sandbox; default-src 'none'; img-src 'self' data:", 'X-Content-Type-Options' => 'nosniff', ]); } public function destroyVersion(Request $request, DocumentationDocumentVersion $version): RedirectResponse { $version->loadMissing('document.folder'); $folder = $version->document->folder; abort_unless($this->accessService->canWrite($this->user($request), $folder), 403); $this->documentationService->deleteVersion($version); return $this->folderRedirect($folder, 'Версия полностью удалена.'); } private function user(Request $request): User { /** @var User $user */ $user = $request->user(); return $user; } private function folderRedirect(DocumentationFolder $folder, string $message): RedirectResponse { return redirect() ->route('documents.index', ['folder' => $folder->id]) ->with('success', $message); } /** * @param SupportCollection $rules * @return array */ private function permissionValues(SupportCollection $rules): array { $all = $rules->firstWhere('subject_type', DocumentationFolderPermission::SUBJECT_ALL); return [ 'all_read' => (bool) ($all?->can_read || $all?->can_write), 'all_write' => (bool) $all?->can_write, 'role_read_ids' => $rules ->where('subject_type', DocumentationFolderPermission::SUBJECT_ROLE) ->filter(fn (DocumentationFolderPermission $rule): bool => $rule->can_read || $rule->can_write) ->pluck('role_id') ->all(), 'role_write_ids' => $rules ->where('subject_type', DocumentationFolderPermission::SUBJECT_ROLE) ->where('can_write', true) ->pluck('role_id') ->all(), 'user_read_ids' => $rules ->where('subject_type', DocumentationFolderPermission::SUBJECT_USER) ->filter(fn (DocumentationFolderPermission $rule): bool => $rule->can_read || $rule->can_write) ->pluck('user_id') ->all(), 'user_write_ids' => $rules ->where('subject_type', DocumentationFolderPermission::SUBJECT_USER) ->where('can_write', true) ->pluck('user_id') ->all(), ]; } }