CoursesAndSessionsCatalog::getPageNumberItem()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 6
nop 8
dl 0
loc 29
rs 9.2222
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\ExtraField;
6
use Chamilo\CoreBundle\Entity\Repository\SequenceResourceRepository;
7
use Chamilo\CoreBundle\Entity\SequenceResource;
8
use Chamilo\CoreBundle\Entity\SessionRelCourse;
9
use Chamilo\CoreBundle\Entity\Tag;
10
use Doctrine\ORM\Query\Expr\Join;
11
use ExtraField as ExtraFieldModel;
12
13
/**
14
 * @todo change class name
15
 */
16
class CoursesAndSessionsCatalog
17
{
18
    public const PAGE_LENGTH = 12;
19
20
    /**
21
     * Check the configuration for the courses and sessions catalog.
22
     */
23
    public static function is(int $value = CATALOG_COURSES): bool
24
    {
25
        $showCoursesSessions = (int) api_get_setting('catalog_show_courses_sessions');
26
27
        return $showCoursesSessions == $value;
28
    }
29
30
    /**
31
     * Check whether to display the sessions list.
32
     */
33
    public static function showSessions(): bool
34
    {
35
        $catalogShow = (int) api_get_setting('catalog_show_courses_sessions');
36
37
        return $catalogShow == CATALOG_SESSIONS || $catalogShow == CATALOG_COURSES_SESSIONS;
38
    }
39
40
    /**
41
     * Check whether to display the courses list.
42
     */
43
    public static function showCourses(): bool
44
    {
45
        $catalogShow = (int) api_get_setting('catalog_show_courses_sessions');
46
47
        return $catalogShow == CATALOG_COURSES || $catalogShow == CATALOG_COURSES_SESSIONS;
48
    }
49
50
    /**
51
     * @return array
52
     */
53
    public static function getCoursesToAvoid()
54
    {
55
        $TABLE_COURSE_FIELD = Database::get_main_table(TABLE_EXTRA_FIELD);
56
        $TABLE_COURSE_FIELD_VALUE = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
57
58
        // Check special courses
59
        $courseListToAvoid = CourseManager::get_special_course_list();
60
61
        $categoryToAvoid = api_get_configuration_value('course_category_code_to_use_as_model');
62
        if (!empty($categoryToAvoid) && api_is_student()) {
63
            $coursesInCategoryToAvoid = CourseCategory::getCoursesInCategory($categoryToAvoid, '', false);
64
            if (!empty($coursesInCategoryToAvoid)) {
65
                foreach ($coursesInCategoryToAvoid as $courseToAvoid) {
66
                    $courseListToAvoid[] = $courseToAvoid['id'];
67
                }
68
            }
69
        }
70
71
        // Checks "hide_from_catalog" extra field
72
        $extraFieldType = ExtraField::COURSE_FIELD_TYPE;
73
74
        $sql = "SELECT item_id FROM $TABLE_COURSE_FIELD_VALUE tcfv
75
                INNER JOIN $TABLE_COURSE_FIELD tcf
76
                ON tcfv.field_id =  tcf.id
77
                WHERE
78
                    tcf.extra_field_type = $extraFieldType AND
79
                    tcf.variable = 'hide_from_catalog' AND
80
                    tcfv.value = 1
81
                ";
82
83
        $result = Database::query($sql);
84
        if (Database::num_rows($result) > 0) {
85
            while ($row = Database::fetch_array($result)) {
86
                $courseListToAvoid[] = $row['item_id'];
87
            }
88
        }
89
90
        return $courseListToAvoid;
91
    }
92
93
    public static function getCoursesToShowInCatalogue()
94
    {
95
        $tblCourseField = Database::get_main_table(TABLE_EXTRA_FIELD);
96
        $tblCourseFieldValue = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
97
        $courseListToShow = [];
98
99
        // Checks "show_in_catalog" extra field
100
        $extraFieldType = ExtraField::COURSE_FIELD_TYPE;
101
        $sql = "SELECT item_id FROM $tblCourseFieldValue tcfv
102
                INNER JOIN $tblCourseField tcf
103
                ON tcfv.field_id =  tcf.id
104
                WHERE
105
                    tcf.extra_field_type = $extraFieldType AND
106
                    tcf.variable = 'show_in_catalogue' AND
107
                    tcfv.value = 1
108
                ";
109
110
        $result = Database::query($sql);
111
        if (Database::num_rows($result) > 0) {
112
            while ($row = Database::fetch_array($result)) {
113
                $courseListToShow[] = $row['item_id'];
114
            }
115
        }
116
117
        return $courseListToShow;
118
    }
119
120
    /**
121
     * @return string
122
     */
123
    public static function getCoursesToShowInCatalogueCondition()
124
    {
125
        $categoriesToShow = api_get_configuration_value('courses_catalogue_show_only_category');
126
        $coursesToShow = api_get_configuration_value('show_courses_in_catalogue');
127
        $coursesCategoryInCatalogue = [];
128
        if (!empty($categoriesToShow)) {
129
            foreach ($categoriesToShow as $categoryCode) {
130
                $courseCategories = CourseCategory::getCoursesInCategory($categoryCode, '', false);
131
                if (!empty($courseCategories)) {
132
                    foreach ($courseCategories as $course) {
133
                        $coursesCategoryInCatalogue[] = $course['id'];
134
                    }
135
                }
136
            }
137
        }
138
139
        $courseListToShow = self::getCoursesToShowInCatalogue();
140
        $courses = [];
141
        if (!empty($coursesCategoryInCatalogue)) {
142
            foreach ($coursesCategoryInCatalogue as $courseId) {
143
                if ($coursesToShow && !in_array($courseId, $courseListToShow)) {
144
                    continue;
145
                }
146
                $courses[] = '"'.$courseId.'"';
147
            }
148
        }
149
150
        if ($coursesToShow && !empty($courseListToShow)) {
151
            foreach ($courseListToShow as $courseId) {
152
                if (!empty($categoriesToShow) && !in_array($courseId, $coursesCategoryInCatalogue)) {
153
                    continue;
154
                }
155
                $courses[] = '"'.$courseId.'"';
156
            }
157
        }
158
159
        if (empty($courses)) {
160
            if ($categoriesToShow || (!$categoriesToShow && $coursesToShow)) {
161
                $courses[] = 0;
162
            }
163
        }
164
165
        $condition = '';
166
        if (!empty($courses)) {
167
            $condition = ' AND course.id IN ('.implode(',', $courses).')';
168
        }
169
170
        return $condition;
171
    }
172
173
    /**
174
     * @return string
175
     */
176
    public static function getAvoidCourseCondition()
177
    {
178
        $courseListToAvoid = self::getCoursesToAvoid();
179
        $condition = '';
180
        if (!empty($courseListToAvoid)) {
181
            $courses = [];
182
            foreach ($courseListToAvoid as $courseId) {
183
                $courses[] = '"'.$courseId.'"';
184
            }
185
            if (!empty($courses)) {
186
                $condition = ' AND course.id NOT IN ('.implode(',', $courses).')';
187
            }
188
        }
189
190
        return $condition;
191
    }
192
193
    /**
194
     * Get available le courses count.
195
     *
196
     * @param int $accessUrlId (optional)
197
     *
198
     * @return int Number of courses
199
     */
200
    public static function countAvailableCoursesToShowInCatalog($accessUrlId = 1)
201
    {
202
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
203
        $tableCourseRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
204
        $courseToAvoidCondition = self::getAvoidCourseCondition();
205
        $courseToShowCondition = self::getCoursesToShowInCatalogueCondition();
206
        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true);
207
208
        $accessUrlId = (int) $accessUrlId;
209
        if (empty($accessUrlId)) {
210
            $accessUrlId = 1;
211
        }
212
213
        $sql = "SELECT count(course.id)
214
                FROM $tableCourse course
215
                INNER JOIN $tableCourseRelAccessUrl u
216
                ON (course.id = u.c_id)
217
                WHERE
218
                    u.access_url_id = $accessUrlId AND
219
                    course.visibility != 0 AND
220
                    course.visibility != 4
221
                    $courseToAvoidCondition
222
                    $courseToShowCondition
223
                    $visibilityCondition
224
                ";
225
226
        $res = Database::query($sql);
227
        $row = Database::fetch_row($res);
228
229
        return $row[0];
230
    }
231
232
    public static function getCourseCategoriesTree()
233
    {
234
        $urlId = 1;
235
        if (api_is_multiple_url_enabled()) {
236
            $urlId = api_get_current_access_url_id();
237
        }
238
239
        $countCourses = self::countAvailableCoursesToShowInCatalog($urlId);
240
        $categories = [];
241
        $list = [];
242
243
        $categories['ALL'] = [
244
            'id' => 0,
245
            'name' => get_lang('DisplayAll'),
246
            'code' => 'ALL',
247
            'parent_id' => null,
248
            'tree_pos' => 0,
249
            'number_courses' => $countCourses,
250
            'level' => 0,
251
        ];
252
253
        $allCategories = CourseCategory::getAllCategories();
254
        $categoryToAvoid = '';
255
        if (api_is_student()) {
256
            $categoryToAvoid = api_get_configuration_value('course_category_code_to_use_as_model');
257
        }
258
259
        $showOnlyCategory = api_get_configuration_value('courses_catalogue_show_only_category');
260
        foreach ($allCategories as $category) {
261
            $categoryCode = $category['code'];
262
            if (!(empty($showOnlyCategory) || in_array($categoryCode, $showOnlyCategory))) {
263
                continue;
264
            }
265
            if (!empty($categoryToAvoid) && $categoryToAvoid == $categoryCode) {
266
                continue;
267
            }
268
269
            if (empty($category['parent_id'])) {
270
                $list[$categoryCode] = $category;
271
                $list[$categoryCode]['level'] = 0;
272
                list($subList, $childrenCount) = self::buildCourseCategoryTree($allCategories, $categoryCode, 0);
273
                foreach ($subList as $item) {
274
                    $list[$item['code']] = $item;
275
                }
276
                // Real course count
277
                $countCourses = CourseCategory::countCoursesInCategory($categoryCode);
278
                $list[$categoryCode]['number_courses'] = $childrenCount + $countCourses;
279
            }
280
        }
281
282
        // count courses that are in no category
283
        $countCourses = CourseCategory::countCoursesInCategory('NONE');
284
        $categories['NONE'] = [
285
            'id' => 0,
286
            'name' => get_lang('WithoutCategory'),
287
            'code' => 'NONE',
288
            'parent_id' => null,
289
            'tree_pos' => 0,
290
            'children_count' => 0,
291
            'auth_course_child' => true,
292
            'auth_cat_child' => true,
293
            'number_courses' => $countCourses,
294
            'level' => 0,
295
        ];
296
297
        return array_merge($list, $categories);
298
    }
299
300
    /**
301
     * Return LIMIT to filter SQL query.
302
     *
303
     * @param array $limit
304
     *
305
     * @return string
306
     */
307
    public static function getLimitFilterFromArray($limit)
308
    {
309
        $limitFilter = '';
310
        if (!empty($limit) && is_array($limit)) {
311
            $limitStart = isset($limit['start']) ? (int) $limit['start'] : 0;
312
            $limitLength = isset($limit['length']) ? (int) $limit['length'] : 12;
313
            $limitFilter = 'LIMIT '.$limitStart.', '.$limitLength;
314
        }
315
316
        return $limitFilter;
317
    }
318
319
    /**
320
     * @param int   $randomValue
321
     * @param array $limit       will be used if $randomValue is not set.
322
     *                           This array should contain 'start' and 'length' keys
323
     *
324
     * @return array
325
     */
326
    public static function getCoursesInCategory(string $categoryCode, $randomValue = null, $limit = [])
