Harden published curriculum and student flows
This commit is contained in:
@@ -517,10 +517,9 @@ class AuthController
|
||||
if ($user) {
|
||||
$rawGrade = (string)($user['grade_level'] ?? 'grade_10');
|
||||
$normGrade = \App\Services\StudentAccessControlService::normalizeGrade($rawGrade);
|
||||
if ($rawGrade === 'tawjihi_2008' || $rawGrade !== $normGrade) {
|
||||
Database::query("UPDATE students SET grade_level = ?, updated_at = NOW() WHERE id = ?", [$normGrade, $userId]);
|
||||
$user['grade_level'] = $normGrade;
|
||||
}
|
||||
// Profile reads may normalize presentation, but must not rewrite
|
||||
// a student's academic record as a side effect of GET.
|
||||
$user['grade_level'] = $normGrade;
|
||||
$isCompleted = !empty($user['full_name']) && $user['full_name'] !== 'طالب جديد' && $user['full_name'] !== 'الطالب المتميز' && !empty($user['grade_level']);
|
||||
$userData = [
|
||||
'id' => $user['id'],
|
||||
@@ -586,11 +585,6 @@ class AuthController
|
||||
$student = Database::selectOne("SELECT * FROM students WHERE national_id_hash = ? LIMIT 1", [$nationalIdHash]);
|
||||
|
||||
if ($student) {
|
||||
if (($student['grade_level'] ?? '') === 'tawjihi_2008') {
|
||||
Database::query("UPDATE students SET grade_level = 'grade_10', updated_at = NOW() WHERE id = ?", [$student['id']]);
|
||||
$student['grade_level'] = 'grade_10';
|
||||
}
|
||||
|
||||
// Student exists. Verify identity linkage.
|
||||
if ($student['identity_id'] === null) {
|
||||
// Pre-registered by Guardian, link to this identity phone now
|
||||
@@ -736,41 +730,14 @@ class AuthController
|
||||
null
|
||||
);
|
||||
} else {
|
||||
// Legacy / Fallback for already logged-in students updating profile
|
||||
$studentId = (int)$request->user_id;
|
||||
if (!$studentId) {
|
||||
$authHeader = $request->getHeader('authorization', '');
|
||||
if ($authHeader && preg_match('/Bearer\s(\S+)/i', $authHeader, $matches)) {
|
||||
$payload = Security::verifyJWT($matches[1]);
|
||||
if ($payload && isset($payload['user_id'])) {
|
||||
$studentId = (int)$payload['user_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$studentId) {
|
||||
$response->status(401)->json(['status' => 'error', 'message' => 'غير مصرح']);
|
||||
return;
|
||||
}
|
||||
|
||||
Database::query(
|
||||
"UPDATE students SET full_name = ?, grade_level = ?, stream = ?, national_id = IF(? != '', ?, national_id), national_id_hash = IF(? != '', ?, national_id_hash), updated_at = NOW() WHERE id = ?",
|
||||
[$fullName, $gradeLevel, $stream, $nationalId, Security::encrypt($nationalId), $nationalId, Security::blindIndex($nationalId), $studentId]
|
||||
);
|
||||
|
||||
$student = Database::selectOne("SELECT * FROM students WHERE id = ? LIMIT 1", [$studentId]);
|
||||
|
||||
$response->json([
|
||||
'status' => 'success',
|
||||
'message' => 'تم استكمال ملف الطالب بنجاح! مرحباً بك في منصة صَقِل',
|
||||
'user' => [
|
||||
'id' => $student['id'],
|
||||
'uuid' => $student['uuid'],
|
||||
'full_name' => $student['full_name'],
|
||||
'grade_level' => $student['grade_level'],
|
||||
'stream' => $student['stream'],
|
||||
'role' => 'student'
|
||||
]
|
||||
// This endpoint accepts only a pending student-identity proof.
|
||||
// A bare JWT here previously bypassed the active-session and role
|
||||
// checks and allowed changing a national ID without re-verification.
|
||||
$response->status(401)->json([
|
||||
'status' => 'identity_proof_required',
|
||||
'message' => 'إكمال الملف يتطلب رمز التحقق من الهوية. لتغيير صف طالب مستقل استخدم مسار تغيير الصف المصادق.',
|
||||
]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,30 +748,45 @@ class AuthController
|
||||
public function updateStudentGrade(Request $request, Response $response): void
|
||||
{
|
||||
$studentId = (int)$request->user_id;
|
||||
if (!$studentId) {
|
||||
$authHeader = $request->getHeader('authorization', '');
|
||||
if ($authHeader && preg_match('/Bearer\s(\S+)/i', $authHeader, $matches)) {
|
||||
$payload = Security::verifyJWT($matches[1]);
|
||||
if ($payload && isset($payload['user_id'])) {
|
||||
$studentId = (int)$payload['user_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$studentId) {
|
||||
$response->status(401)->json(['status' => 'error', 'message' => 'غير مصرح']);
|
||||
return;
|
||||
}
|
||||
|
||||
$body = $request->getBody();
|
||||
$rawGrade = trim((string)($body['grade_level'] ?? 'grade_10'));
|
||||
$stream = trim((string)($body['stream'] ?? 'general'));
|
||||
$rawGrade = trim((string)($body['grade_level'] ?? ''));
|
||||
$stream = trim((string)($body['stream'] ?? ''));
|
||||
|
||||
$normalizedGrade = \App\Services\StudentAccessControlService::normalizeGrade($rawGrade);
|
||||
if (!preg_match('/^grade_(?:[1-9]|1[0-2])$/', $normalizedGrade)
|
||||
|| !in_array($stream, ['scientific', 'literary', 'vocational', 'general'], true)) {
|
||||
$response->status(400)->json(['status' => 'error', 'message' => 'الصف أو المسار غير صالح.']);
|
||||
return;
|
||||
}
|
||||
|
||||
Database::query(
|
||||
"UPDATE students SET grade_level = ?, stream = ?, updated_at = NOW() WHERE id = ?",
|
||||
$scope = Database::selectOne('SELECT id, school_id, is_school_sponsored FROM students WHERE id=? LIMIT 1', [$studentId]);
|
||||
if (!$scope) {
|
||||
$response->status(404)->json(['status' => 'error', 'message' => 'طالب غير موجود']);
|
||||
return;
|
||||
}
|
||||
if (!empty($scope['school_id']) || !empty($scope['is_school_sponsored'])) {
|
||||
$response->status(403)->json(['status' => 'forbidden', 'message' => 'صف الطالب المدرسي يحدّثه مسؤول المدرسة من كشف الانتساب.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$changed = Database::execute(
|
||||
"UPDATE students SET grade_level = ?, stream = ?, updated_at = NOW()
|
||||
WHERE id = ? AND school_id IS NULL AND is_school_sponsored = 0",
|
||||
[$normalizedGrade, $stream, $studentId]
|
||||
);
|
||||
if ($changed !== 1) {
|
||||
$after = Database::selectOne('SELECT grade_level, stream, school_id, is_school_sponsored FROM students WHERE id=? LIMIT 1', [$studentId]);
|
||||
if (!$after || !empty($after['school_id']) || !empty($after['is_school_sponsored'])
|
||||
|| (string)$after['grade_level'] !== $normalizedGrade || (string)$after['stream'] !== $stream) {
|
||||
$response->status(409)->json(['status' => 'conflict', 'message' => 'تغير نطاق حساب الطالب؛ أعد تحميل ملفك الدراسي.']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$student = Database::selectOne(
|
||||
"SELECT s.id, s.uuid, s.full_name, s.national_id, s.grade_level, s.stream, s.readiness_score, s.school_id, ai.phone_number, ai.status, s.created_at
|
||||
|
||||
Reference in New Issue
Block a user