Issues (2037)

main/admin/course_list_admin.php (1 issue)

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
/**
6
 * This script shows a list of courses and allows searching for courses codes
7
 * and names.
8
 */
9
$cidReset = true;
10
require_once __DIR__.'/../inc/global.inc.php';
11
$this_section = SECTION_PLATFORM_ADMIN;
12
api_protect_admin_script();
13
$sessionId = isset($_GET['session_id']) ? $_GET['session_id'] : null;
14
$addTeacherColumn = true;
15
16
/**
17
 * Get the number of courses which will be displayed.
18
 *
19
 * @throws Exception
20
 *
21
 * @return int The number of matching courses
22
 */
23
function get_number_of_courses()
24
{
25
    return get_course_data(0, 0, 0, 0, null, true);
26
}
27
28
/**
29
 * Get course data to display.
30
 *
31
 * @param int    $from
32
 * @param int    $number_of_items
33
 * @param int    $column
34
 * @param string $direction
35
 *
36
 * @throws Exception
37
 *
38
 * @return array
39
 */
40
function get_course_data($from, $number_of_items, $column, $direction, $dataFunctions = [], $getCount = false)
41
{
42
    $addTeacherColumn = true;
43
    $table = Database::get_main_table(TABLE_MAIN_COURSE);
44
45
    $from = (int) $from;
46
    $number_of_items = (int) $number_of_items;
47
    $column = (int) $column;
48
49
    if (!in_array(strtolower($direction), ['asc', 'desc'])) {
50
        $direction = 'desc';
51
    }
52
53
    $teachers = '';
54
    if ($addTeacherColumn) {
55
        $teachers = " GROUP_CONCAT(cu.user_id SEPARATOR ',') as col4, ";
56
    }
57
    $select = "SELECT
58
                code AS col0,
59
                title AS col1,
60
                creation_date AS col2,
61
                $teachers
62
                visibility,
63
                directory,
64
                visual_code,
65
                course.code,
66
                course.id ";
67
68
    if ($getCount) {
69
        $select = 'SELECT COUNT(DISTINCT(course.id)) as count ';
70
    }
71
72
    $sql = "$select FROM $table course";
73
    if (api_is_multiple_url_enabled()) {
74
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
75
        $sql .= " INNER JOIN $access_url_rel_course_table url_rel_course
76
                  ON (course.id = url_rel_course.c_id)";
77
    }
78
79
    $tableCourseRelUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
80
    $sql .= "
81
            LEFT JOIN $tableCourseRelUser cu
82
            ON (course.id = cu.c_id AND cu.status = ".COURSEMANAGER." )
83
        ";
84
85
    $sql .= ' WHERE 1=1 ';
86
    if (isset($_GET['keyword'])) {
87
        $keyword = Database::escape_string("%".trim($_GET['keyword'])."%");
88
        $sql .= " AND  (
89
            title LIKE '".$keyword."' OR
90
            code LIKE '".$keyword."' OR
91
            visual_code LIKE '".$keyword."'
92
        )
93
        ";
94
    } elseif (isset($_GET['keyword_code'])) {
95
        $keyword_code = Database::escape_string("%".$_GET['keyword_code']."%");
96
        $keyword_title = Database::escape_string("%".$_GET['keyword_title']."%");
97
        $keyword_category = isset($_GET['keyword_category'])
98
            ? Database::escape_string("%".$_GET['keyword_category']."%")
99
            : null;
100
        $keyword_language = Database::escape_string("%".$_GET['keyword_language']."%");
101
        $keyword_visibility = Database::escape_string("%".$_GET['keyword_visibility']."%");
102
        $keyword_subscribe = Database::escape_string($_GET['keyword_subscribe']);
103
        $keyword_unsubscribe = Database::escape_string($_GET['keyword_unsubscribe']);
104
105
        $sql .= " AND
106
                title LIKE '".$keyword_title."' AND
107
                (code LIKE '".$keyword_code."' OR visual_code LIKE '".$keyword_code."') AND
108
                course_language LIKE '".$keyword_language."' AND
109
                visibility LIKE '".$keyword_visibility."' AND
110
                subscribe LIKE '".$keyword_subscribe."' AND
111
                unsubscribe LIKE '".$keyword_unsubscribe."'";
112
113
        if (!empty($keyword_category)) {
114
            $sql .= " AND category_code LIKE '".$keyword_category."' ";
115
        }
116
    }