327
    {
328
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
329
        $avoidCoursesCondition = self::getAvoidCourseCondition();
330
        $showCoursesCondition = self::getCoursesToShowInCatalogueCondition();
331
        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true);
332
333
        if (!empty($randomValue)) {
334
            $randomValue = (int) $randomValue;
335
336
            $sql = "SELECT COUNT(*) FROM $tbl_course";
337
            $result = Database::query($sql);
338
            list($num_records) = Database::fetch_row($result);
339
340
            if (api_is_multiple_url_enabled()) {
341
                $urlId = api_get_current_access_url_id();
342
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
343
344
                $urlCondition = ' access_url_id = '.$urlId.' ';
345
                $allowBaseCategories = api_get_configuration_value('allow_base_course_category');
346
                if ($allowBaseCategories) {
347
                    $urlCondition = ' (access_url_id = '.$urlId.' OR access_url_id = 1)  ';
348
                }
349
350
                $sql = "SELECT COUNT(*)
351
                        FROM $tbl_course course
352
                        INNER JOIN $tbl_url_rel_course as url_rel_course
353
                        ON (url_rel_course.c_id = course.id)
354
                        WHERE access_url_id = $urlId $showCoursesCondition";
355
                $result = Database::query($sql);
356
                list($num_records) = Database::fetch_row($result);
357
358
                $sql = "SELECT course.id, course.id as real_id
359
                        FROM $tbl_course course
360
                        INNER JOIN $tbl_url_rel_course as url_rel_course
361
                        ON (url_rel_course.c_id = course.id)
362
                        WHERE
363
                            $urlCondition AND
364
                            RAND()*$num_records< $randomValue
365
                            $avoidCoursesCondition
366
                            $showCoursesCondition
367
                            $visibilityCondition
368
                        ORDER BY RAND()
369
                        LIMIT 0, $randomValue";
370
            } else {
371
                $sql = "SELECT id, id as real_id
372
                        FROM $tbl_course course
373
                        WHERE
374
                            RAND()*$num_records< $randomValue
375
                            $avoidCoursesCondition
376
                            $showCoursesCondition
377
                            $visibilityCondition
378
                        ORDER BY RAND()
379
                        LIMIT 0, $randomValue";
380
            }
381
382
            $result = Database::query($sql);
383
            $id_in = null;
384
            while (list($id) = Database::fetch_row($result)) {
385
                if ($id_in) {
386
                    $id_in .= ",$id";
387
                } else {
388
                    $id_in = "$id";
389
                }
390
            }
391
            if (null === $id_in) {
392
                return [];
393
            }
394
            $sql = "SELECT *, id as real_id FROM $tbl_course WHERE id IN($id_in)";
395
        } else {
396
            $limitFilter = self::getLimitFilterFromArray($limit);
397
            $categoryCode = Database::escape_string($categoryCode);
398
            $listCode = self::childrenCategories($categoryCode);
399
            $conditionCode = ' ';
400
401
            if (empty($listCode)) {
402
                if ($categoryCode === 'NONE') {
403
                    $conditionCode .= " category_code='' ";
404
                } else {
405
                    $conditionCode .= " category_code='$categoryCode' ";
406
                }
407
            } else {
408
                foreach ($listCode as $code) {
409
                    $conditionCode .= " category_code='$code' OR ";
410
                }
411
                $conditionCode .= " category_code='$categoryCode' ";
412
            }
413
414
            if (empty($categoryCode) || $categoryCode === 'ALL') {
415
                $sql = "SELECT *, id as real_id
416
                        FROM $tbl_course course
417
                        WHERE
418
                          1=1
419
                          $avoidCoursesCondition
420
                          $showCoursesCondition
421
                          $visibilityCondition
422
                        ORDER BY title $limitFilter ";
423
            } else {
424
                $sql = "SELECT *, id as real_id FROM $tbl_course course
425
                        WHERE
426
                            $conditionCode
427
                            $avoidCoursesCondition
428
                            $showCoursesCondition
429
                            $visibilityCondition
430
                        ORDER BY title $limitFilter ";
431
            }
432
433
            // Showing only the courses of the current Chamilo access_url_id
434
            if (api_is_multiple_url_enabled()) {
435
                $urlId = api_get_current_access_url_id();
436
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
437
438
                $urlCondition = ' access_url_id = '.$urlId.' ';
439
                if ($categoryCode !== 'ALL') {
440
                    $sql = "SELECT *, course.id real_id
441
                            FROM $tbl_course as course
442
                            INNER JOIN $tbl_url_rel_course as url_rel_course
443
                            ON (url_rel_course.c_id = course.id)
444
                            WHERE
445
                                $urlCondition AND
446
                                $conditionCode
447
                                $avoidCoursesCondition
448
                                $showCoursesCondition
449
                                $visibilityCondition
450
                            ORDER BY title $limitFilter";
451
                } else {
452
                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
453
                            INNER JOIN $tbl_url_rel_course as url_rel_course
454
                            ON (url_rel_course.c_id = course.id)
455
                            WHERE
456
                                $urlCondition
457
                                $avoidCoursesCondition
458
                                $showCoursesCondition
459
                                $visibilityCondition
460
                            ORDER BY title $limitFilter";
461
                }
462
            }
463
        }
464
465
        $result = Database::query($sql);
466
        $courses = [];
467
        while ($row = Database::fetch_array($result)) {
468
            $row['registration_code'] = !empty($row['registration_code']);
469
            $count_users = CourseManager::get_users_count_in_course($row['code']);
470
471
            if ($row['tutor_name'] == '0') {
472
                $row['tutor_name'] = get_lang('NoManager');
473
            }
474
475
            $courses[] = [
476
                'real_id' => $row['real_id'],
477
                'point_info' => CourseManager::get_course_ranking($row['id'], 0),
478
                'code' => $row['code'],
479
                'directory' => $row['directory'],
480
                'visual_code' => $row['visual_code'],
481
                'title' => $row['title'],
482
                'tutor' => $row['tutor_name'],
483
                'subscribe' => $row['subscribe'],
484
                'unsubscribe' => $row['unsubscribe'],
485
                'registration_code' => $row['registration_code'],
486
                'creation_date' => $row['creation_date'],
487
                'visibility' => $row['visibility'],
488
                'category' => $row['category_code'],
489
                'count_users' => $count_users,
490
            ];
491
        }
492
493
        return $courses;
494
    }
495
496
    /**
497
     * Search the courses database for a course that matches the search term.
498
     * The search is done on the code, title and tutor field of the course table.
499
     *
500
     * @param string $categoryCode
501
     * @param string $keyword              The string that the user submitted
502
     * @param array  $limit
503
     * @param bool   $justVisible          search only on visible courses in the catalogue
504
     * @param array  $conditions
505
     * @param string $courseLanguageFilter search only courses in the indicated language
506
     *
507
     * @return array an array containing a list of all the courses matching the the search term
508
     */
509
    public static function searchCourses(
510
        $categoryCode,
511
        $keyword,
512
        $limit,
513
        $justVisible = false,
514
        $conditions = [],
515
        $courseLanguageFilter = null
516
    ) {
517
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
518
        $limitFilter = self::getLimitFilterFromArray($limit);
519
        $avoidCoursesCondition = self::getAvoidCourseCondition();
520
        $showCoursesCondition = self::getCoursesToShowInCatalogueCondition();
521
        $visibilityCondition = $justVisible ? CourseManager::getCourseVisibilitySQLCondition('course', true) : '';
522
523
        $keyword = Database::escape_string($keyword);
524
        $categoryCode = Database::escape_string($categoryCode);
525
526
        $sqlInjectJoins = '';
527
        $courseLanguageWhere = '';
528
        $where = 'AND 1 = 1 ';
529
        $sqlInjectWhere = '';
530
        $injectExtraFields = '1';
531
        if (!empty($conditions)) {
532
            $sqlInjectJoins = $conditions['inject_joins'];
533
            $where = $conditions['where'];
534
            $sqlInjectWhere = $conditions['inject_where'];
535
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
536
            $injectExtraFields = rtrim($injectExtraFields, ', ');
537
        }
538
539
        // If have courseLanguageFilter, search for it
540
        if (!empty($courseLanguageFilter)) {
541
            $courseLanguageFilter = Database::escape_string($courseLanguageFilter);
542
            $courseLanguageWhere = "AND course.course_language = '$courseLanguageFilter'";
543
        }
544
545
        $categoryFilter = '';
546
        if ($categoryCode === 'ALL' || empty($categoryCode)) {
547
            // Nothing to do
548
        } elseif ($categoryCode === 'NONE') {
549
            $categoryFilter = ' AND category_code = "" ';
550
        } else {
551
            $categoryFilter = ' AND category_code = "'.$categoryCode.'" ';
552
        }
553
554
        //$sql = "SELECT DISTINCT course.*, $injectExtraFields
555
        $sql = "SELECT DISTINCT(course.id)
556
                FROM $courseTable course
557
                $sqlInjectJoins
558
                WHERE (
559
                        course.code LIKE '%".$keyword."%' OR
560
                        course.title LIKE '%".$keyword."%' OR
561
                        course.tutor_name LIKE '%".$keyword."%'
562
                    )
563
                    $where
564
                    $categoryFilter
565
                    $sqlInjectWhere
566
                    $courseLanguageWhere
567
                    $avoidCoursesCondition
568
                    $showCoursesCondition
569
                    $visibilityCondition
570
                ORDER BY title, visual_code ASC
571
                $limitFilter
572
                ";
573
574
        if (api_is_multiple_url_enabled()) {
575
            $urlId = api_get_current_access_url_id();
576
            if (-1 != $urlId) {
577
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
578
                $urlCondition = ' access_url_id = '.$urlId.' AND';
579
                $allowBaseCategories = api_get_configuration_value('allow_base_course_category');
580
                if ($allowBaseCategories) {
581
                    $urlCondition = ' (access_url_id = '.$urlId.' OR access_url_id = 1) AND ';
582
                }
583
                //SELECT DISTINCT course.*, $injectExtraFields
584
                $sql = "SELECT DISTINCT(course.id)
585
                        FROM $courseTable as course
586
                        INNER JOIN $tbl_url_rel_course as url_rel_course
587
                        ON (url_rel_course.c_id = course.id)
588
                        $sqlInjectJoins
589
                        WHERE
590
                            access_url_id = $urlId AND
591
                            (
592
                                code LIKE '%".$keyword."%' OR
593
                                title LIKE '%".$keyword."%' OR
594
                                tutor_name LIKE '%".$keyword."%'
595
                            )
596
                            $where
597
                            $categoryFilter
598
                            $sqlInjectWhere
599
                            $courseLanguageWhere
600
                            $avoidCoursesCondition
601
                            $showCoursesCondition
602
                            $visibilityCondition
603
                        ORDER BY title, visual_code ASC
604
                        $limitFilter
605
                       ";
606
            }
607
        }
608
609
        $result = Database::query($sql);
610
        $courses = [];
611
        while ($row = Database::fetch_array($result)) {
612
            $courseId = $row['id'];
613
            $courseInfo = api_get_course_info_by_id($courseId);
614
            if (empty($courseInfo)) {
615
                continue;
616
            }
617
            $courseCode = $courseInfo['code'];
618
619
            $countUsers = CourseManager::get_user_list_from_course_code(
620
                $courseCode,
621
                0,
622
                null,
623
                null,
624
                null,
625
                true
626
            );
627
628
            $courseInfo['point_info'] = CourseManager::get_course_ranking($courseId, 0);
629
            $courseInfo['tutor'] = $courseInfo['tutor_name'];
630
            $courseInfo['registration_code'] = !empty($courseInfo['registration_code']);
631
            $courseInfo['count_users'] = $countUsers;
632
            $courseInfo['course_language'] = api_get_language_info(
633
                api_get_language_id($courseInfo['course_language'])
634
            )['original_name'];
635
636
            $courses[] = $courseInfo;
637
        }
638
639
        return $courses;
640
    }
