Skip to content

Commit 45498fe

Browse files
committed
Merge branch 'master' of github.com:chamilo/chamilo-lms
2 parents 0f430f1 + bbbf6f0 commit 45498fe

File tree

20 files changed

+704
-236
lines changed

20 files changed

+704
-236
lines changed

assets/vue/components/Login.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
/>
5050

5151
<a
52+
v-if="allowRegistration"
5253
v-t="'Register oneself'"
5354
class="btn btn--primary-outline"
5455
href="/main/auth/inscription.php"
@@ -72,16 +73,19 @@
7273
</template>
7374

7475
<script setup>
75-
import { ref } from "vue"
76+
import { ref, computed } from "vue"
7677
import Button from "primevue/button"
7778
import InputText from "primevue/inputtext"
7879
import Password from "primevue/password"
7980
import InputSwitch from "primevue/inputswitch"
8081
import { useI18n } from "vue-i18n"
8182
import { useLogin } from "../composables/auth/login"
8283
import ExternalLoginButtons from "./login/LoginExternalButtons.vue"
84+
import { usePlatformConfig } from "../store/platformConfig"
8385
8486
const { t } = useI18n()
87+
const platformConfigStore = usePlatformConfig()
88+
const allowRegistration = computed(() => "false" !== platformConfigStore.getSetting("registration.allow_registration"))
8589
8690
const { redirectNotAuthenticated, performLogin, isLoading } = useLogin()
8791

assets/vue/components/layout/TopbarNotLoggedIn.vue

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useI18n } from "vue-i18n"
1515
import { useRouter } from "vue-router"
1616
import { useLocale } from "../../composables/locale"
1717
import PlatformLogo from "./PlatformLogo.vue"
18+
import {usePlatformConfig} from "../../store/platformConfig"
1819
1920
const { t } = useI18n()
2021
const router = useRouter()
@@ -27,31 +28,42 @@ const languageItems = languageList.map((language) => ({
2728
command: (event) => reloadWithLocale(event.item.isoCode),
2829
}))
2930
30-
const menuItems = computed(() => [
31-
{
32-
label: t("Home"),
33-
url: router.resolve({ name: "Index" }).href,
34-
},
35-
{
36-
label: t("FAQ"),
37-
url: router.resolve({ name: "Faq" }).href,
38-
},
39-
{
40-
label: t("Registration"),
41-
url: "/main/auth/inscription.php",
42-
},
43-
{
44-
label: t("Demo"),
45-
url: router.resolve({ name: "Demo" }).href,
46-
},
47-
{
48-
label: t("Contact"),
49-
url: "/contact",
50-
},
51-
{
52-
key: "language_selector",
53-
label: currentLanguageFromList.originalName,
54-
items: languageItems,
55-
},
56-
])
31+
const platformConfigStore = usePlatformConfig()
32+
const allowRegistration = computed(() => "false" !== platformConfigStore.getSetting("registration.allow_registration"))
33+
34+
const menuItems = computed(() => {
35+
const items = [
36+
{
37+
label: t("Home"),
38+
url: router.resolve({ name: "Index" }).href,
39+
},
40+
{
41+
label: t("FAQ"),
42+
url: router.resolve({ name: "Faq" }).href,
43+
},
44+
{
45+
label: t("Demo"),
46+
url: router.resolve({ name: "Demo" }).href,
47+
},
48+
{
49+
label: t("Contact"),
50+
url: "/contact",
51+
},
52+
{
53+
key: "language_selector",
54+
label: currentLanguageFromList.originalName,
55+
items: languageItems,
56+
},
57+
]
58+
59+
if (allowRegistration.value) {
60+
items.splice(2, 0, {
61+
label: t("Registration"),
62+
url: "/main/auth/inscription.php",
63+
})
64+
}
65+
66+
console.log("Menu Items:", items)
67+
return items
68+
})
5769
</script>
Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
import gql from 'graphql-tag';
22

33
export const GET_COURSE_REL_USER = gql`
4-
query getCourses($user: String!) {
5-
courseRelUsers(user: $user) {
6-
edges {
4+
query getCourses($user: String!, $first: Int!, $after: String) {
5+
courseRelUsers(user: $user, first: $first, after: $after) {
6+
edges {
7+
cursor
8+
node {
9+
course {
10+
_id,
11+
title,
12+
illustrationUrl,
13+
duration,
14+
users(status: 1, first: 4) {
15+
edges {
716
node {
8-
course {
9-
_id,
10-
title,
11-
illustrationUrl,
12-
duration,
13-
users(status: 1, first: 4) {
14-
edges {
15-
node {
16-
id
17-
status
18-
user {
19-
illustrationUrl,
20-
username,
21-
fullName
22-
}
23-
}
24-
}
25-
}
26-
}
17+
id
18+
status
19+
user {
20+
illustrationUrl,
21+
username,
22+
fullName
23+
}
2724
}
25+
}
2826
}
27+
}
2928
}
29+
}
30+
pageInfo {
31+
endCursor
32+
hasNextPage
33+
}
3034
}
35+
}
3136
`;
3237