117
118
    // Adding the filter to see the user's only of the current access_url.
119
    if (api_is_multiple_url_enabled()) {
120
        $sql .= " AND url_rel_course.access_url_id = ".api_get_current_access_url_id();
121
    }
122
123
    if ($addTeacherColumn) {
124
        $teachers = isset($_GET['course_teachers']) ? $_GET['course_teachers'] : [];
125
        if (!empty($teachers)) {
126
            $teachers = array_map('intval', $teachers);
127
            $addNull = '';
128
            foreach ($teachers as $key => $teacherId) {
129
                if (0 === $teacherId) {
130
                    $addNull = 'OR cu.user_id IS NULL ';
131
                    unset($key);
132
                }
133
            }
134
            $sql .= ' AND ( cu.user_id IN ("'.implode('", "', $teachers).'") '.$addNull.' ) ';
135
        }
136
137
        if (false === $getCount) {
138
            $sql .= " GROUP BY course.id ";
139
        }
140
    }
141
142
    if ($getCount) {
143
        $res = Database::query($sql);
144
        $row = Database::fetch_array($res);
145
        if ($row) {
146
            return (int) $row['count'];
147
        }
148
149
        return 0;
150
    }
151
    $sql .= " ORDER BY col$column $direction ";
152
    $sql .= " LIMIT $from, $number_of_items";
153
154
    $res = Database::query($sql);
155
    $courses = [];
156
    $path = api_get_path(WEB_CODE_PATH);
157
    $coursePath = api_get_path(WEB_COURSE_PATH);
158
159
    $icon = Display::return_icon('teacher.png', get_lang('Teacher'), [], ICON_SIZE_TINY);
160
161
    while ($course = Database::fetch_array($res)) {
162
        $courseId = $course['id'];
163
        $courseCode = $course['code'];
164
165
        // Place colour icons in front of courses.
166
        $showVisualCode = $course['visual_code'] != $courseCode ? Display::label($course['visual_code'], 'info') : null;
167
        $course[1] = get_course_visibility_icon($course['visibility']).PHP_EOL
168
            .Display::url(Security::remove_XSS($course[1]), $coursePath.$course['directory'].'/index.php').PHP_EOL
169
            .$showVisualCode;
170
        $course[5] = $course[5] == SUBSCRIBE_ALLOWED ? get_lang('Yes') : get_lang('No');
171
        $course[6] = $course[6] == UNSUBSCRIBE_ALLOWED ? get_lang('Yes') : get_lang('No');
172
173
        $actions = [];
174
        $actions[] = Display::url(
175
            Display::return_icon('info2.png', get_lang('Info')),
176
            "course_information.php?code=$courseCode"
177
        );
178
        /*$actions[] = Display::url(
179
            Display::return_icon('course_home.png', get_lang('CourseHomepage')),
180
            $coursePath.$course['directory'].'/index.php'
181
        );*/
182
        $actions[] = Display::url(
183
            Display::return_icon('statistics.png', get_lang('Tracking')),
184
            $path.'tracking/courseLog.php?'.api_get_cidreq_params($courseCode)
185
        );
186
        $actions[] = Display::url(
187
            Display::return_icon('edit.png', get_lang('Edit')),
188
            $path.'admin/course_edit.php?id='.$courseId
189
        );
190
        $actions[] = Display::url(
191
            Display::return_icon('backup.png', get_lang('CreateBackup')),
192
            $path.'coursecopy/create_backup.php?'.api_get_cidreq_params($courseCode)
193
        );
194
        $actions[] = Display::url(
195
            Display::return_icon('delete.png', get_lang('Delete')),
196
            $path.'admin/course_list_admin.php?'.http_build_query([
197
                  'delete_course' => $courseCode,
198
                    'sec_token' => Security::getTokenFromSession(),
199
                ]),
200
            [
201
                'onclick' => "javascript: if (!confirm('"
202
                    .addslashes(api_htmlentities(get_lang('ConfirmYourChoice'), ENT_QUOTES))."')) return false;",
203
            ]
204
        );
205
206
        $course['creation_date'] = api_get_local_time($course['col2']);
207
        $lastAccessLocalTime = '';
208
        $lastAccess = Tracking::getLastConnectionDateByCourse($courseId);
209
        if ($lastAccess) {
210
            $lastAccessLocalTime = api_get_local_time($lastAccess);
211
        }
212
213
        $courseItem = [
214
            $course[0],
215
            $course[1],
216
            $course['creation_date'],
217
            $lastAccessLocalTime,
218
        ];
219
220
        if ($addTeacherColumn) {
221
            $teacherIdList = array_filter(explode(',', $course['col4']));
222
            $teacherList = [];
223
            if (!empty($teacherIdList)) {
224
                foreach ($teacherIdList as $teacherId) {
225
                    $userInfo = api_get_user_info($teacherId);
226
                    if ($userInfo) {
227
                        $teacherList[] = $userInfo['complete_name'];
228
                    }
229
                }
230
            }
231
            $courseItem[] = '<ul class="list-inline"><li>'
232
                ."$icon ".implode("</li><li>$icon ", $teacherList)
233
                .'</li></ul>';
234
        }
235
        $courseItem[] = implode(PHP_EOL, $actions);
236
        $courses[] = $courseItem;
237
    }