641
642
    /**
643
     * Gets extra fields listed in configuration option course_catalog_settings/extra_field_sort_options sorting order.
644
     *
645
     * @return array "extra_field_$id" => order (1 = ascending, -1 = descending)
646
     */
647
    public static function courseExtraFieldSortingOrder()
648
    {
649
        $order = [];
650
        $variableOrder = api_get_configuration_sub_value('course_catalog_settings/extra_field_sort_options', []);
651
        foreach (self::getCourseExtraFieldsAvailableForSorting() as $extraField) {
652
            $order['extra_field_'.$extraField->getId()] = $variableOrder[$extraField->getVariable()];
653
        }
654
655
        return $order;
656
    }
657
658
    /**
659
     * Gets the extra fields listed in configuration option course_catalog_settings/extra_field_sort_options.
660
     *
661
     * @return ExtraField[]
662
     */
663
    public static function getCourseExtraFieldsAvailableForSorting()
664
    {
665
        $variables = array_keys(
666
            api_get_configuration_sub_value('course_catalog_settings/extra_field_sort_options', [])
667
        );
668
        if (is_array($variables) && !empty($variables)) {
669
            return ExtraField::getExtraFieldsFromVariablesOrdered($variables, ExtraField::COURSE_FIELD_TYPE);
670
        }
671
672
        return [];
673
    }
674
675
    /**
676
     * Builds the list of possible course standard sort criteria.
677
     *
678
     * @return array option name => order (1 = ascending, -1 = descending)
679
     */
680
    public static function courseStandardSortOrder()
681
    {
682
        return api_get_configuration_sub_value(
683
            'course_catalog_settings/standard_sort_options',
684
            [
685
                'title' => 1,
686
                'creation_date' => -1,
687
                'count_users' => -1, // subscription count
688
                'point_info/point_average' => -1, // average score
689
                'point_info/total_score' => -1, // score sum
690
                'point_info/users' => -1, // vote count
691
            ]
692
        );
693
    }
694
695
    /**
696
     * Builds the list of possible course sort criteria to be used in an HTML select element.
697
     *
698
     * @return array select option name => display text
699
     */
700
    public static function courseSortOptions()
701
    {
702
        /** @var $extraFields ExtraField[] */
703
        $standardLabels = [
704
            'title' => get_lang('Title'),
705
            'creation_date' => get_lang('CreationDate'),
706
            'count_users' => get_lang('SubscriptionCount'),
707
            'point_info/point_average' => get_lang('PointAverage'),
708
            'point_info/total_score' => get_lang('TotalScore'),
709
            'point_info/users' => get_lang('VoteCount'),
710
        ];
711
        $options = [];
712
        foreach (array_keys(self::courseStandardSortOrder()) as $name) {
713
            $options[$name] = $standardLabels[$name] ?: $name;
714
        }
715
        foreach (self::getCourseExtraFieldsAvailableForSorting() as $extraField) {
716
            $options['extra_field_'.$extraField->getId()] = $extraField->getDisplayText();
717
        }
718
719
        return $options;
720
    }
721
722
    public static function courseSortOrder()
723
    {
724
        return self::courseStandardSortOrder() + self::courseExtraFieldSortingOrder();
725
    }
726
727
    /**
728
     * Wrapper for self::searchCourses which locally sorts the results according to $sortKey.
729
     *
730
     * @param string   $categoryCode         can be 'ALL', 'NONE' or any existing course category code
731
     * @param string   $keyword              search pattern to be found in course code, title or tutor_name
732
     * @param array    $limit                associative array generated by \CoursesAndSessionsCatalog::getLimitArray()
733
     * @param bool     $justVisible          search only on visible courses in the catalogue
734
     * @param array    $conditions           associative array generated using \ExtraField::parseConditions
735
     * @param string[] $sortKeys             a subset of the keys of the array returned by courseSortOptions()
736
     * @param string   $courseLanguageFilter search only courses in the indicated language
737
     *
738
     * @return array list of all the courses matching the the search term
739
     */
740
    public static function searchAndSortCourses(
741
        $categoryCode,
742
        $keyword,
743
        $limit,
744
        $justVisible = false,
745
        $conditions = [],
746
        $sortKeys = [],
747
        $courseLanguageFilter = null
748
    ) {
749
        // Get ALL matching courses (no limit)
750
        $courses = self::searchCourses($categoryCode, $keyword, null, $justVisible, $conditions, $courseLanguageFilter);
751
        // Do we have extra fields to sort on ?
752
        $extraFieldsToSortOn = [];
753
        foreach (self::getCourseExtraFieldsAvailableForSorting() as $extraField) {
754
            if (in_array('extra_field_'.$extraField->getId(), $sortKeys)) {
755
                $extraFieldsToSortOn[] = $extraField;
756
            }
757
        }
758
        if (!empty($extraFieldsToSortOn)) {
759
            // load extra field values and store them in $courses
760
            $courseIds = [];
761
            foreach ($courses as $course) {
762
                $courseIds[] = $course['real_id'];
763
            }
764
            $values = ExtraField::getValueForEachExtraFieldForEachItem($extraFieldsToSortOn, $courseIds);
765
            foreach ($courses as &$course) {
766
                $courseId = $course['real_id'];
767
                if (array_key_exists($courseId, $values)) {
768
                    foreach ($values[$courseId] as $extraFieldId => $value) {
769
                        $course['extra_field_'.$extraFieldId] = $value;
770
                    }
771
                }
772
            }
773
            unset($course);
774
        }
775
776
        // do we have $course['groupKey']['subKey'] to sort on, such as 'point_info/users' ?
777
        foreach ($sortKeys as $key) {
778
            if (false !== strpos($key, '/')) {
779
                foreach ($courses as &$course) {
780
                    $subValue = api_array_sub_value($course, $key);
781
                    if (!is_null($subValue)) {
782
                        $course[$key] = $subValue;
783
                    }
784
                }
785
                unset($course);
786
            }
787
        }
788
        $sortOrder = self::courseSortOrder();
789
        usort($courses, function ($a, $b) use ($sortKeys, $sortOrder) {
790
            foreach ($sortKeys as $key) {
791
                $valueA = array_key_exists($key, $a) ? $a[$key] : null;
792
                $valueB = array_key_exists($key, $b) ? $b[$key] : null;
793
                if ($valueA !== $valueB) {
794
                    $aIsLessThanB = (is_string($valueA) && is_string($valueB))
795
                        ? strtolower($valueA) < strtolower($valueB)
796
                        : $valueA < $valueB;
797
                    $reverseOrder = (array_key_exists($key, $sortOrder) && -1 === $sortOrder[$key]);
798
                    $aIsBeforeB = ($aIsLessThanB xor $reverseOrder);
799
800
                    return $aIsBeforeB ? -1 : 1;
801
                }
802
            }
803
804
            return 0;
805
        });
806
807
        return array_slice($courses, $limit['start'], $limit['length']);
808
    }
809
810
    /**
811
     * List the sessions.
812
     *
813
     * @param string $date
814
     * @param array  $limit
815
     * @param bool   $returnQueryBuilder
816
     * @param bool   $getCount
817
     *
818
     * @return array|\Doctrine\ORM\Query The session list
819
     */
820
    public static function browseSessions($date = null, $limit = [], $returnQueryBuilder = false, $getCount = false)
821
    {
822
        $urlId = api_get_current_access_url_id();
823
        $em = Database::getManager();
824
        $qb = $em->createQueryBuilder();
825
        $qb2 = $em->createQueryBuilder();
826
827
        $qb = $qb
828
            ->select('s')
829
            ->from('ChamiloCoreBundle:Session', 's')
830
            ->where(
831
                $qb->expr()->in(
832
                    's',
833
                    $qb2
834
                        ->select('s2')
835
                        ->from('ChamiloCoreBundle:AccessUrlRelSession', 'url')
836
                        ->join('ChamiloCoreBundle:Session', 's2')
837
                        ->where(
838
                            $qb->expr()->eq('url.sessionId ', 's2.id')
839
                        )->andWhere(
840
                            $qb->expr()->eq('url.accessUrlId ', $urlId))
841
                        ->getDQL()
842
                )
843
            )
844
            ->andWhere($qb->expr()->gt('s.nbrCourses', 0))
845
        ;
846
847
        if (!empty($date)) {
848
            $qb->andWhere(
849
                $qb->expr()->orX(
850
                    $qb->expr()->isNull('s.accessEndDate'),
851
                    $qb->expr()->andX(
852
                        $qb->expr()->isNotNull('s.accessStartDate'),
853
                        $qb->expr()->isNotNull('s.accessEndDate'),
854
                        $qb->expr()->lte('s.accessStartDate', $date),
855
                        $qb->expr()->gte('s.accessEndDate', $date)
856
                    ),
857
                    $qb->expr()->andX(
858
                        $qb->expr()->isNull('s.accessStartDate'),
859
                        $qb->expr()->isNotNull('s.accessEndDate'),
860
                        $qb->expr()->gte('s.accessEndDate', $date)
861
                    )
862
                )
863
            );
864
        }
865
866
        if ($getCount) {
867
            $qb->select('count(s)');
868
        }
869
870
        $qb = self::hideFromSessionCatalogCondition($qb);
871
872
        if (!empty($limit)) {
873
            $qb
874
                ->setFirstResult($limit['start'])
875
                ->setMaxResults($limit['length'])
876
            ;
877
        }
878
879
        $query = $qb->getQuery();
880
881
        if ($returnQueryBuilder) {
882
            return $query;
883
        }
884
885
        if ($getCount) {
886
            return $query->getSingleScalarResult();
887
        }
888
889
        return $query->getResult();
890
    }
891
892
    /**
893
     * @param \Doctrine\ORM\QueryBuilder $qb
894
     *
895
     * @return mixed
896
     */
897
    public static function hideFromSessionCatalogCondition($qb)
898
    {
899
        $em = Database::getManager();
900
        $qb3 = $em->createQueryBuilder();
901
902
        $extraField = new ExtraFieldModel('session');
903
        $extraFieldInfo = $extraField->get_handler_field_info_by_field_variable('hide_from_catalog');
904
        if (!empty($extraFieldInfo)) {
905
            $qb->andWhere(
906
                $qb->expr()->notIn(
907
                    's',
908
                    $qb3
909
                        ->select('s3')
910
                        ->from('ChamiloCoreBundle:ExtraFieldValues', 'fv')
911
                        ->innerJoin('ChamiloCoreBundle:Session', 's3', Join::WITH, 'fv.itemId = s3.id')
912
                        ->where(
913
                            $qb->expr()->eq('fv.field', $extraFieldInfo['id'])
914
                        )->andWhere(
915
                            $qb->expr()->eq('fv.value ', 1)
916
                        )
917
                        ->getDQL()
918
                )
919
            );
920
        }
921
922
        return $qb;
923
    }
924
925
    /**
926
     * Search sessions by the tags in their courses.
927
     *
928
     * @param string $termTag Term for search in tags
929
     * @param array  $limit   Limit info
930
     *
931
     * @return array The sessions
932
     */
933
    public static function browseSessionsByTags($termTag, array $limit, $getCount = false)
