where('user_id', auth()->id()) ->get() ->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->name, 'count_of_arts' => $q->arts->count(), 'count_of_bylaws' => $q->bylaws->count(), 'count_of_judicials' => $q->judicialPrecedents->count(), 'count_of_opinions' => $q->legalOpinions->count(), 'created_at' => $q->created_at, ]; }); return $this->success($folder); } public function create(Request $request) { $validted = $request->validate([ 'name' => 'required', ]); Folder::create([ 'user_id' => auth()->id(), 'name' => $validted['name'], ]); return $this->success([], 'ایجاد شد', 'پوشه با موفقیت ایجاد شد.'); } public function assign(Request $request) { $validted = $request->validate([ 'folder_id' => 'required', 'art_id' => 'nullable', 'bylaw_document_id' => 'nullable', 'judicial_precedent_id' => 'nullable', 'legal_opinion_id' => 'nullable', ]); // 🚨 SECURITY FIX: Check if the folder exists AND belongs to the logged-in user if (Folder::where('id', $validted['folder_id'])->where('user_id', auth()->id())->count() == 0) { return $this->failed([], 'پوشه مورد نظر یافت نشد یا شما دسترسی ندارید', 404); } if ($request->filled('art_id')) { if (FolderArt::where('folder_id', $validted['folder_id'])->where('art_id', $validted['art_id'])->exists()) { return $this->failed([], 'ارت مورد نظر قبلا اضافه شده است', 400); } FolderArt::create(['folder_id' => $validted['folder_id'], 'art_id' => $validted['art_id']]); } elseif ($request->filled('bylaw_document_id')) { if (FolderBylaw::where('folder_id', $validted['folder_id'])->where('bylaw_document_id', $validted['bylaw_document_id'])->exists()) { return $this->failed([], 'آیین‌نامه قبلا اضافه شده است', 400); } FolderBylaw::create(['folder_id' => $validted['folder_id'], 'bylaw_document_id' => $validted['bylaw_document_id']]); } elseif ($request->filled('judicial_precedent_id')) { if (FolderJudicialPrecedent::where('folder_id', $validted['folder_id'])->where('judicial_precedent_id', $validted['judicial_precedent_id'])->exists()) { return $this->failed([], 'رأی قبلا اضافه شده است', 400); } FolderJudicialPrecedent::create(['folder_id' => $validted['folder_id'], 'judicial_precedent_id' => $validted['judicial_precedent_id']]); } elseif ($request->filled('legal_opinion_id')) { if (FolderLegalOpinion::where('folder_id', $validted['folder_id'])->where('legal_opinion_id', $validted['legal_opinion_id'])->exists()) { return $this->failed([], 'نظریه قبلا اضافه شده است', 400); } FolderLegalOpinion::create(['folder_id' => $validted['folder_id'], 'legal_opinion_id' => $validted['legal_opinion_id']]); } else { return $this->failed([], 'موردی برای افزودن یافت نشد', 400); } return $this->success([], 'اضافه شد', 'با موفیت اضافه شد'); } public function folder($id) { // Added 'arts.law.category' so we can fetch category_name without slowing down the DB $folder = Folder::with([ 'arts.chapter', 'arts.part', 'arts.volum', 'arts.law.category', 'arts.book', 'arts.section', 'arts.gate', 'bylaws.category', 'bylaws.approvalAuthority', 'bylaws.promulgationAuthority', 'judicialPrecedents.category', 'legalOpinions.category' ])->where('user_id', auth()->id())->find($id); if (!$folder) { return $this->failed([], 'پوشه یافت نشد یا شما دسترسی ندارید', 404); } $items = collect(); // 1. Process Arts $arts = $folder->arts->map(function ($art) { $text = $art->text ?? ''; $shortText = ''; if (strlen($text) > 50) { $shortText = mb_substr($text, 0, 50); $shortText = mb_substr($shortText, 0, mb_strrpos($shortText, ' ')).'...'; } else { $shortText = $text; } $isLocked = ! auth()->user()->isSubscriber() && (bool) $art->law?->is_locked; return [ 'id' => $art->id, 'title' => $art->title, 'text' => $isLocked ? null : $shortText, 'number' => $art->number, 'is_locked' => $isLocked, 'type' => 'art', 'assigned_at' => $art->pivot->created_at, 'law_title' => $art->law?->title, 'law' => $art->law != null ? [ 'id' => $art->law->id, 'title' => $art->law->title, 'number' => $art->law->number, ] : null, 'chapter' => $art->chapter != null ? [ 'id' => $art->chapter->id, 'title' => $art->chapter->title, 'number' => $art->chapter->number, ] : null, 'part' => $art->part != null ? [ 'id' => $art->part->id, 'title' => $art->part->title, 'number' => $art->part->number, ] : null, 'volum' => $art->volum != null ? [ 'id' => $art->volum->id, 'title' => $art->volum->title, 'number' => $art->volum->number, ] : null, 'book' => $art->book != null ? [ 'id' => $art->book->id, 'title' => $art->book->title, 'number' => $art->book->number, ] : null, 'section' => $art->section != null ? [ 'id' => $art->section->id, 'title' => $art->section->title, 'number' => $art->section->number, ] : null, 'gate' => $art->gate != null ? [ 'id' => $art->gate->id, 'title' => $art->gate->title, 'number' => $art->gate->number, ] : null, ]; }); $items = $items->concat($arts); // 2. Process Bylaws $bylawIds = $folder->bylaws->pluck('id')->toArray(); $allBylawNodes = collect(); if (!empty($bylawIds)) { $allBylawNodes = DB::table('bylaw_nodes') ->whereIn('document_id', $bylawIds) ->orderBy('sort_order', 'asc') ->get(); } $bylaws = $folder->bylaws->map(function ($bylaw) use ($allBylawNodes) { $itemNodes = $allBylawNodes->where('document_id', $bylaw->id); $formatted = $this->formatBylaw($bylaw, $itemNodes); $formatted['type'] = 'bylaw'; $formatted['assigned_at'] = $bylaw->pivot->created_at; return $formatted; }); $items = $items->concat($bylaws); // 3. Process Judicial Precedents $judicials = $folder->judicialPrecedents->map(function ($judicial) { $formatted = $this->formatJudicial($judicial); $formatted['type'] = 'judicial_precedent'; $formatted['assigned_at'] = $judicial->pivot->created_at; return $formatted; }); $items = $items->concat($judicials); // 4. Process Legal Opinions $opinions = $folder->legalOpinions->map(function ($opinion) { $formatted = $this->formatOpinion($opinion); $formatted['type'] = 'legal_opinion'; $formatted['assigned_at'] = $opinion->pivot->created_at; return $formatted; }); $items = $items->concat($opinions); // Sort everything together based on when it was added to the folder! $sortedItems = $items->sortByDesc('assigned_at')->values()->toArray(); return $this->success($sortedItems); } public function delete_folder($id) { $folder = Folder::where('id', $id)->where('user_id', auth()->id())->first(); if (!$folder) { return $this->failed('پوشه یافت نشد', 'پوشه مورد نظر یافت نشد یا شما دسترسی ندارید', 404); } $folder->delete(); return $this->success([], 'حذف شد', 'پوشه با موفقیت حذف شد'); } private function checkFolderOwnership($folder_id) { return Folder::where('id', $folder_id)->where('user_id', auth()->id())->exists(); } public function delete_art($id, $art_id) { if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403); if (FolderArt::where('folder_id', $id)->where('art_id', $art_id)->count() == 0) { return $this->failed([], 'مورد نظر یافت نشد', 404); } FolderArt::where('folder_id', $id)->where('art_id', $art_id)->delete(); return $this->success([], 'حذف شد', 'با موفقیت حذف شد'); } public function delete_bylaw($id, $bylaw_id) { if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403); if (FolderBylaw::where('folder_id', $id)->where('bylaw_document_id', $bylaw_id)->count() == 0) { return $this->failed([], 'مورد نظر یافت نشد', 404); } FolderBylaw::where('folder_id', $id)->where('bylaw_document_id', $bylaw_id)->delete(); return $this->success([], 'حذف شد', 'با موفقیت حذف شد'); } public function delete_judicial($id, $judicial_id) { if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403); if (FolderJudicialPrecedent::where('folder_id', $id)->where('judicial_precedent_id', $judicial_id)->count() == 0) { return $this->failed([], 'مورد نظر یافت نشد', 404); } FolderJudicialPrecedent::where('folder_id', $id)->where('judicial_precedent_id', $judicial_id)->delete(); return $this->success([], 'حذف شد', 'با موفقیت حذف شد'); } public function delete_opinion($id, $opinion_id) { if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403); if (FolderLegalOpinion::where('folder_id', $id)->where('legal_opinion_id', $opinion_id)->count() == 0) { return $this->failed([], 'مورد نظر یافت نشد', 404); } FolderLegalOpinion::where('folder_id', $id)->where('legal_opinion_id', $opinion_id)->delete(); return $this->success([], 'حذف شد', 'با موفقیت حذف شد'); } /* ----------------------------------------------------------------- * FORMATTING HELPERS * ----------------------------------------------------------------- */ private function isLockedForCurrentUserGeneric($item): bool { if (auth()->check() && auth()->user() && method_exists(auth()->user(), 'isSubscriber')) { return !auth()->user()->isSubscriber() && (bool) ($item->is_locked ?? false); } return (bool) ($item->is_locked ?? false); } private function formatBylaw($item, $itemNodes = null) { $isLocked = $this->isLockedForCurrentUserGeneric($item); $description = $item->description; $cleanDesc = trim(str_replace([' ', '‌', "\r", "\n", "\t", ' '], '', strip_tags($description ?? ''))); if (strlen($cleanDesc) < 5) { if ($itemNodes === null || (is_object($itemNodes) && $itemNodes->isEmpty())) { $dbNodes = DB::table('bylaw_nodes')->where('document_id', $item->id)->count(); if ($dbNodes > 0) { $itemNodes = DB::table('bylaw_nodes')->where('document_id', $item->id)->orderBy('sort_order', 'asc')->get(); } } $nodeContents = []; if ($itemNodes) { foreach ($itemNodes as $node) { $content = trim(strip_tags($node->node_content ?? '')); if (!empty($content)) { $nodeContents[] = $content; } } } $description = implode("\n\n", $nodeContents); } return [ 'id' => $item->id, 'title' => $item->title, 'category_name' => optional($item->category)->name, 'approval_authority_name' => optional($item->approvalAuthority)->name, 'approval_date' => $item->approval_date, 'promulgation_number' => $item->promulgation_number, 'description' => $isLocked ? null : mb_substr($description, 0, 100) . '...', 'is_locked' => $isLocked, ]; } private function formatJudicial($item) { $isLocked = $this->isLockedForCurrentUserGeneric($item); return [ 'id' => $item->id, 'title' => $item->subject, 'ruling_number' => $item->ruling_number, 'ruling_date' => $item->ruling_date, 'text' => $isLocked ? null : mb_substr($item->full_text, 0, 100) . '...', 'issuing_authority' => $item->issuing_authority, 'category_name' => optional($item->category)->name, 'is_locked' => $isLocked, ]; } private function formatOpinion($item) { $isLocked = $this->isLockedForCurrentUserGeneric($item); return [ 'id' => $item->id, 'title' => $item->subject, 'opinion_number' => $item->opinion_number, 'opinion_date' => $item->opinion_date, 'text' => $isLocked ? null : mb_substr($item->full_text, 0, 100) . '...', 'issuing_authority' => $item->issuing_authority, 'category_name' => optional($item->category)->name, 'is_locked' => $isLocked, ]; } }