diff --git a/public/main/admin/add_users_to_usergroup.php b/public/main/admin/add_users_to_usergroup.php index 98d6f2b04c0..262a7ff85c3 100644 --- a/public/main/admin/add_users_to_usergroup.php +++ b/public/main/admin/add_users_to_usergroup.php @@ -125,14 +125,23 @@ function change_select(val) { if (1 == $form_sent) { // Added a parameter to send emails when registering a user - $usergroup->subscribe_users_to_usergroup( + $result = $usergroup->subscribe_users_to_usergroup( $id, $elements_posted, true, $relation ); - $_SESSION['usergroup_flash_message'] = get_lang('Update successful'); - $_SESSION['usergroup_flash_type'] = 'success'; + + if ($result) { + // Success: users were subscribed to usergroup (and related courses/sessions) + $_SESSION['usergroup_flash_message'] = get_lang('Update successful'); + $_SESSION['usergroup_flash_type'] = 'success'; + } else { + // Global hosting limit reached: full operation cancelled. + $_SESSION['usergroup_flash_message'] = CourseManager::getGlobalLimitCancelMessage(); + $_SESSION['usergroup_flash_type'] = 'warning'; + } + header('Location: usergroups.php'); exit; } diff --git a/public/main/admin/course_user_import.php b/public/main/admin/course_user_import.php index cb7207c08ae..1297b72f9b2 100644 --- a/public/main/admin/course_user_import.php +++ b/public/main/admin/course_user_import.php @@ -66,11 +66,14 @@ function validate_data($users_courses) */ function save_data($users_courses) { + global $globalLimitUserCoursePairs; + $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER); $csv_data = []; $inserted_in_course = []; $courseListCache = []; $courseListById = []; + foreach ($users_courses as $user_course) { if (!in_array($user_course['CourseCode'], array_keys($courseListCache))) { $courseInfo = api_get_course_info($user_course['CourseCode']); @@ -80,6 +83,12 @@ function save_data($users_courses) } else { $courseInfo = $courseListCache[$user_course['CourseCode']]; } + + if (empty($courseInfo) || empty($courseInfo['real_id'])) { + // Skip invalid course info. + continue; + } + $courseListById[$courseInfo['real_id']] = $courseInfo; $csv_data[$user_course['UserName']][$courseInfo['real_id']] = $user_course['Status']; } @@ -91,6 +100,7 @@ function save_data($users_courses) } $user_id = $userInfo['user_id']; + $sql = "SELECT * FROM $course_user_table cu WHERE cu.user_id = $user_id AND cu.relation_type <> ".COURSE_RELATION_TYPE_RRHH.' '; $res = Database::query($sql); @@ -104,7 +114,23 @@ function save_data($users_courses) if (isset($_POST['subscribe']) && $_POST['subscribe']) { foreach ($to_subscribe as $courseId) { + if (!isset($courseListById[$courseId])) { + // Safety check in case of inconsistent data. + continue; + } + $courseInfo = $courseListById[$courseId]; + + // Check global hosting limit for this user/course pair. + if (CourseManager::wouldOperationExceedGlobalLimit((int) $courseId, [$user_id])) { + // Collect pair for later reporting in a single batch message. + $pairLabel = $username.' / '.$courseInfo['title']; + $globalLimitUserCoursePairs[] = $pairLabel; + + // Do not subscribe this user to this course; continue with next course. + continue; + } + $result = CourseManager::subscribeUser( $user_id, $courseId, @@ -123,6 +149,11 @@ function save_data($users_courses) } else { $courseInfo = api_get_course_info_by_id($courseId); } + + if (empty($courseInfo) || empty($courseInfo['code'])) { + continue; + } + $courseCode = $courseInfo['code']; CourseManager::unsubscribe_user($user_id, $courseCode); } @@ -159,6 +190,7 @@ function parse_csv_data($file) $interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('Administration')]; set_time_limit(0); +$globalLimitUserCoursePairs = []; // Creating the form. $form = new FormValidator('course_user_import'); @@ -182,6 +214,16 @@ function parse_csv_data($file) if (0 == count($errors)) { $inserted_in_course = save_data($users_courses); + // Show global hosting limit warning if some subscriptions were blocked. + if (!empty($globalLimitUserCoursePairs)) { + $limitMessage = CourseManager::getGlobalLimitPartialImportMessage($globalLimitUserCoursePairs); + Display::addFlash( + Display::return_message($limitMessage, 'warning', false) + ); + // Reset for safety in case of reuse within the same request. + $globalLimitUserCoursePairs = []; + } + if (!empty($inserted_in_course)) { $warn = get_lang('File imported'); } else { diff --git a/public/main/admin/subscribe_user2course.php b/public/main/admin/subscribe_user2course.php index 8a9d10f158c..d45893c99be 100644 --- a/public/main/admin/subscribe_user2course.php +++ b/public/main/admin/subscribe_user2course.php @@ -95,30 +95,87 @@ function validate_filter() { echo Display::return_message(get_lang('You must select at least one user and one course'), 'error'); } else { $errorDrh = 0; - foreach ($courses as $course_code) { - foreach ($users as $user_id) { - $user = api_get_user_info($user_id); - if (DRH != $user['status']) { - $courseInfo = api_get_course_info($course_code); - CourseManager::subscribeUser($user_id, $courseInfo['real_id']); - } else { - $errorDrh = 1; + + // ----------------------------------------------------------------- + // Build user info map once and list of users allowed for subscription + // (DRH users are excluded from global limit computation and from + // course subscription). + // ----------------------------------------------------------------- + $usersInfo = []; + $subscribableUserIds = []; + + foreach ($users as $user_id) { + $user = api_get_user_info($user_id); + $usersInfo[$user_id] = $user; + + if (DRH != $user['status']) { + $subscribableUserIds[] = $user_id; + } else { + // Keep original behaviour: mark DRH presence to show message later + $errorDrh = 1; + } + } + + // ----------------------------------------------------------------- + // Global hosting limit: cancel whole operation if any selected + // course would exceed platform.hosting_limit_users_per_course. + // ----------------------------------------------------------------- + $limitExceeded = false; + + if (!empty($subscribableUserIds)) { + foreach ($courses as $course_code) { + $courseInfo = api_get_course_info($course_code); + if (empty($courseInfo) || empty($courseInfo['real_id'])) { + continue; // Skip invalid course records. + } + + $courseId = (int) $courseInfo['real_id']; + + if (CourseManager::wouldOperationExceedGlobalLimit($courseId, $subscribableUserIds)) { + $limitExceeded = true; + break; } } } - if (0 == $errorDrh) { + if ($limitExceeded) { + // No subscription is executed if the global limit would be exceeded. echo Display::return_message( - get_lang('The selected users are subscribed to the selected course'), - 'confirm' + CourseManager::getGlobalLimitCancelMessage(), + 'warning' ); } else { - echo Display::return_message( - get_lang( - 'Human resources managers should not be registered to courses. The corresponding users you selected have not been subscribed.' - ), - 'error' - ); + // ----------------------------------------------------------------- + // Original behaviour: subscribe all non-DRH users to all selected courses. + // DRH users are skipped and reported. + // ----------------------------------------------------------------- + foreach ($courses as $course_code) { + $courseInfo = api_get_course_info($course_code); + + foreach ($users as $user_id) { + $user = $usersInfo[$user_id] ?? api_get_user_info($user_id); + + if (DRH != $user['status']) { + CourseManager::subscribeUser($user_id, $courseInfo['real_id']); + } else { + $errorDrh = 1; + } + } + } + + if (0 == $errorDrh) { + echo Display::return_message( + get_lang('The selected users are subscribed to the selected course'), + 'confirm' + ); + } else { + echo Display::return_message( + get_lang( + 'Human resources managers should not be registered to courses. The corresponding users you selected have not been subscribed.' + ), + 'error' + ); + } } } } diff --git a/public/main/admin/user_import.php b/public/main/admin/user_import.php index 017ff8e87e1..e852379fa9a 100644 --- a/public/main/admin/user_import.php +++ b/public/main/admin/user_import.php @@ -20,6 +20,7 @@ api_protect_admin_script(true, null); api_protect_limit_for_session_admin(); set_time_limit(0); +$globalLimitUserCoursePairs = []; /** * @param array $users @@ -202,7 +203,12 @@ function complete_missing_data($user) */ function save_data($users, $sendMail = false) { - global $inserted_in_course, $extra_fields; + global $inserted_in_course, $extra_fields, $globalLimitUserCoursePairs; + + // Ensure array for global limit tracking. + if (!isset($globalLimitUserCoursePairs) || !is_array($globalLimitUserCoursePairs)) { + $globalLimitUserCoursePairs = []; + } // Not all scripts declare the $inserted_in_course array (although they should). if (!isset($inserted_in_course)) { @@ -254,12 +260,33 @@ function save_data($users, $sendMail = false) if ($user_id) { $returnMessage = Display::return_message(get_lang('The user has been added'), 'success'); + // Normalize Courses for both CSV and XML imports. + if (isset($user['Courses']) && !is_array($user['Courses'])) { + $user['Courses'] = explode('|', trim((string) $user['Courses'])); + } + if (isset($user['Courses']) && is_array($user['Courses'])) { foreach ($user['Courses'] as $course) { if (CourseManager::course_exists($course)) { $course_info = api_get_course_info($course); + if (empty($course_info) || empty($course_info['real_id'])) { + // Skip invalid course records. + continue; + } + + $courseId = (int) $course_info['real_id']; + + // Check global hosting limit for this user/course pair. + if (CourseManager::wouldOperationExceedGlobalLimit($courseId, [$user_id])) { + // Collect pair for later reporting in a single batch message. + $pairLabel = $user['UserName'].' / '.$course_info['title']; + $globalLimitUserCoursePairs[] = $pairLabel; + + // Do not subscribe this user to this course; continue with next course. + continue; + } - $result = CourseManager::subscribeUser($user_id, $course_info['real_id'], $user['Status']); + $result = CourseManager::subscribeUser($user_id, $courseId, $user['Status']); if ($result) { $inserted_in_course[$course] = $course_info['title']; } @@ -449,6 +476,8 @@ function parse_xml_data($file) */ function processUsers(&$users, $sendMail) { + global $globalLimitUserCoursePairs; + $users = save_data($users, $sendMail); $warningMessage = ''; @@ -481,6 +510,22 @@ function processUsers(&$users, $sendMail) $warningMessage = $table->toHtml(); } + // Append global hosting limit report if needed (batch imports). + if (!empty($globalLimitUserCoursePairs)) { + $limitMessage = CourseManager::getGlobalLimitPartialImportMessage($globalLimitUserCoursePairs); + + // Show the warning once as a flash message. + Display::addFlash( + Display::return_message($limitMessage, 'warning', false) + ); + + // Also append to the per-import log that is shown in the "Results and feedback" section. + $warningMessage .= Display::return_message($limitMessage, 'warning', false); + + // Reset for potential subsequent imports in the same request. + $globalLimitUserCoursePairs = []; + } + // if the warning message is too long then we display the warning message trough a session Display::addFlash(Display::return_message(get_lang('File imported'), 'confirmation', false)); diff --git a/public/main/admin/usergroups.php b/public/main/admin/usergroups.php index 8327c07f99b..ee57e98e103 100644 --- a/public/main/admin/usergroups.php +++ b/public/main/admin/usergroups.php @@ -183,7 +183,13 @@ } Display::display_header(); - +if (!empty($_SESSION['usergroup_flash_message'])) { + echo Display::return_message( + $_SESSION['usergroup_flash_message'], + $_SESSION['usergroup_flash_type'] + ); + unset($_SESSION['usergroup_flash_message'], $_SESSION['usergroup_flash_type']); +} ?>