Skip to content

Commit b3d23f5

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents 09a3f85 + e554d1b commit b3d23f5

22 files changed

Lines changed: 972 additions & 158 deletions

File tree

assets/vue/views/course/CourseHome.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,23 @@ async function updateDisplayOrder(htmlItem, newIndex) {
577577
578578
const { isAllowedToEdit } = useIsAllowedToEdit()
579579
580+
581+
async function enforceCourseLegalAgreement() {
582+
if (!course.value?.id) {
583+
return
584+
}
585+
586+
try {
587+
const response = await axios.get(`/plugin/CourseLegal/check.php?cid=${course.value.id}&sid=${session.value?.id || 0}&gid=0`)
588+
589+
if (response.data?.required && !response.data?.accepted && response.data?.url) {
590+
window.location.href = response.data.url
591+
}
592+
} catch (error) {
593+
console.error("[CourseLegal] Failed to check course legal agreement", error)
594+
}
595+
}
596+
580597
async function loadCourseHomeNotification() {
581598
if (!course.value?.id) {
582599
return
@@ -612,6 +629,7 @@ const showCourseSequence = computed(() => {
612629
})
613630
614631
onMounted(() => {
632+
enforceCourseLegalAgreement()
615633
loadCourseHomeNotification()
616634
617635
documentAutoLaunch.value = parseInt(courseSettingsStore.getSetting("enable_document_auto_launch"), 10) || 0

config/authentication.dist.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ parameters:
9595
filter: null
9696
uid_key: 'uid'
9797
password_attribute: 'userPassword'
98+
# When true (default), the LDAP role is synced to Chamilo on every update (sync command, login, admin import).
99+
# When false, the role is only applied when creating a new user; existing users keep their current Chamilo role.
100+
synch_user_role_on_update: true
98101
data_correspondence:
99102
firstname: 'givenName'
100103
lastname: 'sn'

public/documentation/changelog.html

Lines changed: 152 additions & 0 deletions
Large diffs are not rendered by default.

public/main/admin/settings.lib.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ function getStablePluginAllowList(): array
323323
'TopLinks',
324324
'ExternalNotificationConnect',
325325
'CourseHomeNotify',
326+
'CourseLegal',
327+
'Static',
326328
];
327329
}
328330

public/main/course_info/infocours.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@
7474
}
7575
}
7676

77+
$courseLegalPluginEntity = Container::getPluginRepository()->findOneByTitle('CourseLegal');
78+
$courseLegalPluginConfiguration = $courseLegalPluginEntity?->getConfigurationsByAccessUrl($currentAccessUrl);
79+
80+
$isCourseLegalEnabled = $courseLegalPluginEntity
81+
&& $courseLegalPluginEntity->isInstalled()
82+
&& $courseLegalPluginConfiguration
83+
&& $courseLegalPluginConfiguration->isActive();
84+
85+
$courseLegalPlugin = null;
86+
$courseLegalPluginPath = api_get_path(SYS_PLUGIN_PATH).'CourseLegal/CourseLegalPlugin.php';
87+
88+
if (is_file($courseLegalPluginPath)) {
89+
require_once $courseLegalPluginPath;
90+
91+
if (class_exists('CourseLegalPlugin')) {
92+
$courseLegalPlugin = CourseLegalPlugin::create();
93+
}
94+
}
95+
7796
$show_delete_watermark_text_message = false;
7897
if ('true' === api_get_setting('pdf_export_watermark_by_course')) {
7998
if (isset($_GET['delete_watermark'])) {
@@ -1245,6 +1264,50 @@ class="block h-full w-full object-cover"
12451264
);
12461265
}
12471266

1267+
if ($isCourseLegalEnabled) {
1268+
$courseLegalTitle = $courseLegalPlugin
1269+
? $courseLegalPlugin->get_title()
1270+
: 'Course legal agreement';
1271+
$courseLegalDescription = $courseLegalPlugin
1272+
? $courseLegalPlugin->get_comment()
1273+
: 'Configure a legal agreement that learners must accept before accessing the course.';
1274+
1275+
$configureButton = Display::toolbarButton(
1276+
$courseLegalPlugin ? $courseLegalPlugin->get_lang('CourseLegal') : 'Configure agreement',
1277+
api_get_path(WEB_PLUGIN_PATH).'CourseLegal/start.php?'.api_get_cidreq(),
1278+
'file-document-edit-outline',
1279+
'primary'
1280+
);
1281+
1282+
$userListButton = Display::toolbarButton(
1283+
get_lang('User list'),
1284+
api_get_path(WEB_PLUGIN_PATH).'CourseLegal/user_list.php?'.api_get_cidreq(),
1285+
'account-check-outline',
1286+
'secondary'
1287+
);
1288+
1289+
$courseLegalInfo = $form->createElement(
1290+
'html',
1291+
'<div class="mb-4">'
1292+
.'<p class="mb-3">'
1293+
.$courseLegalDescription
1294+
.'</p>'
1295+
.'<div class="flex flex-wrap gap-2">'
1296+
.$configureButton
1297+
.$userListButton
1298+
.'</div>'
1299+
.'</div>'
1300+
);
1301+
1302+
$form->addPanelOption(
1303+
'course_legal',
1304+
$courseLegalTitle,
1305+
[$courseLegalInfo],
1306+
ToolIcon::COURSE,
1307+
false
1308+
);
1309+
}
1310+
12481311
$normalizeQuizNotificationSetting = static function ($value) use ($validQuizNotificationValues): array {
12491312
if (\is_array($value)) {
12501313
$items = [];

public/main/course_info/legal.php

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222

2323
$pluginExtra = null;
2424
$pluginLegal = false;
25-
$plugin = CourseLegalPlugin::create();
25+
$pluginConfigPath = api_get_path(SYS_PLUGIN_PATH).'CourseLegal/config.php';
2626

27-
if ($plugin->isEnabled()) {
27+
if (is_file($pluginConfigPath)) {
28+
require_once $pluginConfigPath;
29+
}
30+
31+
$plugin = class_exists('CourseLegalPlugin') ? CourseLegalPlugin::create() : null;
32+
33+
if ($plugin && $plugin->isEnabled()) {
2834
$pluginLegal = true;
29-
require_once api_get_path(SYS_PLUGIN_PATH).'CourseLegal/config.php';
3035
$data = $plugin->getData($course_info['real_id'], $session_id);
3136

3237
if (!empty($data)) {
@@ -53,16 +58,6 @@
5358
$form = new FormValidator('legal', 'GET', api_get_self().'?course_code='.$course_code.'&session_id='.$session_id);
5459
$pluginMessage = null;
5560
$hideForm = false;
56-
if ($pluginLegal && isset($userData) && !empty($userData)) {
57-
if (1 == $userData['web_agreement']) {
58-
if (empty($userData['mail_agreement'])) {
59-
$pluginMessage = Display::return_message(
60-
$plugin->get_lang('You need to confirm your agreement to our terms first. Please check your e-mail.')
61-
);
62-
$hideForm = true;
63-
}
64-
}
65-
}
6661
$form->addElement('header', get_lang('Legal agreement for this course'));
6762
$form->addElement('label', null, $course_legal);
6863
if ($pluginLegal && !empty($plugin)) {
@@ -88,6 +83,16 @@
8883
}
8984

9085
CourseManager::save_user_legal($user_id, $course_info, $session_id);
86+
87+
if ($pluginLegal && !empty($plugin)) {
88+
$plugin->saveUserLegal(
89+
$user_id,
90+
$course_info['code'],
91+
$session_id,
92+
false
93+
);
94+
}
95+
9196
if (api_check_user_access_to_legal($course_info)) {
9297
Session::write($variable, true);
9398
}

public/main/inc/lib/course.lib.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4600,10 +4600,13 @@ public static function is_user_accepted_legal($user_id, $course_code, $session_i
46004600
$courseId = $courseInfo['real_id'];
46014601

46024602
// Course legal
4603-
$plugin = CourseLegalPlugin::create();
4604-
if ($plugin->isEnabled()) {
4605-
require_once api_get_path(SYS_PLUGIN_PATH).'courselegal/config.php';
4603+
$courseLegalConfigPath = api_get_path(SYS_PLUGIN_PATH).'CourseLegal/config.php';
4604+
if (is_file($courseLegalConfigPath)) {
4605+
require_once $courseLegalConfigPath;
4606+
}
46064607

4608+
$plugin = class_exists('CourseLegalPlugin') ? CourseLegalPlugin::create() : null;
4609+
if ($plugin && $plugin->isEnabled()) {
46074610
return $plugin->isUserAcceptedLegal($user_id, $course_code, $session_id);
46084611
}
46094612

@@ -4653,10 +4656,13 @@ public static function save_user_legal($user_id, $courseInfo, $session_id = 0)
46534656
$course_code = $courseInfo['code'];
46544657

46554658
// Course plugin legal
4656-
$plugin = CourseLegalPlugin::create();
4657-
if ($plugin->isEnabled()) {
4658-
require_once api_get_path(SYS_PLUGIN_PATH).'courselegal/config.php';
4659+
$courseLegalConfigPath = api_get_path(SYS_PLUGIN_PATH).'CourseLegal/config.php';
4660+
if (is_file($courseLegalConfigPath)) {
4661+
require_once $courseLegalConfigPath;
4662+
}
46594663

4664+
$plugin = class_exists('CourseLegalPlugin') ? CourseLegalPlugin::create() : null;
4665+
if ($plugin && $plugin->isEnabled()) {
46604666
return $plugin->saveUserLegal($user_id, $course_code, $session_id);
46614667
}
46624668

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/* For licensing terms, see /license.txt */
3+
4+
require_once __DIR__.'/../../main/inc/global.inc.php';
5+
6+
api_protect_admin_script();
7+
8+
$plugin = CourseHomeNotifyPlugin::create();
9+
10+
$interbreadcrumb[] = [
11+
'url' => api_get_path(WEB_CODE_PATH).'admin/index.php',
12+
'name' => get_lang('PlatformAdmin'),
13+
];
14+
15+
$title = $plugin->get_title();
16+
17+
$content = Display::page_subheader($title);
18+
$content .= Display::return_message(
19+
$plugin->get_comment(),
20+
'info',
21+
false
22+
);
23+
24+
$content .= '<div class="card">';
25+
$content .= '<div class="card-body">';
26+
$content .= '<p>This plugin is configured per course, not from the global plugin administration page.</p>';
27+
$content .= '<p>To configure a course notification, open the target course as a teacher or administrator, then go to the course settings page and use the "'
28+
.$plugin->get_lang('SetNotification')
29+
.'" option.</p>';
30+
$content .= '<p>The notification will be displayed on the Vue course homepage when the plugin is active and the course has a configured notification.</p>';
31+
$content .= '</div>';
32+
$content .= '</div>';
33+
34+
$template = new Template($title);
35+
$template->assign('header', $title);
36+
$template->assign('content', $content);
37+
$template->display_one_col_template();
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
/* For licensing terms, see /license.txt */
3+
4+
use Chamilo\PluginBundle\CourseHomeNotify\Entity\Notification;
5+
use Chamilo\PluginBundle\CourseHomeNotify\Entity\NotificationRelUser;
6+
7+
require_once __DIR__.'/../../main/inc/global.inc.php';
8+
9+
api_block_anonymous_users(true);
10+
api_protect_course_script(true);
11+
12+
header('Content-Type: application/json');
13+
14+
$plugin = CourseHomeNotifyPlugin::create();
15+
$courseId = api_get_course_int_id();
16+
$userId = api_get_user_id();
17+
18+
if (
19+
empty($courseId) ||
20+
empty($userId) ||
21+
!$plugin->isEnabled()
22+
) {
23+
echo json_encode(['show' => false]);
24+
exit;
25+
}
26+
27+
$course = api_get_course_entity($courseId);
28+
$user = api_get_user_entity($userId);
29+
$em = Database::getManager();
30+
$schemaManager = $em->getConnection()->createSchemaManager();
31+
32+
if (
33+
!$schemaManager->tablesExist([
34+
'course_home_notify_notification',
35+
'course_home_notify_notification_rel_user',
36+
])
37+
) {
38+
echo json_encode(['show' => false]);
39+
exit;
40+
}
41+
42+
/** @var Notification|null $notification */
43+
$notification = $em
44+
->getRepository(Notification::class)
45+
->findOneBy(['course' => $course]);
46+
47+
if (!$notification) {
48+
echo json_encode(['show' => false]);
49+
exit;
50+
}
51+
52+
$expirationLink = $notification->getExpirationLink();
53+
54+
if ($expirationLink) {
55+
/** @var NotificationRelUser|null $notificationUser */
56+
$notificationUser = $em
57+
->getRepository(NotificationRelUser::class)
58+
->findOneBy(['notification' => $notification, 'user' => $user]);
59+
60+
if ($notificationUser) {
61+
echo json_encode(['show' => false]);
62+
exit;
63+
}
64+
}
65+
66+
$contentUrl = '';
67+
68+
if ($expirationLink) {
69+
$contentUrl = api_get_path(WEB_PLUGIN_PATH)
70+
.$plugin->get_name()
71+
.'/content.php?hash='
72+
.urlencode($notification->getHash())
73+
.'&'
74+
.api_get_cidreq();
75+
}
76+
77+
echo json_encode(
78+
[
79+
'show' => true,
80+
'title' => $plugin->get_lang('CourseNotice'),
81+
'content' => $notification->getContent(),
82+
'requiresLink' => !empty($expirationLink),
83+
'linkLabel' => $plugin->get_lang('PleaseFollowThisLink'),
84+
'contentUrl' => $contentUrl,
85+
]
86+
);

0 commit comments

Comments
 (0)