diff --git a/main/mySpace/myStudents.php b/main/mySpace/myStudents.php index 40b729a3030..c86a136725b 100755 --- a/main/mySpace/myStudents.php +++ b/main/mySpace/myStudents.php @@ -23,8 +23,8 @@ '; $export = isset($_GET['export']) ? $_GET['export'] : false; -$sessionId = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0; -$action = isset($_GET['action']) ? $_GET['action'] : ''; +$sessionId = isset($_GET['id_session']) ? (int) $_GET['id_session'] : (isset($_POST['id_session']) ? (int) $_POST['id_session'] : 0); +$action = isset($_GET['action']) ? $_GET['action'] : (isset($_POST['action']) ? $_POST['action'] : ''); $origin = api_get_origin(); $course_code = isset($_GET['course']) ? Security::remove_XSS($_GET['course']) : ''; $courseInfo = api_get_course_info($course_code); @@ -32,7 +32,7 @@ if ($courseInfo) { $courseCode = $courseInfo['code']; } -$student_id = isset($_GET['student']) ? (int) $_GET['student'] : 0; +$student_id = isset($_GET['student']) ? (int) $_GET['student'] : (isset($_POST['student']) ? (int) $_POST['student'] : 0); $coachId = isset($_GET['id_coach']) ? (int) $_GET['id_coach'] : 0; $details = isset($_GET['details']) ? Security::remove_XSS($_GET['details']) : ''; $currentUrl = api_get_self().'?student='.$student_id.'&course='.$courseCode.'&id_session='.$sessionId @@ -152,6 +152,15 @@ if (false === $subscriptionColumnEnabled) { break; } + $canManageSubscriptions = api_is_platform_admin(true, true) + || api_is_session_admin() + || api_is_allowed_to_edit(null, true) + || api_is_course_admin() + || api_is_teacher() + || api_is_coach(); + if (!$canManageSubscriptions) { + api_not_allowed(true); + } $courseCodeParam = isset($_GET['course_code']) ? Security::remove_XSS($_GET['course_code']) : ''; $sessionParam = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0; if ('' !== $courseCodeParam && $sessionParam > 0) { @@ -174,6 +183,15 @@ if (false === $subscriptionColumnEnabled) { break; } + $canManageSubscriptions = api_is_platform_admin(true, true) + || api_is_session_admin() + || api_is_allowed_to_edit(null, true) + || api_is_course_admin() + || api_is_teacher() + || api_is_coach(); + if (!$canManageSubscriptions) { + api_not_allowed(true); + } $courseCodeParam = isset($_GET['course_code']) ? Security::remove_XSS($_GET['course_code']) : ''; $sessionParam = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0; if ('' !== $courseCodeParam && $sessionParam > 0) { @@ -193,6 +211,56 @@ ]); header('Location: '.$redirectUrl); exit; + case 'bulk_session_course_subscription': + // Bulk subscribe/unsubscribe selected courses within a session + if (false === $subscriptionColumnEnabled) { + break; + } + if (!Security::check_token('post')) { + api_not_allowed(true); + } + $canManageSubscriptions = api_is_platform_admin(true, true) + || api_is_session_admin() + || api_is_allowed_to_edit(null, true) + || api_is_course_admin() + || api_is_teacher() + || api_is_coach(); + if (!$canManageSubscriptions) { + api_not_allowed(true); + } + $sessionParam = isset($_POST['id_session']) ? (int) $_POST['id_session'] : 0; + $courseCodes = isset($_POST['course_codes']) && is_array($_POST['course_codes']) ? $_POST['course_codes'] : []; + $bulkAction = isset($_POST['bulk_action']) ? Security::remove_XSS($_POST['bulk_action']) : ''; + if ($sessionParam > 0 && !empty($courseCodes) && in_array($bulkAction, ['subscribe', 'unsubscribe'], true)) { + if ($bulkAction === 'subscribe') { + foreach ($courseCodes as $cc) { + $cc = Security::remove_XSS($cc); + if (!empty($cc)) { + SessionManager::subscribe_users_to_session_course([$student_id], $sessionParam, $cc); + } + } + } else { // unsubscribe + foreach ($courseCodes as $cc) { + $cc = Security::remove_XSS($cc); + if (!empty($cc)) { + $ci = api_get_course_info($cc); + SessionManager::removeUsersFromCourseSession([$student_id], $sessionParam, $ci); + } + } + } + Display::addFlash(Display::return_message(get_lang('Updated'))); + Security::clear_token(); + } else { + Display::addFlash(Display::return_message(get_lang('NoItemSelected'), 'warning')); + } + $redirectUrl = api_get_self().'?' . http_build_query([ + 'student' => $student_id, + 'origin' => $origin, + 'details' => $details, + 'id_session' => $sessionParam, + ]); + header('Location: '.$redirectUrl); + exit; case 'export_one_session_row': $sessionToExport = isset($_GET['session_to_export']) ? (int) $_GET['session_to_export'] : 0; $exportList = Session::read('export_course_list'); @@ -705,39 +773,47 @@ } $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION); +$sessionCourseTable = Database::get_main_table(TABLE_MAIN_SESSION_COURSE); +$courseTable = Database::get_main_table(TABLE_MAIN_COURSE); -$sessionPositionOrder = ''; +// Determine order like before for sessions $allowOrder = api_get_configuration_value('session_list_order'); -if ($allowOrder) { - $sessionPositionOrder = 's.position ASC, '; -} +$orderCondition = $allowOrder ? ' ORDER BY s.position ASC, s.display_end_date DESC' : ' ORDER BY s.display_end_date DESC'; + +// Use core helper to fetch sessions followed by user with the expected order +$sessionsForStudent = SessionManager::getSessionsFollowedByUser( + $student_id, + null, + null, + null, + false, + false, + false, + $orderCondition +); -// Get the list of sessions where the user is subscribed as student -$sql = 'SELECT DISTINCT sc.session_id, sc.c_id - FROM '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE).' sc - INNER JOIN '.$sessionTable.' as s - ON (s.id = sc.session_id) - INNER JOIN '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).' as scu - ON (scu.session_id = sc.session_id) - WHERE s.id = scu.session_id - AND user_id = '.$student_id.' - ORDER BY '.$sessionPositionOrder.'display_end_date DESC, sc.position ASC - '; -$rs = Database::query($sql); $tmp_sessions = []; -while ($row = Database::fetch_array($rs, 'ASSOC')) { - $tmp_sessions[] = $row['session_id']; - if ($drh_can_access_all_courses) { - if (in_array($row['session_id'], $tmp_sessions)) { - $courses_in_session[$row['session_id']][] = $row['c_id']; - } - } else { - if (isset($courses_in_session_by_coach[$row['session_id']])) { - if (in_array($row['session_id'], $tmp_sessions)) { - $courses_in_session[$row['session_id']][] = $row['c_id']; - } +foreach ($sessionsForStudent as $sessionItem) { + $sid = (int) $sessionItem['id']; + $tmp_sessions[] = $sid; + + // Fetch all courses of the session ordered by sc.position ASC + $sqlCourses = 'SELECT sc.c_id, sc.position, c.code + FROM '.$sessionCourseTable.' sc + INNER JOIN '.$courseTable.' c ON (c.id = sc.c_id) + WHERE sc.session_id = '.$sid.' + ORDER BY sc.position ASC'; + $rsCourses = Database::query($sqlCourses); + $cidList = []; + while ($rowC = Database::fetch_array($rsCourses, 'ASSOC')) { + $code = $rowC['code']; + // Respect coach restrictions when applicable + if ($drh_can_access_all_courses || !isset($courses_in_session_by_coach[$sid]) || isset($courses_in_session_by_coach[$sid][$code])) { + $cidList[] = (int) $rowC['c_id']; } } + // Always define the session key, even if empty, to render + $courses_in_session[$sid] = $cidList; } $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(api_get_user_id(), $courseInfo); @@ -1517,26 +1593,57 @@ .' '.$session_name.($date_session ? ' ('.$date_session.')' : ''); } + // Permission for subscription actions (admin, session admin, teacher) + $canManageSubscriptions = api_is_platform_admin(true, true) + || api_is_session_admin() + || api_is_allowed_to_edit(null, true) + || api_is_course_admin() + || api_is_teacher() + || api_is_coach(); + // Courses echo '

