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

This commit is contained in:
root
2026-07-06 02:14:16 -04:00
parent 39228168c8
commit 90f9857b06
43 changed files with 4323 additions and 132 deletions
@@ -11,6 +11,8 @@ use App\Http\Resources\Reports\ReportCards\ReportCardClassSectionResource;
use App\Http\Resources\Reports\ReportCards\ReportCardCompletenessStudentResource;
use App\Http\Resources\Reports\ReportCards\ReportCardStudentMetaResource;
use App\Models\Configuration;
use App\Models\User;
use App\Services\Parents\ParentReportCardService;
use App\Services\Reports\ReportCards\ReportCardService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -20,8 +22,10 @@ use Symfony\Component\HttpFoundation\Response;
class ReportCardsController extends BaseApiController
{
public function __construct(private ReportCardService $service)
{
public function __construct(
private ReportCardService $service,
private ParentReportCardService $parentReportCards,
) {
parent::__construct();
}
@@ -31,7 +35,8 @@ class ReportCardsController extends BaseApiController
$result = $this->service->meta(
$payload['school_year'] ?? null,
$payload['semester'] ?? null
$payload['semester'] ?? null,
$this->parentScopeId($request)
);
return $this->success([
@@ -88,6 +93,10 @@ class ReportCardsController extends BaseApiController
$validated = $validator->validated();
$studentId = (int) ($validated['student_id'] ?? 0);
if (! $this->parentCanAccessStudent($request, $studentId)) {
return $this->error('Forbidden.', Response::HTTP_FORBIDDEN);
}
$schoolYear = $this->resolveSchoolYear($validated['school_year'] ?? null);
$semester = $this->resolveSemester($validated['semester'] ?? null);
@@ -98,6 +107,10 @@ class ReportCardsController extends BaseApiController
public function studentReport(ReportCardPdfRequest $request, int $studentId)
{
if (! $this->parentCanAccessStudent($request, $studentId)) {
return $this->error('Forbidden.', Response::HTTP_FORBIDDEN);
}
try {
$result = $this->service->generateSingleReport($studentId, $request->validated());
} catch (\Throwable $e) {
@@ -122,6 +135,10 @@ class ReportCardsController extends BaseApiController
public function classReport(ReportCardPdfRequest $request, int $classSectionId)
{
if ($this->parentScopeId($request) !== null) {
return $this->error('Forbidden.', Response::HTTP_FORBIDDEN);
}
try {
$result = $this->service->generateClassReport($classSectionId, $request->validated());
} catch (\Throwable $e) {
@@ -163,4 +180,45 @@ class ReportCardsController extends BaseApiController
return trim((string) (Configuration::getConfig('semester') ?? ''));
}
private function parentCanAccessStudent(Request $request, int $studentId): bool
{
$parentId = $this->parentScopeId($request);
if ($parentId === null) {
return true;
}
return $this->parentReportCards->studentBelongsToPrimaryParent($studentId, $parentId);
}
private function parentScopeId(Request $request): ?int
{
/** @var User|null $user */
$user = $request->user();
if (! $this->isParentFamilyUser($user)) {
return null;
}
$parentId = $this->parentReportCards->resolvePrimaryParentUserId($user);
return $parentId !== null && $parentId > 0 ? $parentId : null;
}
private function isParentFamilyUser(?User $user): bool
{
if (! $user) {
return false;
}
$roles = $user->roles()
->pluck('roles.name')
->map(fn ($name) => strtolower((string) $name))
->toArray();
if (in_array('parent', $roles, true)) {
return true;
}
return in_array(strtolower(trim((string) ($user->user_type ?? ''))), ['secondary', 'tertiary'], true);
}
}
@@ -59,6 +59,37 @@ class StickersController extends BaseApiController
]);
}
public function preview(StickerFormDataRequest $request): JsonResponse
{
$payload = $request->validated();
$schoolYear = $this->queryService->resolveSchoolYear($payload['school_year'] ?? null);
$classSectionId = (int) ($payload['class_section_id'] ?? 0);
$students = $classSectionId > 0
? $this->queryService->listStudentsByClass($classSectionId, $schoolYear)
: $this->queryService->listStudentsForYear($schoolYear);
$rows = [];
foreach ($students as $student) {
$id = (int) ($student['id'] ?? $student['student_id'] ?? 0);
if ($id <= 0) {
continue;
}
$rows[] = [
'student_id' => $id,
'primary_count' => 1,
];
}
return $this->success([
'students' => $rows,
'totals' => [
'students' => count($rows),
'stickers' => count($rows),
],
]);
}
public function print(StickerPrintRequest $request)
{
try {