From 9aacbe897294a84e96a6407a1f7e0d8665a1afc6 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 8 May 2026 00:33:40 -0400 Subject: [PATCH] fix draft exam upload --- app/Config/Routes.php | 3 + app/Controllers/View/ExamDraftController.php | 89 ++++++++++++++++---- app/Views/administrator/exam_drafts.php | 9 +- 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 83dfadb..1ec333d 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -745,6 +745,9 @@ $routes->post('/administrator/teacher-submissions/notify', 'View\AdministratorCo $routes->get('/administrator/exam-drafts', 'View\ExamDraftController::adminIndex', ['filter' => 'auth:admin']); $routes->post('/administrator/exam-drafts/review', 'View\ExamDraftController::adminReview', ['filter' => 'auth:admin']); $routes->post('/administrator/exam-drafts/upload-legacy', 'View\ExamDraftController::adminUploadLegacy', ['filter' => 'auth:admin']); +$routes->get('/principal/exam-drafts', 'View\ExamDraftController::principalIndex', ['filter' => 'auth:admin']); +$routes->post('/principal/exam-drafts/review', 'View\ExamDraftController::principalReview', ['filter' => 'auth:admin']); +$routes->post('/principal/exam-drafts/upload-legacy', 'View\ExamDraftController::principalUploadLegacy', ['filter' => 'auth:admin']); /* * -------------------------------------------------------------------- diff --git a/app/Controllers/View/ExamDraftController.php b/app/Controllers/View/ExamDraftController.php index 4d7ef66..f4862f6 100644 --- a/app/Controllers/View/ExamDraftController.php +++ b/app/Controllers/View/ExamDraftController.php @@ -348,6 +348,16 @@ class ExamDraftController extends BaseController } public function adminIndex() + { + return $this->reviewIndex(); + } + + public function principalIndex() + { + return $this->reviewIndex(); + } + + public function reviewIndex() { if ($this->reviewerIdColumn !== '') { $allDrafts = $this->examDraftModel @@ -487,10 +497,23 @@ class ExamDraftController extends BaseController 'visibleClasses' => $visibleClasses, 'classDraftGroups' => $classDraftGroups, 'newSubmissionClasses' => $newSubmissionClasses, + 'reviewActionUrl' => base_url($this->reviewRoutePrefix() . '/exam-drafts/review'), + 'legacyUploadUrl' => base_url($this->reviewRoutePrefix() . '/exam-drafts/upload-legacy'), + 'reviewPortalLabel' => $this->reviewPortalLabel(), ]); } public function adminUploadLegacy() + { + return $this->reviewUploadLegacy(); + } + + public function principalUploadLegacy() + { + return $this->reviewUploadLegacy(); + } + + public function reviewUploadLegacy() { $adminId = (int) (session()->get('user_id') ?? 0); if ($adminId <= 0) { @@ -571,13 +594,23 @@ class ExamDraftController extends BaseController } if ($saved > 0) { - return redirect()->to('/administrator/exam-drafts')->with('success', 'Old exam uploaded successfully.'); + return redirect()->to($this->reviewDashboardPath())->with('success', 'Old exam uploaded successfully.'); } return redirect()->back()->withInput()->with('error', 'Unable to save the old exam.'); } public function adminReview() + { + return $this->reviewSubmission(); + } + + public function principalReview() + { + return $this->reviewSubmission(); + } + + public function reviewSubmission() { $draftId = (int) ($this->request->getPost('draft_id') ?? 0); if ($draftId <= 0) { @@ -690,11 +723,6 @@ class ExamDraftController extends BaseController if ($this->hasAcceptanceTypeColumn) { $newRow['acceptance_type'] = $status === 'accepted' ? $acceptanceType : null; } - $teacherFile = $this->draftTeacherFile($draft); - if (!empty($teacherFile)) { - $newRow[$this->authorFileColumn] = $teacherFile; - $newRow[$this->authorFilenameColumn] = $this->draftTeacherFilename($draft) ?? $teacherFile; - } if ($this->hasIsLegacyColumn && !empty($draft['is_legacy'])) { $newRow['is_legacy'] = 1; } @@ -711,16 +739,6 @@ class ExamDraftController extends BaseController return redirect()->back()->with('success', 'Review saved successfully.'); } return redirect()->back()->with('error', 'Unable to save the review.'); - } elseif (strtolower($status) === 'accepted' && !empty($this->draftTeacherFile($draft))) { - $copied = $this->copyDraftToFinal($this->draftTeacherFile($draft)); - if ($copied !== null) { - $update['final_file'] = $copied; - $update['final_filename'] = $this->draftTeacherFilename($draft) ?? $this->draftTeacherFile($draft); - $pdfName = $this->ensurePdfExists($copied, pathinfo($copied, PATHINFO_EXTENSION)); - if ($pdfName !== null && $this->hasFinalPdfColumn) { - $update['final_pdf_file'] = $pdfName; - } - } } if ($status === 'legacy') { $this->prepareLegacyPdfVersion($update, $draft); @@ -1010,7 +1028,7 @@ class ExamDraftController extends BaseController . 'Status' . esc((string) ($draft['status'] ?? 'submitted')) . '' . 'Submitted at' . esc((string) $submittedAt) . '' . '' - . '

Review drafts: Admin exam drafts

'; + . '

Review drafts: ' . esc($this->reviewPortalLabel()) . ' exam drafts

'; $mailer = \Config\Services::emailService(); $mailer->send($principalEmail, $subject, $body, 'notifications'); @@ -1019,6 +1037,38 @@ class ExamDraftController extends BaseController } } + private function reviewDashboardPath(): string + { + return '/' . $this->reviewRoutePrefix() . '/exam-drafts'; + } + + private function reviewRoutePrefix(): string + { + return $this->isPrincipalReviewer() ? 'principal' : 'administrator'; + } + + private function reviewPortalLabel(): string + { + return $this->isPrincipalReviewer() ? 'Principal' : 'Administrator'; + } + + private function isPrincipalReviewer(): bool + { + $roles = session()->get('roles'); + if (!is_array($roles)) { + $roles = $roles === null ? [] : [$roles]; + } + + foreach ($roles as $role) { + $normalized = strtolower(trim(str_replace([' ', '-'], '_', (string) $role))); + if ($normalized === 'principal') { + return true; + } + } + + return false; + } + private function nextDraftVersion(array $draft): int { $query = $this->examDraftModel @@ -1373,6 +1423,11 @@ class ExamDraftController extends BaseController $base = pathinfo($sourcePath, PATHINFO_FILENAME); $targetPath = $targetDir . '/' . $base . '.pdf'; + if (!function_exists('exec')) { + log_message('warning', 'ExamDraftController::convertDocToPdf skipped because exec() is unavailable.'); + return is_file($targetPath) ? basename($targetPath) : null; + } + // Attempt conversion via LibreOffice if available $cmd = 'soffice --headless --convert-to pdf --outdir ' . escapeshellarg($targetDir) . ' ' . escapeshellarg($sourcePath) . ' 2>/dev/null'; @\exec($cmd); diff --git a/app/Views/administrator/exam_drafts.php b/app/Views/administrator/exam_drafts.php index da74d90..402cc5e 100644 --- a/app/Views/administrator/exam_drafts.php +++ b/app/Views/administrator/exam_drafts.php @@ -13,6 +13,9 @@ $schoolYear = $schoolYear ?? ''; $semester = $semester ?? ''; $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024); $allowedExtensions = $allowedExtensions ?? ['doc', 'docx', 'pdf']; +$reviewActionUrl = $reviewActionUrl ?? base_url('administrator/exam-drafts/review'); +$legacyUploadUrl = $legacyUploadUrl ?? base_url('administrator/exam-drafts/upload-legacy'); +$reviewPortalLabel = $reviewPortalLabel ?? 'Administrator'; $renderBadge = static function (string $status, array $badges): string { $b = $badges[$status] ?? ['label' => $status, 'class' => 'bg-secondary text-white']; @@ -27,7 +30,7 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi

Exam draft submissions

- + review portal ยท

@@ -205,7 +208,7 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi
- 'vstack gap-2', 'id' => $formId]) ?> + 'vstack gap-2', 'id' => $formId]) ?> @@ -276,7 +279,7 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi Admin only
- 'row g-3']) ?> + 'row g-3']) ?>