934
    {
935
        $em = Database::getManager();
936
        $qb = $em->createQueryBuilder();
937
938
        $urlId = api_get_current_access_url_id();
939
940
        $qb->select('s')
941
            ->distinct()
942
            ->from('ChamiloCoreBundle:Session', 's')
943
            ->innerJoin(
944
                'ChamiloCoreBundle:SessionRelCourse',
945
                'src',
946
                Join::WITH,
947
                's.id = src.session'
948
            )
949
            ->innerJoin(
950
                'ChamiloCoreBundle:AccessUrlRelSession',
951
                'url',
952
                Join::WITH,
953
                'url.sessionId = s.id'
954
            )
955
            ->innerJoin(
956
                'ChamiloCoreBundle:ExtraFieldRelTag',
957
                'frt',
958
                Join::WITH,
959
                'src.course = frt.itemId'
960
            )
961
            ->innerJoin(
962
                'ChamiloCoreBundle:Tag',
963
                't',
964
                Join::WITH,
965
                'frt.tagId = t.id'
966
            )
967
            ->innerJoin(
968
                'ChamiloCoreBundle:ExtraField',
969
                'f',
970
                Join::WITH,
971
                'frt.fieldId = f.id'
972
            )
973
            ->where(
974
                $qb->expr()->like('t.tag', ':tag')
975
            )
976
            ->andWhere(
977
                $qb->expr()->eq('f.extraFieldType', ExtraField::COURSE_FIELD_TYPE)
978
            )
979
            ->andWhere($qb->expr()->gt('s.nbrCourses', 0))
980
            ->andWhere($qb->expr()->eq('url.accessUrlId', $urlId))
981
            ->setParameter('tag', "$termTag%")
982
        ;
983
984
        if (!empty($limit)) {
985
            $qb
986
                ->setFirstResult($limit['start'])
987
                ->setMaxResults($limit['length'])
988
            ;
989
        }
990
991
        $qb = self::hideFromSessionCatalogCondition($qb);
992
993
        if ($getCount) {
994
            $qb->select('count(s)');
995
996
            return $qb->getQuery()->getSingleScalarResult();
997
        }
998
999
        return $qb->getQuery()->getResult();
1000
    }
1001
1002
    /**
1003
     * Search sessions by the title.
1004
     *
1005
     * @param string $keyword
1006
     * @param array  $limit   Limit info
1007
     *
1008
     * @return array The sessions
1009
     */
1010
    public static function getSessionsByName($keyword, array $limit, $getCount = false)
1011
    {
1012
        $em = Database::getManager();
1013
        $qb = $em->createQueryBuilder();
1014
        $urlId = api_get_current_access_url_id();
1015
1016
        $qb->select('s')
1017
            ->distinct()
1018
            ->from('ChamiloCoreBundle:Session', 's')
1019
            ->innerJoin(
1020
                'ChamiloCoreBundle:SessionRelCourse',
1021
                'src',
1022
                Join::WITH,
1023
                's.id = src.session'
1024
            )
1025
            ->innerJoin(
1026
                'ChamiloCoreBundle:AccessUrlRelSession',
1027
                'url',
1028
                Join::WITH,
1029
                'url.sessionId = s.id'
1030
            )
1031
            ->andWhere($qb->expr()->eq('url.accessUrlId', $urlId))
1032
            ->andWhere('s.name LIKE :keyword')
1033
            ->andWhere($qb->expr()->gt('s.nbrCourses', 0))
1034
            ->setParameter('keyword', "%$keyword%")
1035
        ;
1036
1037
        if (!empty($limit)) {
1038
            $qb
1039
                ->setFirstResult($limit['start'])
1040
                ->setMaxResults($limit['length']);
1041
        }
1042
1043
        $qb = self::hideFromSessionCatalogCondition($qb);
1044
1045
        if ($getCount) {
1046
            $qb->select('count(s)');
1047
1048
            return $qb->getQuery()->getSingleScalarResult();
1049
        }
1050
1051
        return $qb->getQuery()->getResult();
1052
    }
1053
1054
    /**
1055
     * Build a recursive tree of course categories.
1056
     *
1057
     * @param array $categories
1058
     * @param int   $parentId
1059
     * @param int   $level
1060
     *
1061
     * @return array
1062
     */
1063
    public static function buildCourseCategoryTree($categories, $parentId = 0, $level = 0)
1064
    {
1065
        $list = [];
1066
        $count = 0;
1067
        $level++;
1068
        foreach ($categories as $category) {
1069
            if (empty($category['parent_id'])) {
1070
                continue;
1071
            }
1072
            if ($category['parent_id'] == $parentId) {
1073
                $list[$category['code']] = $category;
1074
                $count += $category['number_courses'];
1075
                $list[$category['code']]['level'] = $level;
1076
                list($subList, $childrenCount) = self::buildCourseCategoryTree(
1077
                    $categories,
1078
                    $category['code'],
1079
                    $level
1080
                );
1081
                $list[$category['code']]['number_courses'] += $childrenCount;
1082
                foreach ($subList as $item) {
1083
                    $list[$item['code']] = $item;
1084
                }
1085
                $count += $childrenCount;
1086
            }
1087
        }
1088
1089
        return [$list, $count];
1090
    }
1091
1092
    /**
1093
     * List Code Search Category.
1094
     *
1095
     * @param string $code
1096
     *
1097
     * @return array
1098
     */
1099
    public static function childrenCategories($code)
1100
    {
1101
        $allCategories = CourseCategory::getAllCategories();
1102
        $list = [];
1103
        $row = [];
1104
1105
        if ($code !== 'ALL' && $code !== 'NONE') {
1106
            foreach ($allCategories as $category) {
1107
                if ($category['code'] === $code) {
1108
                    $list = self::buildCourseCategoryTree($allCategories, $category['code'], 0);
1109
                }
1110
            }
1111
            foreach ($list[0] as $item) {
1112
                $row[] = $item['code'];
1113
            }
1114
        }
1115
1116
        return $row;
1117
    }
1118
1119
    /**
1120
     * Get the image of a course for the catalog.
1121
     */
1122
    public static function returnThumbnail(array $course): string
1123
    {
1124
        $course_path = api_get_path(SYS_COURSE_PATH).$course['directory'];
1125
1126
        if (file_exists($course_path.'/course-pic.png')) {
1127
            // redimensioned image 85x85
1128
            return api_get_path(WEB_COURSE_PATH).$course['directory'].'/course-pic.png';
1129
        }
1130
1131
        // without picture
1132
        return Display::return_icon(
1133
            'session_default.png',
1134
            null,
1135
            null,
1136
            null,
1137
            null,
1138
            true
1139
        );
1140
    }
1141
1142
    public static function return_teacher(array $courseInfo): string
1143
    {
1144
        $teachers = CourseManager::getTeachersFromCourse($courseInfo['real_id']);
1145
        $length = count($teachers);
1146
1147
        if (!$length) {
1148
            return '';
1149
        }
1150
1151
        $html = '<div class="block-author">';
1152
        if ($length > 6) {
1153
            $html .= '<a
1154
            id="plist"
1155
            data-trigger="focus"
1156
            tabindex="0" role="button"
1157
            class="btn btn-default panel_popover"
1158
            data-toggle="popover"
1159
            title="'.addslashes(get_lang('CourseTeachers')).'"
1160
            data-html="true"
1161
        >
1162
            <i class="fa fa-graduation-cap" aria-hidden="true"></i>
1163
        </a>';
1164
            $html .= '<div id="popover-content-plist" class="hide">';
1165
            foreach ($teachers as $value) {
1166
                $name = $value['firstname'].' '.$value['lastname'];
1167
                $html .= '<div class="popover-teacher">';
1168
                $html .= '<a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">
1169
                        <img src="'.$value['avatar'].'" title="'.$name.'" alt="'.get_lang('UserPicture').'"/></a>';
1170
                $html .= '<div class="teachers-details"><h5>
1171
                        <a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">'
1172
                    .$name.'</a></h5></div>';
1173
                $html .= '</div>';
1174
            }
1175
            $html .= '</div>';
1176
        } else {
1177
            foreach ($teachers as $value) {
1178
                $name = $value['firstname'].' '.$value['lastname'];
1179
                if ($length > 2) {
1180
                    $html .= '<a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">
1181
                        <img src="'.$value['avatar'].'" title="'.$name.'" alt="'.get_lang('UserPicture').'"/></a>';
1182
                } else {
1183
                    $html .= '<a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">
1184
                        <img src="'.$value['avatar'].'" title="'.$name.'" alt="'.get_lang('UserPicture').'"/></a>';
1185
                    $html .= '<div class="teachers-details"><h5>
1186
                        <a href="'.$value['url'].'" class="ajax" data-title="'.$name.'">'
1187
                        .$name.'</a></h5><p>'.get_lang('Teacher').'</p></div>';
1188
                }
1189
            }
1190
        }
1191
        $html .= '</div>';
1192
1193
        return $html;
1194
    }
1195
1196
    /**
1197
     * Display the already registerd text in a course in the course catalog.
1198
     *
1199
     * @param $status
1200
     *
1201
     * @return string HTML string
1202
     */
1203
    public static function return_already_registered_label($status)
1204
    {
1205
        $icon = '<em class="fa fa-check"></em>';
1206
        $title = get_lang('YouAreATeacherOfThisCourse');
1207
        if ($status === 'student') {
1208
            $icon = '<em class="fa fa-check"></em>';
1209
            $title = get_lang('AlreadySubscribed');
1210
        }
1211
1212
        $html = Display::tag(
1213
            'span',
1214
            $icon.' '.$title,
1215
            [
1216
                'id' => 'register',
1217
                'class' => 'label-subscribed text-success',
1218
                'title' => $title,
1219
                'aria-label' => $title,
1220
            ]
1221
        );
1222
1223
        return $html.PHP_EOL;
1224
    }
1225
1226
    /**
1227
     * Display the register button of a course in the course catalog.
1228
     *
1229
     * @param $course
1230
     * @param $stok
1231
     * @param $categoryCode
1232
     * @param $search_term
1233
     *
1234
     * @return string
1235
     */
1236
    public static function return_register_button($course, $stok, $categoryCode, $search_term)
1237
    {
1238
        $title = get_lang('Subscribe');
1239
        $action = 'subscribe_course';
1240
        if (!empty($course['registration_code'])) {
1241
            $action = 'subscribe_course_validation';
1242
        }
1243
1244
        $em = Database::getManager();
1245
        /** @var SequenceResourceRepository $sequenceResourceRepo */
1246
        $sequenceResourceRepo = $em->getRepository('ChamiloCoreBundle:SequenceResource');
1247
        $requirements = $sequenceResourceRepo->getRequirements(
1248
            $course['real_id'],
1249
            SequenceResource::COURSE_TYPE
1250
        );
1251
        $hasRequirements = false;
1252
        foreach ($requirements as $sequence) {
1253
            if (!empty($sequence['requirements'])) {
1254
                $hasRequirements = true;
1255
                break;
1256
            }
1257
        }
1258
        $allowSubscribe = true;
1259
        $reqSubscribeBlock = '';
1260
        $btnSubscribe = '';
1261
        if ($hasRequirements) {
1262
            $sequenceList = $sequenceResourceRepo->checkRequirementsForUser($requirements, SequenceResource::COURSE_TYPE, api_get_user_id());
1263
            $allowSubscribe = $sequenceResourceRepo->checkSequenceAreCompleted($sequenceList);
1264
            $btnReqSubscribe = CoursesAndSessionsCatalog::getRequirements(
1265
                $course['real_id'],
1266
                SequenceResource::COURSE_TYPE,
1267
                true,
1268
                true
1269
            );
1270
            $reqSubscribeBlock = '<div class="session-requirements2">
1271
                                <h5>'.get_lang('RequiredCourses').'</h5>
1272
                                <p>'.$btnReqSubscribe.'</p>';
1273
            $seq = '';
1274
            foreach ($requirements as $sequence) {
1275
                if (!empty($sequence['requirements'])) {
1276
                    $seq .= $sequence['name'].':';
1277
                    foreach ($sequence['requirements'] as $req) {
1278
                        $seq .= '<a href="'.api_get_path(WEB_PATH).'course/'.$req->getId().'/about/">'.$req->getTitle().'</a>';
1279
                    }
1280
                }
1281
            }
1282
            $reqSubscribeBlock .= '<p>'.$seq.'</p>';
1283
            $reqSubscribeBlock .= '</div>';
1284
        }
1285
1286
        if ($allowSubscribe) {
1287
            $btnSubscribe = Display::url(
1288
                Display::returnFontAwesomeIcon('check').PHP_EOL.$title,
1289
                api_get_path(WEB_CODE_PATH).'auth/courses.php'
1290
                .'?action='.$action.'&sec_token='.$stok
1291
                .'&course_code='.$course['code'].'&search_term='.$search_term.'&category_code='.$categoryCode,
1292
                ['class' => 'btn btn-success btn-sm', 'title' => $title, 'aria-label' => $title]
1293
            );
1294
        } else {
1295
            $btnSubscribe = Display::url(
1296
                Display::returnFontAwesomeIcon('check').PHP_EOL.$title,
1297
                '#',
1298
                ['class' => 'btn btn-default btn-sm', 'title' => $title, 'aria-label' => $title, 'disabled' => true]
1299
            );
1300
        }
1301
1302
        return $reqSubscribeBlock.$btnSubscribe;
1303
    }
