Skip to content

Commit 899aaa1

Browse files
committed
improve option Show subscription column in session course list on main/mySpace/myStudents.php
Date: Tue Oct 28 21:18:11 2025 +0100 On branch improve-subscription-option Changes to be committed: modified: main/mySpace/myStudents.php
1 parent 857d594 commit 899aaa1

File tree

1 file changed

+202
-49
lines changed

1 file changed

+202
-49
lines changed

main/mySpace/myStudents.php

Lines changed: 202 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@
2323
</style>';
2424

2525
$export = isset($_GET['export']) ? $_GET['export'] : false;
26-
$sessionId = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
27-
$action = isset($_GET['action']) ? $_GET['action'] : '';
26+
$sessionId = isset($_GET['id_session']) ? (int) $_GET['id_session'] : (isset($_POST['id_session']) ? (int) $_POST['id_session'] : 0);
27+
$action = isset($_GET['action']) ? $_GET['action'] : (isset($_POST['action']) ? $_POST['action'] : '');
2828
$origin = api_get_origin();
2929
$course_code = isset($_GET['course']) ? Security::remove_XSS($_GET['course']) : '';
3030
$courseInfo = api_get_course_info($course_code);
3131
$courseCode = '';
3232
if ($courseInfo) {
3333
$courseCode = $courseInfo['code'];
3434
}
35-
$student_id = isset($_GET['student']) ? (int) $_GET['student'] : 0;
35+
$student_id = isset($_GET['student']) ? (int) $_GET['student'] : (isset($_POST['student']) ? (int) $_POST['student'] : 0);
3636
$coachId = isset($_GET['id_coach']) ? (int) $_GET['id_coach'] : 0;
3737
$details = isset($_GET['details']) ? Security::remove_XSS($_GET['details']) : '';
3838
$currentUrl = api_get_self().'?student='.$student_id.'&course='.$courseCode.'&id_session='.$sessionId
@@ -152,6 +152,15 @@
152152
if (false === $subscriptionColumnEnabled) {
153153
break;
154154
}
155+
$canManageSubscriptions = api_is_platform_admin(true, true)
156+
|| api_is_session_admin()
157+
|| api_is_allowed_to_edit(null, true)
158+
|| api_is_course_admin()
159+
|| api_is_teacher()
160+
|| api_is_coach();
161+
if (!$canManageSubscriptions) {
162+
api_not_allowed(true);
163+
}
155164
$courseCodeParam = isset($_GET['course_code']) ? Security::remove_XSS($_GET['course_code']) : '';
156165
$sessionParam = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
157166
if ('' !== $courseCodeParam && $sessionParam > 0) {
@@ -174,6 +183,15 @@
174183
if (false === $subscriptionColumnEnabled) {
175184
break;
176185
}
186+
$canManageSubscriptions = api_is_platform_admin(true, true)
187+
|| api_is_session_admin()
188+
|| api_is_allowed_to_edit(null, true)
189+
|| api_is_course_admin()
190+
|| api_is_teacher()
191+
|| api_is_coach();
192+
if (!$canManageSubscriptions) {
193+
api_not_allowed(true);
194+
}
177195
$courseCodeParam = isset($_GET['course_code']) ? Security::remove_XSS($_GET['course_code']) : '';
178196
$sessionParam = isset($_GET['id_session']) ? (int) $_GET['id_session'] : 0;
179197
if ('' !== $courseCodeParam && $sessionParam > 0) {
@@ -193,6 +211,56 @@
193211
]);
194212
header('Location: '.$redirectUrl);
195213
exit;
214+
case 'bulk_session_course_subscription':
215+
// Bulk subscribe/unsubscribe selected courses within a session
216+
if (false === $subscriptionColumnEnabled) {
217+
break;
218+
}
219+
if (!Security::check_token('post')) {
220+
api_not_allowed(true);
221+
}
222+
$canManageSubscriptions = api_is_platform_admin(true, true)
223+
|| api_is_session_admin()
224+
|| api_is_allowed_to_edit(null, true)
225+
|| api_is_course_admin()
226+
|| api_is_teacher()
227+
|| api_is_coach();
228+
if (!$canManageSubscriptions) {
229+
api_not_allowed(true);
230+
}
231+
$sessionParam = isset($_POST['id_session']) ? (int) $_POST['id_session'] : 0;
232+
$courseCodes = isset($_POST['course_codes']) && is_array($_POST['course_codes']) ? $_POST['course_codes'] : [];
233+
$bulkAction = isset($_POST['bulk_action']) ? Security::remove_XSS($_POST['bulk_action']) : '';
234+
if ($sessionParam > 0 && !empty($courseCodes) && in_array($bulkAction, ['subscribe', 'unsubscribe'], true)) {
235+
if ($bulkAction === 'subscribe') {
236+
foreach ($courseCodes as $cc) {
237+
$cc = Security::remove_XSS($cc);
238+
if (!empty($cc)) {
239+
SessionManager::subscribe_users_to_session_course([$student_id], $sessionParam, $cc);
240+
}
241+
}
242+
} else { // unsubscribe
243+
foreach ($courseCodes as $cc) {
244+
$cc = Security::remove_XSS($cc);
245+
if (!empty($cc)) {
246+
$ci = api_get_course_info($cc);
247+
SessionManager::removeUsersFromCourseSession([$student_id], $sessionParam, $ci);
248+
}
249+
}
250+
}
251+
Display::addFlash(Display::return_message(get_lang('Updated')));
252+
Security::clear_token();
253+
} else {
254+
Display::addFlash(Display::return_message(get_lang('NoItemSelected'), 'warning'));
255+
}
256+
$redirectUrl = api_get_self().'?' . http_build_query([
257+
'student' => $student_id,
258+
'origin' => $origin,
259+
'details' => $details,
260+
'id_session' => $sessionParam,
261+
]);
262+
header('Location: '.$redirectUrl);
263+
exit;
196264
case 'export_one_session_row':
197265
$sessionToExport = isset($_GET['session_to_export']) ? (int) $_GET['session_to_export'] : 0;
198266
$exportList = Session::read('export_course_list');
@@ -705,39 +773,47 @@
705773
}
706774

