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;
+ }
}