1304
1305
    /**
1306
     * Display the unregister button of a course in the course catalog.
1307
     *
1308
     * @param array  $course
1309
     * @param string $stok
1310
     * @param string $search_term
1311
     * @param string $categoryCode
1312
     * @param int    $sessionId
1313
     *
1314
     * @return string
1315
     */
1316
    public static function return_unregister_button($course, $stok, $search_term, $categoryCode, $sessionId = 0)
1317
    {
1318
        $title = get_lang('Unsubscription');
1319
        $search_term = Security::remove_XSS($search_term);
1320
        $categoryCode = Security::remove_XSS($categoryCode);
1321
        $sessionId = (int) $sessionId;
1322
1323
        $url = api_get_path(WEB_CODE_PATH).'auth/courses.php'
1324
            .'?action=unsubscribe&sec_token='.$stok.'&sid='.$sessionId.'&course_code='.$course['code'].
1325
            '&search_term='.$search_term.'&category_code='.$categoryCode;
1326
1327
        return Display::url(
1328
            Display::returnFontAwesomeIcon('sign-in').PHP_EOL.$title,
1329
            $url,
1330
            ['class' => 'btn btn-danger btn-sm', 'title' => $title, 'aria-label' => $title]
1331
        );
1332
    }
1333
1334
    /**
1335
     * Get a HTML button for subscribe to session.
1336
     *
1337
     * @param int    $sessionId         The session ID
1338
     * @param string $sessionName       The session name
1339
     * @param bool   $checkRequirements Optional.
1340
     *                                  Whether the session has requirement. Default is false
1341
     * @param bool   $includeText       Optional. Whether show the text in button
1342
     * @param bool   $btnBing
1343
     *
1344
     * @return string The button HTML
1345
     */
1346
    public static function getRegisteredInSessionButton(
1347
        $sessionId,
1348
        $sessionName,
1349
        $checkRequirements = false,
1350
        $includeText = false,
1351
        $btnBing = false
1352
    ) {
1353
        $sessionId = (int) $sessionId;
1354
        $class = 'btn-sm';
1355
        if ($btnBing) {
1356
            $class = 'btn-lg btn-block';
1357
        }
1358
1359
        if ($checkRequirements) {
1360
            return self::getRequirements($sessionId, SequenceResource::SESSION_TYPE, $includeText, $class);
1361
        }
1362
1363
        $catalogSessionAutoSubscriptionAllowed = false;
1364
        if (api_get_setting('catalog_allow_session_auto_subscription') === 'true') {
1365
            $catalogSessionAutoSubscriptionAllowed = true;
1366
        }
1367
1368
        $url = api_get_path(WEB_CODE_PATH);
1369
1370
        if ($catalogSessionAutoSubscriptionAllowed) {
1371
            $url .= 'auth/courses.php?';
1372
            $url .= http_build_query([
1373
                'action' => 'subscribe_to_session',
1374
                'session_id' => $sessionId,
1375
            ]);
1376
1377
            $result = Display::toolbarButton(
1378
                get_lang('Subscribe'),
1379
                $url,
1380
                'pencil',
1381
                'primary',
1382
                [
1383
                    'class' => $class.' ajax',
1384
                    'data-title' => get_lang('AreYouSureToSubscribe'),
1385
                    'data-size' => 'md',
1386
                    'title' => get_lang('Subscribe'),
1387
                ],
1388
                $includeText
1389
            );
1390
        } else {
1391
            $url .= 'inc/email_editor.php?';
1392
            $url .= http_build_query([
1393
                'action' => 'subscribe_me_to_session',
1394
                'session' => Security::remove_XSS($sessionName),
1395
            ]);
1396
1397
            $result = Display::toolbarButton(
1398
                get_lang('SubscribeToSessionRequest'),
1399
                $url,
1400
                'pencil',
1401
                'primary',
1402
                ['class' => $class],
1403
                $includeText
1404
            );
1405
        }
1406
1407
        $hook = HookResubscribe::create();
1408
        if (!empty($hook)) {
1409
            $hook->setEventData([
1410
                'session_id' => $sessionId,
1411
            ]);
1412
            try {
1413
                $hook->notifyResubscribe(HOOK_EVENT_TYPE_PRE);
1414
            } catch (Exception $exception) {
1415
                $result = $exception->getMessage();
1416
            }
1417
        }
1418
1419
        return $result;
1420
    }
1421
1422
    public static function getRequirements($id, $type, $includeText, $class, $sessionId = 0)
1423
    {
1424
        $id = (int) $id;
1425
        $type = (int) $type;
1426
        $url = api_get_path(WEB_AJAX_PATH).'sequence.ajax.php?';
1427
        $url .= http_build_query(
1428
            [
1429
                'a' => 'get_requirements',
1430
                'id' => $id,
1431
                'type' => $type,
1432
                'sid' => $sessionId,
1433
            ]
1434
        );
1435
1436
        return Display::toolbarButton(
1437
            get_lang('CheckRequirements'),
1438
            $url,
1439
            'shield',
1440
            'info',
1441
            [
1442
                'class' => $class.' ajax',
1443
                'data-title' => get_lang('CheckRequirements'),
1444
                'data-size' => 'md',
1445
                'title' => get_lang('CheckRequirements'),
1446
            ],
1447
            $includeText
1448
        );
1449
    }
1450
1451
    /**
1452
     * Generate a label if the user has been  registered in session.
1453
     *
1454
     * @return string The label
1455
     */
1456
    public static function getAlreadyRegisteredInSessionLabel()
1457
    {
1458
        $icon = '<em class="fa fa-graduation-cap"></em>';
1459
1460
        return Display::div(
1461
            $icon,
1462
            [
1463
                'class' => 'btn btn-default btn-sm registered',
1464
                'title' => get_lang("AlreadyRegisteredToSession"),
1465
            ]
1466
        );
1467
    }
1468
1469
    public static function getSessionPagination($action, $countSessions, $limit)
1470
    {
1471
        $pageTotal = ceil($countSessions / $limit['length']);
1472
        $pagination = '';
1473
        // Do NOT show pagination if only one page or less
1474
        if ($pageTotal > 1) {
1475
            $pagination = self::getCatalogPagination(
1476
                $limit['current'],
1477
                $limit['length'],
1478
                $pageTotal,
1479
                null,
1480
                $action
1481
            );
1482
        }
1483
1484
        return $pagination;
1485
    }
1486
1487
    /**
1488
     * Return Session catalog rendered view.
1489
     */
1490
    public static function sessionList(bool $returnHtml = false): ?string
1491
    {
1492
        $date = $_POST['date'] ?? '';
1493
        $limit = self::getLimitArray();
1494
1495
        $countSessions = self::browseSessions($date, [], false, true);
1496
        $sessions = self::browseSessions($date, $limit);
1497
1498
        $pagination = self::getSessionPagination('display_sessions', $countSessions, $limit);
1499
        $sessionsBlocks = self::getFormattedSessionsBlock($sessions);
1500
1501
        // Get session search catalogue URL
1502
        $courseUrl = self::getCatalogUrl(
1503
            1,
1504
            $limit['length'],
1505
            null,
1506
            'subscribe'
1507
        );
1508
1509
        $tpl = new Template();
1510
        $tpl->assign('actions', self::getTabList(2));
1511
        $tpl->assign('show_courses', self::showCourses());
1512
        $tpl->assign('show_sessions', self::showSessions());
1513
        $tpl->assign('show_tutor', api_get_setting('show_session_coach') === 'true');
1514
        $tpl->assign('course_url', $courseUrl);
1515
        $tpl->assign('catalog_pagination', $pagination);
1516
        $tpl->assign('search_token', Security::get_token());
1517
        $tpl->assign('search_date', $date);
1518
        $tpl->assign('web_session_courses_ajax_url', api_get_path(WEB_AJAX_PATH).'course.ajax.php');
1519
        $tpl->assign('sessions', $sessionsBlocks);
1520
        $tpl->assign('already_subscribed_label', self::getAlreadyRegisteredInSessionLabel());
1521
        $tpl->assign('catalog_settings', self::getCatalogSearchSettings());
1522
1523
        $templateContent = $tpl->fetch(
1524
            $tpl->get_template('catalog/session_catalog.tpl')
1525
        );
1526
1527
        if ($returnHtml) {
1528
            return $templateContent;
1529
        }
1530
1531
        $tpl->assign('content', $templateContent);
1532
        $tpl->display_one_col_template();
1533
1534
        return null;
1535
    }
1536
1537
    /**
1538
     * Show the Session Catalogue with filtered session by course tags.
1539
     */
1540
    public static function sessionsListByName()
1541
    {
1542
        $limit = self::getLimitArray();
1543
        $keyword = $_REQUEST['keyword'] ?? null;
1544
        $courseUrl = self::getCatalogUrl(
1545
            1,
1546
            $limit['length'],
1547
            null,
1548
            'subscribe'
1549
        );
1550
1551
        $count = self::getSessionsByName($keyword, [], true);
1552
        $sessions = self::getSessionsByName($keyword, $limit);
1553
        $sessionsBlocks = self::getFormattedSessionsBlock($sessions);
1554
        $pagination = self::getSessionPagination('search_session_title', $count, $limit);
1555
1556
        $tpl = new Template();
1557
        $tpl->assign('catalog_pagination', $pagination);
1558
        $tpl->assign('actions', self::getTabList(2));
1559
        $tpl->assign('show_courses', self::showCourses());
1560
        $tpl->assign('show_sessions', self::showSessions());
1561
        $tpl->assign('show_tutor', api_get_setting('show_session_coach') === 'true');
1562
        $tpl->assign('course_url', $courseUrl);
1563
        $tpl->assign('already_subscribed_label', self::getAlreadyRegisteredInSessionLabel());
1564
        $tpl->assign('search_token', Security::get_token());
1565
        $tpl->assign('keyword', Security::remove_XSS($keyword));
1566
        $tpl->assign('sessions', $sessionsBlocks);
1567
        $tpl->assign('catalog_settings', self::getCatalogSearchSettings());
1568
1569
        $templateContent = $tpl->fetch(
1570
            $tpl->get_template('catalog/session_catalog.tpl')
1571
        );
1572
1573
        $tpl->assign('content', $templateContent);
1574
        $tpl->display_one_col_template();
1575
    }
1576
1577
    public static function getCatalogSearchSettings()
