Skip to content

Commit 4bcfffd

Browse files
authored
Merge branch 'chamilo:master' into fix/document-upload-cid-injection
2 parents 8fe1f30 + a89c493 commit 4bcfffd

58 files changed

Lines changed: 2907 additions & 907 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/behat.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
php-versions: [ '8.2', '8.3' ]
1515
steps:
1616
- name: Checkout
17-
uses: actions/checkout@v6
17+
uses: actions/checkout@v7
1818

1919
- name: Setup apache
2020
run: |

.github/workflows/format_code.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
php-versions: ['8.2']
1818
steps:
1919
- name: Checkout
20-
uses: actions/checkout@v6
20+
uses: actions/checkout@v7
2121

2222
- name: Setup PHP, with composer and extensions
2323
uses: shivammathur/setup-php@v2

.github/workflows/php_analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
php-versions: ['8.2']
1515
steps:
1616
- name: Checkout
17-
uses: actions/checkout@v6
17+
uses: actions/checkout@v7
1818

1919
- name: Setup PHP, with composer and extensions
2020
uses: shivammathur/setup-php@v2

.github/workflows/phpunit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
php-versions: ['8.2']
1818
steps:
1919
- name: Checkout
20-
uses: actions/checkout@v6
20+
uses: actions/checkout@v7
2121

2222
- name: Setup node
2323
run: |

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,14 @@ sudo apt install -y apache2 libapache2-mod-php mariadb-client mariadb-server php
113113
sudo mysql -e "CREATE USER chamilo2@localhost IDENTIFIED BY 'chamilo2';"
114114
sudo mysql -e "GRANT ALL PRIVILEGES ON chamilo2.* TO chamilo2@localhost;"
115115
cd /var/www
116-
sudo wget https://github.com/chamilo/chamilo-lms/releases/download/v2.0.0-RC.2/chamilo-2.0.0-RC.2.tar.gz
117-
sudo tar zxf chamilo-2.0.0-RC.2.tar.gz
118-
sudo mv chamilo-2.0.0-RC.2 chamilo
116+
sudo wget https://github.com/chamilo/chamilo-lms/releases/download/v2.0.3/chamilo-2.0.3.tar.gz
117+
sudo tar zxf chamilo-2.0.3.tar.gz
119118
cd chamilo
120119
sudo cp public/main/install/apache.dist.conf /etc/apache2/sites-available/my.chamilo.net.conf
121-
# Edit /etc/apache2/sites-available/my.chamilo.net.conf to match your domain
120+
# Edit /etc/apache2/sites-available/my.chamilo.net.conf to match your domain name
122121
sudo a2ensite my.chamilo.net
123122
sudo a2enmod rewrite ssl headers expires
123+
sudo touch .env
124124
sudo chown -R www-data: .env config/ var/
125125
sudo systemctl restart apache2
126126
# Open http://my.chamilo.net in your browser to go through the installation wizard