707775
$sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
776+
$sessionCourseTable = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
777+
$courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
708778

709-
$sessionPositionOrder = '';
779+
// Determine order like before for sessions
710780
$allowOrder = api_get_configuration_value('session_list_order');
711-
if ($allowOrder) {
712-
$sessionPositionOrder = 's.position ASC, ';
713-
}
781+
$orderCondition = $allowOrder ? ' ORDER BY s.position ASC, s.display_end_date DESC' : ' ORDER BY s.display_end_date DESC';
782+
783+
// Use core helper to fetch sessions followed by user with the expected order
784+
$sessionsForStudent = SessionManager::getSessionsFollowedByUser(
785+
$student_id,
786+
null,
787+
null,
788+
null,
789+
false,
790+
false,
791+
false,
792+
$orderCondition
793+
);
714794

715-
// Get the list of sessions where the user is subscribed as student
716-
$sql = 'SELECT DISTINCT sc.session_id, sc.c_id
717-
FROM '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE).' sc
718-
INNER JOIN '.$sessionTable.' as s
719-
ON (s.id = sc.session_id)
720-
INNER JOIN '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).' as scu
721-
ON (scu.session_id = sc.session_id)
722-
WHERE s.id = scu.session_id
723-
AND user_id = '.$student_id.'
724-
ORDER BY '.$sessionPositionOrder.'display_end_date DESC, sc.position ASC
725-
';
726-
$rs = Database::query($sql);
727795
$tmp_sessions = [];
728-
while ($row = Database::fetch_array($rs, 'ASSOC')) {
729-
$tmp_sessions[] = $row['session_id'];
730-
if ($drh_can_access_all_courses) {
731-
if (in_array($row['session_id'], $tmp_sessions)) {
732-
$courses_in_session[$row['session_id']][] = $row['c_id'];
733-
}
734-
} else {
735-
if (isset($courses_in_session_by_coach[$row['session_id']])) {
736-
if (in_array($row['session_id'], $tmp_sessions)) {
737-
$courses_in_session[$row['session_id']][] = $row['c_id'];
738-
}
796+
foreach ($sessionsForStudent as $sessionItem) {
797+
$sid = (int) $sessionItem['id'];
798+
$tmp_sessions[] = $sid;
799+
800+
// Fetch all courses of the session ordered by sc.position ASC
801+
$sqlCourses = 'SELECT sc.c_id, sc.position, c.code
802+
FROM '.$sessionCourseTable.' sc
803+
INNER JOIN '.$courseTable.' c ON (c.id = sc.c_id)
804+
WHERE sc.session_id = '.$sid.'
805+
ORDER BY sc.position ASC';
806+
$rsCourses = Database::query($sqlCourses);
807+
$cidList = [];
808+
while ($rowC = Database::fetch_array($rsCourses, 'ASSOC')) {
809+
$code = $rowC['code'];
810+
// Respect coach restrictions when applicable
811+
if ($drh_can_access_all_courses || !isset($courses_in_session_by_coach[$sid]) || isset($courses_in_session_by_coach[$sid][$code])) {
812+
$cidList[] = (int) $rowC['c_id'];
739813
}
740814
}
815+
// Always define the session key, even if empty, to render
816+
$courses_in_session[$sid] = $cidList;
741817
}
742818

743819
$isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(api_get_user_id(), $courseInfo);
@@ -1517,26 +1593,57 @@
15171593
.' '.$session_name.($date_session ? ' ('.$date_session.')' : '');
15181594
}
15191595

1596+
// Permission for subscription actions (admin, session admin, teacher)
1597+
$canManageSubscriptions = api_is_platform_admin(true, true)
1598+
|| api_is_session_admin()
1599+
|| api_is_allowed_to_edit(null, true)
1600+
|| api_is_course_admin()
1601+
|| api_is_teacher()
1602+
|| api_is_coach();
1603+
15201604
// Courses
15211605
echo '<h3>'.$title.'</h3>';
1606+
1607+
// Determine columns count dynamically (including optional columns)
1608+
$hasBulk = ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions);
1609+
$columnsCount = 0;
1610+
// Checkbox bulk column
1611+
if ($hasBulk) { $columnsCount += 1; }
1612+
// Base columns
1613+
$columnsCount += 4; // Course, Time, Progress, Score
1614+
// Theoretical time
1615+
if ($theoreticalTimeEnabled) { $columnsCount += 1; }
1616+
// Subscription icon column (single action)
1617+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { $columnsCount += 1; }
1618+
// Attendances, Evaluations, Details
1619+
$columnsCount += 3;
1620+
1621+
echo '<form method="post" action="'.api_get_self().'">';
15221622
echo '<div class="table-responsive">';
15231623
echo '<table class="table table-striped table-hover courses-tracking">';
15241624
echo '<thead>';
1525-
echo '<tr>
1526-
<th>'.get_lang('Course').'</th>
1527-
<th>'.get_lang('Time').'</th>
1528-
<th>'.get_lang('Progress').' '.Display::return_icon('info3.gif', get_lang('progressBasedOnVisiblesLPsInEachCourse'), [], ICON_SIZE_TINY).' </th>
1529-
<th>'.get_lang('Score').'</th>';
1530-
if($theoreticalTimeEnabled) {
1625+
echo '<tr>';
1626+
if ($hasBulk) {
1627+
// Select all for this session's courses
1628+
$selectAllId = 'bulk-select-all-s'.$sId;
1629+
echo '<th style="width:20px;">'
1630+
.'<input type="checkbox" id="'.$selectAllId.'" class="bulk-select-all" data-target="s'.$sId.'" />'
1631+
.'</th>';
1632+
}
1633+
echo '<th>'.get_lang('Course').'</th>';
1634+
echo '<th>'.get_lang('Time').'</th>';
1635+
echo '<th>'.get_lang('Progress').' '.Display::return_icon('info3.gif', get_lang('progressBasedOnVisiblesLPsInEachCourse'), [], ICON_SIZE_TINY).' </th>';
1636+
echo '<th>'.get_lang('Score').'</th>';
1637+
if ($theoreticalTimeEnabled) {
15311638
echo '<th>'.get_lang('TheoreticalTime').'</th>';
15321639
}
1533-
if ($subscriptionColumnEnabled && !empty($sId)) {
1640+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
15341641
echo '<th>'.get_lang('Subscription').'</th>';
15351642
}
1536-
echo '<th>'.get_lang('AttendancesFaults').'</th>
1537-
<th>'.get_lang('Evaluations').'</th>
1538-
<th>'.get_lang('Details').'</th>
1539-
</tr>';
1643+
echo '<th>'.get_lang('AttendancesFaults').'</th>';
1644+
echo '<th>'.get_lang('Evaluations').'</th>';
1645+
echo '<th>'.get_lang('Details').'</th>';
1646+
echo '</tr>';
15401647
echo '</thead>';
15411648
echo '<tbody>';
15421649

@@ -1609,7 +1716,7 @@
16091716
$score = '0%';
16101717
$subscriptionIcon = '';
16111718
$subscriptionCsv = '';
1612-
if ($subscriptionColumnEnabled && !empty($sId)) {
1719+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
16131720
$subscribeUrl = api_get_self().'?' . http_build_query([
16141721
'action' => 'subscribe_course',
16151722
'id_session' => $sId,
@@ -1647,7 +1754,7 @@
16471754
}
16481755

16491756
if ($isSubscribed) {
1650-
if ($subscriptionColumnEnabled && !empty($sId)) {
1757+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
16511758
$subscriptionIcon = Display::url(
16521759
Display::return_icon('delete.png', get_lang('Registered')),
16531760
$unsubscribeUrl
@@ -1773,8 +1880,15 @@
17731880

17741881
$csv_content[] = $csvRow;
17751882
$exportCourseList[$sId][] = $csvRow;
1776-
$rowClass = $isSubscribed ? '' : ' class="course-unsubscribed"';
1777-
echo '<tr'.$rowClass.'>',
1883+
$rowClass = (!$isSubscribed && $subscriptionColumnEnabled) ? ' class="course-unsubscribed"' : '';
1884+
echo '<tr'.$rowClass.'>';
1885+
if ($hasBulk) {
1886+
echo '<td>'
1887+
.'<input type="checkbox" name="course_codes[]" value="'.Security::remove_XSS($courseCodeItem).'"'
1888+
.' class="bulk-course-checkbox bulk-course-checkbox-s'.$sId.'" />'
1889+
.'</td>';
1890+
}
1891+
echo
17781892
'<td>',
17791893
'<a href="'.$courseInfoItem['course_public_url'].'?id_session='.$sId.'">'.
17801894
$courseInfoItem['title'].
@@ -1786,7 +1900,7 @@
17861900
if($theoreticalTimeEnabled) {
17871901
echo '<td>'.$theoreticalTimeDisplay.'</td>';
17881902
}
1789-
if ($subscriptionColumnEnabled && !empty($sId)) {
1903+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
17901904
echo '<td>'.$subscriptionIcon.'</td>';
17911905
}
17921906
echo '<td>'.$attendances_faults_avg.'</td>',
@@ -1815,8 +1929,11 @@
18151929
);
18161930
$totalEvaluations = $scoreDisplay->display_score($gradeBookTotal);
18171931
$totalTimeFormatted = api_time_to_hms($totalCourseTime);
1818-
echo '<tr>
1819-
<th>'.get_lang('Total').'</th>
1932+
echo '<tr>';
1933+
if ($hasBulk) {
1934+
echo '<th></th>';
1935+
}
1936+
echo '<th>'.get_lang('Total').'</th>
18201937
<th>'.$totalTimeFormatted.'</th>
18211938
<th>'.$totalProgressFormatted.'</th>
18221939
<th>'.$totalScoreFormatted.'</th>';
@@ -1968,12 +2085,48 @@
19682085
}
19692086
echo $sessionAction;
19702087
} else {
1971-
echo "<tr><td colspan='5'>".get_lang('NoCourse')."</td></tr>";
2088+
echo "<tr><td colspan='".$columnsCount."'>".get_lang('NoCourse')."</td></tr>";
19722089
}
19732090
Session::write('export_course_list', $exportCourseList);
19742091
echo '</tbody>';
19752092
echo '</table>';
19762093
echo '</div>';
2094+
if ($hasBulk) {
2095+
// Bulk action controls per session
2096+
echo '<div class="row" style="margin:10px 0;">'
2097+
.'<div class="col-sm-12">'
2098+
.'<div class="form-inline">'
2099+
.'<input type="hidden" name="action" value="bulk_session_course_subscription" />'
2100+
.'<input type="hidden" name="id_session" value="'.$sId.'" />'
2101+
.'<input type="hidden" name="student" value="'.$student_id.'" />'
2102+
.'<input type="hidden" name="origin" value="'.Security::remove_XSS($origin).'" />'
2103+
.'<input type="hidden" name="details" value="'.Security::remove_XSS($details).'" />'
2104+
.'<input type="hidden" name="sec_token" value="'.$token.'" />'
2105+
.'<label class="control-label" for="bulk-action-'.$sId.'" style="margin-right:8px;">'.get_lang('Action').'</label>'
2106+
.'<select id="bulk-action-'.$sId.'" name="bulk_action" class="form-control" style="margin-right:8px;">'
2107+
.'<option value="subscribe">'.get_lang('Subscribe').'</option>'
2108+
.'<option value="unsubscribe">'.get_lang('Unsubscribe').'</option>'
2109+
.'</select>'
2110+
.'<button type="submit" class="btn btn-primary">'.get_lang('Validate').'</button>'
2111+
.'</div>'
2112+
.'</div>'
2113+
.'</div>';
2114+
2115+
// Small JS to toggle all checkboxes per session
2116+
echo "<script>\n".
2117+
"(function(){\n".
2118+
" var el = document.getElementById('".$selectAllId."');\n".
2119+
" if (el) {\n".
2120+
" el.addEventListener('change', function(){\n".
2121+
" var target = this.getAttribute('data-target');\n".
2122+
" var boxes = document.querySelectorAll('.bulk-course-checkbox-' + target);\n".
2123+
" for (var i=0;i<boxes.length;i++){ boxes[i].checked = this.checked; }\n".
2124+
" });\n".
2125+
" }\n".
2126+
"})();\n".
2127+
"</script>";
2128+
}
2129+
echo '</form>';
19772130
}
19782131
} else {
19792132
$columnHeaders = [

0 commit comments

Comments
 (0)