1578
    {
1579
        $settings = api_get_configuration_value('catalog_settings');
1580
        if (empty($settings)) {
1581
            // Default everything is visible
1582
            $settings = [
1583
                'sessions' => [
1584
                    'by_title' => true,
1585
                    'by_date' => true,
1586
                    'by_tag' => true,
1587
                    'show_session_info' => true,
1588
                    'show_session_date' => true,
1589
                ],
1590
                'courses' => [
1591
                    'by_title' => true,
1592
                ],
1593
            ];
1594
        }
1595
1596
        return $settings;
1597
    }
1598
1599
    /**
1600
     * @param int $active
1601
     *
1602
     * @return string
1603
     */
1604
    public static function getTabList($active = 1)
1605
    {
1606
        $pageLength = isset($_GET['pageLength']) ? (int) $_GET['pageLength'] : self::PAGE_LENGTH;
1607
1608
        $url = self::getCatalogUrl(1, $pageLength, null, 'display_sessions');
1609
        $headers = [];
1610
        if (self::showCourses()) {
1611
            $headers[] = [
1612
                'url' => api_get_path(WEB_CODE_PATH).'auth/courses.php',
1613
                'content' => get_lang('CourseManagement'),
1614
            ];
1615
        }
1616
1617
        if (self::showSessions()) {
1618
            $headers[] = [
1619
                'url' => $url,
1620
                'content' => get_lang('SessionList'),
1621
            ];
1622
        }
1623
1624
        // If only one option hide menu.
1625
        if (1 === count($headers)) {
1626
            return '';
1627
        }
1628
1629
        return Display::tabsOnlyLink($headers, $active);
1630
    }
1631
1632
    /**
1633
     * Show the Session Catalogue with filtered session by course tags.
1634
     */
1635
    public static function sessionsListByCoursesTag()
1636
    {
1637
        $limit = self::getLimitArray();
1638
        $searchTag = $_REQUEST['search_tag'] ?? '';
1639
        $searchDate = $_REQUEST['date'] ?? date('Y-m-d');
1640
        $courseUrl = self::getCatalogUrl(
1641
            1,
1642
            $limit['length'],
1643
            null,
1644
            'subscribe'
1645
        );
1646
1647
        $sessions = self::browseSessionsByTags($searchTag, $limit);
1648
        $sessionsBlocks = self::getFormattedSessionsBlock($sessions);
1649
1650
        $count = self::browseSessionsByTags($searchTag, [], true);
1651
        $pagination = self::getSessionPagination('search_tag', $count, $limit);
1652
1653
        $tpl = new Template();
1654
        $tpl->assign('catalog_pagination', $pagination);
1655
        $tpl->assign('show_courses', self::showCourses());
1656
        $tpl->assign('show_sessions', self::showSessions());
1657
        $tpl->assign('show_tutor', api_get_setting('show_session_coach') === 'true');
1658
        $tpl->assign('course_url', $courseUrl);
1659
        $tpl->assign('already_subscribed_label', self::getAlreadyRegisteredInSessionLabel());
1660
        $tpl->assign('search_token', Security::get_token());
1661
        $tpl->assign('search_date', Security::remove_XSS($searchDate));
1662
        $tpl->assign('search_tag', Security::remove_XSS($searchTag));
1663
        $tpl->assign('sessions', $sessionsBlocks);
1664
1665
        $templateContent = $tpl->fetch(
1666
            $tpl->get_template('catalog/session_catalog.tpl')
1667
        );
1668
1669
        $tpl->assign('content', $templateContent);
1670
        $tpl->display_one_col_template();
1671
    }
1672
1673
    /**
1674
     * @return array
1675
     */
1676
    public static function getLimitArray()
1677
    {
1678
        $pageCurrent = isset($_REQUEST['pageCurrent']) ? (int) $_GET['pageCurrent'] : 1;
1679
        $pageLength = isset($_REQUEST['pageLength']) ? (int) $_GET['pageLength'] : self::PAGE_LENGTH;
1680
1681
        return [
1682
            'start' => ($pageCurrent - 1) * $pageLength,
1683
            'current' => $pageCurrent,
1684
            'length' => $pageLength,
1685
        ];
1686
    }
1687
1688
    /**
1689
     * Get the formatted data for sessions block to be displayed on Session Catalog page.
1690
     *
1691
     * @param array $sessions The session list
1692
     *
1693
     * @return array
1694
     */
1695
    public static function getFormattedSessionsBlock(array $sessions)
1696
    {
1697
        $extraFieldValue = new ExtraFieldValue('session');
1698
        $userId = api_get_user_id();
1699
        $sessionsBlocks = [];
1700
        $entityManager = Database::getManager();
1701
        $sessionRelCourseRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourse');
1702
        $extraFieldRepo = $entityManager->getRepository('ChamiloCoreBundle:ExtraField');
1703
        $extraFieldRelTagRepo = $entityManager->getRepository('ChamiloCoreBundle:ExtraFieldRelTag');
1704
1705
        $tagsField = $extraFieldRepo->findOneBy([
1706
            'extraFieldType' => Chamilo\CoreBundle\Entity\ExtraField::COURSE_FIELD_TYPE,
1707
            'variable' => 'tags',
1708
        ]);
1709
1710
        /** @var \Chamilo\CoreBundle\Entity\Session $session */
1711
        foreach ($sessions as $session) {
1712
            $sessionDates = SessionManager::parseSessionDates([
1713
                'display_start_date' => $session->getDisplayStartDate(),
1714
                'display_end_date' => $session->getDisplayEndDate(),
1715
                'access_start_date' => $session->getAccessStartDate(),
1716
                'access_end_date' => $session->getAccessEndDate(),
1717
                'coach_access_start_date' => $session->getCoachAccessStartDate(),
1718
                'coach_access_end_date' => $session->getCoachAccessEndDate(),
1719
            ]);
1720
1721
            $imageField = $extraFieldValue->get_values_by_handler_and_field_variable(
1722
                $session->getId(),
1723
                'image'
1724
            );
1725
            $sessionCourseTags = [];
1726
            if (!is_null($tagsField)) {
1727
                $sessionRelCourses = $sessionRelCourseRepo->findBy([
1728
                    'session' => $session,
1729
                ]);
1730
                /** @var SessionRelCourse $sessionRelCourse */
1731
                foreach ($sessionRelCourses as $sessionRelCourse) {
1732
                    $courseTags = $extraFieldRelTagRepo->getTags(
1733
                        $tagsField,
1734
                        $sessionRelCourse->getCourse()->getId()
1735
                    );
1736
                    /** @var Tag $tag */
1737
                    foreach ($courseTags as $tag) {
1738
                        $sessionCourseTags[] = $tag->getTag();
1739
                    }
1740
                }
1741
            }
1742
1743
            if (!empty($sessionCourseTags)) {
1744
                $sessionCourseTags = array_unique($sessionCourseTags);
1745
            }
1746
1747
            /** @var SequenceResourceRepository $repo */
1748
            $repo = $entityManager->getRepository('ChamiloCoreBundle:SequenceResource');
1749
            $sequences = $repo->getRequirementsAndDependenciesWithinSequences(
1750
                $session->getId(),
1751
                SequenceResource::SESSION_TYPE
1752
            );
1753
1754
            $hasRequirements = false;
1755
            foreach ($sequences as $sequence) {
1756
                if (count($sequence['requirements']) === 0) {
1757
                    continue;
1758
                }
1759
                $hasRequirements = true;
1760
                break;
1761
            }
1762
            $cat = $session->getCategory();
1763
            if (empty($cat)) {
1764
                $cat = null;
1765
                $catName = '';
1766
            } else {
1767
                $catName = $cat->getName();
1768
            }
1769
1770
            $generalCoach = $session->getGeneralCoach();
1771
            $coachId = $generalCoach ? $generalCoach->getId() : 0;
1772
            $coachName = $generalCoach ? UserManager::formatUserFullName($session->getGeneralCoach()) : '';
1773
1774
            $actions = null;
1775
            if (api_is_platform_admin()) {
1776
                $actions = api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$session->getId();
1777
            }
1778
1779
            $plugin = \BuyCoursesPlugin::create();
1780
            $isThisSessionOnSale = $plugin->getBuyCoursePluginPrice($session);
1781
1782
            $userIdHash = UserManager::generateUserHash($coachId);
1783
            $sessionsBlock = [
1784
                'id' => $session->getId(),
1785
                'name' => $session->getName(),
1786
                'image' => isset($imageField['value']) ? $imageField['value'] : null,
1787
                'nbr_courses' => $session->getNbrCourses(),
1788
                'nbr_users' => $session->getNbrUsers(),
1789
                'coach_id' => $coachId,
1790
                'coach_url' => $generalCoach
1791
                    ? api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&hash='.$userIdHash
1792
                    : '',
1793
                'coach_name' => $coachName,
1794
                'coach_avatar' => UserManager::getUserPicture($coachId, USER_IMAGE_SIZE_SMALL),
1795
                'is_subscribed' => SessionManager::isUserSubscribedAsStudent($session->getId(), $userId),
1796
                '' => Display::return_icon(
1797
                    'window_list.png',
1798
                    $session->getName(),
1799
                    [],
1800
                    ICON_SIZE_MEDIUM
1801
                ),
1802
                'date' => $sessionDates['display'],
1803
                'price' => !empty($isThisSessionOnSale['html']) ? $isThisSessionOnSale['html'] : '',
1804
                'subscribe_button' => isset($isThisSessionOnSale['buy_button']) ? $isThisSessionOnSale['buy_button'] : self::getRegisteredInSessionButton(
1805
                    $session->getId(),
1806
                    $session->getName(),
1807
                    $hasRequirements
1808
                ),
1809
                'show_description' => $session->getShowDescription(),
1810
                'description' => $session->getDescription(),
1811
                'category' => $catName,
1812
                'tags' => $sessionCourseTags,
1813
                'edit_actions' => $actions,
1814
                'duration' => SessionManager::getDayLeftInSession(
1815
                    ['id' => $session->getId(), 'duration' => $session->getDuration()],
1816
                    $userId
1817
                ),
1818
            ];
1819
1820
            $sessionsBlocks[] = array_merge($sessionsBlock, $sequences);
1821
        }
1822
1823
        return $sessionsBlocks;
1824
    }
1825
1826
    /**
1827
     * Get Pagination HTML div.
1828
     *
1829
     * @param int    $pageCurrent
1830
     * @param int    $pageLength
1831
     * @param int    $pageTotal
1832
     * @param string $categoryCode
1833
     * @param string $action
1834
     * @param array  $fields
1835
     * @param array  $sortKeys
1836
     *
1837
     * @return string
1838
     */