'.$title.'

'; + + // Determine columns count dynamically (including optional columns) + $hasBulk = ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions); + $columnsCount = 0; + // Checkbox bulk column + if ($hasBulk) { $columnsCount += 1; } + // Base columns + $columnsCount += 4; // Course, Time, Progress, Score + // Theoretical time + if ($theoreticalTimeEnabled) { $columnsCount += 1; } + // Subscription icon column (single action) + if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { $columnsCount += 1; } + // Attendances, Evaluations, Details + $columnsCount += 3; + + echo '
'; echo '
'; echo ''; echo ''; - echo ' - - - - '; - if($theoreticalTimeEnabled) { + echo ''; + if ($hasBulk) { + // Select all for this session's courses + $selectAllId = 'bulk-select-all-s'.$sId; + echo ''; + } + echo ''; + echo ''; + echo ''; + echo ''; + if ($theoreticalTimeEnabled) { echo ''; } - if ($subscriptionColumnEnabled && !empty($sId)) { + if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { echo ''; } - echo ' - - - '; + echo ''; + echo ''; + echo ''; + echo ''; echo ''; echo ''; @@ -1609,7 +1716,7 @@ $score = '0%'; $subscriptionIcon = ''; $subscriptionCsv = ''; - if ($subscriptionColumnEnabled && !empty($sId)) { + if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { $subscribeUrl = api_get_self().'?' . http_build_query([ 'action' => 'subscribe_course', 'id_session' => $sId, @@ -1647,7 +1754,7 @@ } if ($isSubscribed) { - if ($subscriptionColumnEnabled && !empty($sId)) { + if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { $subscriptionIcon = Display::url( Display::return_icon('delete.png', get_lang('Registered')), $unsubscribeUrl @@ -1773,8 +1880,15 @@ $csv_content[] = $csvRow; $exportCourseList[$sId][] = $csvRow; - $rowClass = $isSubscribed ? '' : ' class="course-unsubscribed"'; - echo '', + $rowClass = (!$isSubscribed && $subscriptionColumnEnabled) ? ' class="course-unsubscribed"' : ''; + echo ''; + if ($hasBulk) { + echo ''; + } + echo ''; } - if ($subscriptionColumnEnabled && !empty($sId)) { + if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { echo ''; } echo '', @@ -1815,8 +1929,11 @@ ); $totalEvaluations = $scoreDisplay->display_score($gradeBookTotal); $totalTimeFormatted = api_time_to_hms($totalCourseTime); - echo ' - + echo ''; + if ($hasBulk) { + echo ''; + } + echo ''; @@ -1968,12 +2085,48 @@ } echo $sessionAction; } else { - echo ""; + echo ""; } Session::write('export_course_list', $exportCourseList); echo ''; echo '
'.get_lang('Course').''.get_lang('Time').''.get_lang('Progress').' '.Display::return_icon('info3.gif', get_lang('progressBasedOnVisiblesLPsInEachCourse'), [], ICON_SIZE_TINY).' '.get_lang('Score').'
' + .'' + .''.get_lang('Course').''.get_lang('Time').''.get_lang('Progress').' '.Display::return_icon('info3.gif', get_lang('progressBasedOnVisiblesLPsInEachCourse'), [], ICON_SIZE_TINY).' '.get_lang('Score').''.get_lang('TheoreticalTime').''.get_lang('Subscription').''.get_lang('AttendancesFaults').''.get_lang('Evaluations').''.get_lang('Details').'
'.get_lang('AttendancesFaults').''.get_lang('Evaluations').''.get_lang('Details').'
' + .'' + .'', ''. $courseInfoItem['title']. @@ -1786,7 +1900,7 @@ if($theoreticalTimeEnabled) { echo ''.$theoreticalTimeDisplay.''.$subscriptionIcon.''.$attendances_faults_avg.'
'.get_lang('Total').'
'.get_lang('Total').' '.$totalTimeFormatted.' '.$totalProgressFormatted.' '.$totalScoreFormatted.'
".get_lang('NoCourse')."
".get_lang('NoCourse')."
'; echo '
'; + if ($hasBulk) { + // Bulk action controls per session + echo '
' + .'
' + .'
' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'
' + .'
' + .'
'; + + // Small JS to toggle all checkboxes per session + echo ""; + } + echo '
'; } } else { $columnHeaders = [