assets/vue/router/index.js

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createRouter, createWebHistory } from "vue-router"
1+
import {createRouter, createWebHistory} from "vue-router"
22
import adminRoutes from "./admin"
33
import courseRoutes from "./course"
44
import accountRoutes from "./account"
@@ -37,13 +37,15 @@ import Login from "../pages/Login.vue"
3737
import Faq from "../pages/Faq.vue"
3838
import Demo from "../pages/Demo.vue"
3939

40-
import { useCidReqStore } from "../store/cidReq"
40+
import {useCidReqStore} from "../store/cidReq"
4141
import courseService from "../services/courseService"
4242

4343
import catalogueCourses from "./cataloguecourses"
4444
import catalogueSessions from "./cataloguesessions"
45-
import { customVueTemplateEnabled } from "../config/env"
46-
import { useUserSessionSubscription } from "../composables/userPermissions"
45+
import {customVueTemplateEnabled} from "../config/env"
46+
import {useCourseSettings} from "../store/courseSettingStore"
47+
import {checkIsAllowedToEdit, useUserSessionSubscription} from "../composables/userPermissions"
48+
import {usePlatformConfig} from "../store/platformConfig"
4749

4850
const router = createRouter({
4951
history: createWebHistory(),
@@ -97,17 +99,97 @@ const router = createRouter({
9799
name: "CourseHome",
98100
component: CourseHome,
99101
beforeEnter: async (to) => {
100-
try {
101-
const check = await courseService.checkLegal(to.params.id, to.query?.sid)
102+
const courseId = to.params.id
103+
const sessionId = to.query?.sid
104+
const autoLaunchKey = `course_autolaunch_${courseId}`
105+
const hasAutoLaunched = sessionStorage.getItem(autoLaunchKey)
102106

107+
if (hasAutoLaunched === "true") {
108+
return true
109+
}
110+
111+
try {
112+
const check = await courseService.checkLegal(courseId, sessionId)
103113
if (check.redirect) {
104114
window.location.href = check.url
105115

106116
return false
107117
}
108-
} catch (e) {
109-
return true
118+
119+
const course = await courseService.getCourseDetails(courseId)
120+
if (!course) {
121+
return false
122+
}
123+
124+
const isAllowedToEdit = await checkIsAllowedToEdit(true, true, true)
125+
if (isAllowedToEdit) {
126+
return true
127+
}
128+
129+
const courseSettingsStore = useCourseSettings()
130+
await courseSettingsStore.loadCourseSettings(courseId, sessionId)
131+
132+
// Document auto-launch
133+
const documentAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_document_auto_launch"), 10) || 0
134+
if (documentAutoLaunch === 1 && course.resourceNode?.id) {
135+
sessionStorage.setItem(autoLaunchKey, "true")
136+
window.location.href = `/resources/document/${course.resourceNode.id}/?cid=${courseId}`
137+
+ (sessionId ? `&sid=${sessionId}` : '')
138+
return false
139+
}
140+
141+
// Exercise auto-launch
142+
const platformConfigStore = usePlatformConfig()
143+
const isExerciseAutoLaunchEnabled = "true" === platformConfigStore.getSetting("exercise.allow_exercise_auto_launch")
144+
if (isExerciseAutoLaunchEnabled) {
145+
const exerciseAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_exercise_auto_launch"), 10) || 0
146+
if (exerciseAutoLaunch === 2) {
147+
sessionStorage.setItem(autoLaunchKey, "true")
148+
window.location.href = `/main/exercise/exercise.php?cid=${courseId}`
149+
+ (sessionId ? `&sid=${sessionId}` : '')
150+
return false
151+
} else if (exerciseAutoLaunch === 1) {
152+
const exerciseId = await courseService.getAutoLaunchExerciseId(courseId, sessionId)
153+
if (exerciseId) {
154+
sessionStorage.setItem(autoLaunchKey, "true")
155+
window.location.href = `/main/exercise/overview.php?exerciseId=${exerciseId}&cid=${courseId}`
156+
+ (sessionId ? `&sid=${sessionId}` : '')
157+
return false
158+
}
159+
}
160+
}
161+
162+
// Learning path auto-launch
163+
const lpAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_lp_auto_launch"), 10) || 0
164+
if (lpAutoLaunch === 2) {
165+
sessionStorage.setItem(autoLaunchKey, "true")
166+
window.location.href = `/main/lp/lp_controller.php?cid=${courseId}`
167+
+ (sessionId ? `&sid=${sessionId}` : '')
168+
return false
169+
} else if (lpAutoLaunch === 1) {
170+
const lpId = await courseService.getAutoLaunchLPId(courseId, sessionId)
171+
if (lpId) {
172+
sessionStorage.setItem(autoLaunchKey, "true")
173+
window.location.href = `/main/lp/lp_controller.php?lp_id=${lpId}&cid=${courseId}&action=view&isStudentView=true`
174+
+ (sessionId ? `&sid=${sessionId}` : '')
175+
return false
176+
}
177+
}
178+
179+
// Forum auto-launch
180+
const forumAutoLaunch = parseInt(courseSettingsStore.getSetting("enable_forum_auto_launch"), 10) || 0
181+
if (forumAutoLaunch === 1) {
182+
sessionStorage.setItem(autoLaunchKey, "true")
183+
window.location.href = `/main/forum/index.php?cid=${courseId}`
184+
+ (sessionId ? `&sid=${sessionId}` : '')
185+
return false
186+
}
187+
188+
} catch (error) {
189+
console.error("Error during CourseHome route guard:", error)
110190
}
191+
192+
return true
111193
},
112194
},
113195
{
@@ -175,6 +257,24 @@ const router = createRouter({
175257
router.beforeEach(async (to, from, next) => {
176258
const securityStore = useSecurityStore()
177259

260+
if (!securityStore.isAuthenticated) {
261+
sessionStorage.clear()
262+
}
263+
264+
let cid = parseInt(to.query?.cid ?? 0)
265+
266+
if ("CourseHome" === to.name) {
267+
cid = parseInt(to.params?.id ?? 0)
268+
}
269+
270+
if (!cid) {
271+
for (const key in sessionStorage) {
272+
if (key.startsWith('course_autolaunch_')) {
273+
sessionStorage.removeItem(key)
274+
}
275+
}
276+
}
277+
178278
if (to.matched.some((record) => record.meta.requiresAuth)) {
179279
if (!securityStore.isLoading) {
180280
await securityStore.checkSession()
@@ -183,6 +283,7 @@ router.beforeEach(async (to, from, next) => {
183283
if (securityStore.isAuthenticated) {
184284
next()
185285
} else {
286+
sessionStorage.clear()
186287
next({
187288
path: "/login",
188289
query: { redirect: to.fullPath },

assets/vue/services/courseService.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,71 @@ export default {
106106
value: item.id,
107107
}))
108108
},
109+
110+
/**
111+
* Fetches course details by course ID.
112+
*
113+
* @param {number} courseId - The ID of the course.
114+
* @returns {Promise<Object|null>} - The course details or null if an error occurs.
115+
*/
116+
getCourseDetails: async (courseId) => {
117+
try {
118+
const response = await api.get(`/api/courses/${courseId}`)
119+
return response.data
120+
} catch (error) {
121+
console.error("Error fetching course details:", error)
122+
return null
123+
}
124+
},
125+
126+
/**
127+
* Retrieves the ID of the auto-launchable exercise in a course, if configured.
128+
*
129+
* @param {number} courseId - The ID of the course.
130+
* @param {number=} sessionId - The ID of the session (optional).
131+
* @returns {Promise<number|null>} The ID of the auto-launchable exercise, or null if none exists.
132+
*/
133+
getAutoLaunchExerciseId: async (courseId, sessionId = 0) => {
134+
try {
135+
const { data } = await api.get(`/course/${courseId}/getAutoLaunchExerciseId`, {
136+
params: {
137+
sid: sessionId,
138+
},
139+
});
140+
141+
if (data && data.exerciseId) {
142+
return data.exerciseId;
143+
}
144+
145+
return null;
146+
} catch (error) {
147+
console.error("Error fetching auto-launch exercise ID:", error);
148+
return null;
149+
}
150+
},
151+
/**
152+
* Retrieves the ID of the auto-launchable learnpaths in a course, if configured.
153+
*
154+
* @param {number} courseId - The ID of the course.
155+
* @param {number=} sessionId - The ID of the session (optional).
156+
* @returns {Promise<number|null>} The ID of the auto-launchable learnpath, or null if none exists.
157+
*/
158+
getAutoLaunchLPId: async (courseId, sessionId = 0) => {
159+
try {
160+
const { data } = await api.get(`/course/${courseId}/getAutoLaunchLPId`, {
161+
params: {
162+
sid: sessionId,
163+
},
164+
});
165+
166+
if (data && data.lpId) {
167+
return data.lpId;
168+
}
169+
170+
return null;
171+
} catch (error) {
172+
console.error("Error fetching auto-launch LP ID:", error);
173+
return null;
174+
}
175+
},
109176
}

0 commit comments

Comments
 (0)