diff --git a/assets/vue/router/index.js b/assets/vue/router/index.js index 455d969b9d..dab77eac5f 100644 --- a/assets/vue/router/index.js +++ b/assets/vue/router/index.js @@ -43,8 +43,9 @@ import courseService from "../services/courseService" import catalogueCourses from "./cataloguecourses" import catalogueSessions from "./cataloguesessions" import {customVueTemplateEnabled} from "../config/env" -import {useCourseSettings} from "../store/courseSettingStore"; -import {checkIsAllowedToEdit} from "../composables/userPermissions"; +import {useCourseSettings} from "../store/courseSettingStore" +import {checkIsAllowedToEdit} from "../composables/userPermissions" +import {usePlatformConfig} from "../store/platformConfig" const router = createRouter({ history: createWebHistory(), @@ -125,8 +126,26 @@ const router = createRouter({ window.location.href = `/resources/document/${course.resourceNode.id}/?cid=${courseId}` + (sessionId ? `&sid=${sessionId}` : '') return false - } else { - console.log("Document auto launch is disabled or resourceNode ID is missing.") + } + + const platformConfigStore = usePlatformConfig() + const isExerciseAutoLaunchEnabled = "true" === platformConfigStore.getSetting("exercise.allow_exercise_auto_launch") + + if (isExerciseAutoLaunchEnabled) { + const exerciseAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_exercise_auto_launch"), 10) || 0 + if (exerciseAutoLaunch === 2) { + window.location.href = `/main/exercise/exercise.php?cid=${courseId}` + + (sessionId ? `&sid=${sessionId}` : '') + return false + } + else if (exerciseAutoLaunch === 1) { + const exerciseId = await courseService.getAutoLaunchExerciseId(courseId, sessionId) + if (exerciseId) { + window.location.href = `/main/exercise/overview.php?exerciseId=${exerciseId}&cid=${courseId}` + + (sessionId ? `&sid=${sessionId}` : '') + return false + } + } } } catch (error) { diff --git a/assets/vue/services/courseService.js b/assets/vue/services/courseService.js index 805945cda1..0cbfb77f02 100644 --- a/assets/vue/services/courseService.js +++ b/assets/vue/services/courseService.js @@ -127,5 +127,31 @@ export default { console.error("Error fetching course details:", error) return null } - } + }, + + /** + * Retrieves the ID of the auto-launchable exercise in a course, if configured. + * + * @param {number} courseId - The ID of the course. + * @param {number=} sessionId - The ID of the session (optional). + * @returns {Promise} The ID of the auto-launchable exercise, or null if none exists. + */ + getAutoLaunchExerciseId: async (courseId, sessionId = 0) => { + try { + const { data } = await api.get(`/course/${courseId}/getAutoLaunchExerciseId`, { + params: { + sid: sessionId, + }, + }); + + if (data && data.exerciseId) { + return data.exerciseId; + } + + return null; + } catch (error) { + console.error("Error fetching auto-launch exercise ID:", error); + return null; + } + }, } diff --git a/assets/vue/views/course/CourseHome.vue b/assets/vue/views/course/CourseHome.vue index 017cd7731d..f1aa9f6042 100644 --- a/assets/vue/views/course/CourseHome.vue +++ b/assets/vue/views/course/CourseHome.vue @@ -85,6 +85,10 @@ {{ t('Document auto-launch is enabled for students') }}

+

+ {{ t('Exercise auto-launch is enabled for students') }} +

+
{ @@ -397,6 +402,7 @@ onMounted(async () => { await courseSettingsStore.loadCourseSettings(course.value.id, session.value?.id) documentAutoLaunch.value = parseInt(courseSettingsStore.getSetting("enable_document_auto_launch"), 10) || 0 + exerciseAutoLaunch.value = parseInt(courseSettingsStore.getSetting("enable_exercise_auto_launch"), 10) || 0 }) const onStudentViewChanged = async () => { diff --git a/public/main/exercise/exercise.class.php b/public/main/exercise/exercise.class.php index 839c412cd7..6cb3a544f6 100644 --- a/public/main/exercise/exercise.class.php +++ b/public/main/exercise/exercise.class.php @@ -8534,10 +8534,22 @@ class Exercise */ public function cleanCourseLaunchSettings() { - $table = Database::get_course_table(TABLE_QUIZ_TEST); - $sql = "UPDATE $table SET autolaunch = 0 - WHERE c_id = ".$this->course_id.' AND session_id = '.$this->sessionId; - Database::query($sql); + $em = Database::getManager(); + + $repo = Container::getQuizRepository(); + + $session = api_get_session_entity(); + $course = api_get_course_entity(); + + $qb = $repo->getResourcesByCourse($course, $session); + $quizzes = $qb->getQuery()->getResult(); + + foreach ($quizzes as $quiz) { + $quiz->setAutoLaunch(false); + $em->persist($quiz); + } + + $em->flush(); } /** diff --git a/src/CoreBundle/Controller/CourseController.php b/src/CoreBundle/Controller/CourseController.php index 8e80544aa7..95e7f341a8 100644 --- a/src/CoreBundle/Controller/CourseController.php +++ b/src/CoreBundle/Controller/CourseController.php @@ -33,6 +33,7 @@ use Chamilo\CourseBundle\Entity\CCourseDescription; use Chamilo\CourseBundle\Entity\CTool; use Chamilo\CourseBundle\Entity\CToolIntro; use Chamilo\CourseBundle\Repository\CCourseDescriptionRepository; +use Chamilo\CourseBundle\Repository\CQuizRepository; use Chamilo\CourseBundle\Repository\CShortcutRepository; use Chamilo\CourseBundle\Repository\CToolRepository; use Chamilo\CourseBundle\Settings\SettingsCourseManager; @@ -761,6 +762,28 @@ class CourseController extends ToolBaseController return new JsonResponse(['success' => false, 'message' => $translator->trans('An error occurred while creating the course.')]); } + #[Route('/{id}/getAutoLaunchExerciseId', name: 'chamilo_core_course_get_auto_launch_exercise_id', methods: ['GET'])] + public function getAutoLaunchExerciseId( + Request $request, + Course $course, + CQuizRepository $quizRepository, + EntityManagerInterface $em + ): JsonResponse { + $data = $request->getContent(); + $data = json_decode($data); + $sessionId = $data->sid ?? 0; + + $sessionRepo = $em->getRepository(Session::class); + $session = null; + if (!empty($sessionId)) { + $session = $sessionRepo->find($sessionId); + } + + $autoLaunchExerciseId = $quizRepository->findAutoLaunchableQuizByCourseAndSession($course, $session); + + return new JsonResponse(['exerciseId' => $autoLaunchExerciseId], Response::HTTP_OK); + } + private function autoLaunch(): void { $autoLaunchWarning = ''; diff --git a/src/CoreBundle/Controller/PlatformConfigurationController.php b/src/CoreBundle/Controller/PlatformConfigurationController.php index f42ecc7665..1c5ee61c36 100644 --- a/src/CoreBundle/Controller/PlatformConfigurationController.php +++ b/src/CoreBundle/Controller/PlatformConfigurationController.php @@ -88,6 +88,7 @@ class PlatformConfigurationController extends AbstractController 'document.students_download_folders', 'social.hide_social_groups_block', 'course.show_course_duration', + 'exercise.allow_exercise_auto_launch', ]; $user = $this->userHelper->getCurrent(); @@ -143,6 +144,7 @@ class PlatformConfigurationController extends AbstractController 'show_course_in_user_language' => $courseSettingsManager->getCourseSettingValue('show_course_in_user_language'), 'allow_user_edit_agenda' => $courseSettingsManager->getCourseSettingValue('allow_user_edit_agenda'), 'enable_document_auto_launch' => $courseSettingsManager->getCourseSettingValue('enable_document_auto_launch'), + 'enable_exercise_auto_launch' => $courseSettingsManager->getCourseSettingValue('enable_exercise_auto_launch'), ]; return new JsonResponse(['settings' => $settings]); diff --git a/src/CourseBundle/Repository/CQuizRepository.php b/src/CourseBundle/Repository/CQuizRepository.php index 2bf73099c1..cf95c8a689 100644 --- a/src/CourseBundle/Repository/CQuizRepository.php +++ b/src/CourseBundle/Repository/CQuizRepository.php @@ -133,4 +133,20 @@ final class CQuizRepository extends ResourceRepository implements ResourceWithLi return $qb; } + + /** + * Finds the auto-launchable quiz for the given course and session. + */ + public function findAutoLaunchableQuizByCourseAndSession(Course $course, ?Session $session = null): ?int + { + $qb = $this->getResourcesByCourse($course, $session) + ->select('resource.iid') + ->andWhere('resource.autoLaunch = 1'); + + $qb->setMaxResults(1); + + $result = $qb->getQuery()->getOneOrNullResult(); + + return $result ? $result['iid'] : null; + } }