Skip to content

Commit

Permalink
Merge pull request #5859 from christianbeeznest/storm-22103
Browse files Browse the repository at this point in the history
Course: Implement document auto-launch and notification for students in course home - refs BT#22103
  • Loading branch information
NicoDucou authored Nov 27, 2024
2 parents 1104d82 + 064af46 commit ef94109
Show file tree
Hide file tree
Showing 11 changed files with 454 additions and 133 deletions.
117 changes: 109 additions & 8 deletions assets/vue/router/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from "vue-router"
import {createRouter, createWebHistory} from "vue-router"
import adminRoutes from "./admin"
import courseRoutes from "./course"
import accountRoutes from "./account"
Expand Down Expand Up @@ -37,13 +37,15 @@ import Login from "../pages/Login.vue"
import Faq from "../pages/Faq.vue"
import Demo from "../pages/Demo.vue"

import { useCidReqStore } from "../store/cidReq"
import {useCidReqStore} from "../store/cidReq"
import courseService from "../services/courseService"

import catalogueCourses from "./cataloguecourses"
import catalogueSessions from "./cataloguesessions"
import { customVueTemplateEnabled } from "../config/env"
import { useUserSessionSubscription } from "../composables/userPermissions"
import {customVueTemplateEnabled} from "../config/env"
import {useCourseSettings} from "../store/courseSettingStore"
import {checkIsAllowedToEdit} from "../composables/userPermissions"
import {usePlatformConfig} from "../store/platformConfig"

