Skip to content

Commit 048470f

Browse files
authored
Merge pull request #6960 from yverhenne/improve-subscription-option
improve option Show subscription column
2 parents 3c588d1 + 899aaa1 commit 048470f

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');
@@ -710,39 +778,47 @@
710778
}
711779

712780
$sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
781+
$sessionCourseTable = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
782+
$courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
713783

714-
$sessionPositionOrder = '';
784+
// Determine order like before for sessions
715785
$allowOrder = api_get_configuration_value('session_list_order');
716-
if ($allowOrder) {
717-
$sessionPositionOrder = 's.position ASC, ';
718-
}
786+
$orderCondition = $allowOrder ? ' ORDER BY s.position ASC, s.display_end_date DESC' : ' ORDER BY s.display_end_date DESC';
787+
788+
// Use core helper to fetch sessions followed by user with the expected order
789+
$sessionsForStudent = SessionManager::getSessionsFollowedByUser(
790+
$student_id,
791+
null,
792+
null,
793+
null,
794+
false,
795+
false,
796+
false,
797+
$orderCondition
798+
);
719799

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

748824
$isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(api_get_user_id(), $courseInfo);
@@ -1522,26 +1598,57 @@
15221598
.' '.$session_name.($date_session ? ' ('.$date_session.')' : '');
15231599
}
15241600

