|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
|
4
|
|
|
|
|
5
|
|
|
/** |
|
6
|
|
|
* This script allows platform admins to add users to courses. |
|
7
|
|
|
* It displays a list of users and a list of courses; |
|
8
|
|
|
* you can select multiple users and courses and then click on |
|
9
|
|
|
* 'Add to this(these) course(s)'. |
|
10
|
|
|
* |
|
11
|
|
|
* @todo use formvalidator for the form |
|
12
|
|
|
*/ |
|
13
|
|
|
|
|
14
|
|
|
use Chamilo\CoreBundle\Enums\ObjectIcon; |
|
15
|
|
|
|
|
16
|
|
|
$cidReset = true; |
|
17
|
|
|
require_once __DIR__.'/../inc/global.inc.php'; |
|
18
|
|
|
$this_section = SECTION_PLATFORM_ADMIN; |
|
19
|
|
|
|
|
20
|
|
|
api_protect_admin_script(); |
|
21
|
|
|
|
|
22
|
|
|
$form_sent = 0; |
|
23
|
|
|
$first_letter_user = ''; |
|
24
|
|
|
$first_letter_course = ''; |
|
25
|
|
|
$courses = []; |
|
26
|
|
|
$users = []; |
|
27
|
|
|
|
|
28
|
|
|
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE); |
|
29
|
|
|
$tbl_user = Database::get_main_table(TABLE_MAIN_USER); |
|
30
|
|
|
|
|
31
|
|
|
/* Header */ |
|
32
|
|
|
$tool_name = get_lang('Add users to course'); |
|
33
|
|
|
$interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('Administration')]; |
|
34
|
|
|
|
|
35
|
|
|
$htmlHeadXtra[] = '<script> |
|
36
|
|
|
function validate_filter() { |
|
37
|
|
|
document.formulaire.form_sent.value=0; |
|
38
|
|
|
document.formulaire.submit(); |
|
39
|
|
|
} |
|
40
|
|
|
</script>'; |
|
41
|
|
|
|
|
42
|
|
|
// displaying the header |
|
43
|
|
|
Display::display_header($tool_name); |
|
44
|
|
|
|
|
45
|
|
|
$link_add_group = '<a href="usergroups.php">'. |
|
46
|
|
|
Display::getMdiIcon(ObjectIcon::MULTI_ELEMENT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Enrolment by classes')).get_lang('Enrolment by classes').'</a>'; |
|
47
|
|
|
echo Display::toolbarAction('subscribe', [$link_add_group]); |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* We show this once at the top so admins are aware before selecting anything. |
|
51
|
|
|
*/ |
|
52
|
|
|
$__globalLimit = (int) api_get_setting('platform.hosting_limit_users_per_course'); // 0 => disabled |
|
53
|
|
|
if ($__globalLimit > 0) { |
|
54
|
|
|
echo Display::return_message( |
|
55
|
|
|
sprintf('A global limit of %d users applies to every course (teachers included).', $__globalLimit), |
|
56
|
|
|
'warning' |
|
57
|
|
|
); |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
$form = new FormValidator('subscribe_user2course'); |
|
61
|
|
|
$form->addElement('header', '', $tool_name); |
|
62
|
|
|
$form->display(); |
|
63
|
|
|
|
|
64
|
|
|
//checking for extra field with filter on |
|
65
|
|
|
$extra_field_list = UserManager::get_extra_fields(); |
|
66
|
|
|
|
|
67
|
|
|
$new_field_list = []; |
|
68
|
|
|
if (is_array($extra_field_list)) { |
|
69
|
|
|
foreach ($extra_field_list as $extra_field) { |
|
70
|
|
|
// if is enabled to filter and is a "<select>" or "tag" type |
|
71
|
|
|
if (1 == $extra_field[8] && ExtraField::FIELD_TYPE_SELECT == $extra_field[2]) { |
|
72
|
|
|
$new_field_list[] = [ |
|
73
|
|
|
'name' => $extra_field[3], |
|
74
|
|
|
'type' => $extra_field[2], |
|
75
|
|
|
'variable' => $extra_field[1], |
|
76
|
|
|
'data' => $extra_field[9], |
|
77
|
|
|
]; |
|
78
|
|
|
} |
|
79
|
|
|
if (1 == $extra_field[8] && ExtraField::FIELD_TYPE_TAG == $extra_field[2]) { |
|
80
|
|
|
$options = UserManager::get_extra_user_data_for_tags($extra_field[1]); |
|
81
|
|
|
|
|
82
|
|
|
$new_field_list[] = [ |
|
83
|
|
|
'name' => $extra_field[3], |
|
84
|
|
|
'type' => $extra_field[2], |
|
85
|
|
|
'variable' => $extra_field[1], |
|
86
|
|
|
'data' => $options['options'], |
|
87
|
|
|
]; |
|
88
|
|
|
} |
|
89
|
|
|
} |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
/* React on POSTed request */ |
|
93
|
|
|
if (isset($_POST['form_sent']) && $_POST['form_sent']) { |
|
94
|
|
|
$form_sent = (int) $_POST['form_sent']; |
|
95
|
|
|
$users = isset($_POST['UserList']) && is_array($_POST['UserList']) ? $_POST['UserList'] : []; |
|
96
|
|
|
$courses = isset($_POST['CourseList']) && is_array($_POST['CourseList']) ? $_POST['CourseList'] : []; |
|
97
|
|
|
$first_letter_user = Database::escape_string($_POST['firstLetterUser']); |
|
98
|
|
|
$first_letter_course = Database::escape_string($_POST['firstLetterCourse']); |
|
99
|
|
|
|
|
100
|
|
|
foreach ($users as $key => $value) { |
|
101
|
|
|
$users[$key] = (int) $value; |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
if (1 === $form_sent) { |
|
105
|
|
|
if (0 === count($users) || 0 === count($courses)) { |
|
106
|
|
|
echo Display::return_message(get_lang('You must select at least one user and one course'), 'error'); |
|
107
|
|
|
} else { |
|
108
|
|
|
$errorDrh = 0; |
|
109
|
|
|
$successCount = 0; |
|
110
|
|
|
$skippedFull = 0; |
|
111
|
|
|
|
|
112
|
|
|
foreach ($courses as $course_code) { |
|
113
|
|
|
$courseInfo = api_get_course_info($course_code); |
|
114
|
|
|
if (empty($courseInfo)) { |
|
115
|
|
|
// Defensive log |
|
116
|
|
|
Display::addFlash(Display::return_message('Course not found: '.$course_code, 'warning')); |
|
117
|
|
|
continue; |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
// Enforce global limit here as well, to avoid needless subscribe calls |
|
121
|
|
|
if ($__globalLimit > 0) { |
|
122
|
|
|
$limitState = _compute_course_limit_state_by_real_id($courseInfo['real_id'], $__globalLimit); |
|
123
|
|
|
if ($limitState['full']) { |
|
124
|
|
|
// Avoid looping users for a known-full course, provide a single message and skip |
|
125
|
|
|
Display::addFlash(Display::return_message( |
|
126
|
|
|
sprintf('Course "%s" is full (%d/%d). Skipping subscriptions for this course.', |
|
127
|
|
|
$courseInfo['title'], $limitState['current'], $limitState['limit'] |
|
128
|
|
|
), |
|
129
|
|
|
'warning' |
|
130
|
|
|
)); |
|
131
|
|
|
$skippedFull++; |
|
132
|
|
|
continue; |
|
133
|
|
|
} |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
foreach ($users as $user_id) { |
|
137
|
|
|
$user = api_get_user_info($user_id); |
|
138
|
|
|
if (DRH != $user['status']) { |
|
139
|
|
|
$result = CourseManager::subscribeUser($user_id, $courseInfo['real_id']); |
|
140
|
|
|
|
|
141
|
|
|
if (is_array($result)) { |
|
142
|
|
|
// Expected keys: ok(bool), message(string) |
|
143
|
|
|
if (isset($result['message'])) { |
|
144
|
|
|
Display::addFlash( |
|
145
|
|
|
Display::return_message($result['message'], !empty($result['ok']) ? 'normal' : 'warning') |
|
146
|
|
|
); |
|
147
|
|
|
} else { |
|
148
|
|
|
// assume ok by presence of array |
|
149
|
|
|
$successCount++; |
|
150
|
|
|
} |
|
151
|
|
|
} else { |
|
152
|
|
|
if ($result === true) { |
|
153
|
|
|
$successCount++; |
|
154
|
|
|
} |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
} else { |
|
158
|
|
|
$errorDrh = 1; |
|
159
|
|
|
} |
|
160
|
|
|
} |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
// Summaries |
|
164
|
|
|
if ($successCount > 0) { |
|
165
|
|
|
echo Display::return_message( |
|
166
|
|
|
sprintf(get_lang('The selected users are subscribed to the selected course').' (%d %s)', $successCount, get_lang('operations')), |
|
167
|
|
|
'confirm' |
|
168
|
|
|
); |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
if ($skippedFull > 0) { |
|
172
|
|
|
echo Display::return_message( |
|
173
|
|
|
sprintf('%d course(s) skipped because they are full.', $skippedFull), |
|
174
|
|
|
'warning' |
|
175
|
|
|
); |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
if (1 === $errorDrh) { |
|
179
|
|
|
echo Display::return_message( |
|
180
|
|
|
get_lang( |
|
181
|
|
|
'Human resources managers should not be registered to courses. The corresponding users you selected have not been subscribed.' |
|
182
|
|
|
), |
|
183
|
|
|
'error' |
|
184
|
|
|
); |
|
185
|
|
|
} |
|
186
|
|
|
} |
|
187
|
|
|
} |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
/* Display GUI */ |
|
191
|
|
|
if (empty($first_letter_user)) { |
|
192
|
|
|
$sql = "SELECT count(*) as nb_users FROM $tbl_user"; |
|
193
|
|
|
$result = Database::query($sql); |
|
194
|
|
|
$num_row = Database::fetch_array($result); |
|
195
|
|
|
if ($num_row['nb_users'] > 1000) { |
|
196
|
|
|
// If there are too many users, default filter to "A" to keep lists light |
|
197
|
|
|
$first_letter_user = 'A'; |
|
198
|
|
|
} |
|
199
|
|
|
unset($result); |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
$where_filter = null; |
|
203
|
|
|
$extra_field_result = []; |
|
204
|
|
|
//Filter by Extra Fields |
|
205
|
|
|
$use_extra_fields = false; |
|
206
|
|
|
if (is_array($extra_field_list)) { |
|
207
|
|
|
if (is_array($new_field_list) && count($new_field_list) > 0) { |
|
208
|
|
|
$result_list = []; |
|
209
|
|
|
foreach ($new_field_list as $new_field) { |
|
210
|
|
|
$varname = 'field_'.$new_field['variable']; |
|
211
|
|
|
$fieldtype = $new_field['type']; |
|
212
|
|
|
if (UserManager::is_extra_field_available($new_field['variable'])) { |
|
213
|
|
|
if (isset($_POST[$varname]) && '0' != $_POST[$varname]) { |
|
214
|
|
|
$use_extra_fields = true; |
|
215
|
|
|
if (ExtraField::FIELD_TYPE_TAG == $fieldtype) { |
|
216
|
|
|
$extra_field_result[] = UserManager::get_extra_user_data_by_tags( |
|
217
|
|
|
(int) $_POST['field_id'], |
|
218
|
|
|
$_POST[$varname] |
|
219
|
|
|
); |
|
220
|
|
|
} else { |
|
221
|
|
|
$extra_field_result[] = UserManager::get_extra_user_data_by_value( |
|
222
|
|
|
$new_field['variable'], |
|
223
|
|
|
$_POST[$varname] |
|
224
|
|
|
); |
|
225
|
|
|
} |
|
226
|
|
|
} |
|
227
|
|
|
} |
|
228
|
|
|
} |
|
229
|
|
|
} |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
if ($use_extra_fields) { |
|
233
|
|
|
$final_result = []; |
|
234
|
|
|
if (count($extra_field_result) > 1) { |
|
235
|
|
|
for ($i = 0; $i < count($extra_field_result) - 1; $i++) { |
|
236
|
|
|
if (is_array($extra_field_result[$i + 1])) { |
|
237
|
|
|
$final_result = array_intersect($extra_field_result[$i], $extra_field_result[$i + 1]); |
|
238
|
|
|
} |
|
239
|
|
|
} |
|
240
|
|
|
} else { |
|
241
|
|
|
$final_result = $extra_field_result[0]; |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
if (api_is_multiple_url_enabled()) { |
|
|
|
|
|
|
245
|
|
|
if (is_array($final_result) && count($final_result) > 0) { |
|
246
|
|
|
$where_filter = " AND u.id IN ('".implode("','", $final_result)."') "; |
|
247
|
|
|
} else { |
|
248
|
|
|
$where_filter = " AND u.id = -1"; |
|
249
|
|
|
} |
|
250
|
|
|
} else { |
|
251
|
|
|
if (is_array($final_result) && count($final_result) > 0) { |
|
252
|
|
|
$where_filter = " AND id IN ('".implode("','", $final_result)."') "; |
|
253
|
|
|
} else { |
|
254
|
|
|
$where_filter = " AND id = -1"; |
|
255
|
|
|
} |
|
256
|
|
|
} |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
$target_name = 'lastname'; |
|
260
|
|
|
$orderBy = $target_name; |
|
261
|
|
|
$showOfficialCode = false; |
|
262
|
|
|
$orderListByOfficialCode = api_get_setting('display.order_user_list_by_official_code'); |
|
263
|
|
|
if ('true' === $orderListByOfficialCode) { |
|
264
|
|
|
$showOfficialCode = true; |
|
265
|
|
|
$orderBy = " official_code, lastname, firstname"; |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
$sql = "SELECT id as user_id, lastname, firstname, username, official_code |
|
269
|
|
|
FROM $tbl_user |
|
270
|
|
|
WHERE id <>2 AND ".$target_name." LIKE '".$first_letter_user."%' $where_filter |
|
271
|
|
|
ORDER BY ".(count($users) > 0 ? "(id IN(".implode(',', $users).")) DESC," : "")." ".$orderBy; |
|
272
|
|
|
|
|
273
|
|
|
if (api_is_multiple_url_enabled()) { |
|
|
|
|
|
|
274
|
|
|
$tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); |
|
275
|
|
|
$access_url_id = api_get_current_access_url_id(); |
|
276
|
|
|
if (-1 != $access_url_id) { |
|
277
|
|
|
$sql = "SELECT u.id as user_id,lastname,firstname,username, official_code |
|
278
|
|
|
FROM $tbl_user u |
|
279
|
|
|
INNER JOIN $tbl_user_rel_access_url user_rel_url |
|
280
|
|
|
ON (user_rel_url.user_id = u.id) |
|
281
|
|
|
WHERE |
|
282
|
|
|
u.id <> 2 AND |
|
283
|
|
|
access_url_id = $access_url_id AND |
|
284
|
|
|
(".$target_name." LIKE '".$first_letter_user."%' ) |
|
285
|
|
|
$where_filter |
|
286
|
|
|
ORDER BY ".(count($users) > 0 ? "(u.id IN(".implode(',', $users).")) DESC," : "")." ".$orderBy; |
|
287
|
|
|
} |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
$result = Database::query($sql); |
|
291
|
|
|
$db_users = Database::store_result($result); |
|
292
|
|
|
unset($result); |
|
293
|
|
|
|
|
294
|
|
|
$sql = "SELECT code,visual_code,title |
|
295
|
|
|
FROM $tbl_course |
|
296
|
|
|
WHERE visual_code LIKE '".$first_letter_course."%' |
|
297
|
|
|
ORDER BY ".(count($courses) > 0 ? "(code IN('".implode("','", $courses)."')) DESC," : "")." visual_code"; |
|
298
|
|
|
|
|
299
|
|
|
if (api_is_multiple_url_enabled()) { |
|
|
|
|
|
|
300
|
|
|
$tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE); |
|
301
|
|
|
$access_url_id = api_get_current_access_url_id(); |
|
302
|
|
|
if (-1 != $access_url_id) { |
|
303
|
|
|
$sql = "SELECT code, visual_code, title |
|
304
|
|
|
FROM $tbl_course as course |
|
305
|
|
|
INNER JOIN $tbl_course_rel_access_url course_rel_url |
|
306
|
|
|
ON (course_rel_url.c_id = course.id) |
|
307
|
|
|
WHERE |
|
308
|
|
|
access_url_id = $access_url_id AND |
|
309
|
|
|
(visual_code LIKE '".$first_letter_course."%' ) |
|
310
|
|
|
ORDER BY ".(count($courses) > 0 ? "(code IN('".implode("','", $courses)."')) DESC," : "")." visual_code"; |
|
311
|
|
|
} |
|
312
|
|
|
} |
|
313
|
|
|
|
|
314
|
|
|
$result = Database::query($sql); |
|
315
|
|
|
$db_courses = Database::store_result($result); |
|
316
|
|
|
unset($result); |
|
317
|
|
|
?> |
|
318
|
|
|
<form name="formulaire" method="post" action="<?php echo api_get_self(); ?>" class="w-full px-4 sm:px-6 lg:px-8"> |
|
319
|
|
|
<input type="hidden" name="form_sent" value="1"/> |
|
320
|
|
|
|
|
321
|
|
|
<?php |
|
322
|
|
|
if (is_array($extra_field_list)) { |
|
323
|
|
|
if (is_array($new_field_list) && count($new_field_list) > 0) { |
|
324
|
|
|
echo '<div class="mb-6 rounded-2xl border border-gray-25 p-4 bg-white shadow-sm w-full overflow-hidden">'; |
|
325
|
|
|
echo '<h3 class="text-lg font-semibold mb-3">'.get_lang('Filter users').'</h3>'; |
|
326
|
|
|
echo '<div class="flex flex-wrap gap-3 min-w-0">'; |
|
327
|
|
|
foreach ($new_field_list as $new_field) { |
|
328
|
|
|
echo '<label class="text-sm font-medium">'.htmlspecialchars($new_field['name']).'</label>'; |
|
329
|
|
|
$varname = 'field_'.$new_field['variable']; |
|
330
|
|
|
$fieldtype = $new_field['type']; |
|
331
|
|
|
|
|
332
|
|
|
echo '<select name="'.$varname.'" class="form-select rounded-xl border border-gray-25 px-3 py-2 text-sm w-full sm:w-auto max-w-full">'; |
|
333
|
|
|
echo '<option value="0">--'.get_lang('Select').'--</option>'; |
|
334
|
|
|
foreach ($new_field['data'] as $option) { |
|
335
|
|
|
$checked = ''; |
|
336
|
|
|
if (ExtraField::FIELD_TYPE_TAG == $fieldtype) { |
|
337
|
|
|
if (isset($_POST[$varname]) && $_POST[$varname] == $option['tag']) { |
|
338
|
|
|
$checked = 'selected="true"'; |
|
339
|
|
|
} |
|
340
|
|
|
echo '<option value="'.Security::remove_XSS($option['tag']).'" '.$checked.'>'.$option['tag'].'</option>'; |
|
341
|
|
|
} else { |
|
342
|
|
|
if (isset($_POST[$varname]) && $_POST[$varname] == $option[1]) { |
|
343
|
|
|
$checked = 'selected="true"'; |
|
344
|
|
|
} |
|
345
|
|
|
echo '<option value="'.Security::remove_XSS($option[1]).'" '.$checked.'>'.$option[2].'</option>'; |
|
346
|
|
|
} |
|
347
|
|
|
} |
|
348
|
|
|
echo '</select>'; |
|
349
|
|
|
$extraHidden = ExtraField::FIELD_TYPE_TAG == $fieldtype ? '<input type="hidden" name="field_id" value="'.(int) $option['field_id'].'" />' : ''; |
|
350
|
|
|
echo $extraHidden; |
|
351
|
|
|
} |
|
352
|
|
|
echo '</div>'; |
|
353
|
|
|
echo '<div class="mt-4">'; |
|
354
|
|
|
echo '<button type="button" onclick="validate_filter()" class="inline-flex items-center rounded-2xl bg-primary px-4 py-2 text-white text-sm font-medium hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary">'.get_lang('Filter').'</button>'; |
|
355
|
|
|
echo '</div>'; |
|
356
|
|
|
echo '</div>'; |
|
357
|
|
|
} |
|
358
|
|
|
} |
|
359
|
|
|
?> |
|
360
|
|
|
|
|
361
|
|
|
<div class="w-full flex flex-col md:flex-row items-stretch gap-6"> |
|
362
|
|
|
|
|
363
|
|
|
<!-- Users pane (left) --> |
|
364
|
|
|
<div class="flex-1 md:basis-5/12 min-w-[420px] rounded-2xl border border-gray-25 p-4 bg-white shadow-sm"> |
|
365
|
|
|
<label class="block text-sm font-medium mb-2"><?php echo get_lang('User list'); ?></label> |
|
366
|
|
|
|
|
367
|
|
|
<div class="flex items-center gap-2 mb-3"> |
|
368
|
|
|
<span class="text-sm text-gray-90"><?php echo get_lang('First letter (last name)'); ?>:</span> |
|
369
|
|
|
<select name="firstLetterUser" |
|
370
|
|
|
onchange="document.formulaire.form_sent.value='2'; document.formulaire.submit();" |
|
371
|
|
|
aria-label="<?php echo get_lang('First letter (last name)'); ?>" |
|
372
|
|
|
class="rounded-xl border border-gray-25 px-3 py-1 text-sm w-auto"> |
|
373
|
|
|
<option value="">--</option> |
|
374
|
|
|
<?php echo Display::get_alphabet_options($first_letter_user); ?> |
|
375
|
|
|
</select> |
|
376
|
|
|
</div> |
|
377
|
|
|
|
|
378
|
|
|
<select name="UserList[]" multiple size="20" |
|
379
|
|
|
class="block w-full max-w-none min-w-[400px] h-[28rem] rounded-2xl border border-gray-25 bg-white px-3 py-2 text-sm"> |
|
380
|
|
|
<?php foreach ($db_users as $user) { ?> |
|
381
|
|
|
<option value="<?php echo (int) $user['user_id']; ?>" <?php if (in_array($user['user_id'], $users)) echo 'selected="selected"'; ?>> |
|
382
|
|
|
<?php |
|
383
|
|
|
$userName = $user['lastname'].' '.$user['firstname'].' ('.$user['username'].')'; |
|
384
|
|
|
if ($showOfficialCode) { |
|
385
|
|
|
$officialCode = !empty($user['official_code']) ? $user['official_code'].' - ' : '? - '; |
|
386
|
|
|
$userName = $officialCode.$userName; |
|
387
|
|
|
} |
|
388
|
|
|
echo Security::remove_XSS($userName); |
|
389
|
|
|
?> |
|
390
|
|
|
</option> |
|
391
|
|
|
<?php } ?> |
|
392
|
|
|
</select> |
|
393
|
|
|
</div> |
|
394
|
|
|
|
|
395
|
|
|
<!-- Center action --> |
|
396
|
|
|
<div class="md:basis-2/12 flex items-center justify-center"> |
|
397
|
|
|
<button type="submit" |
|
398
|
|
|
class="w-full md:w-auto inline-flex items-center justify-center rounded-2xl bg-primary px-6 py-3 text-white font-semibold shadow hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary"> |
|
399
|
|
|
<svg class="mr-2 h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 5v14m-7-7h14"/></svg> |
|
400
|
|
|
<?php echo get_lang('Add to the course(s)'); ?> |
|
401
|
|
|
</button> |
|
402
|
|
|
</div> |
|
403
|
|
|
|
|
404
|
|
|
<!-- Courses pane (right) --> |
|
405
|
|
|
<div class="flex-1 md:basis-5/12 min-w-[420px] rounded-2xl border border-gray-25 p-4 bg-white shadow-sm"> |
|
406
|
|
|
<label class="block text-sm font-medium mb-2"><?php echo get_lang('Course list'); ?></label> |
|
407
|
|
|
|
|
408
|
|
|
<div class="flex items-center gap-2 mb-3"> |
|
409
|
|
|
<span class="text-sm text-gray-90"><?php echo get_lang('First letter (code)'); ?>:</span> |
|
410
|
|
|
<select name="firstLetterCourse" |
|
411
|
|
|
onchange="document.formulaire.form_sent.value='2'; document.formulaire.submit();" |
|
412
|
|
|
aria-label="<?php echo get_lang('First letter (code)'); ?>" |
|
413
|
|
|
class="rounded-xl border border-gray-25 px-3 py-1 text-sm w-auto"> |
|
414
|
|
|
<option value="">--</option> |
|
415
|
|
|
<?php echo Display::get_alphabet_options($first_letter_course); ?> |
|
416
|
|
|
</select> |
|
417
|
|
|
</div> |
|
418
|
|
|
|
|
419
|
|
|
<select name="CourseList[]" multiple size="20" |
|
420
|
|
|
class="block w-full max-w-none min-w-[400px] h-[28rem] rounded-2xl border border-gray-25 bg-white px-3 py-2 text-sm"> |
|
421
|
|
|
<?php foreach ($db_courses as $course) { |
|
422
|
|
|
$suffix = ''; |
|
423
|
|
|
if ($__globalLimit > 0) { |
|
424
|
|
|
$state = _compute_course_limit_state_by_code($course['code'], $__globalLimit); |
|
425
|
|
|
$suffix = $state['full'] |
|
426
|
|
|
? ' — [full '.$state['current'].'/'.$state['limit'].']' |
|
427
|
|
|
: ' — [seats left: '.$state['seatsLeft'].'/'.$state['limit'].']'; |
|
428
|
|
|
} ?> |
|
429
|
|
|
<option value="<?php echo Security::remove_XSS($course['code']); ?>" <?php if (in_array($course['code'], $courses)) echo 'selected="selected"'; ?>> |
|
430
|
|
|
<?php echo '('.Security::remove_XSS($course['visual_code']).') '.Security::remove_XSS($course['title']).$suffix; ?> |
|
431
|
|
|
</option> |
|
432
|
|
|
<?php } ?> |
|
433
|
|
|
</select> |
|
434
|
|
|
</div> |
|
435
|
|
|
|
|
436
|
|
|
</div> |
|
437
|
|
|
|
|
438
|
|
|
|
|
439
|
|
|
</form> |
|
440
|
|
|
<?php |
|
441
|
|
|
|
|
442
|
|
|
Display::display_footer(); |
|
443
|
|
|
|
|
444
|
|
|
/** |
|
445
|
|
|
* Compute occupancy state by course code (for option labels). |
|
446
|
|
|
* |
|
447
|
|
|
* @return array{limit:int,current:int,seatsLeft:int,full:bool} |
|
448
|
|
|
*/ |
|
449
|
|
|
function _compute_course_limit_state_by_code(string $courseCode, int $globalLimit): array |
|
450
|
|
|
{ |
|
451
|
|
|
$info = api_get_course_info($courseCode); |
|
452
|
|
|
if (empty($info) || $globalLimit <= 0) { |
|
453
|
|
|
return ['limit' => max(0, $globalLimit), 'current' => 0, 'seatsLeft' => $globalLimit, 'full' => false]; |
|
454
|
|
|
} |
|
455
|
|
|
return _compute_course_limit_state_by_real_id((int) $info['real_id'], $globalLimit); |
|
456
|
|
|
} |
|
457
|
|
|
|
|
458
|
|
|
/** |
|
459
|
|
|
* Compute occupancy state by real course id (for precheck and labels). |
|
460
|
|
|
* Counts all users (teachers included), excluding RRHH relation type. |
|
461
|
|
|
*/ |
|
462
|
|
|
function _compute_course_limit_state_by_real_id(int $courseRealId, int $globalLimit): array |
|
463
|
|
|
{ |
|
464
|
|
|
$current = 0; |
|
465
|
|
|
if ($globalLimit > 0) { |
|
466
|
|
|
$sqlCount = "SELECT COUNT(*) AS total |
|
467
|
|
|
FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)." |
|
468
|
|
|
WHERE c_id = $courseRealId |
|
469
|
|
|
AND relation_type <> ".COURSE_RELATION_TYPE_RRHH; |
|
470
|
|
|
$row = Database::fetch_array(Database::query($sqlCount), 'ASSOC'); |
|
471
|
|
|
$current = (int) ($row['total'] ?? 0); |
|
472
|
|
|
} |
|
473
|
|
|
$seatsLeft = max(0, $globalLimit - $current); |
|
474
|
|
|
return [ |
|
475
|
|
|
'limit' => $globalLimit, |
|
476
|
|
|
'current' => $current, |
|
477
|
|
|
'seatsLeft' => $seatsLeft, |
|
478
|
|
|
'full' => $globalLimit > 0 && $current >= $globalLimit, |
|
479
|
|
|
]; |
|
480
|
|
|
} |
|
481
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.