getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } return $this->success($this->portal->teacherBootstrap($uid)); } public function admin(): JsonResponse { return $this->success([ 'print_requests' => $this->portal->adminPrintRequests(), ]); } public function store(Request $request): JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $validator = Validator::make($request->all(), [ 'file' => ['required', 'file', 'max:5120', 'mimes:pdf,jpg,jpeg,png,doc,docx,txt', 'mimetypes:application/pdf,image/jpeg,image/png,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'page_selection' => ['nullable', 'string', 'regex:/^\s*\d+(?:\s*-\s*\d+)?(?:\s*,\s*\d+(?:\s*-\s*\d+)?)*\s*$/'], 'num_copies' => ['required', 'integer', 'min:1'], 'required_by' => ['required', 'date'], 'pickup_method' => ['required', 'string', 'max:255'], 'class_id' => ['required', 'integer', 'min:1'], ]); if ($validator->fails()) { return $this->respondValidationError($validator->errors()->toArray()); } /** @var UploadedFile $file */ $file = $request->file('file'); $model = $this->portal->storeTeacherUpload($validator->validated(), $file, $uid); return $this->respondCreated( ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())], 'Print request created successfully.' ); } public function teacherUpdate(Request $request, int $id): JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $pr = PrintRequest::query()->find($id); if (! $pr) { return $this->error('Print request not found.', Response::HTTP_NOT_FOUND); } if (! $this->portal->teacherOwnsRequest($pr, $uid)) { return $this->error('You are not authorized to edit this request.', Response::HTTP_FORBIDDEN); } if (! $this->portal->teacherMayEditOrDelete($pr)) { return $this->error('Cannot edit a request that is already being processed.', Response::HTTP_BAD_REQUEST); } $rules = [ 'num_copies' => ['required', 'integer', 'min:1'], 'required_by' => ['required', 'date'], 'pickup_method' => ['required', 'string', Rule::in(['Self Pickup', 'Delivered to class'])], 'page_selection' => ['nullable', 'string', 'regex:/^\s*\d+(?:\s*-\s*\d+)?(?:\s*,\s*\d+(?:\s*-\s*\d+)?)*\s*$/'], ]; if ($request->hasFile('file') && $request->file('file')->isValid()) { $rules['file'] = ['required', 'file', 'max:5120', 'mimes:pdf,jpg,jpeg,png,doc,docx,txt', 'mimetypes:application/pdf,image/jpeg,image/png,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document']; } $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return $this->respondValidationError($validator->errors()->toArray()); } $data = $validator->validated(); $newFile = ($request->hasFile('file') && $request->file('file')->isValid()) ? $request->file('file') : null; $model = $this->portal->updateTeacherFields($pr, $data, $newFile); return $this->success( ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())], 'Print request updated successfully.' ); } public function adminStatus(Request $request, int $id): JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $validator = Validator::make($request->all(), [ 'status' => ['required', 'string', Rule::in(['not_assigned', 'assigned', 'done', 'delivered'])], ]); if ($validator->fails()) { return $this->respondValidationError($validator->errors()->toArray()); } $pr = PrintRequest::query()->find($id); if (! $pr) { return $this->error('Print request not found.', Response::HTTP_NOT_FOUND); } $newStatus = (string) $validator->validated()['status']; try { $model = $this->portal->applyAdminStatus($pr, $newStatus, $uid); } catch (\InvalidArgumentException $e) { return $this->error($e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY); } return $this->success( ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())], 'Status updated successfully.' ); } public function destroy(int $id): JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $pr = PrintRequest::query()->find($id); if (! $pr) { return $this->error('Print request not found.', Response::HTTP_NOT_FOUND); } if (! $this->portal->teacherOwnsRequest($pr, $uid)) { return $this->error('You are not authorized to delete this request.', Response::HTTP_FORBIDDEN); } if (! $this->portal->teacherMayEditOrDelete($pr)) { return $this->error('Cannot delete a request that is already being processed.', Response::HTTP_BAD_REQUEST); } $this->portal->deleteTeacherRequest($pr); return $this->respondDeleted(null, 'Print request deleted successfully.'); } public function copy(int $id): JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $pr = PrintRequest::query()->find($id); if (! $pr) { return $this->error('Print request not found.', Response::HTTP_NOT_FOUND); } if (! $this->portal->teacherOwnsRequest($pr, $uid)) { return $this->error('You are not authorized to copy this request.', Response::HTTP_FORBIDDEN); } try { $model = $this->portal->copyFromExisting($pr, $uid); } catch (\InvalidArgumentException $e) { return $this->error($e->getMessage(), Response::HTTP_BAD_REQUEST); } return $this->respondCreated( ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())], 'Print request copied successfully.' ); } public function handCopy(Request $request): JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $validator = Validator::make($request->all(), [ 'num_copies' => ['required', 'integer', 'min:1'], 'required_by' => ['required', 'date'], 'pickup_method' => ['required', 'string', 'max:255'], 'class_id' => ['required', 'integer', 'min:1'], ]); if ($validator->fails()) { return $this->respondValidationError($validator->errors()->toArray()); } $model = $this->portal->storeHandCopy($validator->validated(), $uid); return $this->respondCreated( ['print_request' => $this->portal->normalizePrintRequestRow($model->toArray())], 'Copy request submitted. Please hand the original document to the copy center.' ); } public function download(int $id): BinaryFileResponse|JsonResponse { $uid = $this->getCurrentUserId(); if (! $uid) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } $pr = PrintRequest::query()->find($id); if (! $pr) { return $this->error('Print request not found.', Response::HTTP_NOT_FOUND); } $isAdmin = $this->userHasPrintAdminRole(Auth::user()); if (! $this->portal->authorizeDownload($pr, $uid, $isAdmin)) { return $this->error('Forbidden.', Response::HTTP_FORBIDDEN); } $path = $this->portal->absolutePathForDownload($pr); if ($path === null || ! is_readable($path)) { return $this->error('File not found.', Response::HTTP_NOT_FOUND); } $mime = @mime_content_type($path) ?: 'application/octet-stream'; $safe = basename($path); return response()->download($path, $safe, [ 'Content-Type' => $mime, ]); } private function userHasPrintAdminRole(?User $user): bool { if (! $user) { return false; } $roles = $user->roles() ->pluck('roles.name') ->map(fn ($name) => strtolower((string) $name)) ->toArray(); foreach (['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'] as $allowed) { if (in_array($allowed, $roles, true)) { return true; } } return false; } }