added foldering apis
This commit is contained in:
@@ -5,8 +5,12 @@ namespace App\Http\Controllers\api;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Folder;
|
||||
use App\Models\FolderArt;
|
||||
use App\Models\FolderBylaw;
|
||||
use App\Models\FolderJudicialPrecedent;
|
||||
use App\Models\FolderLegalOpinion;
|
||||
use App\Traits\BaseApiResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class FolderController extends Controller
|
||||
{
|
||||
@@ -14,12 +18,18 @@ class FolderController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
$folder = Folder::query()->where('user_id', auth()->id())->get()
|
||||
// Eager load everything to prevent N+1 Database issues
|
||||
$folder = Folder::with(['arts', 'bylaws', 'judicialPrecedents', 'legalOpinions'])
|
||||
->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,
|
||||
];
|
||||
});
|
||||
@@ -34,7 +44,7 @@ class FolderController extends Controller
|
||||
]);
|
||||
|
||||
Folder::create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'user_id' => auth()->id(),
|
||||
'name' => $validted['name'],
|
||||
]);
|
||||
|
||||
@@ -44,40 +54,72 @@ class FolderController extends Controller
|
||||
public function assign(Request $request)
|
||||
{
|
||||
$validted = $request->validate([
|
||||
'folder_id' => 'required',
|
||||
'art_id' => 'required',
|
||||
'folder_id' => 'required',
|
||||
'art_id' => 'nullable',
|
||||
'bylaw_document_id' => 'nullable',
|
||||
'judicial_precedent_id' => 'nullable',
|
||||
'legal_opinion_id' => 'nullable',
|
||||
]);
|
||||
|
||||
if (Folder::query()->where('id', $validted['folder_id'])->count() == 0) {
|
||||
return $this->failed([], 'پوشه مورد نظر یافت نشد', 404);
|
||||
// 🚨 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 (FolderArt::query()->where('folder_id', $validted['folder_id'])->where('art_id', $validted['art_id'])->count() > 0) {
|
||||
return $this->failed([], 'ارت مورد نظر قبلا اضافه شده است', 400);
|
||||
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);
|
||||
}
|
||||
|
||||
FolderArt::create([
|
||||
'folder_id' => $validted['folder_id'],
|
||||
'art_id' => $validted['art_id'],
|
||||
]);
|
||||
|
||||
return $this->success([], 'اضافه شد', 'ارت با موفیت اضافه شد');
|
||||
return $this->success([], 'اضافه شد', 'با موفیت اضافه شد');
|
||||
}
|
||||
|
||||
public function folder($id)
|
||||
{
|
||||
$arts = FolderArt::where('folder_id', $id)
|
||||
->with('arts.chapter', 'arts.part', 'arts.volum', 'arts.law', 'arts.book', 'arts.section', 'arts.gate')
|
||||
->get()
|
||||
->pluck('arts')
|
||||
->flatten();
|
||||
// 🚨 SECURITY FIX: Check user_id when fetching the folder contents
|
||||
$folder = Folder::with([
|
||||
'arts.chapter', 'arts.part', 'arts.volum', 'arts.law', 'arts.book', 'arts.section', 'arts.gate',
|
||||
'bylaws.category', 'bylaws.approvalAuthority', 'bylaws.promulgationAuthority',
|
||||
'judicialPrecedents.category',
|
||||
'legalOpinions.category'
|
||||
])->where('user_id', auth()->id())->find($id);
|
||||
|
||||
$arts = $arts->map(function ($art) {
|
||||
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 = substr($text, 0, 50);
|
||||
$shortText = substr($shortText, 0, strrpos($shortText, ' ')).'...';
|
||||
$shortText = mb_substr($text, 0, 50); // Changed to mb_substr for Persian text safety
|
||||
$shortText = mb_substr($shortText, 0, mb_strrpos($shortText, ' ')).'...';
|
||||
} else {
|
||||
$shortText = $text;
|
||||
}
|
||||
@@ -89,6 +131,8 @@ class FolderController extends Controller
|
||||
'text' => $isLocked ? null : $shortText,
|
||||
'number' => $art->number,
|
||||
'is_locked' => $isLocked,
|
||||
'type' => 'art',
|
||||
'assigned_at' => $art->pivot->created_at,
|
||||
'chapter' => $art->chapter != null ? [
|
||||
'id' => $art->chapter->id,
|
||||
'title' => $art->chapter->title,
|
||||
@@ -126,27 +170,193 @@ class FolderController extends Controller
|
||||
] : null,
|
||||
];
|
||||
});
|
||||
$items = $items->concat($arts);
|
||||
|
||||
return $this->success($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)
|
||||
{
|
||||
if (Folder::query()->where('id', $id)->count() == 0) {
|
||||
return $this->failed('پوشه یافت نشد', 'پوشه مورد نظر یافت نشد', 404);
|
||||
}
|
||||
Folder::query()->where('id', $id)->delete();
|
||||
// 🚨 SECURITY FIX
|
||||
$folder = Folder::where('id', $id)->where('user_id', auth()->id())->first();
|
||||
|
||||
if (!$folder) {
|
||||
return $this->failed('پوشه یافت نشد', 'پوشه مورد نظر یافت نشد یا شما دسترسی ندارید', 404);
|
||||
}
|
||||
|
||||
$folder->delete();
|
||||
return $this->success([], 'حذف شد', 'پوشه با موفقیت حذف شد');
|
||||
}
|
||||
|
||||
// 🚨 SECURITY FIX FOR ALL PIVOT DELETES: Verify the folder belongs to the user first!
|
||||
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 (FolderArt::query()->where('folder_id', $id)->where('art_id', $art_id)->count() == 0) {
|
||||
return $this->failed([], 'ارت مورد نظر یافت نشد', 404);
|
||||
}
|
||||
FolderArt::query()->where('folder_id', $id)->where('art_id', $art_id)->delete();
|
||||
if (!$this->checkFolderOwnership($id)) return $this->failed([], 'عدم دسترسی', 403);
|
||||
|
||||
return $this->success([], 'حذف شد', 'ارت با موفقیت حذف شد');
|
||||
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 (Safely cloned from GlobalSearch)
|
||||
* ----------------------------------------------------------------- */
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user