security fix and fix parent pages
API CI/CD / Validate (composer + pint) (push) Successful in 3m7s
API CI/CD / Test (PHPUnit) (push) Failing after 5m46s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 49s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
API CI/CD / Validate (composer + pint) (push) Successful in 3m7s
API CI/CD / Test (PHPUnit) (push) Failing after 5m46s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 49s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
This commit is contained in:
@@ -9,6 +9,7 @@ use App\Models\TeacherClass;
|
||||
use App\Models\User;
|
||||
use App\Services\SchoolYears\SchoolYearContextService;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ClassProgressQueryService
|
||||
@@ -29,22 +30,37 @@ class ClassProgressQueryService
|
||||
->orderBy($filters['sort_by'] ?? 'week_start', $filters['sort_dir'] ?? 'desc');
|
||||
|
||||
if (! $this->isAdmin($user)) {
|
||||
$relaxedSectionIds = TeacherClass::distinctSectionIdsForTeacher((int) $user->id);
|
||||
if ($this->isParent($user)) {
|
||||
$parentSectionIds = $this->parentSectionIds((int) $user->id);
|
||||
|
||||
if (! empty($filters['class_section_id'])) {
|
||||
$sid = (int) $filters['class_section_id'];
|
||||
// Section filter: if this user has ever taught the section, show all rows for that section
|
||||
// (legacy DB often has `class_progress_reports` without matching current-year `teacher_class`).
|
||||
if (! in_array($sid, $relaxedSectionIds, true)) {
|
||||
$query->where('teacher_id', (int) $user->id);
|
||||
if (! empty($filters['class_section_id'])) {
|
||||
$sid = (int) $filters['class_section_id'];
|
||||
if (! in_array($sid, $parentSectionIds, true)) {
|
||||
$query->whereRaw('1 = 0');
|
||||
}
|
||||
} elseif ($parentSectionIds !== []) {
|
||||
$query->whereIn('class_progress_reports.class_section_id', $parentSectionIds);
|
||||
} else {
|
||||
$query->whereRaw('1 = 0');
|
||||
}
|
||||
} else {
|
||||
$query->where(function ($q) use ($user, $relaxedSectionIds) {
|
||||
$q->where('teacher_id', (int) $user->id);
|
||||
if ($relaxedSectionIds !== []) {
|
||||
$q->orWhereIn('class_section_id', $relaxedSectionIds);
|
||||
$relaxedSectionIds = TeacherClass::distinctSectionIdsForTeacher((int) $user->id);
|
||||
|
||||
if (! empty($filters['class_section_id'])) {
|
||||
$sid = (int) $filters['class_section_id'];
|
||||
// Section filter: if this user has ever taught the section, show all rows for that section
|
||||
// (legacy DB often has `class_progress_reports` without matching current-year `teacher_class`).
|
||||
if (! in_array($sid, $relaxedSectionIds, true)) {
|
||||
$query->where('teacher_id', (int) $user->id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$query->where(function ($q) use ($user, $relaxedSectionIds) {
|
||||
$q->where('teacher_id', (int) $user->id);
|
||||
if ($relaxedSectionIds !== []) {
|
||||
$q->orWhereIn('class_section_id', $relaxedSectionIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} elseif (! empty($filters['teacher_id'])) {
|
||||
$query->where('teacher_id', (int) $filters['teacher_id']);
|
||||
@@ -62,12 +78,50 @@ class ClassProgressQueryService
|
||||
$query->where('status', (string) $filters['status']);
|
||||
}
|
||||
|
||||
if (! empty($filters['school_year']) && Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||
$query->where('school_year', (string) $filters['school_year']);
|
||||
$schoolYear = $this->resolveSchoolYearFilter($filters);
|
||||
$schoolYearRange = $this->schoolYearDateRange($schoolYear);
|
||||
if ($schoolYear !== '' && Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||
$query->where(function ($yearQuery) use ($schoolYear, $schoolYearRange) {
|
||||
$yearQuery->where('class_progress_reports.school_year', $schoolYear)
|
||||
->orWhere(function ($legacyQuery) use ($schoolYear, $schoolYearRange) {
|
||||
$legacyQuery->where(function ($blankQuery) {
|
||||
$blankQuery->whereNull('class_progress_reports.school_year')
|
||||
->orWhere('class_progress_reports.school_year', '');
|
||||
});
|
||||
|
||||
if ($schoolYearRange !== null) {
|
||||
$legacyQuery->whereBetween('class_progress_reports.week_start', [
|
||||
$schoolYearRange['start'],
|
||||
$schoolYearRange['end'],
|
||||
]);
|
||||
} else {
|
||||
$legacyQuery->whereExists(function ($sectionQuery) use ($schoolYear) {
|
||||
$sectionQuery->select(DB::raw(1))
|
||||
->from('classSection as progress_filter_cs')
|
||||
->whereColumn('progress_filter_cs.class_section_id', 'class_progress_reports.class_section_id')
|
||||
->where('progress_filter_cs.school_year', $schoolYear);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (! empty($filters['semester']) && Schema::hasColumn('class_progress_reports', 'semester')) {
|
||||
$query->where('semester', (string) $filters['semester']);
|
||||
$semester = (string) $filters['semester'];
|
||||
$query->where(function ($semesterQuery) use ($semester) {
|
||||
$semesterQuery->where('class_progress_reports.semester', $semester)
|
||||
->orWhere(function ($legacyQuery) use ($semester) {
|
||||
$legacyQuery->where(function ($blankQuery) {
|
||||
$blankQuery->whereNull('class_progress_reports.semester')
|
||||
->orWhere('class_progress_reports.semester', '');
|
||||
})->whereExists(function ($sectionQuery) use ($semester) {
|
||||
$sectionQuery->select(DB::raw(1))
|
||||
->from('classSection as progress_filter_cs')
|
||||
->whereColumn('progress_filter_cs.class_section_id', 'class_progress_reports.class_section_id')
|
||||
->where('progress_filter_cs.semester', $semester);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (! empty($filters['week_start'])) {
|
||||
@@ -157,6 +211,60 @@ class ClassProgressQueryService
|
||||
return $this->schoolYears->options($schoolYear, $semester);
|
||||
}
|
||||
|
||||
private function resolveSchoolYearFilter(array $filters): string
|
||||
{
|
||||
$schoolYear = trim((string) ($filters['school_year'] ?? ''));
|
||||
if ($schoolYear !== '') {
|
||||
return $schoolYear;
|
||||
}
|
||||
|
||||
$schoolYearId = (int) ($filters['school_year_id'] ?? 0);
|
||||
if ($schoolYearId <= 0 || ! Schema::hasTable('school_years')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
return trim((string) DB::table('school_years')->where('id', $schoolYearId)->value('name'));
|
||||
} catch (\Throwable) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{start:string,end:string}|null
|
||||
*/
|
||||
private function schoolYearDateRange(string $schoolYear): ?array
|
||||
{
|
||||
$schoolYear = trim($schoolYear);
|
||||
if ($schoolYear === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Schema::hasTable('school_years')) {
|
||||
try {
|
||||
$row = DB::table('school_years')
|
||||
->where('name', $schoolYear)
|
||||
->first(['start_date', 'end_date']);
|
||||
if ($row && $row->start_date && $row->end_date) {
|
||||
return [
|
||||
'start' => substr((string) $row->start_date, 0, 10),
|
||||
'end' => substr((string) $row->end_date, 0, 10),
|
||||
];
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $matches) === 1) {
|
||||
return [
|
||||
'start' => $matches[1].'-08-01',
|
||||
'end' => $matches[2].'-07-31',
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function statusLabel(?string $status): string
|
||||
{
|
||||
$options = (array) config('progress.status_options', []);
|
||||
@@ -180,6 +288,35 @@ class ClassProgressQueryService
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isParent(User $user): bool
|
||||
{
|
||||
return $user->roles()
|
||||
->pluck('roles.name')
|
||||
->map(fn ($name) => strtolower((string) $name))
|
||||
->contains('parent');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
private function parentSectionIds(int $parentId): array
|
||||
{
|
||||
if ($parentId <= 0 || ! Schema::hasTable('enrollments')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return DB::table('enrollments')
|
||||
->where('parent_id', $parentId)
|
||||
->where('is_withdrawn', 0)
|
||||
->whereNotNull('class_section_id')
|
||||
->distinct()
|
||||
->pluck('class_section_id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->filter(fn ($id) => $id > 0)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* legacy `ClassProgressController::resolveAssignedTeacherIds`.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user