1839
    public static function getCatalogPagination(
1840
        $pageCurrent,
1841
        $pageLength,
1842
        $pageTotal,
1843
        $categoryCode = '',
1844
        $action = '',
1845
        $fields = [],
1846
        $sortKeys = []
1847
    ) {
1848
        // Start empty html
1849
        $pageDiv = '';
1850
        $html = '';
1851
        $pageBottom = max(1, $pageCurrent - 3);
1852
        $pageTop = min($pageTotal, $pageCurrent + 3);
1853
1854
        if ($pageBottom > 1) {
1855
            $pageDiv .= self::getPageNumberItem(
1856
                1,
1857
                $pageLength,
1858
                   null,
1859
                '',
1860
                $categoryCode,
1861
                $action,
1862
                   $fields,
1863
                   $sortKeys
1864
            );
1865
            if ($pageBottom > 2) {
1866
                $pageDiv .= self::getPageNumberItem(
1867
                    $pageBottom - 1,
1868
                    $pageLength,
1869
                    null,
1870
                    '...',
1871
                    $categoryCode,
1872
                    $action,
1873
                    $fields,
1874
                    $sortKeys
1875
                );
1876
            }
1877
        }
1878
1879
        // For each page add its page button to html
1880
        for ($i = $pageBottom; $i <= $pageTop; $i++) {
1881
            if ($i === $pageCurrent) {
1882
                $pageItemAttributes = ['class' => 'active'];
1883
            } else {
1884
                $pageItemAttributes = [];
1885
            }
1886
            $pageDiv .= self::getPageNumberItem(
1887
                $i,
1888
                $pageLength,
1889
                $pageItemAttributes,
1890
                '',
1891
                $categoryCode,
1892
                $action,
1893
                $fields,
1894
                $sortKeys
1895
            );
1896
        }
1897
1898
        // Check if current page is the last page
1899
        if ($pageTop < $pageTotal) {
1900
            if ($pageTop < ($pageTotal - 1)) {
1901
                $pageDiv .= self::getPageNumberItem(
1902
                    $pageTop + 1,
1903
                    $pageLength,
1904
                    null,
1905
                    '...',
1906
                    $categoryCode,
1907
                    $action,
1908
                    $fields,
1909
                    $sortKeys
1910
                );
1911
            }
1912
            $pageDiv .= self::getPageNumberItem(
1913
                $pageTotal,
1914
                $pageLength,
1915
                [],
1916
                '',
1917
                $categoryCode,
1918
                $action,
1919
                $fields,
1920
                $sortKeys
1921
            );
1922
        }
1923
1924
        // Complete pagination html
1925
        $pageDiv = Display::tag('ul', $pageDiv, ['class' => 'pagination']);
1926
        $html .= '<nav>'.$pageDiv.'</nav>';
1927
1928
        return $html;
1929
    }
1930
1931
    /**
1932
     * Get li HTML of page number.
1933
     *
1934
     * @param $pageNumber
1935
     * @param $pageLength
1936
     * @param array  $liAttributes
1937
     * @param string $content
1938
     * @param string $categoryCode
1939
     * @param string $action
1940
     * @param array  $fields
1941
     * @param array  $sortKeys
1942
     *
1943
     * @return string
1944
     */
1945
    public static function getPageNumberItem(
1946
        $pageNumber,
1947
        $pageLength,
1948
        $liAttributes = [],
1949
        $content = '',
1950
        $categoryCode = '',
1951
        $action = '',
1952
        $fields = [],
1953
        $sortKeys = []
1954
    ) {
1955
        // Get page URL
1956
        $url = self::getCatalogUrl($pageNumber, $pageLength, $categoryCode, $action, $fields, $sortKeys);
1957
1958
        // If is current page ('active' class) clear URL
1959
        if (isset($liAttributes) && is_array($liAttributes) && isset($liAttributes['class'])) {
1960
            if (strpos('active', $liAttributes['class']) !== false) {
1961
                $url = '';
1962
            }
1963
        }
1964
1965
        $content = !empty($content) ? $content : $pageNumber;
1966
1967
        return Display::tag(
1968
            'li',
1969
            Display::url(
1970
                $content,
1971
                $url
1972
            ),
1973
            $liAttributes
1974
        );
1975
    }
1976
1977
    /**
1978
     * Return URL to course catalog.
1979
     *
1980
     * @param int    $pageCurrent
1981
     * @param int    $pageLength
1982
     * @param string $categoryCode
1983
     * @param string $action
1984
     * @param array  $extraFields
1985
     * @param array  $sortKeys
1986
     *
1987
     * @return string
1988
     */
1989
    public static function getCatalogUrl(
1990
        $pageCurrent,
1991
        $pageLength,
1992
        $categoryCode = null,
1993
        $action = null,
1994
        $extraFields = [],
1995
        $sortKeys = []
1996
    ) {
1997
        $requestAction = isset($_REQUEST['action']) ? Security::remove_XSS($_REQUEST['action']) : '';
1998
        $action = isset($action) ? Security::remove_XSS($action) : $requestAction;
1999
        $searchTerm = isset($_REQUEST['search_term']) ? Security::remove_XSS($_REQUEST['search_term']) : '';
2000
        $keyword = isset($_REQUEST['keyword']) ? Security::remove_XSS($_REQUEST['keyword']) : '';
2001
        $searchTag = isset($_REQUEST['search_tag']) ? Security::remove_XSS($_REQUEST['search_tag']) : '';
2002
        $languageSelect = isset($_REQUEST['course_language']) ? Security::remove_XSS($_REQUEST['course_language']) : '';
2003
2004
        if ($action === 'subscribe_user_with_password') {
2005
            $action = 'subscribe';
2006
        }
2007
2008
        $categoryCode = !empty($categoryCode) ? Security::remove_XSS($categoryCode) : 'ALL';
2009
2010
        // Start URL with params
2011
        $pageUrl = api_get_path(WEB_CODE_PATH).'auth/courses.php'.
2012
            '?action='.$action.
2013
            '&search_term='.$searchTerm.
2014
            '&keyword='.$keyword.
2015
            '&search_tag='.$searchTag.
2016
            '&category_code='.$categoryCode.
2017
            '&pageCurrent='.$pageCurrent.
2018
            '&pageLength='.$pageLength.
2019
            '&course_language='.$languageSelect;
2020
2021
        if (!empty($extraFields)) {
2022
            $params = [];
2023
            foreach ($extraFields as $variable => $value) {
2024
                $params[Security::remove_XSS($variable)] = Security::remove_XSS($value);
2025
            }
2026
            if (!empty($params)) {
2027
                $pageUrl .= '&'.http_build_query($params);
2028
            }
2029
        }
2030
2031
        if (!empty($sortKeys)) {
2032
            foreach ($sortKeys as $sortKey) {
2033
                $pageUrl .= '&sortKeys%5B%5D='.Security::remove_XSS($sortKey);
2034
            }
2035
        }
2036
2037
        switch ($action) {
2038
            case 'subscribe':
2039
                // for search
2040
                $pageUrl .=
2041
                    '&sec_token='.Security::getTokenFromSession();
2042
                break;
2043
            case 'display_courses':
2044
            default:
2045
                break;
2046
        }
2047
2048
        return $pageUrl;
2049
    }
2050
2051
    public static function generateRedirectUrlAfterSubscription(string $coursePublicUrl): string
2052
    {
2053
        $settings = api_get_configuration_value('course_catalog_settings');
2054
2055
        $redirectAfterSubscription = 'course_home';
2056
2057
        if (!empty($settings) && isset($settings['redirect_after_subscription'])) {
2058
            $redirectAfterSubscription = $settings['redirect_after_subscription'];
2059
        }
2060
2061
        if ('course_home' !== $redirectAfterSubscription) {
2062
            return api_get_path(WEB_CODE_PATH).'auth/courses.php';
2063
        }
2064
2065
        if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
2066
            $user = api_get_user_entity(api_get_user_id());
2067
2068
            if ($user && $accesibleSessions = $user->getCurrentlyAccessibleSessions()) {
2069
                return $coursePublicUrl.'?id_session='.$accesibleSessions[0]->getId();
2070
            }
2071
        }
2072
2073
        return $coursePublicUrl;
2074
    }
2075
2076
    /**
2077
     * @throws Exception
2078
     */