assets/vue/router/coursemaintenance.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
1+
import { useCidReqStore } from "../store/cidReq"
2+
import { checkIsAllowedToEdit } from "../composables/userPermissions"
3+
4+
/**
5+
* Course Maintenance is a teacher-only course tool (admin category): students
6+
* and teachers of other courses must not reach any of its pages.
7+
*
8+
* Runs as beforeEnter (before beforeResolve), so the course context is loaded
9+
* here first, then edit permission is resolved for the current cid/sid. Mirrors
10+
* the backend gate (ROLE_CURRENT_COURSE_TEACHER / platform admin), so course
11+
* teachers and admins pass while everyone else is redirected away.
12+
*
13+
* @param {import('vue-router').RouteLocationNormalized} to
14+
* @returns {Promise<boolean|import('vue-router').RouteLocationRaw>}
15+
*/
16+
async function courseMaintenanceBeforeEnter(to) {
17+
const courseId = parseInt(to.query?.cid ?? 0)
18+
const sessionId = parseInt(to.query?.sid ?? 0)
19+
20+
if (!courseId) {
21+
return { name: "Home", replace: true }
22+
}
23+
24+
const cidReqStore = useCidReqStore()
25+
await cidReqStore.setCourseAndSessionById(courseId, sessionId)
26+
27+
if (!cidReqStore.course) {
28+
return { name: "Home", replace: true }
29+
}
30+
31+
const isAllowedToEdit = await checkIsAllowedToEdit()
32+
33+
if (!isAllowedToEdit) {
34+
return { name: "CourseHome", params: { id: courseId }, query: sessionId ? { sid: sessionId } : {} }
35+
}
36+
37+
return true
38+
}
39+
140
export default {
241
path: "/resources/course_maintenance/:node(\\d+)",
342
meta: { requiresAuth: true, showBreadcrumb: true, breadcrumb: "Course maintenance" },
@@ -8,36 +47,42 @@ export default {
847
{
948
name: "CMImportBackup",
1049
path: "import",
50+
beforeEnter: courseMaintenanceBeforeEnter,
1151
component: () => import("../views/coursemaintenance/ImportBackup.vue"),
1252
meta: { breadcrumb: "Import backup" },
1353
},
1454
{
1555
name: "CMCreateBackup",
1656
path: "create",
57+
beforeEnter: courseMaintenanceBeforeEnter,
1758
component: () => import("../views/coursemaintenance/CreateBackup.vue"),
1859
meta: { breadcrumb: "Create backup" },
1960
},
2061
{
2162
name: "CMCopyCourse",
2263
path: "copy",
64+
beforeEnter: courseMaintenanceBeforeEnter,
2365
component: () => import("../views/coursemaintenance/CopyCourse.vue"),
2466
meta: { breadcrumb: "Copy course" },
2567
},
2668
{
2769
name: "CMCc13",
2870
path: "cc13",
71+
beforeEnter: courseMaintenanceBeforeEnter,
2972
component: () => import("../views/coursemaintenance/Cc13.vue"),
3073
meta: { breadcrumb: "IMS CC 1.3" },
3174
},
3275
{
3376
name: "CMRecycle",
3477
path: "recycle",
78+
beforeEnter: courseMaintenanceBeforeEnter,
3579
component: () => import("../views/coursemaintenance/RecycleCourse.vue"),
3680
meta: { breadcrumb: "Recycle course" },
3781
},
3882
{
3983
name: "CMDelete",
4084
path: "delete",
85+
beforeEnter: courseMaintenanceBeforeEnter,
4186
component: () => import("../views/coursemaintenance/DeleteCourse.vue"),
4287
meta: { breadcrumb: "Delete course" },
4388
},

assets/vue/services/courseService.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,6 @@ export default {
2222
return await baseService.get("/plugin/CourseLegal/check.php", { cid, sid, gid: 0 })
2323
},
2424

25-
/**
26-
* Fetches the CourseHomeNotify plugin notification for a course.
27-
* @param {number} cid
28-
* @param {number} sid
29-
* @returns {Promise<Object>}
30-
*/
31-
getCourseHomeNotification: async (cid, sid = 0) => {
32-
return await baseService.get("/plugin/CourseHomeNotify/ajax.php", { cid, sid, gid: 0 })
33-
},
34-
3525
/**
3626
* Lists the current user's courses (paginated). Returns the raw response body
3727
* so the caller can normalize it; accepts extra axios options (e.g. a signal).

assets/vue/services/forumService.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function buildForumFormData(payload = {}) {
1717
const attachments = Array.isArray(payload.attachments) ? payload.attachments : []
1818

1919
for (const [key, value] of Object.entries(payload)) {
20-
if (key === "attachments") {
20+
if (["attachments", "image"].includes(key)) {
2121
continue
2222
}
2323

@@ -30,15 +30,27 @@ function buildForumFormData(payload = {}) {
3030
formData.append("attachments[]", file)
3131
})
3232

33+
if (hasForumImage(payload)) {
34+
formData.append("image", payload.image)
35+
}
36+
3337
return formData
3438
}
3539

3640
function hasAttachments(payload = {}) {
3741
return Array.isArray(payload.attachments) && payload.attachments.length > 0
3842
}
3943

40-
export default {
44+
function hasForumImage(payload = {}) {
45+
const image = payload.image
4146

47+
return (
48+
(typeof File !== "undefined" && image instanceof File) ||
49+
(typeof Blob !== "undefined" && image instanceof Blob)
50+
)
51+
}
52+
53+
export default {
4254
async searchForums(params = {}) {
4355
return await baseService.get("/api/forum/search", cleanParams(params))
4456
},
@@ -82,7 +94,6 @@ export default {
8294
return await baseService.get(`/api/forums/${forumId}`, cleanParams(params))
8395
},
8496

85-
8697
async createForum(params, payload) {
8798
return await baseService.post("/api/forums/create", payload, {}, { params: cleanParams(params) })
8899
},
@@ -111,6 +122,12 @@ export default {
111122
return await baseService.put(`/api/forums/${forumId}/toggle-subscription`, payload, { params: cleanParams(params) })
112123
},
113124

125+
async uploadForumImage(forumId, params, payload) {
126+
const body = hasForumImage(payload) ? buildForumFormData(payload) : payload
127+
128+
return await baseService.post(`/api/forums/${forumId}/image`, body, {}, { params: cleanParams(params) })
129+
},
130+
114131
async getThreads(forumId, params) {
115132
const { items } = await baseService.getCollection(
116133
"/api/forum_threads",
@@ -190,7 +207,6 @@ export default {
190207
return await baseService.put(`/api/forum_posts/${postId}/toggle-visibility`, payload, { params: cleanParams(params) })
191208
},
192209

193-
194210
async approvePost(postId, params, payload) {
195211
return await baseService.put(`/api/forum_posts/${postId}/approve`, payload, { params: cleanParams(params) })
196212
},

0 commit comments

Comments
 (0)