Skip to content

Commit bbba653

Browse files
Admin: Enforce global users-per-course limit on course subscriptions - refs #5114
1 parent 56aaa5a commit bbba653

File tree

9 files changed

+593
-57
lines changed

9 files changed

+593
-57
lines changed

public/main/admin/add_users_to_usergroup.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,23 @@ function change_select(val) {
125125

126126
if (1 == $form_sent) {
127127
// Added a parameter to send emails when registering a user
128-
$usergroup->subscribe_users_to_usergroup(
128+
$result = $usergroup->subscribe_users_to_usergroup(
129129
$id,
130130
$elements_posted,
131131
true,
132132
$relation
133133
);
134-
$_SESSION['usergroup_flash_message'] = get_lang('Update successful');
135-
$_SESSION['usergroup_flash_type'] = 'success';
134+
135+
if ($result) {
136+
// Success: users were subscribed to usergroup (and related courses/sessions)
137+
$_SESSION['usergroup_flash_message'] = get_lang('Update successful');
138+
$_SESSION['usergroup_flash_type'] = 'success';
139+
} else {
140+
// Global hosting limit reached: full operation cancelled.
141+
$_SESSION['usergroup_flash_message'] = CourseManager::getGlobalLimitCancelMessage();
142+
$_SESSION['usergroup_flash_type'] = 'warning';
143+
}
144+
136145
header('Location: usergroups.php');
137146
exit;
138147
}

public/main/admin/course_user_import.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@ function validate_data($users_courses)
6666
*/
6767
function save_data($users_courses)
6868
{
69+
global $globalLimitUserCoursePairs;
70+
6971
$course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
7072
$csv_data = [];
7173
$inserted_in_course = [];
7274
$courseListCache = [];
7375
$courseListById = [];
76+
7477
foreach ($users_courses as $user_course) {
7578
if (!in_array($user_course['CourseCode'], array_keys($courseListCache))) {
7679
$courseInfo = api_get_course_info($user_course['CourseCode']);
@@ -80,6 +83,12 @@ function save_data($users_courses)
8083
} else {
8184
$courseInfo = $courseListCache[$user_course['CourseCode']];
8285
}
86+
87+
if (empty($courseInfo) || empty($courseInfo['real_id'])) {
88+
// Skip invalid course info.
89+
continue;
90+
}
91+
8392
$courseListById[$courseInfo['real_id']] = $courseInfo;
8493
$csv_data[$user_course['UserName']][$courseInfo['real_id']] = $user_course['Status'];
8594
}
@@ -91,6 +100,7 @@ function save_data($users_courses)
91100
}
92101

93102
$user_id = $userInfo['user_id'];
103+
94104
$sql = "SELECT * FROM $course_user_table cu
95105
WHERE cu.user_id = $user_id AND cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH.' ';
96106
$res = Database::query($sql);
@@ -104,7 +114,23 @@ function save_data($users_courses)
104114

105115
if (isset($_POST['subscribe']) && $_POST['subscribe']) {
106116
foreach ($to_subscribe as $courseId) {
117+
if (!isset($courseListById[$courseId])) {
118+
// Safety check in case of inconsistent data.
119+
continue;
120+
}
121+
107122
$courseInfo = $courseListById[$courseId];
123+
124+
// Check global hosting limit for this user/course pair.
125+
if (CourseManager::wouldOperationExceedGlobalLimit((int) $courseId, [$user_id])) {
126+
// Collect pair for later reporting in a single batch message.
127+
$pairLabel = $username.' / '.$courseInfo['title'];
128+
$globalLimitUserCoursePairs[] = $pairLabel;
129+
130+
// Do not subscribe this user to this course; continue with next course.
131+
continue;
132+
}
133+
108134
$result = CourseManager::subscribeUser(
109135
$user_id,
110136
$courseId,
@@ -123,6 +149,11 @@ function save_data($users_courses)
123149
} else {
124150
$courseInfo = api_get_course_info_by_id($courseId);
125151
}
152+
153+
if (empty($courseInfo) || empty($courseInfo['code'])) {
154+
continue;
155+
}
156+
126157
$courseCode = $courseInfo['code'];
127158
CourseManager::unsubscribe_user($user_id, $courseCode);
128159
}
@@ -159,6 +190,7 @@ function parse_csv_data($file)
159190
$interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('Administration')];
160191

161192
set_time_limit(0);
193+
$globalLimitUserCoursePairs = [];
162194

163195
// Creating the form.
164196
$form = new FormValidator('course_user_import');
@@ -182,6 +214,16 @@ function parse_csv_data($file)
182214
if (0 == count($errors)) {
183215
$inserted_in_course = save_data($users_courses);
184216

217+
// Show global hosting limit warning if some subscriptions were blocked.
218+
if (!empty($globalLimitUserCoursePairs)) {
219+
$limitMessage = CourseManager::getGlobalLimitPartialImportMessage($globalLimitUserCoursePairs);
220+
Display::addFlash(
221+
Display::return_message($limitMessage, 'warning', false)
222+
);
223+
// Reset for safety in case of reuse within the same request.
224+
$globalLimitUserCoursePairs = [];
225+
}
226+
185227
if (!empty($inserted_in_course)) {
186228
$warn = get_lang('File imported');
187229
} else {

public/main/admin/subscribe_user2course.php

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,30 +95,87 @@ function validate_filter() {
9595
echo Display::return_message(get_lang('You must select at least one user and one course'), 'error');
9696
} else {
9797
$errorDrh = 0;
98-
foreach ($courses as $course_code) {
99-
foreach ($users as $user_id) {
100-
$user = api_get_user_info($user_id);
101-
if (DRH != $user['status']) {
102-
$courseInfo = api_get_course_info($course_code);
103-
CourseManager::subscribeUser($user_id, $courseInfo['real_id']);
104-
} else {
105-
$errorDrh = 1;
98+
99+
// -----------------------------------------------------------------
100+
// Build user info map once and list of users allowed for subscription
101+
// (DRH users are excluded from global limit computation and from
102+
// course subscription).
103+
// -----------------------------------------------------------------
104+
$usersInfo = [];
105+
$subscribableUserIds = [];
106+
107+
foreach ($users as $user_id) {
108+
$user = api_get_user_info($user_id);
109+
$usersInfo[$user_id] = $user;
110+
111+
if (DRH != $user['status']) {
112+
$subscribableUserIds[] = $user_id;
113+
} else {
114+
// Keep original behaviour: mark DRH presence to show message later
115+
$errorDrh = 1;
116+
}
117+
}
118+
119+
// -----------------------------------------------------------------
120+
// Global hosting limit: cancel whole operation if any selected
121+
// course would exceed platform.hosting_limit_users_per_course.
122+
// -----------------------------------------------------------------
123+
$limitExceeded = false;
124+
125+
if (!empty($subscribableUserIds)) {
126+
foreach ($courses as $course_code) {
127+
$courseInfo = api_get_course_info($course_code);
128+
if (empty($courseInfo) || empty($courseInfo['real_id'])) {
129+
continue; // Skip invalid course records.
130+
}
131+
132+
$courseId = (int) $courseInfo['real_id'];
133+
134+
if (CourseManager::wouldOperationExceedGlobalLimit($courseId, $subscribableUserIds)) {
135+
$limitExceeded = true;
136+
break;
106137
}
107138
}
108139
}
109140

110-
if (0 == $errorDrh) {
141+
if ($limitExceeded) {
142+
// No subscription is executed if the global limit would be exceeded.
111143
echo Display::return_message(
112-
get_lang('The selected users are subscribed to the selected course'),
113-
'confirm'
144+
CourseManager::getGlobalLimitCancelMessage(),
145+
'warning'
114146
);
115147
} else {
116-
echo Display::return_message(
117-
get_lang(
118-
'Human resources managers should not be registered to courses. The corresponding users you selected have not been subscribed.'
119-
),
120-
'error'
121-
);
148+
// -----------------------------------------------------------------
149+
// Original behaviour: subscribe all non-DRH users to all selected courses.
150+
// DRH users are skipped and reported.
151+
// -----------------------------------------------------------------
152+
foreach ($courses as $course_code) {
153+
$courseInfo = api_get_course_info($course_code);
154+
155+
foreach ($users as $user_id) {
156+
$user = $usersInfo[$user_id] ?? api_get_user_info($user_id);
157+
158+
if (DRH != $user['status']) {
159+
CourseManager::subscribeUser($user_id, $courseInfo['real_id']);
160+
} else {
161+
$errorDrh = 1;
162+
}
163+
}
164+
}
165+
166+
if (0 == $errorDrh) {
167+
echo Display::return_message(
168+
get_lang('The selected users are subscribed to the selected course'),
169+
'confirm'
170+
);
171+
} else {
172+
echo Display::return_message(
173+
get_lang(
174+
'Human resources managers should not be registered to courses. The corresponding users you selected have not been subscribed.'
175+
),
176+
'error'
177+
);
178+
}
122179
}
123180
}
124181
}

public/main/admin/user_import.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
api_protect_admin_script(true, null);
2121
api_protect_limit_for_session_admin();
2222
set_time_limit(0);
23+
$globalLimitUserCoursePairs = [];
2324

2425
/**
2526
* @param array $users
@@ -202,7 +203,12 @@ function complete_missing_data($user)
202203
*/
203204
function save_data($users, $sendMail = false)
204205
{
205-
global $inserted_in_course, $extra_fields;
206+
global $inserted_in_course, $extra_fields, $globalLimitUserCoursePairs;
207+
208+
// Ensure array for global limit tracking.
209+
if (!isset($globalLimitUserCoursePairs) || !is_array($globalLimitUserCoursePairs)) {
210+
$globalLimitUserCoursePairs = [];
211+
}
206212

207213
// Not all scripts declare the $inserted_in_course array (although they should).
208214
if (!isset($inserted_in_course)) {
@@ -254,12 +260,33 @@ function save_data($users, $sendMail = false)
254260
if ($user_id) {
255261
$returnMessage = Display::return_message(get_lang('The user has been added'), 'success');
256262

263+
// Normalize Courses for both CSV and XML imports.
264+
if (isset($user['Courses']) && !is_array($user['Courses'])) {
265+
$user['Courses'] = explode('|', trim((string) $user['Courses']));
266+
}
267+
257268
if (isset($user['Courses']) && is_array($user['Courses'])) {
258269
foreach ($user['Courses'] as $course) {
259270
if (CourseManager::course_exists($course)) {
260271
$course_info = api_get_course_info($course);
272+
if (empty($course_info) || empty($course_info['real_id'])) {
273+
// Skip invalid course records.
274+
continue;
275+
}
276+
277+
$courseId = (int) $course_info['real_id'];
278+
279+
// Check global hosting limit for this user/course pair.
280+
if (CourseManager::wouldOperationExceedGlobalLimit($courseId, [$user_id])) {
281+
// Collect pair for later reporting in a single batch message.
282+
$pairLabel = $user['UserName'].' / '.$course_info['title'];
283+
$globalLimitUserCoursePairs[] = $pairLabel;
284+
285+
// Do not subscribe this user to this course; continue with next course.
286+
continue;
287+
}
261288

262-
$result = CourseManager::subscribeUser($user_id, $course_info['real_id'], $user['Status']);
289+
$result = CourseManager::subscribeUser($user_id, $courseId, $user['Status']);
263290
if ($result) {
264291
$inserted_in_course[$course] = $course_info['title'];
265292
}
@@ -449,6 +476,8 @@ function parse_xml_data($file)
449476
*/
450477
function processUsers(&$users, $sendMail)
451478
{
479+
global $globalLimitUserCoursePairs;
480+
452481
$users = save_data($users, $sendMail);
453482

454483
$warningMessage = '';
@@ -481,6 +510,22 @@ function processUsers(&$users, $sendMail)
481510
$warningMessage = $table->toHtml();
482511
}
483512

513+
// Append global hosting limit report if needed (batch imports).
514+
if (!empty($globalLimitUserCoursePairs)) {
515+
$limitMessage = CourseManager::getGlobalLimitPartialImportMessage($globalLimitUserCoursePairs);
516+
517+
// Show the warning once as a flash message.
518+
Display::addFlash(
519+
Display::return_message($limitMessage, 'warning', false)
520+
);
521+
522+
// Also append to the per-import log that is shown in the "Results and feedback" section.
523+
$warningMessage .= Display::return_message($limitMessage, 'warning', false);
524+
525+
// Reset for potential subsequent imports in the same request.
526+
$globalLimitUserCoursePairs = [];
527+
}
528+
484529
// if the warning message is too long then we display the warning message trough a session
485530
Display::addFlash(Display::return_message(get_lang('File imported'), 'confirmation', false));
486531

public/main/admin/usergroups.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,13 @@
183183
}
184184

185185
Display::display_header();
186-
186+
if (!empty($_SESSION['usergroup_flash_message'])) {
187+
echo Display::return_message(
188+
$_SESSION['usergroup_flash_message'],
189+
$_SESSION['usergroup_flash_type']
190+
);
191+
unset($_SESSION['usergroup_flash_message'], $_SESSION['usergroup_flash_type']);
192+
}
187193
?>
188194
<script>
189195
$(function() {

0 commit comments

Comments
 (0)