fix parent and teacher pages to follow year filter
Tests / PHPUnit (push) Failing after 1m15s

This commit is contained in:
root
2026-07-15 20:03:36 -04:00
parent feb1b29a32
commit 5f27dccd0f
32 changed files with 582 additions and 364 deletions
+4
View File
@@ -206,7 +206,11 @@ abstract class BaseController extends Controller
'admin',
'administrator',
'administrative staff',
'parent',
'principal',
'teacher',
'teacher assistant',
'teacher_assistant',
'vice principal',
]);
}
+99 -24
View File
@@ -54,6 +54,7 @@ class ClassProgressController extends BaseController
public function create()
{
$context = $this->resolveSchoolYearContext();
$teacherId = (int) session()->get('user_id');
$assignments = $this->loadTeacherSections($teacherId);
$first = $assignments[0] ?? null;
@@ -76,12 +77,15 @@ class ClassProgressController extends BaseController
'classId' => $classId,
'sundayOptions' => $sundayOptions,
'defaultWeekStart' => $defaultWeekStart,
'isSchoolYearReadonly' => $context->isReadonly(),
];
return view('teacher/class_progress_submit', $data);
}
public function store()
{
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
[, $schoolYear] = $this->resolveCurrentTerm();
$subjectSections = self::SUBJECT_SECTIONS;
$rules = [
'class_section_id' => 'required|integer',
@@ -132,8 +136,9 @@ class ClassProgressController extends BaseController
->select('id')
->where('class_section_id', $classSectionId)
->where('week_start', $weekStart)
->where('teacher_id', $teacherId)
->findAll();
->where('teacher_id', $teacherId);
$this->applyProgressSchoolYearScope($existingReports, $schoolYear);
$existingReports = $existingReports->findAll();
if (! $confirmOverwrite && ! empty($existingReports)) {
return redirect()->back()
@@ -175,7 +180,7 @@ class ClassProgressController extends BaseController
'homework' => $homework ?: null,
'status' => $status,
'flags_json' => $this->normalizeFlags($this->request->getPost('flags')),
];
] + $this->progressReportSchoolYearData($schoolYear);
$reportId = $this->reportModel->insert($data, true);
$attachmentField = "attachment_$slug";
@@ -198,6 +203,7 @@ class ClassProgressController extends BaseController
public function history()
{
$teacherId = (int) session()->get('user_id');
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$assignments = $this->loadTeacherSections($teacherId);
$selectedSectionId = (int) $this->request->getGet('class_section_id');
$validSectionIds = array_column($assignments, 'class_section_id');
@@ -207,7 +213,6 @@ class ClassProgressController extends BaseController
if ($selectedSectionId && ! in_array($selectedSectionId, $validSectionIds, true)) {
$selectedSectionId = $validSectionIds[0] ?? null;
}
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds($selectedSectionId, $semester, $schoolYear);
if (empty($allowedTeacherIds)) {
$allowedTeacherIds = [$teacherId];
@@ -217,6 +222,7 @@ class ClassProgressController extends BaseController
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->whereIn('teacher_id', $allowedTeacherIds);
$this->applyProgressSchoolYearScope($builder, $schoolYear, true);
if ($selectedSectionId) {
$builder->where('class_progress_reports.class_section_id', $selectedSectionId);
}
@@ -251,24 +257,27 @@ class ClassProgressController extends BaseController
'subjectSections' => self::SUBJECT_SECTIONS,
'classSectionOptions' => $sectionOptions,
'selectedSectionId' => $selectedSectionId,
'schoolYear' => $schoolYear,
'isSchoolYearReadonly' => $this->resolveSchoolYearContext()->isReadonly(),
]);
}
public function view($id)
{
$teacherId = (int) session()->get('user_id');
$row = $this->reportModel
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$rowQuery = $this->reportModel
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name')
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->where('class_progress_reports.id', (int) $id)
->first();
->where('class_progress_reports.id', (int) $id);
$this->applyProgressSchoolYearScope($rowQuery, $schoolYear, true);
$row = $rowQuery->first();
if (! $row) {
throw new PageNotFoundException('Progress report not found.');
}
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear);
if (empty($allowedTeacherIds)) {
if ($teacherId !== (int) $row['teacher_id']) {
@@ -286,7 +295,9 @@ class ClassProgressController extends BaseController
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->whereIn('teacher_id', $allowedTeacherIds)
->where('class_progress_reports.class_section_id', $row['class_section_id'])
->where('week_start', $row['week_start'])
->where('week_start', $row['week_start']);
$this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear);
$weeklyReports = $weeklyReportsQuery
->orderBy('subject', 'ASC')
->findAll();
@@ -313,17 +324,18 @@ class ClassProgressController extends BaseController
public function edit($id)
{
$teacherId = (int) session()->get('user_id');
$row = $this->reportModel
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$rowQuery = $this->reportModel
->select('class_progress_reports.*, cs.class_section_name')
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->where('class_progress_reports.id', (int) $id)
->first();
->where('class_progress_reports.id', (int) $id);
$this->applyProgressSchoolYearScope($rowQuery, $schoolYear);
$row = $rowQuery->first();
if (! $row) {
throw new PageNotFoundException('Progress report not found.');
}
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear);
if (empty($allowedTeacherIds)) {
if ($teacherId !== (int) $row['teacher_id']) {
@@ -338,7 +350,9 @@ class ClassProgressController extends BaseController
->select('class_progress_reports.*')
->whereIn('teacher_id', $allowedTeacherIds)
->where('class_section_id', $row['class_section_id'])
->where('week_start', $row['week_start'])
->where('week_start', $row['week_start']);
$this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear);
$weeklyReports = $weeklyReportsQuery
->orderBy('subject', 'ASC')
->findAll();
@@ -405,18 +419,23 @@ class ClassProgressController extends BaseController
'isEdit' => true,
'formAction' => base_url('teacher/progress/update/' . (int) $row['id']),
'submitLabel' => 'Update Progress',
'isSchoolYearReadonly' => $this->resolveSchoolYearContext()->isReadonly(),
]);
}
public function update($id)
{
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$teacherId = (int) session()->get('user_id');
$row = $this->reportModel->find((int) $id);
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$rowQuery = $this->reportModel
->where('class_progress_reports.id', (int) $id);
$this->applyProgressSchoolYearScope($rowQuery, $schoolYear);
$row = $rowQuery->first();
if (! $row) {
throw new PageNotFoundException('Progress report not found.');
}
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear);
if (empty($allowedTeacherIds)) {
if ($teacherId !== (int) $row['teacher_id']) {
@@ -473,8 +492,9 @@ class ClassProgressController extends BaseController
->select('id')
->where('class_section_id', $classSectionId)
->where('week_start', $weekStart)
->where('teacher_id', $teacherId)
->findAll();
->where('teacher_id', $teacherId);
$this->applyProgressSchoolYearScope($conflicts, $schoolYear);
$conflicts = $conflicts->findAll();
if (! $confirmOverwrite && ! empty($conflicts)) {
return redirect()->back()
->withInput()
@@ -497,7 +517,9 @@ class ClassProgressController extends BaseController
->select('class_progress_reports.*')
->whereIn('teacher_id', $allowedTeacherIds)
->where('class_section_id', $classSectionId)
->where('week_start', $row['week_start'])
->where('week_start', $row['week_start']);
$this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear);
$weeklyReports = $weeklyReportsQuery
->orderBy('subject', 'ASC')
->findAll();
@@ -533,7 +555,7 @@ class ClassProgressController extends BaseController
'unit_title' => $unitTitle,
'covered' => $covered,
'homework' => $homework ?: null,
];
] + $this->progressReportSchoolYearData($schoolYear);
if ($flagsInput !== null) {
$data['flags_json'] = $this->normalizeFlags($flagsInput);
}
@@ -698,8 +720,7 @@ class ClassProgressController extends BaseController
protected function loadTeacherSections(int $teacherId): array
{
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
$semester = (string) ($this->configModel->getConfig('semester') ?? '');
[$semester, $schoolYear] = $this->resolveCurrentTerm();
return $this->teacherClassModel->getClassAssignmentsByUserId($teacherId, $schoolYear, $semester);
}
@@ -878,7 +899,7 @@ class ClassProgressController extends BaseController
protected function resolveProgressDateRange(): ?array
{
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
if ($schoolYear === '') {
return null;
}
@@ -946,11 +967,65 @@ class ClassProgressController extends BaseController
protected function resolveCurrentTerm(): array
{
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$semester = (string) ($this->configModel->getConfig('semester') ?? '');
return [$semester, $schoolYear];
}
protected function applyProgressSchoolYearScope($builder, string $schoolYear, bool $canScopeBySection = false): void
{
if ($schoolYear === '') {
return;
}
$db = db_connect();
$hasReportYear = $db->fieldExists('school_year', 'class_progress_reports');
$hasSectionYear = $db->fieldExists('school_year', 'classSection');
$semesterResolver = new SemesterRangeService($this->configModel);
[$rangeStart, $rangeEnd] = $semesterResolver->getSchoolYearRange($schoolYear);
if ($hasReportYear) {
if ($rangeStart !== '' && $rangeEnd !== '') {
$builder
->groupStart()
->where('class_progress_reports.school_year', $schoolYear)
->orGroupStart()
->groupStart()
->where('class_progress_reports.school_year IS NULL', null, false)
->orWhere('class_progress_reports.school_year', '')
->groupEnd()
->where('class_progress_reports.week_start >=', $rangeStart)
->where('class_progress_reports.week_start <=', $rangeEnd)
->groupEnd()
->groupEnd();
return;
}
$builder->where('class_progress_reports.school_year', $schoolYear);
return;
}
if ($canScopeBySection && $hasSectionYear) {
$builder->where('cs.school_year', $schoolYear);
return;
}
if ($rangeStart !== '' && $rangeEnd !== '') {
$builder
->where('class_progress_reports.week_start >=', $rangeStart)
->where('class_progress_reports.week_start <=', $rangeEnd);
}
}
protected function progressReportSchoolYearData(string $schoolYear): array
{
if ($schoolYear === '' || ! db_connect()->fieldExists('school_year', 'class_progress_reports')) {
return [];
}
return ['school_year' => $schoolYear];
}
protected function resolveAssignedTeacherIds(?int $classSectionId, string $semester, string $schoolYear): array
{
if (! $classSectionId || $schoolYear === '') {
+8 -1
View File
@@ -29,6 +29,7 @@ class ParentProgressController extends BaseController
public function index()
{
$schoolYear = $this->currentSchoolYearName();
$students = $this->getParentStudents();
$sectionIds = array_values(array_unique(array_filter(array_map(
static fn (array $student): int => (int) ($student['class_section_id'] ?? 0),
@@ -42,7 +43,8 @@ class ParentProgressController extends BaseController
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name')
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->whereIn('class_progress_reports.class_section_id', $sectionIds);
->whereIn('class_progress_reports.class_section_id', $sectionIds)
->where('class_progress_reports.school_year', $schoolYear);
$rows = $builder
->orderBy('week_start', 'DESC')
@@ -75,10 +77,12 @@ class ParentProgressController extends BaseController
public function view($id)
{
$schoolYear = $this->currentSchoolYearName();
$row = $this->reportModel
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name')
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->where('class_progress_reports.school_year', $schoolYear)
->find((int) $id);
if (! $row || ! $this->isSectionAccessible($row['class_section_id'] ?? null)) {
@@ -92,6 +96,7 @@ class ParentProgressController extends BaseController
->select('class_progress_reports.*')
->where('class_section_id', $row['class_section_id'])
->where('week_start', $row['week_start'])
->where('school_year', $schoolYear)
->orderBy('subject', 'ASC')
->findAll();
@@ -165,6 +170,7 @@ class ParentProgressController extends BaseController
$rows = $this->enrollmentModel
->select('class_section_id')
->where('parent_id', $parentId)
->where('school_year', $this->currentSchoolYearName())
->where('is_withdrawn', 0)
->groupBy('class_section_id')
->findAll();
@@ -213,6 +219,7 @@ class ParentProgressController extends BaseController
->join('students s', 's.id = e.student_id')
->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left')
->where('e.parent_id', $parentId)
->where('e.school_year', $this->currentSchoolYearName())
->where('e.is_withdrawn', 0)
->orderBy('e.updated_at', 'DESC')
->orderBy('e.created_at', 'DESC')
@@ -31,7 +31,7 @@ class ParentReportCardController extends BaseController
return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
}
$schoolYear = trim((string) ($this->request->getGet('school_year') ?? $this->currentSchoolYearName()));
$schoolYear = trim((string) $this->currentSchoolYearName());
$semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? ''));
$builder = $this->db->table('students s')
@@ -82,7 +82,7 @@ class ParentReportCardController extends BaseController
throw new PageNotFoundException('Student not found.');
}
$schoolYear = trim((string) ($this->request->getGet('school_year') ?? $this->currentSchoolYearName()));
$schoolYear = trim((string) $this->currentSchoolYearName());
$semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? ''));
$this->touchAcknowledgement($parentId, (int) $studentId, $schoolYear, $semester, [
+115 -6
View File
@@ -12,6 +12,7 @@ use App\Models\TeacherClassModel;
use App\Models\AdminNotificationSubjectModel;
use App\Models\NotificationModel;
use App\Models\UserNotificationModel;
use App\Services\SemesterRangeService;
use CodeIgniter\Exceptions\PageNotFoundException;
class PrintRequests extends BaseController
@@ -46,18 +47,26 @@ class PrintRequests extends BaseController
public function teacher_index()
{
$teacher_id = session()->get('user_id');
$context = $this->resolveSchoolYearContext();
$schoolYear = $context->yearName();
$data['print_requests'] = $this->printRequestModel
$printRequestsQuery = $this->printRequestModel
->select('print_requests.*, admins.firstname as admin_firstname, admins.lastname as admin_lastname')
->join('users as admins', 'admins.id = print_requests.admin_id', 'left')
->where('print_requests.teacher_id', $teacher_id)
->join('classSection cs', 'cs.class_section_id = print_requests.class_id', 'left')
->where('print_requests.teacher_id', $teacher_id);
$this->applyPrintRequestSchoolYearScope($printRequestsQuery, $schoolYear);
$data['print_requests'] = $printRequestsQuery
->orderBy('print_requests.required_by', 'DESC')
->orderBy('print_requests.id', 'DESC')
->findAll();
$teacher_classes = $this->teacherClassModel->getClassByTeacherId($teacher_id);
$teacher_classes = $this->teacherClassModel->getClassAssignmentsByUserId((int) $teacher_id, $schoolYear);
$data['class_id'] = !empty($teacher_classes) ? $teacher_classes[0]['class_section_id'] : null;
$dateOptions = $this->buildRequiredByOptions();
$data['sundays'] = $dateOptions['sundays'];
$data['times'] = $dateOptions['times'];
$data['isSchoolYearReadonly'] = $context->isReadonly();
return view('print_requests/teacher_index', $data);
}
@@ -94,6 +103,9 @@ class PrintRequests extends BaseController
public function create()
{
$context = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($context);
$schoolYear = $context->yearName();
$validationRules = [
'file' => 'uploaded[file]|max_size[file,5120]|ext_in[file,pdf,jpg,png,jpeg,doc,docx,txt]',
'page_selection' => 'permit_empty|regex_match[/^\\s*\\d+(?:\\s*-\\s*\\d+)?(?:\\s*,\\s*\\d+(?:\\s*-\\s*\\d+)?)*\\s*$/]',
@@ -125,7 +137,7 @@ class PrintRequests extends BaseController
'required_by' => $this->request->getPost('required_by'),
'pickup_method' => $this->request->getPost('pickup_method'),
'status' => 'not_assigned',
];
] + $this->printRequestSchoolYearData($schoolYear);
$printRequestId = (int) $this->printRequestModel->insert($data, true);
if ($printRequestId > 0) {
@@ -175,6 +187,7 @@ class PrintRequests extends BaseController
// Case 2: Teacher edit
if ($this->request->getPost('num_copies')) {
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$user_id = session()->get('user_id');
if ($request['teacher_id'] != $user_id) {
return redirect()->to('teacher/print-requests')->with('error', 'You are not authorized to edit this request.');
@@ -230,6 +243,7 @@ class PrintRequests extends BaseController
public function delete($id)
{
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$teacher_id = session()->get('user_id');
$request = $this->printRequestModel->find($id);
@@ -260,6 +274,9 @@ class PrintRequests extends BaseController
public function copy($id)
{
$teacher_id = session()->get('user_id');
$context = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($context);
$schoolYear = $context->yearName();
$request = $this->printRequestModel->find($id);
if (!$request) {
@@ -296,7 +313,7 @@ class PrintRequests extends BaseController
'required_by' => $request['required_by'],
'pickup_method' => $request['pickup_method'],
'status' => 'not_assigned',
];
] + $this->printRequestSchoolYearData($schoolYear);
$copiedId = (int) $this->printRequestModel->insert($data, true);
if ($copiedId > 0) {
@@ -308,6 +325,9 @@ class PrintRequests extends BaseController
public function createCopy()
{
$context = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($context);
$schoolYear = $context->yearName();
$validationRules = [
'num_copies' => 'required|integer|greater_than[0]',
'required_by' => 'required|valid_date',
@@ -329,7 +349,7 @@ class PrintRequests extends BaseController
'required_by' => $this->request->getPost('required_by'),
'pickup_method' => $this->request->getPost('pickup_method'),
'status' => 'not_assigned',
];
] + $this->printRequestSchoolYearData($schoolYear);
$printRequestId = (int) $this->printRequestModel->insert($data, true);
if ($printRequestId > 0) {
@@ -374,6 +394,95 @@ class PrintRequests extends BaseController
];
}
private function applyPrintRequestSchoolYearScope($query, string $schoolYear): void
{
if ($schoolYear === '') {
return;
}
$hasPrintRequestYear = $this->printRequestsHaveSchoolYearColumn();
$hasClassSectionYear = db_connect()->fieldExists('school_year', 'classSection');
$range = (new SemesterRangeService($this->configModel))->getSchoolYearRange($schoolYear);
[$rangeStart, $rangeEnd] = $range;
if ($hasPrintRequestYear && $hasClassSectionYear) {
$query->groupStart()
->where('print_requests.school_year', $schoolYear)
->orGroupStart()
->groupStart()
->where('print_requests.school_year IS NULL', null, false)
->orWhere('print_requests.school_year', '')
->groupEnd()
->where('cs.school_year', $schoolYear)
->groupEnd();
if ($rangeStart !== '' && $rangeEnd !== '') {
$query
->orGroupStart()
->groupStart()
->where('print_requests.school_year IS NULL', null, false)
->orWhere('print_requests.school_year', '')
->groupEnd()
->groupStart()
->where('cs.school_year IS NULL', null, false)
->orWhere('cs.school_year', '')
->groupEnd()
->where('print_requests.required_by >=', $rangeStart . ' 00:00:00')
->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59')
->groupEnd();
}
$query->groupEnd();
return;
}
if (! $hasPrintRequestYear && $hasClassSectionYear) {
$query->where('cs.school_year', $schoolYear);
return;
}
if ($rangeStart !== '' && $rangeEnd !== '') {
if ($hasPrintRequestYear) {
$query
->groupStart()
->where('print_requests.school_year', $schoolYear)
->orGroupStart()
->groupStart()
->where('print_requests.school_year IS NULL', null, false)
->orWhere('print_requests.school_year', '')
->groupEnd()
->where('print_requests.required_by >=', $rangeStart . ' 00:00:00')
->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59')
->groupEnd()
->groupEnd();
return;
}
$query
->where('print_requests.required_by >=', $rangeStart . ' 00:00:00')
->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59');
return;
}
if ($hasPrintRequestYear) {
$query->where('print_requests.school_year', $schoolYear);
}
}
private function printRequestSchoolYearData(string $schoolYear): array
{
if ($schoolYear === '' || ! $this->printRequestsHaveSchoolYearColumn()) {
return [];
}
return ['school_year' => $schoolYear];
}
private function printRequestsHaveSchoolYearColumn(): bool
{
return db_connect()->fieldExists('school_year', 'print_requests');
}
public function serveFile(string $filename, string $mode = 'inline')
{
$safeName = basename(trim($filename));
+30 -10
View File
@@ -91,6 +91,9 @@ class ExamDraftController extends BaseController
public function teacherIndex()
{
$this->syncAcademicContext();
$context = $this->resolveSchoolYearContext();
$teacherId = (int) (session()->get('user_id') ?? 0);
if ($teacherId <= 0) {
return redirect()->to('/login');
@@ -175,11 +178,15 @@ class ExamDraftController extends BaseController
'maxUploadBytes' => self::MAX_UPLOAD_BYTES,
'validation' => $validation,
'printableDraftIds' => $printableIds,
'isSchoolYearReadonly' => $context->isReadonly(),
]);
}
public function teacherStore()
{
$this->syncAcademicContext();
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$teacherId = (int) (session()->get('user_id') ?? 0);
if ($teacherId <= 0) {
return redirect()->to('/login');
@@ -193,10 +200,13 @@ class ExamDraftController extends BaseController
return redirect()->back()->withInput()->with('error', 'Select a class section before submitting.');
}
$assignment = $this->teacherClassModel
$assignmentQuery = $this->teacherClassModel
->where('teacher_id', $teacherId)
->where('class_section_id', $classSectionId)
->first();
->where('class_section_id', $classSectionId);
if ($this->schoolYear !== '') {
$assignmentQuery->where('school_year', $this->schoolYear);
}
$assignment = $assignmentQuery->first();
if (empty($assignment)) {
return redirect()->back()->withInput()->with('error', 'You are not assigned to the selected class section.');
@@ -785,6 +795,8 @@ class ExamDraftController extends BaseController
public function teacherStatusFeed()
{
$this->syncAcademicContext();
$teacherId = (int) (session()->get('user_id') ?? 0);
if ($teacherId <= 0) {
return $this->response->setStatusCode(401);
@@ -848,6 +860,8 @@ class ExamDraftController extends BaseController
->join('classSection cs', 'cs.class_section_id = exam_drafts.class_section_id', 'left')
->join('users u', 'u.id = exam_drafts.' . $this->authorIdColumn, 'left');
$this->applyExamDraftYearScope($query);
if (empty($classSectionIds)) {
return $query->where('exam_drafts.' . $this->authorIdColumn, $teacherId);
}
@@ -859,19 +873,25 @@ class ExamDraftController extends BaseController
->where('exam_drafts.' . $this->authorIdColumn . ' !=', $teacherId)
->where('exam_drafts.status !=', 'draft');
if ($this->schoolYear !== '') {
$query->where('exam_drafts.school_year', $this->schoolYear);
}
if ($this->semester !== '') {
$query->where('exam_drafts.semester', $this->semester);
}
$query->groupEnd()
->groupEnd();
return $query;
}
private function syncAcademicContext(): void
{
$this->schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$this->semester = (string) ($this->configModel->getConfig('semester') ?? '');
}
private function applyExamDraftYearScope($query): void
{
if ($this->schoolYear !== '') {
$query->where('exam_drafts.school_year', $this->schoolYear);
}
}
private function attachTeacherDraftContext(array $row, int $viewerId): array
{
$isOwn = $this->draftTeacherId($row) === $viewerId;
+11 -7
View File
@@ -1065,13 +1065,7 @@ class GradingController extends Controller
public function belowSixty()
{
$configuredYear = (string) $this->schoolYear;
$schoolYear = trim((string)($this->request->getGet('school_year') ?? ''));
if ($schoolYear === '') {
$schoolYear = $configuredYear;
}
$schoolYear = $this->currentSchoolYearName((string) $this->schoolYear);
// This page is Fall only.
$semester = 'fall';
@@ -1124,6 +1118,16 @@ public function belowSixty()
'canViewGrading' => $canViewGrading,
]);
}
private function currentSchoolYearName(?string $fallback = null): string
{
try {
return service('schoolYearContext')->resolve($this->request)->yearName();
} catch (\Throwable) {
return trim((string) ($fallback ?? ''));
}
}
public function editBelowSixtyEmail()
{
$studentId = (int)$this->request->getGet('student_id');
+34 -15
View File
@@ -5,7 +5,7 @@ namespace App\Controllers\View;
use App\Models\HomeworkModel;
use App\Models\StudentModel;
use App\Models\StudentClassModel;
use CodeIgniter\Controller;
use App\Controllers\BaseController;
use App\Models\TeacherClassModel;
use App\Models\ConfigurationModel;
use App\Models\UserModel;
@@ -16,7 +16,7 @@ use App\Controllers\View\GradingController;
use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel;
class HomeworkController extends Controller
class HomeworkController extends BaseController
{
protected $db;
protected $semesterScoreService;
@@ -59,11 +59,12 @@ class HomeworkController extends Controller
}
}
public function updateHomeworkScores(array $scores = null, int $updatedBy = null, int $classSectionId = null)
public function updateHomeworkScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
{
$scores = $this->request->getPost('scores');
$semester = $this->request->getPost('semester') ?? $this->semester;
$schoolYear = $this->request->getPost('school_year') ?? $this->schoolYear;
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
if ($updatedBy === null) {
$updatedBy = session()->get('user_id');
@@ -195,6 +196,8 @@ class HomeworkController extends Controller
$selectedSemester = $this->getSelectedSemester();
$normalized = $this->normalizeSemesterSelection($selectedSemester);
$semesterLabel = $normalized !== '' ? ucfirst($normalized) : $selectedSemester;
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
// Step 1: Get the highest existing homework_index
$existingIndexes = $this->homeworkModel
@@ -202,7 +205,7 @@ class HomeworkController extends Controller
->where('class_section_id', $classSectionId)
//->where('teacher_id', $updatedBy)
->whereIn('semester', $this->getSemesterVariants($semesterLabel))
->where('school_year', $this->schoolYear)
->where('school_year', $schoolYear)
->groupBy('homework_index')
->orderBy('homework_index', 'DESC')
->findAll();
@@ -219,6 +222,7 @@ class HomeworkController extends Controller
// Step 2: Get all students in the class
$students = $this->studentClassModel
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->findAll();
// Step 3: Insert a new homework row for each student if not already exists
@@ -233,7 +237,7 @@ class HomeworkController extends Controller
->where('class_section_id', $classSectionId)
//->where('teacher_id', $updatedBy)
->whereIn('semester', $this->getSemesterVariants($semesterLabel))
->where('school_year', $this->schoolYear)
->where('school_year', $schoolYear)
->first();
if ($existing) continue;
@@ -246,7 +250,7 @@ class HomeworkController extends Controller
'homework_index' => $nextIndex,
'score' => null,
'semester' => $semesterLabel,
'school_year' => $this->schoolYear,
'school_year' => $schoolYear,
'created_at' => utc_now(),
'updated_at' => utc_now()
];
@@ -269,15 +273,23 @@ class HomeworkController extends Controller
public function showHomework()
{
$updatedBy = session()->get('user_id');
$classSectionId = $this->getClassSectionIdForTeacher($updatedBy);
$semester = $this->getSelectedSemester();
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$classSectionId = (int) (
$this->request->getGet('class_section_id')
?? $this->request->getPost('class_section_id')
?? 0
);
if ($classSectionId <= 0) {
$classSectionId = (int) ($this->getClassSectionIdForTeacher($updatedBy, $schoolYear, $semester) ?? 0);
}
if (!$classSectionId) {
return redirect()->back()->with('status', 'No class section found for the current teacher.');
}
session()->set('class_section_id', $classSectionId);
$semester = $this->getSelectedSemester();
$homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $semester, $this->schoolYear);
$homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $semester, $schoolYear);
if (empty($homeworkHeaders)) {
$homeworkHeaders = [1];
}
@@ -285,15 +297,15 @@ class HomeworkController extends Controller
$classSectionId,
$homeworkHeaders,
$semester,
$this->schoolYear
$schoolYear
);
$missingOkMap = $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $this->schoolYear, 'homework');
$missingOkMap = $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'homework');
return view('teacher/add_homework', [
'students' => $students,
'homeworkHeaders' => $homeworkHeaders,
'semester' => $semester,
'schoolYear' => $this->schoolYear,
'schoolYear' => $schoolYear,
'class_section_id' => $classSectionId,
'missingOkMap' => $missingOkMap,
]);
@@ -503,9 +515,16 @@ class HomeworkController extends Controller
return $students;
}
private function getClassSectionIdForTeacher($updatedBy)
private function getClassSectionIdForTeacher($updatedBy, ?string $schoolYear = null, ?string $semester = null)
{
$class = $this->teacherClassModel->where('teacher_id', $updatedBy)->first();
$builder = $this->teacherClassModel->where('teacher_id', $updatedBy);
if ($schoolYear !== null && $schoolYear !== '') {
$builder->where('school_year', $schoolYear);
}
if ($semester !== null && $semester !== '') {
$builder->where('semester', $semester);
}
$class = $builder->first();
return $class['class_section_id'] ?? null;
}
+2 -18
View File
@@ -1644,23 +1644,8 @@ private function getGradeLevel($grade): array
return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
}
$currentSchoolYear = $this->schoolYear;
// Get available school years from invoices
$schoolYears = $this->invoiceModel
->select('school_year')
->distinct()
->where('parent_id', $parentId)
->orderBy('school_year', 'DESC')
->findAll();
// Determine which year to show
$selectedYear = $this->request->getGet('school_year');
if (empty($selectedYear)) {
$hasCurrentYear = in_array($currentSchoolYear, array_column($schoolYears, 'school_year'));
$selectedYear = $hasCurrentYear ? $currentSchoolYear : (!empty($schoolYears) ? $schoolYears[0]['school_year'] : null);
}
$currentSchoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$selectedYear = $currentSchoolYear;
// Fetch invoices for the selected year
$invoices = [];
@@ -1718,7 +1703,6 @@ private function getGradeLevel($grade): array
return view('/parent/invoice_payment', [
'invoices' => $invoices,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear,
'currentSchoolYear' => $currentSchoolYear,
'dueDate' => $this->dueDate,
@@ -58,7 +58,7 @@ class ParentAttendanceReportController extends BaseController
[$sundays, $defaultDate] = $this->computeSundays();
// Load upcoming reports for preview/edit (today and forward within this school year)
$schoolYear = (string) $this->configModel->getConfig('school_year');
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$todayYmd = local_date(utc_now(), 'Y-m-d');
$previewRows = $this->reportModel->builder()
->select('parent_attendance_reports.*, s.firstname, s.lastname')
@@ -141,6 +141,7 @@ class ParentAttendanceReportController extends BaseController
$dismissTime = $post['dismiss_time'] ?? null;
$reasonRaw = $post['reason'] ?? null;
$reason = is_string($reasonRaw) ? trim($reasonRaw) : null;
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
// Enforce Sunday-only and future-or-today selection
$todayCheck = new \DateTime('today');
@@ -157,7 +158,7 @@ class ParentAttendanceReportController extends BaseController
}
$validDates = [];
$cap = $this->firstSundayOfJune((string) $this->configModel->getConfig('school_year'));
$cap = $this->firstSundayOfJune($schoolYear);
$lateCutoff = null;
$lateNow = null;
@@ -236,7 +237,6 @@ class ParentAttendanceReportController extends BaseController
// Upper bound already enforced by $validDates building
$semester = (string) $this->configModel->getConfig('semester');
$schoolYear = (string) $this->configModel->getConfig('school_year');
$semesterResolver = new SemesterRangeService($this->configModel);
$inserted = 0;
@@ -493,7 +493,7 @@ class ParentAttendanceReportController extends BaseController
}
// Upper bound: first Sunday of June (school year end)
$schoolYear = (string) $this->configModel->getConfig('school_year');
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$cap = $this->firstSundayOfJune($schoolYear);
$dates = [];
@@ -1458,12 +1458,15 @@ class ParentAttendanceReportController extends BaseController
return $this->response->setJSON(['ok' => true, 'students' => []]);
}
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
// Fetch existing submissions for these students on this date
$rows = $this->reportModel->builder()
->select('parent_attendance_reports.student_id, parent_attendance_reports.type, parent_attendance_reports.report_date, s.firstname, s.lastname')
->join('students s', 's.id = parent_attendance_reports.student_id', 'left')
->whereIn('parent_attendance_reports.report_date', $dateValues)
->whereIn('parent_attendance_reports.student_id', $studentIds)
->where('parent_attendance_reports.school_year', $schoolYear)
->get()->getResultArray();
$map = [];
+4 -31
View File
@@ -164,10 +164,7 @@ class ParentController extends BaseController
return redirect()->back()->with('error', 'Parent session not found.');
}
$currentSchoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
// Get selected school year (no semester filter on parent view)
$selectedYear = $this->request->getVar('school_year') ?? $currentSchoolYear;
$selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
// Build query to retrieve attendance
$builder = $this->db->table('attendance_data');
@@ -180,19 +177,10 @@ class ParentController extends BaseController
$query = $builder->get();
$attendanceResults = $query->getResultArray();
// Get list of available school years
$schoolYears = $this->db->table('attendance_data')
->select('school_year')
->distinct()
->orderBy('school_year', 'DESC')
->get()
->getResultArray();
// If no records found, set a flag to show message in the view
if (empty($attendanceResults)) {
return view('/parent/attendance', [
'attendance' => null,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear,
'selectedSemester' => null,
//'error' => 'No attendance records found for the selected school year and semester.'
@@ -202,7 +190,6 @@ class ParentController extends BaseController
// Return view with attendance results
return view('/parent/attendance', [
'attendance' => $attendanceResults,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear,
'selectedSemester' => null,
'error' => null
@@ -212,7 +199,6 @@ class ParentController extends BaseController
return view('/parent/attendance', [
'attendance' => null,
'schoolYears' => [],
'selectedYear' => $selectedYear ?? null,
'selectedSemester' => null,
'error' => 'Failed to retrieve attendance data. Please try again later.'
@@ -269,9 +255,9 @@ class ParentController extends BaseController
return redirect()->back()->with('error', 'Configuration error: School year missing.');
}
// Get selected school year from request or fallback
$selectedYear = $this->request->getGet('school_year') ?? $this->schoolYear;
$isEditable = ($selectedYear === $this->schoolYear);
$context = $this->resolveSchoolYearContext();
$selectedYear = $context->yearName();
$isEditable = ! $context->isReadonly();
// Get parent ID from session
$parentId = session()->get('user_id');
@@ -370,22 +356,9 @@ class ParentController extends BaseController
);
}
// Fetch available school years
$schoolYears = $this->db->table('enrollments')
->select('school_year')
->distinct()
->orderBy('school_year', 'DESC')
->get()
->getResultArray();
if (empty($schoolYears)) {
$schoolYears[] = ['school_year' => $this->schoolYear];
}
// Render view
return view('/parent/enroll_classes', [
'students' => $students,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear,
'isEditable' => $isEditable,
'withdrawalDeadline' => $this->withdrawalDeadline,
+28 -13
View File
@@ -5,7 +5,7 @@ namespace App\Controllers\View;
use App\Models\ProjectModel;
use App\Models\StudentModel;
use App\Models\StudentClassModel;
use CodeIgniter\Controller;
use App\Controllers\BaseController;
use App\Models\TeacherClassModel;
use App\Models\ConfigurationModel;
use RuntimeException;
@@ -15,7 +15,7 @@ use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel;
class ProjectController extends Controller
class ProjectController extends BaseController
{
protected $db;
protected ConfigurationModel $configModel;
@@ -42,16 +42,26 @@ class ProjectController extends Controller
$studentModel = new StudentModel();
$projectModel = new ProjectModel();
$teacherClass = $teacherClassModel->where('teacher_id', $updatedBy)->first();
if (!$teacherClass) {
return redirect()->back()->with('status', 'No class section found for the current teacher.');
}
$classSectionId = $teacherClass['class_section_id'];
session()->set('class_section_id', $classSectionId);
$semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->getTeacherSchoolYear();
$classSectionId = (int) (
$this->request->getGet('class_section_id')
?? $this->request->getPost('class_section_id')
?? 0
);
if ($classSectionId <= 0) {
$teacherClass = $teacherClassModel
->where('teacher_id', $updatedBy)
->where('school_year', $schoolYear)
->where('semester', $semester)
->first();
if (!$teacherClass) {
return redirect()->back()->with('status', 'No class section found for the current teacher.');
}
$classSectionId = (int) $teacherClass['class_section_id'];
}
session()->set('class_section_id', $classSectionId);
$projectRows = $projectModel
->select('id, project_index')
@@ -71,6 +81,7 @@ class ProjectController extends Controller
$studentsClasses = $studentClassModel
->active()
->where('student_class.class_section_id', $classSectionId)
->where('student_class.school_year', $schoolYear)
->findAll();
$students = [];
@@ -115,7 +126,7 @@ class ProjectController extends Controller
]);
}
public function updateProjectScores(array $scores = null, int $updatedBy = null, int $classSectionId = null)
public function updateProjectScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
{
$scores = $this->request->getPost('scores');
$projectModel = new ProjectModel();
@@ -127,6 +138,7 @@ class ProjectController extends Controller
$classSectionId = session()->get('class_section_id');
$semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->getTeacherSchoolYear();
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$classSectionId = (int) ($classSectionId ?? 0);
if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) {
return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
@@ -190,7 +202,8 @@ class ProjectController extends Controller
'student_id' => $studentId,
'project_index' => $index,
'class_section_id' => $classSectionId,
'semester' => $semester
'semester' => $semester,
'school_year' => $schoolYear,
])->first();
$data = [
@@ -231,6 +244,7 @@ class ProjectController extends Controller
$classSectionId = session()->get('class_section_id');
$semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->getTeacherSchoolYear();
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$projectModel = new ProjectModel();
$studentClassModel = new StudentClassModel();
@@ -257,6 +271,7 @@ class ProjectController extends Controller
$students = $studentClassModel
->active()
->where('student_class.class_section_id', $classSectionId)
->where('student_class.school_year', $schoolYear)
->findAll();
foreach ($students as $student) {
@@ -533,6 +548,6 @@ class ProjectController extends Controller
private function getTeacherSchoolYear(): string
{
return (string) $this->schoolYear;
return $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
}
}
+13 -10
View File
@@ -2,7 +2,7 @@
namespace App\Controllers\View;
use CodeIgniter\Controller;
use App\Controllers\BaseController;
use App\Models\TeacherClassModel;
use App\Models\StudentClassModel;
use App\Models\StudentModel;
@@ -17,7 +17,7 @@ use Config\Services;
use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel;
class QuizController extends Controller
class QuizController extends BaseController
{
protected $db;
protected $semesterScoreService;
@@ -50,7 +50,7 @@ class QuizController extends Controller
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
}
public function updateQuizScores(array $scores = null, int $updatedBy = null, int $classSectionId = null)
public function updateQuizScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
{
$scores = $this->request->getPost('scores');
log_message('error', '✅ Raw Scores: ' . print_r($scores, true));
@@ -60,7 +60,8 @@ class QuizController extends Controller
}
$classSectionId = $this->request->getPost('class_section_id') ?? session()->get('class_section_id');
$semester = $this->request->getPost('semester') ?? $this->getTeacherSelectedSemester();
$schoolYear = $this->request->getPost('school_year') ?? $this->schoolYear;
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$classSectionId = (int) ($classSectionId ?? 0);
if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) {
return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
@@ -206,7 +207,7 @@ class QuizController extends Controller
// 3) Headers: distinct quiz_index for this class/term
$semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->schoolYear;
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$quizHeaderRows = $this->quizModel->select('quiz_index')
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
@@ -272,9 +273,9 @@ class QuizController extends Controller
'students' => $students,
'quizHeaders' => $quizHeaders, // e.g., [1,2,3]
'semester' => $semester,
'schoolYear' => $this->schoolYear,
'schoolYear' => $schoolYear,
'classSectionId' => $classSectionId,
'missingOkMap' => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $this->schoolYear, 'quiz'),
'missingOkMap' => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'quiz'),
]);
}
@@ -294,7 +295,8 @@ class QuizController extends Controller
try {
$semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->schoolYear;
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$existingQuizNumbers = $this->quizModel
->select('quiz_index')
->where('class_section_id', $classSectionId)
@@ -323,6 +325,7 @@ class QuizController extends Controller
$students = $this->studentClassModel
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->findAll();
if (empty($students)) {
@@ -341,8 +344,8 @@ class QuizController extends Controller
'quiz_index' => $nextQuizNumber,
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
'semester' => $semester,
'school_year' => $schoolYear,
])
->first();
@@ -46,9 +46,11 @@ class ScoreCommentController extends BaseController
public function saveComments()
{
$schoolYearContext = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($schoolYearContext);
$comments = $this->request->getPost('comments');
$classSectionId = (int)($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0);
$schoolYear = (string) ($this->request->getPost('school_year') ?? $this->schoolYear);
$schoolYear = $schoolYearContext->yearName();
$rawSelectedSemester = $this->request->getPost('selected_semester');
$normalizedSelected = $this->normalizeSemesterSelection($rawSelectedSemester);
if ($normalizedSelected !== '') {
+21 -35
View File
@@ -2,7 +2,7 @@
namespace App\Controllers\View;
use CodeIgniter\Controller;
use App\Controllers\BaseController;
use App\Models\TeacherClassModel;
use App\Models\StudentClassModel;
use App\Models\StudentModel;
@@ -28,7 +28,7 @@ use App\Models\MissingScoreOverrideModel;
class ScoreController extends Controller
class ScoreController extends BaseController
{
protected $semesterScoreService;
protected $db;
@@ -88,6 +88,8 @@ class ScoreController extends Controller
public function index()
{
log_message('debug', 'ScoreController::index invoked');
$schoolYearContext = $this->resolveSchoolYearContext();
$this->schoolYear = $schoolYearContext->yearName();
$semesterChoiceParam = $this->request->getGet('semester_choice');
$session = session();
if ($this->request->getGet('choose_semester') !== null) {
@@ -139,25 +141,13 @@ class ScoreController extends Controller
);
if (empty($assignments)) {
// Fallback: any class regardless of term
$fallback = $this->teacherClassModel
->where('teacher_id', $teacherId)
->first();
if ($fallback) {
$assignments = [[
'class_section_id' => $fallback['class_section_id'],
'class_section_name' => null,
'school_year' => $fallback['school_year'] ?? $effectiveSchoolYear,
'semester' => $fallback['semester'] ?? $effectiveSemester,
]];
$effectiveSemester = $fallback['semester'] ?? $effectiveSemester;
$effectiveSchoolYear = $fallback['school_year'] ?? $effectiveSchoolYear;
} else {
log_message('info', 'No class section assigned to teacher ID: ' . $teacherId);
return redirect()
->to('no-classes')
->with('message', 'You do not have an assigned class yet. Please contact the administration.');
}
log_message(
'info',
"No {$effectiveSchoolYear} {$effectiveSemester} class section assigned to teacher ID: {$teacherId}"
);
return redirect()
->to('no-classes')
->with('message', 'You do not have an assigned class for the selected school year and semester. Please contact the administration.');
}
$allowedIds = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments);
@@ -222,6 +212,7 @@ class ScoreController extends Controller
$csRow = $this->db->table('classSection')
->select('class_section_name')
->where('class_section_id', $classSectionId)
->where('school_year', $effectiveSchoolYear)
->get()->getRowArray();
$classSectionName = $csRow['class_section_name'] ?? '';
log_message('debug', "ScoreController::index teacher {$teacherId} classSection {$classSectionId} semester {$effectiveSemester}");
@@ -281,6 +272,7 @@ class ScoreController extends Controller
'focusTarget' => $focusTarget,
'schoolYear' => $effectiveSchoolYear,
'scoresLocked' => $scoresLocked,
'isSchoolYearReadonly' => $schoolYearContext->isReadonly(),
];
return view('/teacher/scores', $data);
@@ -288,11 +280,13 @@ class ScoreController extends Controller
public function submitScoresLock()
{
$schoolYearContext = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($schoolYearContext);
$classSectionId = (int) ($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0);
$semesterRaw = (string) ($this->request->getPost('semester') ?? $this->request->getPost('selected_semester') ?? '');
$semesterNormalized = $this->normalizeSemester($semesterRaw);
$semester = $semesterNormalized !== '' ? ucfirst($semesterNormalized) : $this->semester;
$schoolYear = (string) ($this->request->getPost('school_year') ?? $this->schoolYear);
$schoolYear = $schoolYearContext->yearName();
if ($classSectionId <= 0 || $semester === '' || $schoolYear === '') {
return redirect()->back()->with('error', 'Missing class section or term.');
@@ -496,6 +490,7 @@ class ScoreController extends Controller
$studentClasses = $this->studentClassModel
->active()
->where('student_class.class_section_id', $classSectionId)
->where('student_class.school_year', $schoolYear)
->findAll();
foreach ($studentClasses as $studentClass) {
@@ -1086,6 +1081,9 @@ class ScoreController extends Controller
public function updateScores(string $table, ?string $redirectUrl = null)
{
$schoolYearContext = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($schoolYearContext);
$this->schoolYear = $schoolYearContext->yearName();
$builder = $this->db->table($table);
$scores = $this->request->getPost('final_score'); // name must match input form
@@ -1322,18 +1320,7 @@ public function viewStudentScore()
return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
}
// Handle selected school year (parents can view scores for any available year)
$selectedYear = $this->request->getVar('school_year') ?? $this->schoolYear;
// Fetch distinct school years only from semester_scores
$query = $this->db->table('semester_scores')
->select('school_year')
->distinct()
->get();
$schoolYears = array_column($query->getResultArray(), 'school_year');
$schoolYears = array_unique($schoolYears);
rsort($schoolYears);
$selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
// Initialize scores array
$scores = [];
@@ -1402,7 +1389,6 @@ $students = $this->db->table('students')
return view('/parent/scores', [
'organizedScores' => $scores,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear,
'semesterOrder' => ['Fall', 'Spring'],
'showExamScores' => $releaseAny,
+2
View File
@@ -21,6 +21,7 @@ class ClassProgressReportModel extends Model
protected $allowedFields = [
'class_section_id',
'teacher_id',
'school_year',
'week_start',
'week_end',
'subject',
@@ -50,6 +51,7 @@ class ClassProgressReportModel extends Model
protected $validationRules = [
'class_section_id' => 'required|integer',
'teacher_id' => 'required|integer',
'school_year' => 'permit_empty|string|max_length[9]',
'week_start' => 'required|valid_date[Y-m-d]',
'week_end' => 'required|valid_date[Y-m-d]',
'subject' => 'required|string|max_length[160]',
+1
View File
@@ -15,6 +15,7 @@ class PrintRequestModel extends Model
'teacher_id',
'admin_id',
'class_id',
'school_year',
'file_path',
'page_selection',
'num_copies',
+79 -3
View File
@@ -24,11 +24,87 @@ class SubjectCurriculumModel extends Model
public function getOptionsForClass(int $classId, string $subject): array
{
return $this->where('class_id', $classId)
->where('subject', $subject)
$rows = $this->orderedOptionsBuilder()
->where('subject_curriculum_items.class_id', $classId)
->where('subject_curriculum_items.subject', $subject)
->get()
->getResultArray();
if (! empty($rows)) {
return $rows;
}
$fallbackClassIds = $this->classIdsWithSameName($classId);
if (empty($fallbackClassIds)) {
return [];
}
$rows = $this->orderedOptionsBuilder()
->whereIn('subject_curriculum_items.class_id', $fallbackClassIds)
->where('subject_curriculum_items.subject', $subject)
->get()
->getResultArray();
return $this->uniqueCurriculumRows($rows);
}
private function orderedOptionsBuilder()
{
return $this->db->table($this->table)
->select('subject_curriculum_items.*')
->orderBy('unit_number', 'ASC')
->orderBy("CAST(SUBSTRING_INDEX(chapter_name, '.', 1) AS UNSIGNED)", 'ASC', false)
->orderBy('chapter_name', 'ASC')
->findAll();
->orderBy('id', 'ASC');
}
private function classIdsWithSameName(int $classId): array
{
$class = $this->db->table('classes')
->select('class_name')
->where('id', $classId)
->get()
->getRowArray();
$className = trim((string) ($class['class_name'] ?? ''));
if ($className === '') {
return [];
}
$rows = $this->db->table('classes')
->select('id')
->where('class_name', $className)
->where('id !=', $classId)
->get()
->getResultArray();
return array_values(array_filter(array_map(
static fn (array $row): int => (int) ($row['id'] ?? 0),
$rows
)));
}
private function uniqueCurriculumRows(array $rows): array
{
$seen = [];
$unique = [];
foreach ($rows as $row) {
$key = implode('|', [
(string) ($row['subject'] ?? ''),
(string) ($row['unit_number'] ?? ''),
(string) ($row['unit_title'] ?? ''),
(string) ($row['chapter_name'] ?? ''),
]);
if (isset($seen[$key])) {
continue;
}
$seen[$key] = true;
$unique[] = $row;
}
return $unique;
}
}
@@ -4,30 +4,6 @@
<div class="wrapper">
<div class="content"></div>
<h2 class="text-center mt-4 mb-3">Classes Lists</h2>
<form method="get" class="row g-2 align-items-center justify-content-center mb-3">
<div class="col-auto">
<label class="form-label mb-0">School year</label>
</div>
<div class="col-auto">
<select name="school_year" class="form-select form-select-sm" style="min-width: 200px;">
<?php if (!empty($schoolYears)): ?>
<?php foreach ($schoolYears as $yearOption): ?>
<option value="<?= esc($yearOption) ?>" <?= ($selectedYear === $yearOption ? 'selected' : '') ?>>
<?= esc($yearOption) ?>
</option>
<?php endforeach; ?>
<?php else: ?>
<option value="<?= esc($schoolYear ?? '') ?>" <?= ($selectedYear === ($schoolYear ?? '') ? 'selected' : '') ?>>
<?= esc($schoolYear ?? 'Current') ?>
</option>
<?php endif; ?>
</select>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
<a class="btn btn-outline-secondary btn-sm" href="<?= base_url('administrator/class_assignment') ?>">Reset</a>
</div>
</form>
<?php if (session()->getFlashdata('message')): ?>
<div class="alert alert-success">
+1 -42
View File
@@ -19,47 +19,6 @@ if (empty($schoolYears) && $schoolYear !== '') {
<div class="wrapper below-sixty-wrapper">
<h2 class="text-center mt-4 mb-4 below-sixty-title">Below 60 Summary</h2>
<!-- School year filter only -->
<div class="card shadow-sm mb-3">
<div class="card-body py-3">
<form method="get"
action="<?= site_url('grading/below-60') ?>"
class="row g-2 align-items-end justify-content-center">
<input type="hidden" name="semester" value="fall">
<div class="col-12 col-sm-auto">
<label class="form-label mb-1 small fw-semibold">School Year</label>
<?php if (!empty($schoolYears)): ?>
<select name="school_year"
class="form-select form-select-sm"
style="min-width:160px;">
<?php foreach ($schoolYears as $yr): ?>
<option value="<?= esc($yr) ?>" <?= (string)$yr === (string)$schoolYear ? 'selected' : '' ?>>
<?= esc($yr) ?>
</option>
<?php endforeach; ?>
</select>
<?php else: ?>
<input type="text"
name="school_year"
class="form-control form-control-sm"
style="min-width:160px;"
value="<?= esc($schoolYear) ?>"
placeholder="2025-2026">
<?php endif; ?>
</div>
<div class="col-12 col-sm-auto">
<button type="submit" class="btn btn-sm btn-primary">
<i class="bi bi-funnel me-1"></i>Apply
</button>
</div>
</form>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<div class="text-muted">
Fall • <?= esc($schoolYear) ?>
@@ -478,4 +437,4 @@ if (empty($schoolYears) && $schoolYear !== '') {
}
})();
</script>
<?= $this->endSection() ?>
<?= $this->endSection() ?>
+14 -1
View File
@@ -23,7 +23,20 @@
<body>
<!-- Navbar -->
<?php include(__DIR__ . '/../partials/navbar.php'); ?>
<?= view('partials/school_year_selector') ?>
<?php
$mainSchoolYearSelectorData = [];
if (!isset($schoolYearContext, $schoolYearOptions) && session()->get('user_id')) {
try {
$mainSchoolYearSelectorData = service('schoolYearViewData')->forCurrentRequest(service('request'));
$mainSchoolYearSelectorData['schoolYearSelectorEnabled'] = true;
} catch (\Throwable $e) {
log_message('warning', 'Unable to load main school-year selector: {message}', [
'message' => $e->getMessage(),
]);
}
}
?>
<?= view('partials/school_year_selector', $mainSchoolYearSelectorData) ?>
<div class="container my-4">
<?= $this->renderSection('content') ?>
+14 -1
View File
@@ -165,7 +165,20 @@ html, body { overflow-x: hidden; }
<body data-app-menu-mode="<?= esc($appMenuMode) ?>">
<?php include(__DIR__ . '/../partials/navbar.php'); ?>
<?= view('partials/school_year_selector') ?>
<?php
$mainSchoolYearSelectorData = [];
if (!isset($schoolYearContext, $schoolYearOptions) && session()->get('user_id')) {
try {
$mainSchoolYearSelectorData = service('schoolYearViewData')->forCurrentRequest(service('request'));
$mainSchoolYearSelectorData['schoolYearSelectorEnabled'] = true;
} catch (\Throwable $e) {
log_message('warning', 'Unable to load main school-year selector: {message}', [
'message' => $e->getMessage(),
]);
}
}
?>
<?= view('partials/school_year_selector', $mainSchoolYearSelectorData) ?>
<?php
$uri = service('uri');
+4 -16
View File
@@ -2,22 +2,10 @@
<?= $this->section('content') ?>
<div class="container my-5">
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Attendance Record</h2>
<?php if (!empty($selectedYear)): ?>
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div>
<!-- Filter Form -->
<form action="" method="get" class="form-inline">
<div class="d-flex align-items-center">
<select name="school_year" id="school_year" class="form-control me-3"> <!-- Added me-3 (right margin) -->
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year['school_year']) ?>" <?= $selectedYear === $year['school_year'] ? 'selected' : '' ?>>
<?= esc($year['school_year']) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<!-- Error Message -->
<?php if (isset($error)): ?>
@@ -52,7 +40,7 @@
</tbody>
</table>
<?php else: ?>
<div class="alert alert-info mb-3 d-inline-block">No attendance records found for the selected school year.</div>
<div class="alert alert-info mb-3 d-inline-block">No attendance records found for this school year.</div>
<?php endif; ?>
</main>
</div>
+3 -13
View File
@@ -2,21 +2,11 @@
<?= $this->section('content') ?>
<div class="container my-5">
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Enroll in Classes</h3>
<?php if (!empty($selectedYear)): ?>
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div>
<!-- School Year Filter -->
<form action="<?= base_url('/parent/enroll_classes') ?>" method="get" class="form-inline mb-3">
<div class="d-flex align-items-center">
<select name="school_year" id="school_year" class="form-control me-3">
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year['school_year']) ?>" <?= $selectedYear === $year['school_year'] ? 'selected' : '' ?>>
<?= esc($year['school_year']) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<?php
// Put this near the very top of the file (before output), or right after the extend/section lines.
$tz = (string) (config('School')->attendance['timezone'] ?? user_timezone());
+4 -29
View File
@@ -63,24 +63,11 @@ if (!function_exists('parseDbDateTime')) {
<div class="container my-5">
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Invoices and Payments</h3>
<?php if (!empty($selectedYear)): ?>
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div>
<!-- School Year Filter -->
<form action="<?= base_url('/parent/invoice_payment') ?>" method="get" class="form-inline mb-3">
<div class="d-flex align-items-center justify-content-center">
<select name="school_year" id="school_year" class="form-control me-3">
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year['school_year']) ?>"
<?= ($selectedYear === $year['school_year']) ? 'selected' : '' ?>
<?= ($currentSchoolYear === $year['school_year']) ? 'data-current="true"' : '' ?>>
<?= esc($year['school_year']) ?><?= ($currentSchoolYear === $year['school_year']) ? ' (Current)' : '' ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<!-- Display Payment Notice / Deadline -->
<?php
$displayTz = user_timezone();
@@ -198,7 +185,7 @@ $deadline = parseDbDateTime($dueDate, 'UTC', $displayTz); // format either DATE
<?php endif; ?>
<?php else: ?>
<div class="alert alert-info mb-3 d-inline-block">
No invoice found for the selected school year.
No invoice found for this school year.
</div>
<?php endif; ?>
@@ -208,18 +195,6 @@ $deadline = parseDbDateTime($dueDate, 'UTC', $displayTz); // format either DATE
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
const dropdown = document.getElementById('school_year');
const options = dropdown.options;
for (let i = 0; i < options.length; i++) {
if (options[i].dataset.current === "true") {
options[i].style.fontWeight = 'bold';
if (!dropdown.value) dropdown.selectedIndex = i;
break;
}
}
});
function showPaymentDevelopmentMessage(event) {
event.preventDefault();
alert('The payment method is currently under development. Please contact the school administration at alrahma.isgl@gmail.com');
+3 -17
View File
@@ -3,24 +3,12 @@
<div class="container my-5">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2">
<h3 class="text-success mb-0" style="font-family: Arial, sans-serif;">Scores</h3>
<?php if (!empty($selectedYear)): ?>
<div class="text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div>
</div>
<!-- Filter Form -->
<form action="" method="get" class="form-inline">
<div class="d-flex align-items-center">
<select name="school_year" id="school_year" class="form-control me-3"> <!-- Added me-3 (right margin) -->
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year) ?>" <?= $selectedYear === $year ? 'selected' : '' ?>>
<?= esc($year) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<!-- Scores Table -->
<?php $yearScores = $organizedScores[$selectedYear] ?? []; ?>
<?php $semesterOrder = $semesterOrder ?? ['Fall', 'Spring']; ?>
@@ -104,12 +92,10 @@ $showExamScoresBySemester = $showExamScoresBySemester ?? [];
<?php
$reportStudentId = (int) ($student['student_id'] ?? 0);
if ($reportStudentId > 0) {
$reportYear = (string) ($selectedYear ?? '');
$reportSemester = (string) ($semester ?? '');
$reportDate = date('Y-m-d');
$reportUrl = base_url('parent/report-cards/view/' . $reportStudentId);
$query = http_build_query([
'school_year' => $reportYear,
'semester' => $reportSemester,
'report_date' => $reportDate,
]);
+11 -5
View File
@@ -1,10 +1,16 @@
<?= $this->extend('layout/main_layout') ?>
<?= $this->section('content') ?>
<?php $isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false); ?>
<div class="container-fluid py-5">
<div class="row">
<div class="col-md-12">
<h2>Print/Copy Requests</h2>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">
This school year is read-only. Existing requests are visible, but changes are disabled.
</div>
<?php endif; ?>
<!-- Print/Copy Request Tabs -->
<div class="card my-4">
@@ -109,7 +115,7 @@
You are not assigned to any class. Please contact the administrator to be able to submit print requests.
</div>
<?php endif; ?>
<button type="submit" class="btn btn-primary mt-3" <?= (!isset($class_id) || !$class_id) ? 'disabled' : '' ?>>Submit Request</button>
<button type="submit" class="btn btn-primary mt-3" <?= (!isset($class_id) || !$class_id || $isSchoolYearReadonly) ? 'disabled' : '' ?>>Submit Request</button>
</div>
</div>
</form>
@@ -164,7 +170,7 @@
<div class="form-text mt-2">
No file upload is required; please hand the original document to the copy staff after submitting.
</div>
<button type="submit" class="btn btn-outline-primary mt-3">Submit Copy Request</button>
<button type="submit" class="btn btn-outline-primary mt-3" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Submit Copy Request</button>
</form>
</div>
</div>
@@ -241,10 +247,10 @@
</td>
<td>
<div class="d-flex flex-wrap gap-1">
<?php if (in_array($request['status'], ['not_assigned', 'assigned'])): ?>
<?php if (!$isSchoolYearReadonly && in_array($request['status'], ['not_assigned', 'assigned'])): ?>
<button type="button" class="btn btn-sm btn-warning mb-1" onclick='editRequest(<?= json_encode($request, JSON_HEX_APOS | JSON_HEX_QUOT) ?>)'>Edit</button>
<?php endif; ?>
<?php if ($request['status'] === 'not_assigned'): ?>
<?php if (!$isSchoolYearReadonly && $request['status'] === 'not_assigned'): ?>
<button type="button" class="btn btn-sm btn-danger mb-1" onclick="deleteRequest(<?= $request['id'] ?>)">Delete</button>
<?php endif; ?>
</div>
@@ -328,7 +334,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="submit" class="btn btn-primary" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Save changes</button>
</div>
</form>
</div>
-15
View File
@@ -7,21 +7,6 @@
<h2 class="mb-0">Student Score Card</h2>
<div class="text-muted small">Select a student to open their score card.</div>
</div>
<?php
$roleRaw = session()->get('role');
if (is_array($roleRaw)) {
$roleRaw = $roleRaw[0] ?? 'guest';
}
$role = strtolower((string)($roleRaw ?? 'guest'));
if (in_array($role, ['parent', 'parent_dashboard'], true)) {
$backUrl = base_url('/parent_dashboard');
} elseif (in_array($role, ['teacher', 'teacher_assistant', 'teacher_dashboard'], true)) {
$backUrl = base_url('/teacher_dashboard');
} else {
$backUrl = base_url('/');
}
?>
<a href="<?= $backUrl ?>" class="btn btn-outline-secondary">Back</a>
</div>
<?php if (empty($students)): ?>
+12 -2
View File
@@ -1,12 +1,17 @@
<?= $this->extend('layout/main_layout') ?>
<?= $this->section('content') ?>
<?php $isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false); ?>
<div class="container py-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<div>
<h3 class="mb-0">Class Progress Reports</h3>
<div class="text-muted">Review weekly submissions and compare Islamic Studies with Quran/Arabic.</div>
</div>
<a href="<?= base_url('teacher/progress/submit') ?>" class="btn btn-outline-secondary">Submit New Report</a>
<?php if ($isSchoolYearReadonly): ?>
<span class="btn btn-outline-secondary disabled" aria-disabled="true">Submit New Report</span>
<?php else: ?>
<a href="<?= base_url('teacher/progress/submit') ?>" class="btn btn-outline-secondary">Submit New Report</a>
<?php endif; ?>
</div>
<?php
@@ -15,6 +20,9 @@
$classSectionOptions = $classSectionOptions ?? [];
$selectedSectionId = $selectedSectionId ?? null;
?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">This school year is read-only. Progress submissions can be viewed but not changed.</div>
<?php endif; ?>
<?php if (!empty($classSectionOptions)): ?>
<form method="get" class="mb-3 row g-2 align-items-center">
<div class="col-auto">
@@ -90,7 +98,9 @@
<td class="text-end">
<?php if ($exampleReport): ?>
<a href="<?= base_url('teacher/progress/view/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-primary">View Weekly Details</a>
<a href="<?= base_url('teacher/progress/edit/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-secondary ms-1">Edit</a>
<?php if (!$isSchoolYearReadonly): ?>
<a href="<?= base_url('teacher/progress/edit/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-secondary ms-1">Edit</a>
<?php endif; ?>
<?php endif; ?>
</td>
</tr>
+13 -7
View File
@@ -11,6 +11,7 @@
$weekStartSelected = set_value('week_start', $defaultWeekStart);
$weekEndValue = set_value('week_end', $existingWeekEnd ?? '');
$existingReports = $existingReports ?? [];
$isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false);
if (!$weekEndValue && $weekStartSelected) {
try {
$dt = new \DateTime($weekStartSelected);
@@ -58,6 +59,9 @@
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">This school year is read-only. Progress submissions are disabled.</div>
<?php endif; ?>
<?php if (session()->getFlashdata('errors')): ?>
<div class="alert alert-danger">
<ul class="mb-0">
@@ -142,7 +146,8 @@
<button type="button"
class="btn btn-sm btn-outline-dark"
data-add-unit-chapter
data-subject="<?= esc($slug) ?>">
data-subject="<?= esc($slug) ?>"
<?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<?= $slug === 'quran' ? '+ Add Surah, Arabic Subject' : '+ Add Unit, Chapter' ?>
</button>
<div class="subject-curriculum-list position-absolute d-none border rounded shadow-sm bg-white mt-1" id="curriculumList-<?= esc($slug) ?>" style="min-width: 220px; z-index: 5;">
@@ -170,7 +175,8 @@
data-subject="<?= esc($slug) ?>"
data-unit-number="<?= esc($unitNumber) ?>"
data-unit-title="<?= esc($unitTitle) ?>"
data-chapter="<?= esc($chapterName) ?>">
data-chapter="<?= esc($chapterName) ?>"
<?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<?= esc($display) ?>
</button>
<?php endforeach; ?>
@@ -179,7 +185,7 @@
<div class="px-2 py-2 border-top">
<div class="input-group input-group-sm">
<input type="text" class="form-control" placeholder="Custom Surah" data-custom-input data-subject="<?= esc($slug) ?>">
<button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>">Add</button>
<button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Add</button>
</div>
<div class="form-text small text-muted">Add a custom Surah or Arabic target.</div>
</div>
@@ -187,7 +193,7 @@
<div class="px-2 py-2 border-top">
<div class="input-group input-group-sm">
<input type="text" class="form-control" placeholder="Type subject or topic" data-custom-input data-subject="<?= esc($slug) ?>">
<button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>">Add</button>
<button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Add</button>
</div>
<div class="form-text small text-muted">Add a subject or unit not listed above (e.g. Seerah, Fiqh, Akhlaq).</div>
</div>
@@ -206,7 +212,7 @@
<input type="text" name="chapter_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Chapter" value="<?= esc($chapterValues[$i] ?? '') ?>" readonly>
</div>
<div class="col-auto">
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-unit-chapter aria-label="Remove entry">X</button>
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-unit-chapter aria-label="Remove entry" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>X</button>
</div>
</div>
<?php endfor; ?>
@@ -232,7 +238,7 @@
<div class="col">
<div class="card shadow-sm">
<div class="card-body d-flex flex-column">
<button class="btn btn-primary w-100 mt-auto" type="submit" <?= $hasClass ? '' : 'disabled' ?>><?= esc($submitLabel) ?></button>
<button class="btn btn-primary w-100 mt-auto" type="submit" <?= ($hasClass && !$isSchoolYearReadonly) ? '' : 'disabled' ?>><?= esc($submitLabel) ?></button>
<?php if (! $hasClass): ?>
<div class="text-muted small mt-2">
You are not assigned to a class. Contact the administrator to submit progress.
@@ -288,7 +294,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirmOverwriteButton">Override</button>
<button type="button" class="btn btn-primary" id="confirmOverwriteButton" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Override</button>
</div>
</div>
</div>
+10 -4
View File
@@ -10,6 +10,7 @@ $examTypes = $examTypes ?? [];
$schoolYear = $schoolYear ?? '';
$semester = $semester ?? '';
$maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
$isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false);
?>
<style>
.teacher-drafts-page,
@@ -48,6 +49,11 @@ $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">
This school year is read-only. Exam draft submissions are disabled.
</div>
<?php endif; ?>
<div class="card mb-4">
<div class="card-body">
@@ -55,7 +61,7 @@ $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
<?= csrf_field() ?>
<div class="col-md-6">
<label class="form-label" for="exam_type">Exam type <span class="text-danger">*</span></label>
<select name="exam_type" id="exam_type" class="form-select" required>
<select name="exam_type" id="exam_type" class="form-select" required <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<option value="">— Select —</option>
<?php foreach ($examTypes as $t): ?>
<option value="<?= esc($t) ?>" <?= old('exam_type') === $t ? 'selected' : '' ?>><?= esc($t) ?></option>
@@ -64,15 +70,15 @@ $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
</div>
<div class="col-8">
<label class="form-label" for="author_comment">Author comment</label>
<textarea name="author_comment" id="author_comment" class="form-control" rows="2" placeholder="Optional note for reviewers"><?= esc(old('author_comment') ?? '') ?></textarea>
<textarea name="author_comment" id="author_comment" class="form-control" rows="2" placeholder="Optional note for reviewers" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>><?= esc(old('author_comment') ?? '') ?></textarea>
</div>
<div class="col-md-8">
<label class="form-label" for="draft_file">File (Word) <span class="text-danger">*</span></label>
<input type="file" name="draft_file" id="draft_file" class="form-control" accept=".doc,.docx" required>
<input type="file" name="draft_file" id="draft_file" class="form-control" accept=".doc,.docx" required <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<div class="form-text">Max <?= esc(number_format($maxUploadBytes / 1048576, 1)) ?> MB.</div>
</div>
<div class="col-12 d-flex flex-wrap gap-2">
<button type="submit" class="btn btn-primary">Submit for review</button>
<button type="submit" class="btn btn-primary" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Submit for review</button>
</div>
<?= form_close() ?>
<?php if ($schoolYear !== '' || $semester !== ''): ?>
+30 -8
View File
@@ -32,7 +32,9 @@
<?php
$classSectionId = (int)($class_section_id ?? 0);
$scoresLocked = !empty($scoresLocked);
$lockAttr = $scoresLocked ? 'disabled' : '';
$isSchoolYearReadonly = !empty($isSchoolYearReadonly);
$editingDisabled = $scoresLocked || $isSchoolYearReadonly;
$lockAttr = $editingDisabled ? 'disabled' : '';
$displayScore = static function ($value) {
return ($value === null || $value === '') ? '' : esc($value);
};
@@ -94,6 +96,11 @@
Scores are submitted and locked for this semester.
</div>
<?php endif; ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-info mt-3 mb-0">
This school year is read-only. You can view scores, but changes are disabled.
</div>
<?php endif; ?>
<div class="mt-3">
<a href="<?= base_url('/teacher/scores?choose_semester=1') ?>" class="btn btn-outline-secondary btn-sm">Choose another semester</a>
</div>
@@ -291,14 +298,29 @@
</div>
<div class="scores-action-bar d-flex flex-wrap gap-2 mt-3">
<a href="<?= base_url('/teacher/addHomework?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Homework</a>
<a href="<?= base_url('/teacher/addQuiz?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Quiz</a>
<a href="<?= base_url('/teacher/addProject?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Project</a>
<a href="<?= base_url('/teacher/addParticipation?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Participation</a>
<?php if ($isSchoolYearReadonly): ?>
<button type="button" class="btn btn-secondary" disabled>Add Homework</button>
<button type="button" class="btn btn-secondary" disabled>Add Quiz</button>
<button type="button" class="btn btn-secondary" disabled>Add Project</button>
<button type="button" class="btn btn-secondary" disabled>Add Participation</button>
<?php else: ?>
<a href="<?= base_url('/teacher/addHomework?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Homework</a>
<a href="<?= base_url('/teacher/addQuiz?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Quiz</a>
<a href="<?= base_url('/teacher/addProject?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Project</a>
<a href="<?= base_url('/teacher/addParticipation?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Participation</a>
<?php endif; ?>
<?php if ($semester === 'fall'): ?>
<a href="<?= base_url('/teacher/addMidtermExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Midterm</a>
<?php if ($isSchoolYearReadonly): ?>
<button type="button" class="btn btn-secondary" disabled>Add Midterm</button>
<?php else: ?>
<a href="<?= base_url('/teacher/addMidtermExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Midterm</a>
<?php endif; ?>
<?php elseif ($semester === 'spring'): ?>
<a href="<?= base_url('/teacher/addFinalExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Final</a>
<?php if ($isSchoolYearReadonly): ?>
<button type="button" class="btn btn-secondary" disabled>Add Final</button>
<?php else: ?>
<a href="<?= base_url('/teacher/addFinalExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Final</a>
<?php endif; ?>
<?php endif; ?>
<button type="submit" class="btn btn-info" <?= $lockAttr ?>>Save Comments</button>
@@ -309,7 +331,7 @@
formaction="<?= base_url('/teacher/submit-scores-lock') ?>"
formmethod="post"
data-confirm-message="Once you submit, you cannot add any scores or comments. Do you want to continue?"
<?= $scoresLocked ? 'disabled' : '' ?>>
<?= $editingDisabled ? 'disabled' : '' ?>>
<?= $scoresLocked ? 'Scores Locked' : 'Submit Semester Scores' ?>
</button>
</div>