fix draft exam upload
This commit is contained in:
@@ -745,6 +745,9 @@ $routes->post('/administrator/teacher-submissions/notify', 'View\AdministratorCo
|
|||||||
$routes->get('/administrator/exam-drafts', 'View\ExamDraftController::adminIndex', ['filter' => 'auth:admin']);
|
$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/review', 'View\ExamDraftController::adminReview', ['filter' => 'auth:admin']);
|
||||||
$routes->post('/administrator/exam-drafts/upload-legacy', 'View\ExamDraftController::adminUploadLegacy', ['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']);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* --------------------------------------------------------------------
|
* --------------------------------------------------------------------
|
||||||
|
|||||||
@@ -348,6 +348,16 @@ class ExamDraftController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function adminIndex()
|
public function adminIndex()
|
||||||
|
{
|
||||||
|
return $this->reviewIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function principalIndex()
|
||||||
|
{
|
||||||
|
return $this->reviewIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reviewIndex()
|
||||||
{
|
{
|
||||||
if ($this->reviewerIdColumn !== '') {
|
if ($this->reviewerIdColumn !== '') {
|
||||||
$allDrafts = $this->examDraftModel
|
$allDrafts = $this->examDraftModel
|
||||||
@@ -487,10 +497,23 @@ class ExamDraftController extends BaseController
|
|||||||
'visibleClasses' => $visibleClasses,
|
'visibleClasses' => $visibleClasses,
|
||||||
'classDraftGroups' => $classDraftGroups,
|
'classDraftGroups' => $classDraftGroups,
|
||||||
'newSubmissionClasses' => $newSubmissionClasses,
|
'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()
|
public function adminUploadLegacy()
|
||||||
|
{
|
||||||
|
return $this->reviewUploadLegacy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function principalUploadLegacy()
|
||||||
|
{
|
||||||
|
return $this->reviewUploadLegacy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reviewUploadLegacy()
|
||||||
{
|
{
|
||||||
$adminId = (int) (session()->get('user_id') ?? 0);
|
$adminId = (int) (session()->get('user_id') ?? 0);
|
||||||
if ($adminId <= 0) {
|
if ($adminId <= 0) {
|
||||||
@@ -571,13 +594,23 @@ class ExamDraftController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($saved > 0) {
|
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.');
|
return redirect()->back()->withInput()->with('error', 'Unable to save the old exam.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function adminReview()
|
public function adminReview()
|
||||||
|
{
|
||||||
|
return $this->reviewSubmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function principalReview()
|
||||||
|
{
|
||||||
|
return $this->reviewSubmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reviewSubmission()
|
||||||
{
|
{
|
||||||
$draftId = (int) ($this->request->getPost('draft_id') ?? 0);
|
$draftId = (int) ($this->request->getPost('draft_id') ?? 0);
|
||||||
if ($draftId <= 0) {
|
if ($draftId <= 0) {
|
||||||
@@ -690,11 +723,6 @@ class ExamDraftController extends BaseController
|
|||||||
if ($this->hasAcceptanceTypeColumn) {
|
if ($this->hasAcceptanceTypeColumn) {
|
||||||
$newRow['acceptance_type'] = $status === 'accepted' ? $acceptanceType : null;
|
$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'])) {
|
if ($this->hasIsLegacyColumn && !empty($draft['is_legacy'])) {
|
||||||
$newRow['is_legacy'] = 1;
|
$newRow['is_legacy'] = 1;
|
||||||
}
|
}
|
||||||
@@ -711,16 +739,6 @@ class ExamDraftController extends BaseController
|
|||||||
return redirect()->back()->with('success', 'Review saved successfully.');
|
return redirect()->back()->with('success', 'Review saved successfully.');
|
||||||
}
|
}
|
||||||
return redirect()->back()->with('error', 'Unable to save the review.');
|
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') {
|
if ($status === 'legacy') {
|
||||||
$this->prepareLegacyPdfVersion($update, $draft);
|
$this->prepareLegacyPdfVersion($update, $draft);
|
||||||
@@ -1010,7 +1028,7 @@ class ExamDraftController extends BaseController
|
|||||||
. '<tr><td style="padding:4px 8px;"><strong>Status</strong></td><td style="padding:4px 8px;">' . esc((string) ($draft['status'] ?? 'submitted')) . '</td></tr>'
|
. '<tr><td style="padding:4px 8px;"><strong>Status</strong></td><td style="padding:4px 8px;">' . esc((string) ($draft['status'] ?? 'submitted')) . '</td></tr>'
|
||||||
. '<tr><td style="padding:4px 8px;"><strong>Submitted at</strong></td><td style="padding:4px 8px;">' . esc((string) $submittedAt) . '</td></tr>'
|
. '<tr><td style="padding:4px 8px;"><strong>Submitted at</strong></td><td style="padding:4px 8px;">' . esc((string) $submittedAt) . '</td></tr>'
|
||||||
. '</table>'
|
. '</table>'
|
||||||
. '<p>Review drafts: <a href="' . esc(base_url('administrator/exam-drafts')) . '">Admin exam drafts</a></p>';
|
. '<p>Review drafts: <a href="' . esc(base_url($this->reviewRoutePrefix() . '/exam-drafts')) . '">' . esc($this->reviewPortalLabel()) . ' exam drafts</a></p>';
|
||||||
|
|
||||||
$mailer = \Config\Services::emailService();
|
$mailer = \Config\Services::emailService();
|
||||||
$mailer->send($principalEmail, $subject, $body, 'notifications');
|
$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
|
private function nextDraftVersion(array $draft): int
|
||||||
{
|
{
|
||||||
$query = $this->examDraftModel
|
$query = $this->examDraftModel
|
||||||
@@ -1373,6 +1423,11 @@ class ExamDraftController extends BaseController
|
|||||||
$base = pathinfo($sourcePath, PATHINFO_FILENAME);
|
$base = pathinfo($sourcePath, PATHINFO_FILENAME);
|
||||||
$targetPath = $targetDir . '/' . $base . '.pdf';
|
$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
|
// Attempt conversion via LibreOffice if available
|
||||||
$cmd = 'soffice --headless --convert-to pdf --outdir ' . escapeshellarg($targetDir) . ' ' . escapeshellarg($sourcePath) . ' 2>/dev/null';
|
$cmd = 'soffice --headless --convert-to pdf --outdir ' . escapeshellarg($targetDir) . ' ' . escapeshellarg($sourcePath) . ' 2>/dev/null';
|
||||||
@\exec($cmd);
|
@\exec($cmd);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ $schoolYear = $schoolYear ?? '';
|
|||||||
$semester = $semester ?? '';
|
$semester = $semester ?? '';
|
||||||
$maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
|
$maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
|
||||||
$allowedExtensions = $allowedExtensions ?? ['doc', 'docx', 'pdf'];
|
$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 {
|
$renderBadge = static function (string $status, array $badges): string {
|
||||||
$b = $badges[$status] ?? ['label' => $status, 'class' => 'bg-secondary text-white'];
|
$b = $badges[$status] ?? ['label' => $status, 'class' => 'bg-secondary text-white'];
|
||||||
@@ -27,7 +30,7 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi
|
|||||||
<div>
|
<div>
|
||||||
<h1 class="h3 mb-1">Exam draft submissions</h1>
|
<h1 class="h3 mb-1">Exam draft submissions</h1>
|
||||||
<p class="text-muted mb-0 small">
|
<p class="text-muted mb-0 small">
|
||||||
<?= esc($semester ?: 'Semester') ?> <?= esc($schoolYear ?: '') ?>
|
<?= esc($reviewPortalLabel) ?> review portal · <?= esc($semester ?: 'Semester') ?> <?= esc($schoolYear ?: '') ?>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted small d-flex flex-column align-items-lg-end">
|
<div class="text-muted small d-flex flex-column align-items-lg-end">
|
||||||
@@ -205,7 +208,7 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td style="min-width: 280px;">
|
<td style="min-width: 280px;">
|
||||||
<?= form_open_multipart(base_url('administrator/exam-drafts/review'), ['class' => 'vstack gap-2', 'id' => $formId]) ?>
|
<?= form_open_multipart($reviewActionUrl, ['class' => 'vstack gap-2', 'id' => $formId]) ?>
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
<input type="hidden" name="draft_id" value="<?= (int) ($draft['id'] ?? 0) ?>">
|
<input type="hidden" name="draft_id" value="<?= (int) ($draft['id'] ?? 0) ?>">
|
||||||
<?php $selectedStatus = ''; ?>
|
<?php $selectedStatus = ''; ?>
|
||||||
@@ -276,7 +279,7 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi
|
|||||||
<span class="badge bg-primary-subtle text-primary">Admin only</span>
|
<span class="badge bg-primary-subtle text-primary">Admin only</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<?= form_open_multipart(base_url('administrator/exam-drafts/upload-legacy'), ['class' => 'row g-3']) ?>
|
<?= form_open_multipart($legacyUploadUrl, ['class' => 'row g-3']) ?>
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label small">Class sections</label>
|
<label class="form-label small">Class sections</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user