|
19 | 19 | use Chamilo\CoreBundle\Helpers\AccessUrlHelper; |
20 | 20 | use Chamilo\CoreBundle\Helpers\CidReqHelper; |
21 | 21 | use Chamilo\CoreBundle\Helpers\CourseHelper; |
| 22 | +use Chamilo\CoreBundle\Helpers\CourseStudentInfoHelper; |
22 | 23 | use Chamilo\CoreBundle\Helpers\UserHelper; |
23 | 24 | use Chamilo\CoreBundle\Repository\AssetRepository; |
24 | 25 | use Chamilo\CoreBundle\Repository\CourseCategoryRepository; |
@@ -1167,6 +1168,194 @@ public function getAutoLaunchLPId( |
1167 | 1168 | return new JsonResponse(['lpId' => $autoLaunchLPId], Response::HTTP_OK); |
1168 | 1169 | } |
1169 | 1170 |
|
| 1171 | + #[IsGranted('ROLE_USER')] |
| 1172 | + #[Route('/{cid}/student-info.json', name: 'chamilo_core_course_student_info_json', methods: ['GET'])] |
| 1173 | + public function studentInfoJson( |
| 1174 | + Request $request, |
| 1175 | + #[MapEntity(expr: 'repository.find(cid)')] |
| 1176 | + Course $course, |
| 1177 | + CourseStudentInfoHelper $studentInfoHelper |
| 1178 | + ): JsonResponse { |
| 1179 | + $user = $this->userHelper->getCurrent(); |
| 1180 | + |
| 1181 | + if (!$user || !method_exists($user, 'getId')) { |
| 1182 | + return new JsonResponse(['error' => 'Authentication required.'], Response::HTTP_UNAUTHORIZED); |
| 1183 | + } |
| 1184 | + |
| 1185 | + $userId = (int) $user->getId(); |
| 1186 | + $sessionId = (int) $request->query->get('sid', 0); |
| 1187 | + |
| 1188 | + if (0 === $sessionId) { |
| 1189 | + $this->denyAccessUnlessGranted(CourseVoter::VIEW, $course); |
| 1190 | + } |
| 1191 | + |
| 1192 | + // Extra safety for invitees (same logic as home.json). |
| 1193 | + if (method_exists($user, 'isInvitee') && $user->isInvitee()) { |
| 1194 | + $isSubscribed = CourseManager::is_user_subscribed_in_course( |
| 1195 | + $userId, |
| 1196 | + $course->getCode(), |
| 1197 | + $sessionId > 0, |
| 1198 | + $sessionId |
| 1199 | + ); |
| 1200 | + |
| 1201 | + if (!$isSubscribed) { |
| 1202 | + throw $this->createAccessDeniedException('User is not subscribed to the course.'); |
| 1203 | + } |
| 1204 | + } |
| 1205 | + |
| 1206 | + $payload = $studentInfoHelper->getStudentInfoForCourse($userId, $course, $sessionId); |
| 1207 | + |
| 1208 | + return new JsonResponse($payload, Response::HTTP_OK); |
| 1209 | + } |
| 1210 | + |
| 1211 | + #[IsGranted('ROLE_USER')] |
| 1212 | + #[Route('/student-info-batch.json', name: 'chamilo_core_course_student_info_batch_json', methods: ['POST'])] |
| 1213 | + public function studentInfoBatchJson( |
| 1214 | + Request $request, |
| 1215 | + CourseStudentInfoHelper $studentInfoHelper |
| 1216 | + ): JsonResponse { |
| 1217 | + $user = $this->userHelper->getCurrent(); |
| 1218 | + |
| 1219 | + if (!$user || !method_exists($user, 'getId')) { |
| 1220 | + return new JsonResponse(['error' => 'Authentication required.'], Response::HTTP_UNAUTHORIZED); |
| 1221 | + } |
| 1222 | + |
| 1223 | + $payload = json_decode($request->getContent() ?: '', true) ?? []; |
| 1224 | + $sessionId = (int) ($payload['sid'] ?? 0); |
| 1225 | + |
| 1226 | + $courseIds = $payload['courseIds'] ?? []; |
| 1227 | + if (!is_array($courseIds)) { |
| 1228 | + $courseIds = []; |
| 1229 | + } |
| 1230 | + |
| 1231 | + $courseIds = array_values(array_unique(array_map('intval', $courseIds))); |
| 1232 | + $courseIds = array_filter($courseIds, static fn (int $id) => $id > 0); |
| 1233 | + if (count($courseIds) > 300) { |
| 1234 | + $courseIds = array_slice($courseIds, 0, 300); |
| 1235 | + } |
| 1236 | + |
| 1237 | + if (empty($courseIds)) { |
| 1238 | + return new JsonResponse([ |
| 1239 | + 'sid' => $sessionId, |
| 1240 | + 'items' => [], |
| 1241 | + ], Response::HTTP_OK); |
| 1242 | + } |
| 1243 | + |
| 1244 | + // Load courses in one go. |
| 1245 | + /** @var Course[] $courses */ |
| 1246 | + $courses = $this->em->getRepository(Course::class)->findBy(['id' => $courseIds]); |
| 1247 | + |
| 1248 | + $allowedCourseIds = []; |
| 1249 | + $courseMap = []; |
| 1250 | + foreach ($courses as $course) { |
| 1251 | + $cid = (int) $course->getId(); |
| 1252 | + $courseMap[$cid] = $course; |
| 1253 | + } |
| 1254 | + |
| 1255 | + $userId = (int) $user->getId(); |
| 1256 | + |
| 1257 | + foreach ($courseIds as $cid) { |
| 1258 | + $course = $courseMap[$cid] ?? null; |
| 1259 | + if (!$course instanceof Course) { |
| 1260 | + continue; |
| 1261 | + } |
| 1262 | + |
| 1263 | + if (0 === $sessionId) { |
| 1264 | + try { |
| 1265 | + $this->denyAccessUnlessGranted(CourseVoter::VIEW, $course); |
| 1266 | + } catch (\Throwable $e) { |
| 1267 | + // Skip unauthorized courses without failing the whole batch. |
| 1268 | + continue; |
| 1269 | + } |
| 1270 | + } |
| 1271 | + |
| 1272 | + // Extra safety for invitees (same logic as home.json / studentInfoJson). |
| 1273 | + if (method_exists($user, 'isInvitee') && $user->isInvitee()) { |
| 1274 | + $isSubscribed = CourseManager::is_user_subscribed_in_course( |
| 1275 | + $userId, |
| 1276 | + $course->getCode(), |
| 1277 | + $sessionId > 0, |
| 1278 | + $sessionId |
| 1279 | + ); |
| 1280 | + |
| 1281 | + if (!$isSubscribed) { |
| 1282 | + continue; |
| 1283 | + } |
| 1284 | + } |
| 1285 | + |
| 1286 | + $allowedCourseIds[] = $cid; |
| 1287 | + } |
| 1288 | + |
| 1289 | + if (empty($allowedCourseIds)) { |
| 1290 | + return new JsonResponse([ |
| 1291 | + 'sid' => $sessionId, |
| 1292 | + 'items' => [], |
| 1293 | + ], Response::HTTP_OK); |
| 1294 | + } |
| 1295 | + |
| 1296 | + // Compute in batch |
| 1297 | + $items = $studentInfoHelper->getStudentInfoBatchForCourses($userId, $allowedCourseIds, $sessionId); |
| 1298 | + |
| 1299 | + return new JsonResponse([ |
| 1300 | + 'sid' => $sessionId, |
| 1301 | + 'items' => $items, // courseId => studentInfo |
| 1302 | + ], Response::HTTP_OK); |
| 1303 | + } |
| 1304 | + |
| 1305 | + #[IsGranted('ROLE_USER')] |
| 1306 | + #[Route('/{cid}/new-content-tools.json', name: 'chamilo_core_course_new_content_tools_json', methods: ['GET'])] |
| 1307 | + public function newContentToolsJson( |
| 1308 | + Request $request, |
| 1309 | + #[MapEntity(expr: 'repository.find(cid)')] |
| 1310 | + Course $course, |
| 1311 | + CourseStudentInfoHelper $studentInfoHelper |
| 1312 | + ): JsonResponse { |
| 1313 | + $user = $this->userHelper->getCurrent(); |
| 1314 | + |
| 1315 | + if (!$user || !method_exists($user, 'getId')) { |
| 1316 | + return new JsonResponse(['error' => 'Authentication required.'], Response::HTTP_UNAUTHORIZED); |
| 1317 | + } |
| 1318 | + |
| 1319 | + $userId = (int) $user->getId(); |
| 1320 | + $sessionId = (int) $request->query->get('sid', 0); |
| 1321 | + $limit = (int) $request->query->get('limit', 20); |
| 1322 | + if ($limit <= 0) { |
| 1323 | + $limit = 20; |
| 1324 | + } |
| 1325 | + if ($limit > 50) { |
| 1326 | + $limit = 50; |
| 1327 | + } |
| 1328 | + |
| 1329 | + if (0 === $sessionId) { |
| 1330 | + $this->denyAccessUnlessGranted(CourseVoter::VIEW, $course); |
| 1331 | + } |
| 1332 | + |
| 1333 | + if (method_exists($user, 'isInvitee') && $user->isInvitee()) { |
| 1334 | + $isSubscribed = CourseManager::is_user_subscribed_in_course( |
| 1335 | + $userId, |
| 1336 | + $course->getCode(), |
| 1337 | + $sessionId > 0, |
| 1338 | + $sessionId |
| 1339 | + ); |
| 1340 | + |
| 1341 | + if (!$isSubscribed) { |
| 1342 | + throw $this->createAccessDeniedException('User is not subscribed to the course.'); |
| 1343 | + } |
| 1344 | + } |
| 1345 | + |
| 1346 | + $items = $studentInfoHelper->getNewContentToolsForCourse($userId, $course, $sessionId, $limit); |
| 1347 | + |
| 1348 | + return new JsonResponse([ |
| 1349 | + 'cid' => (int) $course->getId(), |
| 1350 | + 'sid' => $sessionId, |
| 1351 | + 'items' => $items, |
| 1352 | + 'meta' => [ |
| 1353 | + // Optional debug/help info for UI: |
| 1354 | + 'lastAccess' => $studentInfoHelper->getLastAccessLabelForCourse($userId, (int) $course->getId(), $sessionId), |
| 1355 | + ], |
| 1356 | + ], Response::HTTP_OK); |
| 1357 | + } |
| 1358 | + |
1170 | 1359 | private function autoLaunch(): void |
1171 | 1360 | { |
1172 | 1361 | $autoLaunchWarning = ''; |
|
0 commit comments