get_course_data()   F
last analyzed

Complexity

Conditions 29
Paths 14592

Size

Total Lines 262
Code Lines 164

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 29
eloc 164
c 0
b 0
f 0
nc 14592
nop 6
dl 0
loc 262
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
/*
8
 * This script shows a list of courses and allows searching for courses codes
9
 * and names.
10
 */
11
12
use Chamilo\CoreBundle\Entity\AccessUrl;
13
use Chamilo\CoreBundle\Entity\CatalogueCourseRelAccessUrlRelUsergroup;
14
use Chamilo\CoreBundle\Enums\ActionIcon;
15
use Chamilo\CoreBundle\Enums\StateIcon;
16
use Chamilo\CoreBundle\Enums\ToolIcon;
17
use Chamilo\CoreBundle\Framework\Container;
18
use Chamilo\CoreBundle\Repository\CatalogueCourseRelAccessUrlRelUsergroupRepository;
19
20
$cidReset = true;
21
22
require_once __DIR__.'/../inc/global.inc.php';
23
24
$this_section = SECTION_PLATFORM_ADMIN;
25
api_protect_admin_script();
26
$sessionId = $_GET['session_id'] ?? null;
27
28
/**
29
 * Get the number of courses which will be displayed.
30
 *
31
 * @return int The number of matching courses
32
 *
33
 * @throws Exception
34
 */
35
function get_number_of_courses(): int
36
{
37
    return get_course_data(0, 0, 0, 'ASC', [], true);
38
}
39
40
/**
41
 * Get course data to display.
42
 *
43
 * @throws Doctrine\DBAL\Exception
44
 * @throws Exception
45
 */