const router = createRouter({
history: createWebHistory(),
Expand Down Expand Up @@ -97,17 +99,97 @@ const router = createRouter({
name: "CourseHome",
component: CourseHome,
beforeEnter: async (to) => {
try {
const check = await courseService.checkLegal(to.params.id, to.query?.sid)
const courseId = to.params.id
const sessionId = to.query?.sid
const autoLaunchKey = `course_autolaunch_${courseId}`
const hasAutoLaunched = sessionStorage.getItem(autoLaunchKey)

if (hasAutoLaunched === "true") {
return true
}

try {
const check = await courseService.checkLegal(courseId, sessionId)
if (check.redirect) {
window.location.href = check.url

return false
}
} catch (e) {
return true

const course = await courseService.getCourseDetails(courseId)
if (!course) {
return false
}

const isAllowedToEdit = await checkIsAllowedToEdit(true, true, true)
if (isAllowedToEdit) {
return true
}

const courseSettingsStore = useCourseSettings()
await courseSettingsStore.loadCourseSettings(courseId, sessionId)

// Document auto-launch
const documentAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_document_auto_launch"), 10) || 0
if (documentAutoLaunch === 1 && course.resourceNode?.id) {
sessionStorage.setItem(autoLaunchKey, "true")
window.location.href = `/resources/document/${course.resourceNode.id}/?cid=${courseId}`
+ (sessionId ? `&sid=${sessionId}` : '')
return false
}

// Exercise auto-launch
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) {
sessionStorage.setItem(autoLaunchKey, "true")
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) {
sessionStorage.setItem(autoLaunchKey, "true")
window.location.href = `/main/exercise/overview.php?exerciseId=${exerciseId}&cid=${courseId}`
+ (sessionId ? `&sid=${sessionId}` : '')
return false
}
}
}

// Learning path auto-launch
const lpAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_lp_auto_launch"), 10) || 0
if (lpAutoLaunch === 2) {
sessionStorage.setItem(autoLaunchKey, "true")
window.location.href = `/main/lp/lp_controller.php?cid=${courseId}`
+ (sessionId ? `&sid=${sessionId}` : '')
return false
} else if (lpAutoLaunch === 1) {
const lpId = await courseService.getAutoLaunchLPId(courseId, sessionId)
if (lpId) {
sessionStorage.setItem(autoLaunchKey, "true")
window.location.href = `/main/lp/lp_controller.php?lp_id=${lpId}&cid=${courseId}&action=view&isStudentView=true`
+ (sessionId ? `&sid=${sessionId}` : '')
return false
}
}

// Forum auto-launch
const forumAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_forum_auto_launch"), 10) || 0
if (forumAutoLaunch === 1) {
sessionStorage.setItem(autoLaunchKey, "true")
window.location.href = `/main/forum/index.php?cid=${courseId}`
+ (sessionId ? `&sid=${sessionId}` : '')
return false
}

} catch (error) {
console.error("Error during CourseHome route guard:", error)
}

return true
},
},
{
Expand Down Expand Up @@ -175,6 +257,24 @@ const router = createRouter({
router.beforeEach(async (to, from, next) => {
const securityStore = useSecurityStore()

if (!securityStore.isAuthenticated) {
sessionStorage.clear()
}

let cid = parseInt(to.query?.cid ?? 0)

if ("CourseHome" === to.name) {
cid = parseInt(to.params?.id ?? 0)
}

if (!cid) {
for (const key in sessionStorage) {
if (key.startsWith('course_autolaunch_')) {
sessionStorage.removeItem(key)
}
}
}

if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!securityStore.isLoading) {
await securityStore.checkSession()
Expand All @@ -183,6 +283,7 @@ router.beforeEach(async (to, from, next) => {
if (securityStore.isAuthenticated) {
next()
} else {
sessionStorage.clear()
next({
path: "/login",
query: { redirect: to.fullPath },
Expand Down
67 changes: 67 additions & 0 deletions assets/vue/services/courseService.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,71 @@ export default {
value: item.id,
}))
},

/**
* Fetches course details by course ID.
*
* @param {number} courseId - The ID of the course.
* @returns {Promise<Object|null>} - The course details or null if an error occurs.
*/
getCourseDetails: async (courseId) => {
try {
const response = await api.get(`/api/courses/${courseId}`)
return response.data
} catch (error) {
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<number|null>} 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;
}
},
/**
* Retrieves the ID of the auto-launchable learnpaths in a course, if configured.
*
* @param {number} courseId - The ID of the course.
* @param {number=} sessionId - The ID of the session (optional).
* @returns {Promise<number|null>} The ID of the auto-launchable learnpath, or null if none exists.
*/
getAutoLaunchLPId: async (courseId, sessionId = 0) => {
try {
const { data } = await api.get(`/course/${courseId}/getAutoLaunchLPId`, {
params: {
sid: sessionId,
},
});

if (data && data.lpId) {
return data.lpId;
}

return null;
} catch (error) {
console.error("Error fetching auto-launch LP ID:", error);
return null;
}
},
}
28 changes: 28 additions & 0 deletions assets/vue/views/course/CourseHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@
<small v-if="session"> ({{ session.title }}) </small>
</h2>

<p v-if="isAllowedToEdit && documentAutoLaunch === 1" class="text-sm text-gray-600">
{{ t('Document auto-launch is enabled for students') }}
</p>

<p v-if="isAllowedToEdit && (exerciseAutoLaunch === 1 || exerciseAutoLaunch === 2)" class="text-sm text-gray-600">
{{ t('Exercise auto-launch is enabled for students') }}
</p>

<p v-if="isAllowedToEdit && (lpAutoLaunch === 1 || lpAutoLaunch === 2)" class="text-sm text-gray-600">
{{ t('LP auto-launch is enabled for students') }}
</p>

<p v-if="isAllowedToEdit && (forumAutoLaunch === 1 || forumAutoLaunch === 2)" class="text-sm text-gray-600">
{{ t('Forum auto-launch is enabled for students') }}
</p>

<div class="grow-0">
<StudentViewButton
v-if="course"
Expand Down Expand Up @@ -220,6 +236,7 @@ import courseService from "../../services/courseService"
import CourseIntroduction from "../../components/course/CourseIntroduction.vue"
import { usePlatformConfig } from "../../store/platformConfig"
import { useSecurityStore } from "../../store/securityStore"
import {useCourseSettings} from "../../store/courseSettingStore"
const { t } = useI18n()
const cidReqStore = useCidReqStore()
Expand All @@ -244,6 +261,11 @@ provide("isCustomizing", isCustomizing)
const courseItems = ref([])
const routerTools = ["document", "link", "glossary", "agenda", "student_publication", "course_homepage"]
const documentAutoLaunch = ref(0)
const exerciseAutoLaunch = ref(0)
const lpAutoLaunch = ref(0)
const forumAutoLaunch = ref(0)
const courseSettingsStore = useCourseSettings()
courseService.loadCTools(course.value.id, session.value?.id).then((cTools) => {
tools.value = cTools.map((element) => {
Expand Down Expand Up @@ -377,6 +399,12 @@ onMounted(async () => {
translateHtml()
}, 1000)
}
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
lpAutoLaunch.value = parseInt(courseSettingsStore.getSetting("enable_lp_auto_launch"), 10) || 0
forumAutoLaunch.value = parseInt(courseSettingsStore.getSetting("enable_forum_auto_launch"), 10) || 0
})
const onStudentViewChanged = async () => {
Expand Down
Loading

0 comments on commit ef94109

Please sign in to comment.