1601+
// Permission for subscription actions (admin, session admin, teacher)
1602+
$canManageSubscriptions = api_is_platform_admin(true, true)
1603+
|| api_is_session_admin()
1604+
|| api_is_allowed_to_edit(null, true)
1605+
|| api_is_course_admin()
1606+
|| api_is_teacher()
1607+
|| api_is_coach();
1608+
15251609
// Courses
15261610
echo '<h3>'.$title.'</h3>';
1611+
1612+
// Determine columns count dynamically (including optional columns)
1613+
$hasBulk = ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions);
1614+
$columnsCount = 0;
1615+
// Checkbox bulk column
1616+
if ($hasBulk) { $columnsCount += 1; }
1617+
// Base columns
1618+
$columnsCount += 4; // Course, Time, Progress, Score
1619+
// Theoretical time
1620+
if ($theoreticalTimeEnabled) { $columnsCount += 1; }
1621+
// Subscription icon column (single action)
1622+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) { $columnsCount += 1; }
1623+
// Attendances, Evaluations, Details
1624+
$columnsCount += 3;
1625+
1626+
echo '<form method="post" action="'.api_get_self().'">';
15271627
echo '<div class="table-responsive">';
15281628
echo '<table class="table table-striped table-hover courses-tracking">';
15291629
echo '<thead>';
1530-
echo '<tr>
1531-
<th>'.get_lang('Course').'</th>
1532-
<th>'.get_lang('Time').'</th>
1533-
<th>'.get_lang('Progress').' '.Display::return_icon('info3.gif', get_lang('progressBasedOnVisiblesLPsInEachCourse'), [], ICON_SIZE_TINY).' </th>
1534-
<th>'.get_lang('Score').'</th>';
1535-
if($theoreticalTimeEnabled) {
1630+
echo '<tr>';
1631+
if ($hasBulk) {
1632+
// Select all for this session's courses
1633+
$selectAllId = 'bulk-select-all-s'.$sId;
1634+
echo '<th style="width:20px;">'
1635+
.'<input type="checkbox" id="'.$selectAllId.'" class="bulk-select-all" data-target="s'.$sId.'" />'
1636+
.'</th>';
1637+
}
1638+
echo '<th>'.get_lang('Course').'</th>';
1639+
echo '<th>'.get_lang('Time').'</th>';
1640+
echo '<th>'.get_lang('Progress').' '.Display::return_icon('info3.gif', get_lang('progressBasedOnVisiblesLPsInEachCourse'), [], ICON_SIZE_TINY).' </th>';
1641+
echo '<th>'.get_lang('Score').'</th>';
1642+
if ($theoreticalTimeEnabled) {
15361643
echo '<th>'.get_lang('TheoreticalTime').'</th>';
15371644
}
1538-
if ($subscriptionColumnEnabled && !empty($sId)) {
1645+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
15391646
echo '<th>'.get_lang('Subscription').'</th>';
15401647
}
1541-
echo '<th>'.get_lang('AttendancesFaults').'</th>
1542-
<th>'.get_lang('Evaluations').'</th>
1543-
<th>'.get_lang('Details').'</th>
1544-
</tr>';
1648+
echo '<th>'.get_lang('AttendancesFaults').'</th>';
1649+
echo '<th>'.get_lang('Evaluations').'</th>';
1650+
echo '<th>'.get_lang('Details').'</th>';
1651+
echo '</tr>';
15451652
echo '</thead>';
15461653
echo '<tbody>';
15471654

@@ -1614,7 +1721,7 @@
16141721
$score = '0%';
16151722
$subscriptionIcon = '';
16161723
$subscriptionCsv = '';
1617-
if ($subscriptionColumnEnabled && !empty($sId)) {
1724+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
16181725
$subscribeUrl = api_get_self().'?' . http_build_query([
16191726
'action' => 'subscribe_course',
16201727
'id_session' => $sId,
@@ -1652,7 +1759,7 @@
16521759
}
16531760

16541761
if ($isSubscribed) {
1655-
if ($subscriptionColumnEnabled && !empty($sId)) {
1762+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
16561763
$subscriptionIcon = Display::url(
16571764
Display::return_icon('delete.png', get_lang('Registered')),
16581765
$unsubscribeUrl
@@ -1778,8 +1885,15 @@
17781885

17791886
$csv_content[] = $csvRow;
17801887
$exportCourseList[$sId][] = $csvRow;
1781-
$rowClass = $isSubscribed ? '' : ' class="course-unsubscribed"';
1782-
echo '<tr'.$rowClass.'>',
1888+
$rowClass = (!$isSubscribed && $subscriptionColumnEnabled) ? ' class="course-unsubscribed"' : '';
1889+
echo '<tr'.$rowClass.'>';
1890+
if ($hasBulk) {
1891+
echo '<td>'
1892+
.'<input type="checkbox" name="course_codes[]" value="'.Security::remove_XSS($courseCodeItem).'"'
1893+
.' class="bulk-course-checkbox bulk-course-checkbox-s'.$sId.'" />'
1894+
.'</td>';
1895+
}
1896+
echo
17831897
'<td>',
17841898
'<a href="'.$courseInfoItem['course_public_url'].'?id_session='.$sId.'">'.
17851899
$courseInfoItem['title'].
@@ -1791,7 +1905,7 @@
17911905
if($theoreticalTimeEnabled) {
17921906
echo '<td>'.$theoreticalTimeDisplay.'</td>';
17931907
}
1794-
if ($subscriptionColumnEnabled && !empty($sId)) {
1908+
if ($subscriptionColumnEnabled && !empty($sId) && $canManageSubscriptions) {
17951909
echo '<td>'.$subscriptionIcon.'</td>';
17961910
}
17971911
echo '<td>'.$attendances_faults_avg.'</td>',
@@ -1820,8 +1934,11 @@
18201934
);
18211935
$totalEvaluations = $scoreDisplay->display_score($gradeBookTotal);
18221936
$totalTimeFormatted = api_time_to_hms($totalCourseTime);
1823-
echo '<tr>
1824-
<th>'.get_lang('Total').'</th>
1937+
echo '<tr>';
1938+
if ($hasBulk) {
1939+
echo '<th></th>';
1940+
}
1941+
echo '<th>'.get_lang('Total').'</th>
18251942
<th>'.$totalTimeFormatted.'</th>
18261943
<th>'.$totalProgressFormatted.'</th>
18271944
<th>'.$totalScoreFormatted.'</th>';
@@ -1973,12 +2090,48 @@
19732090
}
19742091
echo $sessionAction;
19752092
} else {
1976-
echo "<tr><td colspan='5'>".get_lang('NoCourse')."</td></tr>";
2093+
echo "<tr><td colspan='".$columnsCount."'>".get_lang('NoCourse')."</td></tr>";
19772094
}
19782095
Session::write('export_course_list', $exportCourseList);
19792096
echo '</tbody>';
19802097
echo '</table>';
19812098
echo '</div>';
2099+
if ($hasBulk) {
2100+
// Bulk action controls per session
2101+
echo '<div class="row" style="margin:10px 0;">'
2102+
.'<div class="col-sm-12">'
2103+
.'<div class="form-inline">'
2104+
.'<input type="hidden" name="action" value="bulk_session_course_subscription" />'
2105+
.'<input type="hidden" name="id_session" value="'.$sId.'" />'
2106+
.'<input type="hidden" name="student" value="'.$student_id.'" />'
2107+
.'<input type="hidden" name="origin" value="'.Security::remove_XSS($origin).'" />'
2108+
.'<input type="hidden" name="details" value="'.Security::remove_XSS($details).'" />'
2109+
.'<input type="hidden" name="sec_token" value="'.$token.'" />'
2110+
.'<label class="control-label" for="bulk-action-'.$sId.'" style="margin-right:8px;">'.get_lang('Action').'</label>'
2111+
.'<select id="bulk-action-'.$sId.'" name="bulk_action" class="form-control" style="margin-right:8px;">'
2112+
.'<option value="subscribe">'.get_lang('Subscribe').'</option>'
2113+
.'<option value="unsubscribe">'.get_lang('Unsubscribe').'</option>'
2114+
.'</select>'
2115+
.'<button type="submit" class="btn btn-primary">'.get_lang('Validate').'</button>'
2116+
.'</div>'
2117+
.'</div>'
2118+
.'</div>';
2119+
2120+
// Small JS to toggle all checkboxes per session
2121+
echo "<script>\n".
2122+
"(function(){\n".
2123+
" var el = document.getElementById('".$selectAllId."');\n".
2124+
" if (el) {\n".
2125+
" el.addEventListener('change', function(){\n".
2126+
" var target = this.getAttribute('data-target');\n".
2127+
" var boxes = document.querySelectorAll('.bulk-course-checkbox-' + target);\n".
2128+
" for (var i=0;i<boxes.length;i++){ boxes[i].checked = this.checked; }\n".
2129+
" });\n".
2130+
" }\n".
2131+
"})();\n".
2132+
"</script>";
2133+
}
2134+
echo '</form>';
19822135
}
19832136
} else {
19842137
$columnHeaders = [

0 commit comments

Comments
 (0)