46
function get_course_data(
47
    int $from,
48
    int $number_of_items,
49
    int $column,
50
    string $direction,
51
    array $dataFunctions = [],
52
    bool $getCount = false
53
): int|array {
54
    $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
55
56
    if (!in_array(strtolower($direction), ['asc', 'desc'])) {
57
        $direction = 'desc';
58
    }
59
60
    $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
61
    $tblCourseRelCategory = Database::get_main_table(TABLE_MAIN_COURSE_REL_CATEGORY);
62
63
    $select = 'SELECT
64
                course.code AS col0,
65
                course.title AS col1,
66
                course.code AS col2,
67
                course_language AS col3,
68
                subscribe AS col5,
69
                unsubscribe AS col6,
70
                course.code AS col7,
71
                course.visibility AS col8,
72
                directory as col9,
73
                visual_code,
74
                directory,
75
                course.id';
76
77
    if ($getCount) {
78
        $select = 'SELECT COUNT(DISTINCT(course.id)) as count ';
79
    }
80
81
    $sql = "$select FROM $course_table course ";
82
83
    if (!empty($_GET['keyword_category'])) {
84
        $sql .= "INNER JOIN $tblCourseRelCategory course_rel_category ON course.id = course_rel_category.course_id
85
            INNER JOIN $tblCourseCategory category ON course_rel_category.course_category_id = category.id ";
86
    }
87
88
    if ((api_is_platform_admin() || api_is_session_admin())
89
        && api_is_multiple_url_enabled() && -1 != api_get_current_access_url_id()
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlUtil::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

89
        && /** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled() && -1 != api_get_current_access_url_id()

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.

Loading history...
90
    ) {
91
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
92
        $sql .= " INNER JOIN $access_url_rel_course_table url_rel_course
93
                 ON (course.id = url_rel_course.c_id)";
94
    }
95
96
    if (!empty($_GET['session_id'])) {
97
        $session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
98
        $session = Database::get_main_table(TABLE_MAIN_SESSION);
99
100
        $sql .= " INNER JOIN $session_rel_course r ON course.id = r.c_id
101
            INNER JOIN $session s ON r.session_id = s.id ";
102
    }
103
104
    if (isset($_GET['keyword'])) {
105
        $keyword = Database::escape_string('%'.trim($_GET['keyword']).'%');
106
        $sql .= " WHERE (
107
            course.title LIKE '".$keyword."' OR
108
            course.code LIKE '".$keyword."' OR
109
            visual_code LIKE '".$keyword."'
110
        )
111
        ";
112
    } elseif (isset($_GET['keyword_code'])) {
113
        $keyword_code = Database::escape_string('%'.$_GET['keyword_code'].'%');
114
        $keyword_title = Database::escape_string('%'.$_GET['keyword_title'].'%');
115
        $keyword_category = isset($_GET['keyword_category'])
116
            ? Database::escape_string($_GET['keyword_category'])
117
            : null;
118
        $keyword_language = Database::escape_string('%'.$_GET['keyword_language'].'%');
119
        $keyword_visibility = Database::escape_string('%'.$_GET['keyword_visibility'].'%');
120
        $keyword_subscribe = Database::escape_string($_GET['keyword_subscribe']);
121
        $keyword_unsubscribe = Database::escape_string($_GET['keyword_unsubscribe']);
122
123
        $sql .= " WHERE
124
                (course.code LIKE '".$keyword_code."' OR visual_code LIKE '".$keyword_code."') AND
125
                course.title LIKE '".$keyword_title."' AND
126
                course_language LIKE '".$keyword_language."' AND
127
                visibility LIKE '".$keyword_visibility."' AND
128
                subscribe LIKE '".$keyword_subscribe."' AND
129
                unsubscribe LIKE '".$keyword_unsubscribe."'";
130
131
        if (!empty($keyword_category)) {
132
            $sql .= ' AND category.id = '.$keyword_category.' ';
133
        }
134
    }
135
136
    // Adding the filter to see the user's only of the current access_url.
137
    if ((api_is_platform_admin() || api_is_session_admin())
138
        && api_is_multiple_url_enabled() && -1 != api_get_current_access_url_id()
0 ignored issues
show
Deprecated Code introduced by
The function api_is_multiple_url_enabled() has been deprecated: Use AccessUrlUtil::isMultiple ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

138
        && /** @scrutinizer ignore-deprecated */ api_is_multiple_url_enabled() && -1 != api_get_current_access_url_id()

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.

Loading history...
139
    ) {
140
        $sql .= ' AND url_rel_course.access_url_id='.api_get_current_access_url_id();
141
    }
142
143
    if (!empty($_GET['session_id'])) {
144
        $sessionId = (int) $_GET['session_id'];
145
        $sql .= ' AND s.id = '.$sessionId.' ';
146
    }
147
148
    if ($getCount) {
149
        $res = Database::query($sql);
150
        $row = Database::fetch_array($res);
151
        if ($row) {
152
            return (int) $row['count'];
153
        }
154
155
        return 0;
156
    }
157
158
    $sql .= ' GROUP BY course.code';
159
    $sql .= " ORDER BY col$column $direction ";
160
    $sql .= " LIMIT $from, $number_of_items";
161
162
    $res = Database::query($sql);
163
    $courses = [];
164
    $languages = api_get_languages();
165
166
    $path = api_get_path(WEB_CODE_PATH);
167
168
    while ($course = Database::fetch_array($res)) {
169
        $courseInfo = api_get_course_info_by_id($course['id']);
170
171
        // get categories
172
        $sqlCategoriesByCourseId = "SELECT category.title FROM $tblCourseCategory category
173
            INNER JOIN $tblCourseRelCategory course_rel_category ON category.id = course_rel_category.course_category_id
174
            WHERE course_rel_category.course_id = ".$course['id'];
175
        $resultCategories = Database::query($sqlCategoriesByCourseId);
176
        $categories = [];
177
178
        while ($category = Database::fetch_array($resultCategories)) {
179
            $categories[] = $category['title'];
180
        }
181
182
        // Place colour icons in front of courses.
183
        $show_visual_code = $course['visual_code'] != $course['col2'] ? Display::label($course['visual_code'], 'info') : null;
184
        $course['col1'] = get_course_visibility_icon($courseInfo['visibility']).\PHP_EOL
185
            .Display::url(Security::remove_XSS($course['col1']), $courseInfo['course_public_url']).\PHP_EOL
186
            .$show_visual_code;
187
        $course['col5'] = SUBSCRIBE_ALLOWED == $course['col5'] ? get_lang('Yes') : get_lang('No');
188
        $course['col6'] = UNSUBSCRIBE_ALLOWED == $course['col6'] ? get_lang('Yes') : get_lang('No');
189
190
        $courseId = $course['id'];
191
192
        $actions = [];
193
        $actions[] = Display::url(
194
            Display::getMdiIcon(
195
                ActionIcon::INFORMATION,
196
                'ch-tool-icon',
197
                null,
198
                ICON_SIZE_SMALL,
199
                get_lang('Information')
200
            ),
201
            "course_information.php?id=$courseId"
202
        );
203
        $actions[] = Display::url(
204
            Display::getMdiIcon(
205
                ToolIcon::COURSE_HOME,
206
                'ch-tool-icon',
207
                null,
208
                ICON_SIZE_SMALL,
209
                get_lang('Course home')
210
            ),
211
            $courseInfo['course_public_url']
212
        );
213
        $actions[] = Display::url(
214
            Display::getMdiIcon(
215
                ToolIcon::TRACKING,
216
                'ch-tool-icon',
217
                null,
218
                ICON_SIZE_SMALL,
219
                get_lang('Reporting')
220
            ),
221
            $path.'tracking/courseLog.php?'.api_get_cidreq_params($courseId)
222
        );
223
        $actions[] = Display::url(
224
            Display::getMdiIcon(
225
                ActionIcon::EDIT,
226
                'ch-tool-icon',
227
                null,
228
                ICON_SIZE_SMALL,
229
                get_lang('Edit')
230
            ),
231
            $path.'admin/course_edit.php?id='.$courseId
232
        );
233
        $actions[] = Display::url(
234
            Display::getMdiIcon(
235
                ActionIcon::TAKE_BACKUP,
236
                'ch-tool-icon',
237
                null,
238
                ICON_SIZE_SMALL,
239
                get_lang('Create a backup')
240
            ),
241
            $path.'course_copy/create_backup.php?'.api_get_cidreq_params($courseId)
242
        );
243
244
        // Single course delete: ask if exclusive documents should also be removed.
245
        $actions[] = Display::url(
246
            Display::getMdiIcon(
247
                ActionIcon::DELETE,
248
                'ch-tool-icon',
249
                null,
250
                ICON_SIZE_SMALL,
251
                get_lang('Delete')
252
            ),
253
            $path.'admin/course_list.php?'
254
            .http_build_query([
255
                'delete_course' => $course['col0'],
256
                // Default: keep documents; JS will toggle this param to 1 if admin agrees.
257
                'delete_docs' => 0,
258
                'sec_token' => Security::getTokenFromSession(),
259
            ]),
260
            [
261
                'onclick' => 'return confirmDeleteCourseWithDocs(this);',
262
            ]
263
        );
264
265
        $em = Database::getManager();
266
        /** @var CatalogueCourseRelAccessUrlRelUsergroupRepository $repo */
267
        $repo = $em->getRepository(CatalogueCourseRelAccessUrlRelUsergroup::class);
268
        $record = $repo->findOneBy([
269
            'course' => $courseId,
270
            'accessUrl' => api_get_current_access_url_id(),
271
            'usergroup' => null,
272
        ]);
273
274
        $isInCatalogue = null !== $record;
275
        $catalogueUrl = api_get_self().'?toggle_catalogue='.$course['id'].'&sec_token='.Security::getTokenFromSession();
276
277
        $actions[] = Display::url(
278
            Display::getMdiIcon(
279
                $isInCatalogue ? StateIcon::CATALOGUE_OFF : StateIcon::CATALOGUE_ON,
280
                'ch-tool-icon',
281
                null,
282
                ICON_SIZE_SMALL,
283
                $isInCatalogue ? get_lang('Remove from catalogue') : get_lang('Add to catalogue'),
284
                [
285
                    'class' => $isInCatalogue ? 'text-warning' : 'text-muted',
286
                ]
287
            ),
288
            $catalogueUrl,
289
            [
290
                'title' => $isInCatalogue ? get_lang('Remove from catalogue') : get_lang('Add to catalogue'),
291
            ]
292
        );
293
294
        $courseItem = [
295
            $course['col0'],
296
            $course['col1'],
297
            $course['col2'],
298
            $languages[$course['col3']] ?? $course['col3'],
299
            implode(', ', $categories),
300
            $course['col5'],
301
            $course['col6'],
302
            implode(\PHP_EOL, $actions),
303
        ];
304
        $courses[] = $courseItem;
305
    }
306
307
    return $courses;
308
}
309
310
/**
311
 * Return an icon representing the visibility of the course.
312
 *
313
 * @param int $visibility
314
 */