2079
    public static function displayCoursesList(
2080
        string $action = '',
2081
        string $searchTerm = '',
2082
        string $categoryCode = '',
2083
        bool $returnHtml = false
2084
    ): ?string {
2085
        $settings = api_get_configuration_value('course_catalog_settings');
2086
        $preFilterOnLanguage = false;
2087
        if (!empty($settings['pre_filter_on_language'])) {
2088
            $preFilterOnLanguage = true;
2089
        }
2090
2091
        $courseCatalogSettings = [
2092
            'info_url' => 'course_description_popup',
2093
            'title_url' => 'course_home',
2094
            'image_url' => 'course_about',
2095
        ];
2096
        // By default, all extra fields are shown (visible and filterable)
2097
        $extraFieldsInSearchForm = [];
2098
        $extraFieldsInCourseBlock = [];
2099
2100
        if (!empty($settings)) {
2101
            if (isset($settings['link_settings'])) {
2102
                $courseCatalogSettings = $settings['link_settings'];
2103
            }
2104
2105
            if (isset($settings['extra_fields_in_search_form'])) {
2106
                $extraFieldsInSearchForm = $settings['extra_fields_in_search_form'];
2107
            }
2108
2109
            if (isset($settings['extra_fields_in_course_block'])) {
2110
                $extraFieldsInCourseBlock = $settings['extra_fields_in_course_block'];
2111
            }
2112
        }
2113
2114
        $settings = CoursesAndSessionsCatalog::getCatalogSearchSettings();
2115
2116
        $form = new FormValidator('search', 'get', '', null, null, FormValidator::LAYOUT_HORIZONTAL);
2117
        $form->addHidden('action', 'search_course');
2118
2119
        if (isset($settings['courses']) && true === $settings['courses']['by_title']) {
2120
            $form->addText('search_term', get_lang('Title'), false);
2121
        }
2122
2123
        $select = $form->addSelect(
2124
            'category_code',
2125
            get_lang('CourseCategories'),
2126
            [],
2127
            ['placeholder' => get_lang('SelectAnOption')]
2128
        );
2129
2130
        $defaults = [];
2131
        $listCategories = CoursesAndSessionsCatalog::getCourseCategoriesTree();
2132
2133
        foreach ($listCategories as $category) {
2134
            $countCourse = (int) $category['number_courses'];
2135
            if (empty($countCourse)) {
2136
                continue;
2137
            }
2138
2139
            $categoryCodeItem = Security::remove_XSS($category['code']);
2140
            $categoryName = Security::remove_XSS($category['name']);
2141
            $level = $category['level'];
2142
            $separate = '';
2143
            if ($level > 0) {
2144
                $separate = str_repeat('--', $level);
2145
            }
2146
            $select->addOption($separate.' '.$categoryName.' ('.$countCourse.')', $categoryCodeItem);
2147
        }
2148
2149
        $allowExtraFields = api_get_configuration_value('allow_course_extra_field_in_catalog');
2150
2151
        $jqueryReadyContent = '';
2152
        if ($allowExtraFields) {
2153
            $extraField = new ExtraFieldModel('course');
2154
            $returnParams = $extraField->addElements($form, null, [], true, false, $extraFieldsInSearchForm);
2155
            $jqueryReadyContent = $returnParams['jquery_ready_content'];
2156
        }
2157
2158
        $sortKeySelect = $form->addSelect(
2159
            'sortKeys',
2160
            get_lang('SortKeys'),
2161
            CoursesAndSessionsCatalog::courseSortOptions(),
2162
            ['multiple' => true]
2163
        );
2164
2165
        if (api_get_setting('show_different_course_language') === 'true') {
2166
            $form->addSelectLanguage(
2167
                'course_language',
2168
                get_lang('Language'),
2169
                ['' => '--'],
2170
                ['style' => 'width:150px']
2171
            );
2172
        }
2173
2174
        $sortKeys = isset($_REQUEST['sortKeys']) ? Security::remove_XSS($_REQUEST['sortKeys']) : '';
2175
        $languageSelect = isset($_REQUEST['course_language']) ? Security::remove_XSS($_REQUEST['course_language']) : '';
2176
        if ($preFilterOnLanguage && empty($languageSelect)) {
2177
            $languageSelect = api_get_user_info()['language'];
2178
        }
2179
        // Check the language is active
2180
        $languagesList = SubLanguageManager::getAllLanguages(true);
2181
        if (empty($languagesList[$languageSelect])) {
2182
            $languageSelect = '';
2183
        }
2184
        $defaults['sortKeys'] = $sortKeys;
2185
        $defaults['search_term'] = $searchTerm;
2186
        $defaults['category_code'] = $categoryCode;
2187
        $defaults['course_language'] = $languageSelect;
2188
2189
        $conditions = [];
2190
        $fields = [];
2191
2192
        if ('display_random_courses' === $action) {
2193
            // Random value is used instead limit filter
2194
            $courses = CoursesAndSessionsCatalog::getCoursesInCategory(null, 12);
2195
            $countCoursesInCategory = count($courses);
2196
        } else {
2197
            $values = $_REQUEST;
2198
            if ($allowExtraFields) {
2199
                $extraResult = $extraField->processExtraFieldSearch($values, $form, 'course', 'AND');
2200
                $conditions = $extraResult['condition'];
2201
                $fields = $extraResult['fields'];
2202
                $defaults = $extraResult['defaults'];
2203
2204
                $defaults['sortKeys'] = $sortKeys;
2205
                $defaults['search_term'] = $searchTerm;
2206
                $defaults['category_code'] = $categoryCode;
2207
                $defaults['course_language'] = $languageSelect;
2208
            }
2209
2210
            $courses = CoursesAndSessionsCatalog::searchAndSortCourses(
2211
                $categoryCode,
2212
                $searchTerm,
2213
                self::getLimitArray(),
2214
                true,
2215
                $conditions,
2216
                $sortKeySelect->getValue(),
2217
                $languageSelect
2218
            );
2219
            $countCoursesInCategory = CourseCategory::countCoursesInCategory(
2220
                $categoryCode,
2221
                $searchTerm,
2222
                true,
2223
                true,
2224
                $conditions,
2225
                $languageSelect,
2226
                true
2227
            );
2228
        }
2229
2230
        $pageCurrent = isset($_GET['pageCurrent']) ? (int) $_GET['pageCurrent'] : 1;
2231
        $pageLength = isset($_GET['pageLength']) ? (int) $_GET['pageLength'] : CoursesAndSessionsCatalog::PAGE_LENGTH;
2232
        $pageTotal = (int) ceil($countCoursesInCategory / $pageLength);
2233
2234
        $url = CoursesAndSessionsCatalog::getCatalogUrl(1, $pageLength, 'ALL', 'search_course', $fields);
2235
        $urlNoExtraFields = CoursesAndSessionsCatalog::getCatalogUrl(1, $pageLength, 'ALL', 'search_course');
2236
        $urlNoCategory = CoursesAndSessionsCatalog::getCatalogUrl(1, $pageLength, '', 'search_course', $fields);
2237
        $urlNoCategory = str_replace('&category_code=ALL', '', $urlNoCategory);
2238
2239
        $form->setAttribute('action', $url);
2240
        $form->addButtonSearch(get_lang('Search'));
2241
        $form->setDefaults($defaults);
2242
2243
        $catalogPagination = '';
2244
2245
        if ($pageTotal > 1) {
2246
            $catalogPagination = CoursesAndSessionsCatalog::getCatalogPagination(
2247
                $pageCurrent,
2248
                $pageLength,
2249
                $pageTotal,
2250
                $categoryCode,
2251
                $action,
2252
                $fields,
2253
                $sortKeySelect->getValue()
2254
            );
2255
        }
2256
2257
        $stok = Security::get_token();
2258
2259
        $showTeacher = 'true' === api_get_setting('display_teacher_in_courselist');
2260
        $ajax_url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=add_course_vote';
2261
        $user_id = api_get_user_id();
2262
        $categoryListFromDatabase = CourseCategory::getAllCategories();
2263
2264
        $categoryList = [];
2265
        if (!empty($categoryListFromDatabase)) {
2266
            foreach ($categoryListFromDatabase as $categoryItem) {
2267
                $categoryList[$categoryItem['code']] = $categoryItem['name'];
2268
            }
2269
        }
2270
2271
        $courseUrl = api_get_path(WEB_COURSE_PATH);
2272
        $hideRating = api_get_configuration_value('hide_course_rating');
2273
2274
        if (!empty($courses)) {
2275
            foreach ($courses as &$course) {
2276
                $courseId = $course['real_id'];
2277
                if (COURSE_VISIBILITY_HIDDEN == $course['visibility']) {
2278
                    continue;
2279
                }
2280
2281
                $aboutPage = api_get_path(WEB_PATH).'course/'.$course['real_id'].'/about';
2282
                $settingsUrl = [
2283
                    'course_description_popup' => api_get_path(WEB_CODE_PATH).'inc/ajax/course_home.ajax.php?a=show_course_information&code='.$course['code'],
2284
                    'course_about' => $aboutPage,
2285
                    'course_home' => $courseUrl.$course['directory'].'/index.php?id_session=0',
2286
                ];
2287
2288
                $infoUrl = $settingsUrl[$courseCatalogSettings['info_url']];
2289
                $course['title_url'] = $settingsUrl[$courseCatalogSettings['title_url']];
2290
                $course['image_url'] = $settingsUrl[$courseCatalogSettings['image_url']];
2291
2292
                $userRegisteredInCourse = CourseManager::is_user_subscribed_in_course($user_id, $course['code']);
2293
                $userRegisteredInCourseAsTeacher = CourseManager::is_course_teacher($user_id, $course['code']);
2294
2295
                $course_private = COURSE_VISIBILITY_REGISTERED == $course['visibility'];
2296
                $courseClosed = COURSE_VISIBILITY_CLOSED == $course['visibility'];
2297
                $course_subscribe_allowed = 1 == $course['subscribe'];
2298
                $course_unsubscribe_allowed = 1 == $course['unsubscribe'];
2299
2300
                // display the course bloc
2301
                $course['category_title'] = '';
2302
2303
                if (!empty($course['category_code'])) {
2304
                    $course['category_title'] = $categoryList[$course['category_code']] ?? '';
2305
                    $course['category_code_link'] = $urlNoCategory.'&category_code='.$course['category_code'];
2306
                }
2307
2308
                // Display thumbnail
2309
                $course['thumbnail'] = CoursesAndSessionsCatalog::returnThumbnail($course);
2310
                $course['description_button'] = CourseManager::returnDescriptionButton($course, $infoUrl);
2311
                $subscribeButton = CoursesAndSessionsCatalog::return_register_button(
2312
                    $course,
2313
                    $stok,
2314
                    $categoryCode,
2315
                    $searchTerm
2316
                );
2317
2318
                // Start buy course validation
2319
                // display the course price and buy button if the buycourses plugin is enabled and this course is configured
2320
                $plugin = BuyCoursesPlugin::create();
2321
                $isThisCourseInSale = $plugin->buyCoursesForGridCatalogValidator(
2322
                    $courseId,
2323
                    BuyCoursesPlugin::PRODUCT_TYPE_COURSE
2324
                );
2325
2326
                $separator = '';
2327
2328
                if ($isThisCourseInSale) {
2329
                    // set the Price label
2330
                    $separator = $isThisCourseInSale['html'];
2331
                    // set the Buy button instead register.
2332
                    if ($isThisCourseInSale['verificator']) {
2333
                        $subscribeButton = $plugin->returnBuyCourseButton(
2334
                            $courseId,
2335
                            BuyCoursesPlugin::PRODUCT_TYPE_COURSE
2336
                        );
2337
                    }
2338
                }
2339
2340
                $course['rating'] = '';
2341
2342
                if ($hideRating === false) {
2343
                    $rating = Display::return_rating_system(
2344
                        'star_'.$course['real_id'],
2345
                        $ajax_url.'&course_id='.$course['real_id'],
2346
                        $course['point_info']
2347
                    );
2348
                    $course['rating'] = '<div class="ranking">'.$rating.'</div>';
2349
                }
2350
2351
                if ($showTeacher) {
2352
                    $course['teacher_info'] = CoursesAndSessionsCatalog::return_teacher($course);
2353
                }
2354
2355
                // display button line
2356
                $course['buy_course'] = $separator;
2357
                $course['extra_data'] = '';
2358
2359
                if ($allowExtraFields) {
2360
                    $course['extra_data'] = $extraField->getDataAndFormattedValues(
2361
                        $courseId,
2362
                        true,
2363
                        $extraFieldsInCourseBlock
2364
                    );
2365
                }
2366
2367
                // if user registered as student
2368
                if ($userRegisteredInCourse) {
2369
                    $course['already_registered_formatted'] = Display::url(
2370
                        Display::returnFontAwesomeIcon('external-link').PHP_EOL.
2371
                        get_lang('GoToCourse'),
2372
                        $courseUrl.$course['directory'].'/index.php?id_session=0',
2373
                        ['class' => 'btn btn-primary btn-sm']
2374
                    );
2375
2376
                    if (!$courseClosed && $course_unsubscribe_allowed
2377
                        && false === $userRegisteredInCourseAsTeacher
2378
                    ) {
2379
                        $course['unregister_formatted'] = CoursesAndSessionsCatalog::return_unregister_button(
2380
                            $course,
2381
                            $stok,
2382
                            $searchTerm,
2383
                            $categoryCode
2384
                        );
2385
                    }
2386
                } elseif ($userRegisteredInCourseAsTeacher) {
2387
                    // if user registered as teacher
2388
                    // Updated teacher cannot unregister himself.
2389
                    /*if ($course_unsubscribe_allowed) {
2390
                        $course['unregister_formatted'] = CoursesAndSessionsCatalog::return_unregister_button(
2391
                            $course,
2392
                            $stok,
2393
                            $searchTerm,
2394
                            $categoryCode
2395
                        );
2396
                    }*/
2397
                } else {
2398
                    // if user not registered in the course
2399
                    if (!$courseClosed && !$course_private && $course_subscribe_allowed) {
2400
                        $course['subscribe_formatted'] = $subscribeButton;
2401
                    }
2402
                }
2403
            }
2404
        } else {
2405
            if (!isset($_REQUEST['subscribe_user_with_password']) &&
2406
                !isset($_REQUEST['subscribe_course'])
2407
            ) {
2408
                Display::addFlash(Display::return_message(get_lang('NoResults'), 'warning'));
2409
            }
2410
        }
2411
2412
        if (api_is_course_admin()) {
2413
            foreach ($courses as &$course) {
2414
                $course['admin_url'] = api_get_path(WEB_CODE_PATH).'/admin/course_list.php?keyword='.$course['code'];
2415
            }
2416
        }
2417
2418
        $toolTitle = get_lang('CourseCatalog');
2419
2420
        $template = new Template($toolTitle, true, true, false, false, false);
2421
        $template->assign('tabs', CoursesAndSessionsCatalog::getTabList());
2422
        $template->assign('frm_filter', $form->returnForm());
2423
        $template->assign('courses', $courses);
2424
        $template->assign(
2425
            'total_number_of_courses',
2426
            CoursesAndSessionsCatalog::countAvailableCoursesToShowInCatalog(
2427
                api_get_current_access_url_id()
2428
            )
2429
        );
2430
        $template->assign('total_number_of_matching_courses', $countCoursesInCategory);
2431
        $template->assign('catalog_url_no_extra_fields', $urlNoExtraFields);
2432
        $template->assign('pagination', $catalogPagination);
2433
        $template->assign('jquery_ready_content', $jqueryReadyContent);
2434
2435
        $templateContent = $template->fetch(
2436
            $template->get_template('catalog/course_catalog.tpl')
2437
        );
2438
2439
        if ($returnHtml) {
2440
            return $templateContent;
2441
        }
2442
2443
        $template->assign('content', $templateContent);
2444
        $template->display_one_col_template();
2445
2446
        return null;
2447
    }
2448
2449
    public static function userCanView(): bool
2450
    {
2451
        // For students
2452
        $userCanViewPage = true;
2453
2454
        if ('false' === api_get_setting('allow_students_to_browse_courses')) {
2455
            $userCanViewPage = false;
2456
        }
2457
2458
        //For teachers/admins
2459
        if (api_is_platform_admin() || api_is_course_admin() || api_is_allowed_to_create_course()) {
2460
            $userCanViewPage = true;
2461
        }
2462
2463
        return $userCanViewPage;
2464
    }
2465
}
2466