CoursesAndSessionsCatalog   F
last analyzed

Complexity

Total Complexity 289

Size/Duplication

Total Lines 2448
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 289
eloc 1347
c 1
b 0
f 0
dl 0
loc 2448
rs 0.8

46 Methods

Rating   Name   Duplication   Size   Complexity  
A showCourses() 0 5 2
A is() 0 5 1
A showSessions() 0 5 2
B getCoursesToAvoid() 0 38 7
B getCourseCategoriesTree() 0 66 10
A getLimitArray() 0 9 3
A sessionsListByName() 0 35 1
A getSessionsByName() 0 42 3
A buildCourseCategoryTree() 0 27 5
B browseSessions() 0 70 6
A childrenCategories() 0 18 6
A getCoursesToShowInCatalogue() 0 25 3
A getAvoidCourseCondition() 0 15 4
A courseStandardSortOrder() 0 11 1
A getCourseExtraFieldsAvailableForSorting() 0 10 3
A courseSortOrder() 0 3 1
A getTabList() 0 26 5
A getLimitFilterFromArray() 0 10 5
B getRegisteredInSessionButton() 0 74 7
B return_teacher() 0 52 6
A returnThumbnail() 0 17 2
A getCatalogSearchSettings() 0 20 2
A getAlreadyRegisteredInSessionLabel() 0 9 1
A browseSessionsByTags() 0 67 3
A getSessionPagination() 0 16 2
A sessionsListByCoursesTag() 0 36 1
A hideFromSessionCatalogCondition() 0 26 2
A courseSortOptions() 0 20 4
A countAvailableCoursesToShowInCatalog() 0 30 2
A getRequirements() 0 26 1
A courseExtraFieldSortingOrder() 0 9 2
D searchAndSortCourses() 0 68 20
A return_unregister_button() 0 15 1
A return_already_registered_label() 0 21 2
A sessionList() 0 45 2
B return_register_button() 0 67 9
D getCoursesToShowInCatalogueCondition() 0 48 19
B generateRedirectUrlAfterSubscription() 0 23 7
B getCatalogPagination() 0 90 7
F displayCoursesList() 0 368 47
F getFormattedSessionsBlock() 0 129 16
A getPageNumberItem() 0 29 6
A userCanView() 0 15 5
D getCoursesInCategory() 0 168 16
F searchCourses() 0 131 13
F getCatalogUrl() 0 60 16

How to fix   Complexity   

Complex Class

Complex classes like CoursesAndSessionsCatalog often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CoursesAndSessionsCatalog, and based on these observations, apply Extract Interface, too.

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