315
function get_course_visibility_icon(int $visibility): string
316
{
317
    $style = 'margin-bottom:0;margin-right:5px;';
318
319
    return match ($visibility) {
320
        0 => Display::getMdiIcon(
321
            StateIcon::CLOSED_VISIBILITY,
322
            'ch-tool-icon',
323
            null,
324
            22,
325
            get_lang('Closed - the course is only accessible to the teachers')
326
        ),
327
        1 => Display::getMdiIcon(
328
            StateIcon::PRIVATE_VISIBILITY,
329
            'ch-tool-icon',
330
            null,
331
            22,
332
            get_lang(
333
                'Private'
334
            )
335
        ),
336
        2 => Display::getMdiIcon(
337
            StateIcon::OPEN_VISIBILITY,
338
            'ch-tool-icon',
339
            null,
340
            22,
341
            get_lang('Open - access allowed for users registered on the platform')
342
        ),
343
        3 => Display::getMdiIcon(
344
            StateIcon::PUBLIC_VISIBILITY,
345
            'ch-tool-icon',
346
            null,
347
            22,
348
            get_lang('Public - access allowed for the whole world')
349
        ),
350
        4 => Display::getMdiIcon(
351
            StateIcon::HIDDEN_VISIBILITY,
352
            'ch-tool-icon',
353
            null,
354
            22,
355
            get_lang('Hidden - Completely hidden to all users except the administrators')
356
        ),
357
        default => '',
358
    };
359
}
360
361
if (isset($_POST['action']) && Security::check_token('get')) {
362
    // Delete selected courses
363
    if ('delete_courses' == $_POST['action']) {
364
        if (!empty($_POST['course'])) {
365
            $course_codes = $_POST['course'];
366
            $deleteDocs = isset($_POST['delete_docs']) && (int) $_POST['delete_docs'] === 1;
367
368
            if (count($course_codes) > 0) {
369
                foreach ($course_codes as $course_code) {
370
                    CourseManager::delete_course($course_code, $deleteDocs);
371
                }
372
            }
373
374
            Display::addFlash(Display::return_message(get_lang('Deleted')));
375
        }
376
    }
377
}
378
379
if (isset($_GET['toggle_catalogue']) && Security::check_token('get')) {
380
    $courseId = (int) $_GET['toggle_catalogue'];
381
    $em = Database::getManager();
382
    $repo = $em->getRepository(CatalogueCourseRelAccessUrlRelUsergroup::class);
383
    $course = api_get_course_entity($courseId);
384
    $accessUrl = Container::getAccessUrlUtil()->getCurrent();
385
386
    if ($course && $accessUrl) {
387
        $record = $repo->findOneBy([
388
            'course' => $course,
389
            'accessUrl' => $accessUrl,
390
            'usergroup' => null,
391
        ]);
392
393
        if ($record) {
394
            $em->remove($record);
395
            Display::addFlash(Display::return_message(get_lang('Removed from catalogue')));
396
        } else {
397
            $newRel = new CatalogueCourseRelAccessUrlRelUsergroup();
398
            $newRel->setCourse($course);
399
            $newRel->setAccessUrl($accessUrl);
400
            $newRel->setUsergroup(null);
401
402
            $em->persist($newRel);
403
            Display::addFlash(Display::return_message(get_lang('Added to catalogue'), 'success'));
404
        }
405
406
        $em->flush();
407
    }
408
}
409
$content = '';
410
$message = '';
411
$actions = '';
412
413
$interbreadcrumb[] = [
414
    'url' => 'index.php',
415
    'name' => get_lang('Administration'),
416
];
417
418
if (isset($_GET['search']) && 'advanced' === $_GET['search']) {
419
    // Get all course categories
420
    $interbreadcrumb[] = [
421
        'url' => 'course_list.php',
422
        'name' => get_lang('Course list'),
423
    ];
424
    $tool_name = get_lang('Search for a course');
425
    $form = new FormValidator('advanced_course_search', 'get');
426
    $form->addElement('header', $tool_name);
427
    $form->addText('keyword_code', get_lang('Course code'), false);
428
    $form->addText('keyword_title', get_lang('Title'), false);
429
430
    // Category code
431
    $url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_category';
432
433
    $form->addElement(
434
        'select_ajax',
435
        'keyword_category',
436
        get_lang('Category'),
437
        null,
438
        [
439
            'url' => $url,
440
        ]
441
    );
442
443
    $el = $form->addSelectLanguage('keyword_language', get_lang('Course language'));
444
    $el->addOption(get_lang('All'), '%');
445
    $form->addElement('radio', 'keyword_visibility', get_lang('Course access'), get_lang('Public - access allowed for the whole world'), COURSE_VISIBILITY_OPEN_WORLD);
446
    $form->addElement('radio', 'keyword_visibility', null, get_lang('Open - access allowed for users registered on the platform'), COURSE_VISIBILITY_OPEN_PLATFORM);
447
    $form->addElement('radio', 'keyword_visibility', null, get_lang('Private'), COURSE_VISIBILITY_REGISTERED);
448
    $form->addElement('radio', 'keyword_visibility', null, get_lang('Closed - the course is only accessible to the teachers'), COURSE_VISIBILITY_CLOSED);
449
    $form->addElement('radio', 'keyword_visibility', null, get_lang('Hidden - Completely hidden to all users except the administrators'), COURSE_VISIBILITY_HIDDEN);
450
    $form->addElement('radio', 'keyword_visibility', null, get_lang('All'), '%');
451
    $form->addElement('radio', 'keyword_subscribe', get_lang('Subscription'), get_lang('Allowed'), 1);
452
    $form->addElement('radio', 'keyword_subscribe', null, get_lang('This function is only available to trainers'), 0);
453
    $form->addElement('radio', 'keyword_subscribe', null, get_lang('All'), '%');
454
    $form->addElement('radio', 'keyword_unsubscribe', get_lang('Unsubscribe'), get_lang('Users are allowed to unsubscribe from this course'), 1);
455
    $form->addElement('radio', 'keyword_unsubscribe', null, get_lang('Users are not allowed to unsubscribe from this course'), 0);
456
    $form->addElement('radio', 'keyword_unsubscribe', null, get_lang('All'), '%');
457
    $form->addButtonSearch(get_lang('Search courses'));
458
    $defaults['keyword_language'] = '%';
459
    $defaults['keyword_visibility'] = '%';
460
    $defaults['keyword_subscribe'] = '%';
461
    $defaults['keyword_unsubscribe'] = '%';
462
    $form->setDefaults($defaults);
463
    $content .= $form->returnForm();
464
} else {
465
    $tool_name = get_lang('Course list');
466
467
    // Single course deletion (from action icon)
468
    if (isset($_GET['delete_course']) && Security::check_token('get')) {
469
        $deleteDocs = isset($_GET['delete_docs']) && (int) $_GET['delete_docs'] === 1;
470
        $result = CourseManager::delete_course($_GET['delete_course'], $deleteDocs);
471
        if ($result) {
472
            Display::addFlash(Display::return_message(get_lang('Deleted')));
473
        }
474
    }
475
476
    if (isset($_GET['new_course_id'])) {
477
        $courseId = (int) $_GET['new_course_id'];
478
        $course = api_get_course_entity($courseId);
479
        if ($course) {
480
            $link = api_get_course_url($course->getId());
481
            $msg = sprintf(
482
                get_lang('Course %s added. You can access it directly %shere%s.'),
483
                '<strong>'.Security::remove_XSS($course->getTitle()).'</strong>',
484
                '<a href="'.$link.'" class="text-primary">',
485
                '</a>'
486
            );
487
488
            $content .=  Display::return_message($msg, 'confirmation', false);
489
        }
490
    }
491
492
    // Create a search-box
493
    $form = new FormValidator(
494
        'search_simple',
495
        'get',
496
        '',
497
        '',
498
        [],
499
        FormValidator::LAYOUT_BOX_SEARCH
500
    );
501
    $form->addElement(
502
        'text',
503
        'keyword',
504
        null,
505
        ['id' => 'course-search-keyword', 'aria-label' => get_lang('Search courses')]
506
    );
507
    $form->addButtonSearch(get_lang('Search courses'));
508
    $advanced = Display::toolbarButton(
509
        get_lang('Advanced search'),
510
        '/main/admin/course_list.php?'.http_build_query(['search' => 'advanced']),
511
        ActionIcon::SEARCH,
512
        'plain'
513
    );
514
515
    // Create a filter by session
516
    $sessionFilter = new FormValidator(
517
        'course_filter',
518
        'get',
519
        '',
520
        '',
521
        [],
522
        FormValidator::LAYOUT_INLINE
523
    );
524
    $url = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=search_session';
525
    $sessionSelect = $sessionFilter->addSelectAjax(
526
        'session_name',
527
        get_lang('Search course by session'),
528
        [],
529
        ['id' => 'session_name', 'url' => $url]
530
    );
531
532
    if (!empty($sessionId)) {
533
        $sessionInfo = SessionManager::fetch($sessionId);
534
        $sessionSelect->addOption(
535
            $sessionInfo['name'],
536
            $sessionInfo['id'],
537
            ['selected' => 'selected']
538
        );
539
    }
540
541
    $courseListUrl = api_get_self();
542
    $actions1 = Display::url(
543
        Display::getMdiIcon(
544
            ToolIcon::COURSE,
545
            'ch-tool-icon-gradient',
546
            null,
547
            32,
548
            get_lang('Create a course')
549
        ),
550
        api_get_path(WEB_CODE_PATH).'admin/course_add.php'
551
    );
552
553
    if ('true' === api_get_setting('course_validation')) {
554
        $actions1 .= Display::url(
555
            Display::getMdiIcon(
556
                'book-heart-outline',
557
                'ch-tool-icon',
558
                null,
559
                ICON_SIZE_MEDIUM,
560
                get_lang('Review incoming course requests')
561
            ),
562
            api_get_path(WEB_CODE_PATH).'admin/course_request_review.php'
563
        );
564
    }
565
566
    $actions2 = $form->returnForm();
567
    $actions3 = $sessionFilter->returnForm();
568
    $actions4 = $advanced;
569
    $actions4 .= '
570
    <script>
571
        $(function() {
572
            $("#session_name").on("change", function() {
573
                var sessionId = $(this).val();
574
                if (!sessionId) {
575
                    return;
576
                }
577
                window.location = "'.$courseListUrl.'?session_id="+sessionId;
578
            });
579
        });
580
    </script>';
581
582
    $actions = Display::toolbarAction('toolbar', [$actions1, $actions3.$actions4.$actions2]);
583
584
    // Create a sortable table with the course data
585
    $table = new SortableTable(
586
        'courses',
587
        'get_number_of_courses',
588
        'get_course_data',
589
        2,
590
        20,
591
        'ASC',
592
        'course-list'
593
    );
594
595
    $parameters = [
596
        'sec_token' => Security::get_token(),
597
    ];
598
599
    if (isset($_GET['keyword'])) {
600
        $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
601
    } elseif (isset($_GET['keyword_code'])) {
602
        $parameters['keyword_code'] = Security::remove_XSS($_GET['keyword_code']);
603
        $parameters['keyword_title'] = Security::remove_XSS($_GET['keyword_title']);
604
        if (isset($_GET['keyword_category'])) {
605
            $parameters['keyword_category'] = Security::remove_XSS($_GET['keyword_category']);
606
        }
607
        $parameters['keyword_language'] = Security::remove_XSS($_GET['keyword_language']);
608
        $parameters['keyword_visibility'] = Security::remove_XSS($_GET['keyword_visibility']);
609
        $parameters['keyword_subscribe'] = Security::remove_XSS($_GET['keyword_subscribe']);
610
        $parameters['keyword_unsubscribe'] = Security::remove_XSS($_GET['keyword_unsubscribe']);
611
    }
612
613
    $table->set_additional_parameters($parameters);
614
615
    $table->set_header(0, '', false, 'width="8px"');
616
    $table->set_header(1, get_lang('Title'), true, null, ['class' => 'title']);
617
    $table->set_header(2, get_lang('Course code'));
618
    $table->set_header(3, get_lang('Language'), false, 'width="70px"');
619
    $table->set_header(4, get_lang('Categories'));
620
    $table->set_header(5, get_lang('Registr. allowed'), true, 'width="60px"');
621
    $table->set_header(6, get_lang('Unreg. allowed'), false, 'width="50px"');
622
    $table->set_header(
623
        7,
624
        get_lang('Action'),
625
        false,
626
        null,
627
        ['class' => 'td_actions']
628
    );
629
    $table->set_form_actions(
630
        ['delete_courses' => get_lang('Delete selected course(s)')],
631
        'course'
632
    );
633
634
    $tab = CourseManager::getCourseListTabs('simple');
635
636
    $content .= $tab.$table->return_table();
637
638
    // JS helper to ask for exclusive document deletion both for single and bulk delete.
639
    $deleteDocsMessage = addslashes(
640
        get_lang(
641
            'When deleting a course or multiple selected courses, any documents that are only used in those course(s) (if any) will normally be kept as orphan files and will remain visible in the "File information" tool (platform admin only). Click "OK" if you also want to permanently delete those orphan files from disk; click "Cancel" to keep them as orphan files.'
642
        )
643
    );
644
645
    // Fallback confirmation text; SortableTable uses data-confirm on the link.
646
    $baseConfirmMessage = addslashes(get_lang('Please confirm your choice'));
647
648
    $content .= '<script>
649
(function () {
650
    var docsMsg = "'.$deleteDocsMessage.'";
651
    var defaultConfirmMsg = "'.$baseConfirmMessage.'";
652
653
    // Single-course delete (trash icon per row)
654
    window.confirmDeleteCourseWithDocs = function (link) {
655
        var baseMsg = link.getAttribute("data-confirm") || defaultConfirmMsg;
656
657
        // Confirm course deletion.
658
        if (!window.confirm(baseMsg)) {
659
            return false;
660
        }
661
662
        // Ask about orphan documents on disk.
663
        if (window.confirm(docsMsg)) {
664
            if (link.href.indexOf("delete_docs=0") !== -1) {
665
                link.href = link.href.replace("delete_docs=0", "delete_docs=1");
666
            } else if (link.href.indexOf("delete_docs=") === -1) {
667
                var sep = link.href.indexOf("?") === -1 ? "?" : "&";
668
                link.href = link.href + sep + "delete_docs=1";
669
            }
670
        }
671
672
        return true;
673
    };
674
675
    // Bulk delete (SortableTable dropdown)
676
    function wrapActionClick() {
677
        // Ensure we only wrap once and only if action_click exists.
678
        if (!window.action_click || window.action_click.__wrappedForCourseList) {
679
            return;
680
        }
681
682
        var originalActionClick = window.action_click;
683
        var docsMsgLocal = docsMsg;
684
685
        window.action_click = function (el, formId) {
686
            var action = el.getAttribute("data-action");
687
            var confirmMsg = el.getAttribute("data-confirm") || defaultConfirmMsg;
688
689
            // Intercept only the bulk delete of this page.
690
            if (formId === "form_courses_id" && action === "delete_courses") {
691
                var form = document.getElementById(formId);
692
                if (!form) {
693
                    return false;
694
                }
695
696
                // 1) Confirm deletion of selected courses.
697
                if (confirmMsg && !window.confirm(confirmMsg)) {
698
                    return false;
699
                }
700
701
                // 2) Ask if orphan documents should also be deleted from disk.
702
                var deleteDocs = window.confirm(docsMsgLocal);
703
704
                // Ensure "action" hidden field exists and is set.
705
                var actionInput = form.querySelector(\'input[name="action"]\');
706
                if (!actionInput) {
707
                    actionInput = document.createElement("input");
708
                    actionInput.type = "hidden";
709
                    actionInput.name = "action";
710
                    form.appendChild(actionInput);
711
                }
712
                actionInput.value = action;
713
714
                // If user accepted the docs deletion, set the delete_docs flag.
715
                if (deleteDocs) {
716
                    var deleteDocsInput = form.querySelector(\'input[name="delete_docs"]\');
717
                    if (!deleteDocsInput) {
718
                        deleteDocsInput = document.createElement("input");
719
                        deleteDocsInput.type = "hidden";
720
                        deleteDocsInput.name = "delete_docs";
721
                        form.appendChild(deleteDocsInput);
722
                    }
723
                    deleteDocsInput.value = "1";
724
                }
725
726
                form.submit();
727
                return false;
728
            }
729
730
            // Fallback: keep original behavior for any other action/form.
731
            return originalActionClick(el, formId);
732
        };
733
734
        window.action_click.__wrappedForCourseList = true;
735
    }
736
737
    if (document.readyState === "loading") {
738
        document.addEventListener("DOMContentLoaded", wrapActionClick);
739
    } else {
740
        wrapActionClick();
741
    }
742
})();
743
</script>';
744
745
}
746
747
$tpl = new Template($tool_name);
748
$tpl->assign('actions', $actions);
749
$tpl->assign('message', $message);
750
$tpl->assign('content', $content);
751
$tpl->display_one_col_template();
752