238
239
    return $courses;
240
}
241
242
/**
243
 * Return an icon representing the visibility of the course.
244
 *
245
 * @param string $visibility
246
 *
247
 * @return string
248
 */
249
function get_course_visibility_icon($visibility)
250
{
251
    $style = 'margin-bottom:0;margin-right:5px;';
252
    switch ($visibility) {
253
        case 0:
254
            return Display::return_icon(
255
                'bullet_red.png',
256
                get_lang('CourseVisibilityClosed'),
257
                ['style' => $style]
258
            );
259
            break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
260
        case 1:
261
            return Display::return_icon(
262
                'bullet_orange.png',
263
                get_lang('Private'),
264
                ['style' => $style]
265
            );
266
            break;
267
        case 2:
268
            return Display::return_icon(
269
                'bullet_green.png',
270
                get_lang('OpenToThePlatform'),
271
                ['style' => $style]
272
            );
273
            break;
274
        case 3:
275
            return Display::return_icon(
276
                'bullet_blue.png',
277
                get_lang('OpenToTheWorld'),
278
                ['style' => $style]
279
            );
280
            break;
281
        case 4:
282
            return Display::return_icon(
283
                'bullet_grey.png',
284
                get_lang('CourseVisibilityHidden'),
285
                ['style' => $style]
286
            );
287
            break;
288
        default:
289
            return '';
290
    }
291
}
292
293
if (isset($_POST['action']) && Security::check_token('get')) {
294
    switch ($_POST['action']) {
295
        // Delete selected courses
296
        case 'delete_courses':
297
            if (!empty($_POST['course'])) {
298
                $course_codes = $_POST['course'];
299
                if (count($course_codes) > 0) {
300
                    foreach ($course_codes as $course_code) {
301
                        CourseManager::delete_course($course_code);
302
                    }
303
                }
304
305
                Display::addFlash(Display::return_message(get_lang('Deleted')));
306
            }
307
            break;
308
    }
309
}
310
$content = '';
311
$message = '';
312
$actions = '';
313
314
if (isset($_GET['search']) && $_GET['search'] === 'advanced') {
315
    // Get all course categories
316
    $interbreadcrumb[] = [
317
        'url' => 'index.php',
318
        'name' => get_lang('PlatformAdmin'),
319
    ];
320
    $interbreadcrumb[] = [
321
        'url' => 'course_list_admin.php',
322
        'name' => get_lang('CourseList'),
323
    ];
324
    $tool_name = get_lang('SearchACourse');
325
    $form = new FormValidator('advanced_course_search', 'get');
326
    $form->addElement('header', $tool_name);
327
    $form->addText('keyword_code', get_lang('CourseCode'), false);
328
    $form->addText('keyword_title', get_lang('Title'), false);
329
330
    // Category code
331
    $url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_category';
332
333
    $form->addElement(
334
        'select_ajax',
335
        'keyword_category',
336
        get_lang('CourseFaculty'),
337
        null,
338
        [
339
            'url' => $url,
340
        ]
341
    );
342
343
    $el = $form->addSelectLanguage('keyword_language', get_lang('CourseLanguage'));
344
    $el->addOption(get_lang('All'), '%');
345
346
    if ($addTeacherColumn) {
347
        $form->addSelectAjax(
348
            'course_teachers',
349
            get_lang('CourseTeachers'),
350
            [0 => get_lang('None')],
351
            [
352
                'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=teacher_to_basis_course',
353
                'id' => 'course_teachers',
354
                'multiple' => 'multiple',
355
            ]
356
        );
357
        $form->addLabel('', '<button id="set_none_teacher" class="btn ">'.get_lang('None').'</button>');
358
    }
359
360
    $form->addElement('radio', 'keyword_visibility', get_lang('CourseAccess'), get_lang('OpenToTheWorld'), COURSE_VISIBILITY_OPEN_WORLD);
361
    $form->addElement('radio', 'keyword_visibility', null, get_lang('OpenToThePlatform'), COURSE_VISIBILITY_OPEN_PLATFORM);
362
    $form->addElement('radio', 'keyword_visibility', null, get_lang('Private'), COURSE_VISIBILITY_REGISTERED);
363
    $form->addElement('radio', 'keyword_visibility', null, get_lang('CourseVisibilityClosed'), COURSE_VISIBILITY_CLOSED);
364
    $form->addElement('radio', 'keyword_visibility', null, get_lang('CourseVisibilityHidden'), COURSE_VISIBILITY_HIDDEN);
365
    $form->addElement('radio', 'keyword_visibility', null, get_lang('All'), '%');
366
    $form->addElement('radio', 'keyword_subscribe', get_lang('Subscription'), get_lang('Allowed'), 1);
367
    $form->addElement('radio', 'keyword_subscribe', null, get_lang('Denied'), 0);
368
    $form->addElement('radio', 'keyword_subscribe', null, get_lang('All'), '%');
369
    $form->addElement('radio', 'keyword_unsubscribe', get_lang('Unsubscription'), get_lang('AllowedToUnsubscribe'), 1);
370
    $form->addElement('radio', 'keyword_unsubscribe', null, get_lang('NotAllowedToUnsubscribe'), 0);
371
    $form->addElement('radio', 'keyword_unsubscribe', null, get_lang('All'), '%');
372
    $form->addButtonSearch(get_lang('SearchCourse'));
373
    $defaults['keyword_language'] = '%';
374
    $defaults['keyword_visibility'] = '%';
375
    $defaults['keyword_subscribe'] = '%';
376
    $defaults['keyword_unsubscribe'] = '%';
377
    $form->setDefaults($defaults);
378
    $content .= $form->returnForm();
379
} else {
380
    $interbreadcrumb[] = [
381
        'url' => 'index.php',
382
        'name' => get_lang('PlatformAdmin'),
383
    ];
384
    $tool_name = get_lang('CourseList');
385
    if (isset($_GET['delete_course']) && Security::check_token('get')) {
386
        $result = CourseManager::delete_course($_GET['delete_course']);
387
        if ($result) {
388
            Display::addFlash(Display::return_message(get_lang('Deleted')));
389
        }
390
    }
391
    // Create a search-box
392
    $form = new FormValidator(
393
        'search_simple',
394
        'get',
395
        '',
396
        '',
397
        [],
398
        FormValidator::LAYOUT_INLINE
399
    );
400
    $form->addElement(
401
        'text',
402
        'keyword',
403
        null,
404
        ['id' => 'course-search-keyword', 'aria-label' => get_lang('SearchCourse')]
405
    );
406
    $form->addButtonSearch(get_lang('SearchCourse'));
407
    $advanced = '<a class="btn btn-default" href="'.api_get_path(WEB_CODE_PATH).'admin/course_list_admin.php?search=advanced">
408
        <em class="fa fa-search"></em> '.
409
        get_lang('AdvancedSearch').'</a>';
410
411
    // Create a filter by session
412
    $sessionFilter = new FormValidator(
413
        'course_filter',
414
        'get',
415
        '',
416
        '',
417
        [],
418
        FormValidator::LAYOUT_INLINE
419
    );
420
421
    $courseListUrl = api_get_self();
422
    $actions1 = Display::url(
423
        Display::return_icon(
424
            'new_course.png',
425
            get_lang('AddCourse'),
426
            [],
427
            ICON_SIZE_MEDIUM
428
        ),
429
        api_get_path(WEB_CODE_PATH).'admin/course_add.php'
430
    );
431
432
    if (api_get_setting('course_validation') === 'true') {
433
        $actions1 .= Display::url(
434
            Display::return_icon(
435
                'course_request_pending.png',
436
                get_lang('ReviewCourseRequests'),
437
                [],
438
                ICON_SIZE_MEDIUM
439
            ),
440
            api_get_path(WEB_CODE_PATH).'admin/course_request_review.php'
441
        );
442
    }
443
444
    $actions2 = $form->returnForm();
445
    //$actions3 = $sessionFilter->returnForm();
446
    $actions4 = $advanced;
447
448
    $actions = Display::toolbarAction(
449
        'toolbar',
450
        [$actions1, $actions2, $actions4],
451
        [2, 4, 3, 3]
452
    );
453
454
    // Create a sortable table with the course data
455
    $table = new SortableTable(
456
        'course_list_admin',
457
        'get_number_of_courses',
458
        'get_course_data',
459
        1,
460
        20,
461
        'ASC',
462
        'course_list_admin'
463
    );
464
465
    $parameters = [];
466
    $parameters['sec_token'] = Security::get_token();
467
    if (isset($_GET['keyword'])) {
468
        $parameters = ['keyword' => Security::remove_XSS($_GET['keyword'])];
469
    } elseif (isset($_GET['keyword_code'])) {
470
        $parameters['keyword_code'] = Security::remove_XSS($_GET['keyword_code']);
471
        $parameters['keyword_title'] = Security::remove_XSS($_GET['keyword_title']);
472
        if (isset($_GET['keyword_category'])) {
473
            $parameters['keyword_category'] = Security::remove_XSS($_GET['keyword_category']);
474
        }
475
        $parameters['keyword_language'] = Security::remove_XSS($_GET['keyword_language']);
476
        $parameters['keyword_visibility'] = Security::remove_XSS($_GET['keyword_visibility']);
477
        $parameters['keyword_subscribe'] = Security::remove_XSS($_GET['keyword_subscribe']);
478
        $parameters['keyword_unsubscribe'] = Security::remove_XSS($_GET['keyword_unsubscribe']);
479
    }
480
481
    if (isset($_GET['course_teachers'])) {
482
        $parsed = array_map('intval', $_GET['course_teachers']);
483
        $parameters["course_teachers"] = '';
484
        foreach ($parsed as $key => $teacherId) {
485
            $parameters["course_teachers[$key]"] = $teacherId;
486
        }
487
    }
488
489
    $table->set_additional_parameters($parameters);
490
    $column = 0;
491
    $table->set_header($column++, '', false, 'width="8px"');
492
    $table->set_header($column++, get_lang('Title'), true, null, ['class' => 'title']);
493
    $table->set_header($column++, get_lang('CreationDate'), true, 'width="70px"');
494
    $table->set_header($column++, get_lang('LatestLoginInCourse'), false, 'width="70px"');
495
    //$table->set_header($column++, get_lang('Category'));
496
    //$table->set_header($column++, get_lang('SubscriptionAllowed'), true, 'width="60px"');
497
    //$table->set_header($column++, get_lang('UnsubscriptionAllowed'), false, 'width="50px"');
498
    if ($addTeacherColumn) {
499
        $table->set_header($column++, get_lang('Teachers'), true, ['style' => 'width:350px;']);
500
    }
501
    $table->set_header(
502
        $column++,
503
        get_lang('Action'),
504
        false,
505
        null,
506
        ['class' => 'td_actions', 'style' => 'width:145px;']
507
    );
508
    $table->set_form_actions(
509
        ['delete_courses' => get_lang('DeleteCourse')],
510
        'course'
511
    );
512
    $tab = CourseManager::getCourseListTabs('admin');
513
    $content .= $tab.$table->return_table();
514
}
515
516
$htmlHeadXtra[] = '
517
<script>
518
$(function() {
519
    $("#set_none_teacher").on("click", function () {
520
        $("#course_teachers").val("0").trigger("change");
521
522
        return false;
523
    });
524
});
525
</script>';
526
527
$tpl = new Template($tool_name);
528
$tpl->assign('actions', $actions);
529
$tpl->assign('message', $message);
530
$tpl->assign('content', $content);
531
$tpl->display_one_col_template();
532