Passed
Push — master ( 65d552...4599b6 )
by Angel Fernando Quiroz
10:41
created

getCatalogSearchSettings()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 0
dl 0
loc 20
rs 9.8666
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField;
5
use Chamilo\CoreBundle\Entity\User;
6
use Doctrine\ORM\Query\Expr\Join;
7
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
8
9
/**
10
 * @todo change class name
11
 */
12
class CoursesAndSessionsCatalog
13
{
14
    public const PAGE_LENGTH = 12;
15
16
    /**
17
     * Check the configuration for the courses and sessions catalog.
18
     *
19
     * @global array $_configuration Configuration
20
     *
21
     * @param int $value The value to check
22
     *
23
     * @return bool Whether the configuration is $value
24
     */
25
    public static function is($value = CATALOG_COURSES)
26
    {
27
        $showCoursesSessions = (int) api_get_setting('catalog_show_courses_sessions');
28
        if ($showCoursesSessions == $value) {
29
            return true;
30
        }
31
32
        return false;
33
    }
34
35
    /**
36
     * Check whether to display the sessions list.
37
     *
38
     * @global array $_configuration Configuration
39
     *
40
     * @return bool whether to display
41
     */
42
    public static function showSessions()
43
    {
44
        $catalogShow = (int) api_get_setting('catalog_show_courses_sessions');
45
46
        if (CATALOG_SESSIONS == $catalogShow || CATALOG_COURSES_SESSIONS == $catalogShow) {
47
            return true;
48
        }
49
50
        return false;
51
    }
52
53
    /**
54
     * Check whether to display the courses list.
55
     *
56
     * @global array $_configuration Configuration
57
     *
58
     * @return bool whether to display
59
     */
60
    public static function showCourses()
61
    {
62
        $catalogShow = (int) api_get_setting('catalog_show_courses_sessions');
63
64
        if (CATALOG_COURSES == $catalogShow || CATALOG_COURSES_SESSIONS == $catalogShow) {
65
            return true;
66
        }
67
68
        return false;
69
    }
70
71
    /**
72
     * @return array
73
     */
74
    public static function getCoursesToAvoid()
75
    {
76
        $TABLE_COURSE_FIELD = Database::get_main_table(TABLE_EXTRA_FIELD);
77
        $TABLE_COURSE_FIELD_VALUE = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
78
79
        // Check special courses
80
        $courseListToAvoid = CourseManager::get_special_course_list();
81
82
        // Checks "hide_from_catalog" extra field
83
        $extraFieldType = ExtraField::COURSE_FIELD_TYPE;
84
85
        $sql = "SELECT item_id FROM $TABLE_COURSE_FIELD_VALUE tcfv
86
                INNER JOIN $TABLE_COURSE_FIELD tcf
87
                ON tcfv.field_id =  tcf.id
88
                WHERE
89
                    tcf.item_type = $extraFieldType AND
90
                    tcf.variable = 'hide_from_catalog' AND
91
                    tcfv.field_value = 1
92
                ";
93
94
        $result = Database::query($sql);
95
        if (Database::num_rows($result) > 0) {
96
            while ($row = Database::fetch_array($result)) {
97
                $courseListToAvoid[] = $row['item_id'];
98
            }
99
        }
100
101
        return $courseListToAvoid;
102
    }
103
104
    /**
105
     * @return string
106
     */
107
    public static function getAvoidCourseCondition()
108
    {
109
        $courseListToAvoid = self::getCoursesToAvoid();
110
        $condition = '';
111
        if (!empty($courseListToAvoid)) {
112
            $courses = [];
113
            foreach ($courseListToAvoid as $courseId) {
114
                $courses[] = '"'.$courseId.'"';
115
            }
116
            $condition = ' AND course.id NOT IN ('.implode(',', $courses).')';
117
        }
118
119
        return $condition;
120
    }
121
122
    /**
123
     * Get available le courses count.
124
     *
125
     * @param int $accessUrlId (optional)
126
     *
127
     * @return int Number of courses
128
     */
129
    public static function countAvailableCoursesToShowInCatalog($accessUrlId = 1)
130
    {
131
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
132
        $tableCourseRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
133
        $courseToAvoidCondition = self::getAvoidCourseCondition();
134
        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true);
135
136
        $accessUrlId = (int) $accessUrlId;
137
        if (empty($accessUrlId)) {
138
            $accessUrlId = 1;
139
        }
140
141
        $sql = "SELECT count(course.id)
142
                FROM $tableCourse course
143
                INNER JOIN $tableCourseRelAccessUrl u
144
                ON (course.id = u.c_id)
145
                WHERE
146
                    u.access_url_id = $accessUrlId AND
147
                    course.visibility != 0 AND
148
                    course.visibility != 4
149
                    $courseToAvoidCondition
150
                    $visibilityCondition
151
                ";
152
153
        $res = Database::query($sql);
154
        $row = Database::fetch_row($res);
155
156
        return $row[0];
157
    }
158
159
    public static function getCourseCategoriesTree()
160
    {
161
        $urlId = 1;
162
        if (api_is_multiple_url_enabled()) {
163
            $urlId = api_get_current_access_url_id();
164
        }
165
166
        $countCourses = self::countAvailableCoursesToShowInCatalog($urlId);
167
        $categories = [];
168
        $list = [];
169
170
        $categories['ALL'] = [
171
            'id' => 0,
172
            'name' => get_lang('Display all'),
173
            'code' => 'ALL',
174
            'parent_id' => null,
175
            'tree_pos' => 0,
176
            'number_courses' => $countCourses,
177
            'level' => 0,
178
        ];
179
180
        $allCategories = CourseCategory::getAllCategories();
181
        $categoryToAvoid = '';
182
        if (api_is_student()) {
183
            $categoryToAvoid = api_get_setting('course.course_category_code_to_use_as_model');
184
        }
185
        foreach ($allCategories as $category) {
186
            $categoryCode = $category['code'];
187
            if (!empty($categoryToAvoid) && $categoryToAvoid == $categoryCode) {
188
                continue;
189
            }
190
            if (empty($category['parent_id'])) {
191
                $list[$categoryCode] = $category;
192
                $list[$categoryCode]['level'] = 0;
193
                list($subList, $childrenCount) = self::buildCourseCategoryTree($allCategories, $categoryCode, 0);
194
                foreach ($subList as $item) {
195
                    $list[$item['code']] = $item;
196
                }
197
                // Real course count
198
                $countCourses = CourseCategory::countCoursesInCategory($categoryCode);
199
                $list[$categoryCode]['number_courses'] = $childrenCount + $countCourses;
200
            }
201
        }
202
203
        // count courses that are in no category
204
        $countCourses = CourseCategory::countCoursesInCategory('NONE');
205
        $categories['NONE'] = [
206
            'id' => 0,
207
            'name' => get_lang('Without category'),
208
            'code' => 'NONE',
209
            'parent_id' => null,
210
            'tree_pos' => 0,
211
            'children_count' => 0,
212
            'auth_course_child' => true,
213
            'auth_cat_child' => true,
214
            'number_courses' => $countCourses,
215
            'level' => 0,
216
        ];
217
218
        return array_merge($list, $categories);
219
    }
220
221
    /**
222
     * Return LIMIT to filter SQL query.
223
     *
224
     * @param array $limit
225
     *
226
     * @return string
227
     */
228
    public static function getLimitFilterFromArray($limit)
229
    {
230
        $limitFilter = '';
231
        if (!empty($limit) && is_array($limit)) {
232
            $limitStart = isset($limit['start']) ? (int) $limit['start'] : 0;
233
            $limitLength = isset($limit['length']) ? (int) $limit['length'] : 12;
234
            $limitFilter = 'LIMIT '.$limitStart.', '.$limitLength;
235
        }
236
237
        return $limitFilter;
238
    }
239
240
    /**
241
     * @param string $categoryCode
242
     * @param int    $randomValue
243
     * @param array  $limit        will be used if $randomValue is not set.
244
     *                             This array should contains 'start' and 'length' keys
245
     *
246
     * @return array
247
     */
248
    public static function getCoursesInCategory($categoryCode, $randomValue = null, $limit = [])
249
    {
250
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
251
        $avoidCoursesCondition = self::getAvoidCourseCondition();
252
        $visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true);
253
254
        if (!empty($randomValue)) {
255
            $randomValue = (int) $randomValue;
256
257
            $sql = "SELECT COUNT(*) FROM $tbl_course";
258
            $result = Database::query($sql);
259
            list($num_records) = Database::fetch_row($result);
260
261
            if (api_is_multiple_url_enabled()) {
262
                $urlId = api_get_current_access_url_id();
263
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
264
265
                $urlCondition = ' access_url_id = '.$urlId.' ';
266
                $allowBaseCategories = ('true' === api_get_setting('course.allow_base_course_category'));
267
                if ($allowBaseCategories) {
268
                    $urlCondition = ' (access_url_id = '.$urlId.' OR access_url_id = 1)  ';
269
                }
270
271
                $sql = "SELECT COUNT(*)
272
                        FROM $tbl_course course
273
                        INNER JOIN $tbl_url_rel_course as url_rel_course
274
                        ON (url_rel_course.c_id = course.id)
275
                        WHERE access_url_id = $urlId";
276
                $result = Database::query($sql);
277
                list($num_records) = Database::fetch_row($result);
278
279
                $sql = "SELECT course.id, course.id as real_id
280
                        FROM $tbl_course course
281
                        INNER JOIN $tbl_url_rel_course as url_rel_course
282
                        ON (url_rel_course.c_id = course.id)
283
                        WHERE
284
                            $urlCondition AND
285
                            RAND()*$num_records< $randomValue
286
                            $avoidCoursesCondition
287
                            $visibilityCondition
288
                        ORDER BY RAND()
289
                        LIMIT 0, $randomValue";
290
            } else {
291
                $sql = "SELECT id, id as real_id
292
                        FROM $tbl_course course
293
                        WHERE
294
                            RAND()*$num_records< $randomValue
295
                            $avoidCoursesCondition
296
                            $visibilityCondition
297
                        ORDER BY RAND()
298
                        LIMIT 0, $randomValue";
299
            }
300
301
            $result = Database::query($sql);
302
            $id_in = null;
303
            while (list($id) = Database::fetch_row($result)) {
304
                if ($id_in) {
305
                    $id_in .= ",$id";
306
                } else {
307
                    $id_in = "$id";
308
                }
309
            }
310
            if (null === $id_in) {
311
                return [];
312
            }
313
            $sql = "SELECT *, id as real_id FROM $tbl_course WHERE id IN($id_in)";
314
        } else {
315
            $limitFilter = self::getLimitFilterFromArray($limit);
316
            $categoryCode = Database::escape_string($categoryCode);
317
            $listCode = self::childrenCategories($categoryCode);
318
            $conditionCode = ' ';
319
320
            if (empty($listCode)) {
321
                if ('NONE' === $categoryCode) {
322
                    $conditionCode .= " category_code='' ";
323
                } else {
324
                    $conditionCode .= " category_code='$categoryCode' ";
325
                }
326
            } else {
327
                foreach ($listCode as $code) {
328
                    $conditionCode .= " category_code='$code' OR ";
329
                }
330
                $conditionCode .= " category_code='$categoryCode' ";
331
            }
332
333
            if (empty($categoryCode) || 'ALL' === $categoryCode) {
334
                $sql = "SELECT *, id as real_id
335
                        FROM $tbl_course course
336
                        WHERE
337
                          1=1
338
                          $avoidCoursesCondition
339
                          $visibilityCondition
340
                    ORDER BY title $limitFilter ";
341
            } else {
342
                $sql = "SELECT *, id as real_id FROM $tbl_course course
343
                        WHERE
344
                            $conditionCode
345
                            $avoidCoursesCondition
346
                            $visibilityCondition
347
                        ORDER BY title $limitFilter ";
348
            }
349
350
            // Showing only the courses of the current Chamilo access_url_id
351
            if (api_is_multiple_url_enabled()) {
352
                $urlId = api_get_current_access_url_id();
353
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
354
355
                $urlCondition = ' access_url_id = '.$urlId.' ';
356
                if ('ALL' !== $categoryCode) {
357
                    $sql = "SELECT *, course.id real_id
358
                            FROM $tbl_course as course
359
                            INNER JOIN $tbl_url_rel_course as url_rel_course
360
                            ON (url_rel_course.c_id = course.id)
361
                            WHERE
362
                                $urlCondition AND
363
                                $conditionCode
364
                                $avoidCoursesCondition
365
                                $visibilityCondition
366
                            ORDER BY title $limitFilter";
367
                } else {
368
                    $sql = "SELECT *, course.id real_id FROM $tbl_course as course
369
                            INNER JOIN $tbl_url_rel_course as url_rel_course
370
                            ON (url_rel_course.c_id = course.id)
371
                            WHERE
372
                                $urlCondition
373
                                $avoidCoursesCondition
374
                                $visibilityCondition
375
                            ORDER BY title $limitFilter";
376
                }
377
            }
378
        }
379
380
        $result = Database::query($sql);
381
        $courses = [];
382
        while ($row = Database::fetch_array($result)) {
383
            $row['registration_code'] = !empty($row['registration_code']);
384
            $count_users = CourseManager::get_users_count_in_course($row['code']);
385
            $connectionsLastMonth = Tracking::get_course_connections_count(
386
                $row['id'],
387
                0,
388
                api_get_utc_datetime(time() - (30 * 86400))
389
            );
390
391
            if ('0' == $row['tutor_name']) {
392
                $row['tutor_name'] = get_lang('No administrator');
393
            }
394
395
            $courses[] = [
396
                'real_id' => $row['real_id'],
397
                'point_info' => CourseManager::get_course_ranking($row['id'], 0),
398
                'code' => $row['code'],
399
                'directory' => $row['directory'],
400
                'visual_code' => $row['visual_code'],
401
                'title' => $row['title'],
402
                'tutor' => $row['tutor_name'],
403
                'subscribe' => $row['subscribe'],
404
                'unsubscribe' => $row['unsubscribe'],
405
                'registration_code' => $row['registration_code'],
406
                'creation_date' => $row['creation_date'],
407
                'visibility' => $row['visibility'],
408
                'category' => $row['category_id'],
409
                'count_users' => $count_users,
410
                'count_connections' => $connectionsLastMonth,
411
            ];
412
        }
413
414
        return $courses;
415
    }
416
417
    /**
418
     * Search the courses database for a course that matches the search term.
419
     * The search is done on the code, title and tutor field of the course table.
420
     *
421
     * @param string $categoryCode
422
     * @param string $keyword      The string that the user submitted
423
     * @param array  $limit
424
     * @param bool   $justVisible  search only on visible courses in the catalogue
425
     * @param array  $conditions
426
     *
427
     * @return array an array containing a list of all the courses matching the the search term
428
     */
429
    public static function searchCourses($categoryCode, $keyword, $limit, $justVisible = false, $conditions = [])
430
    {
431
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
432
        $limitFilter = self::getLimitFilterFromArray($limit);
433
        $avoidCoursesCondition = self::getAvoidCourseCondition();
434
        $visibilityCondition = $justVisible ? CourseManager::getCourseVisibilitySQLCondition('course', true) : '';
435
436
        $keyword = Database::escape_string($keyword);
437
        $categoryCode = Database::escape_string($categoryCode);
438
439
        $sqlInjectJoins = '';
440
        $where = 'AND 1 = 1 ';
441
        $sqlInjectWhere = '';
442
        $injectExtraFields = '1';
443
        if (!empty($conditions)) {
444
            $sqlInjectJoins = $conditions['inject_joins'];
445
            $where = $conditions['where'];
446
            $sqlInjectWhere = $conditions['inject_where'];
447
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
448
            $injectExtraFields = rtrim($injectExtraFields, ', ');
449
        }
450
451
        $categoryFilter = '';
452
        if ('ALL' === $categoryCode || empty($categoryCode)) {
453
            // Nothing to do
454
        } elseif ('NONE' === $categoryCode) {
455
            $categoryFilter = ' AND category_code = "" ';
456
        } else {
457
            $categoryFilter = ' AND category_code = "'.$categoryCode.'" ';
458
        }
459
460
        //$sql = "SELECT DISTINCT course.*, $injectExtraFields
461
        $sql = "SELECT DISTINCT(course.id)
462
                FROM $courseTable course
463
                $sqlInjectJoins
464
                WHERE (
465
                        course.code LIKE '%".$keyword."%' OR
466
                        course.title LIKE '%".$keyword."%' OR
467
                        course.tutor_name LIKE '%".$keyword."%'
468
                    )
469
                    $where
470
                    $categoryFilter
471
                    $sqlInjectWhere
472
                    $avoidCoursesCondition
473
                    $visibilityCondition
474
                ORDER BY title, visual_code ASC
475
                $limitFilter
476
                ";
477
478
        if (api_is_multiple_url_enabled()) {
479
            $urlId = api_get_current_access_url_id();
480
            if (-1 != $urlId) {
481
                $tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
482
                $urlCondition = ' access_url_id = '.$urlId.' AND';
483
                $allowBaseCategories = ('true' === api_get_setting('course.allow_base_course_category'));
484
                if ($allowBaseCategories) {
485
                    $urlCondition = ' (access_url_id = '.$urlId.' OR access_url_id = 1) AND ';
486
                }
487
488
                $sql = "SELECT DISTINCT(course.id)
489
                        FROM $courseTable as course
490
                        INNER JOIN $tbl_url_rel_course as url_rel_course
491
                        ON (url_rel_course.c_id = course.id)
492
                        $sqlInjectJoins
493
                        WHERE
494
                            access_url_id = $urlId AND
495
                            (
496
                                code LIKE '%".$keyword."%' OR
497
                                title LIKE '%".$keyword."%' OR
498
                                tutor_name LIKE '%".$keyword."%'
499
                            )
500
                            $where
501
                            $categoryFilter
502
                            $sqlInjectWhere
503
                            $avoidCoursesCondition
504
                            $visibilityCondition
505
                        ORDER BY title, visual_code ASC
506
                        $limitFilter
507
                       ";
508
            }
509
        }
510
511
        $result = Database::query($sql);
512
        $courses = [];
513
        while ($row = Database::fetch_array($result)) {
514
            $courseId = $row['id'];
515
            $courseInfo = api_get_course_info_by_id($courseId);
516
            if (empty($courseInfo)) {
517
                continue;
518
            }
519
            $courseCode = $courseInfo['code'];
520
521
            $countUsers = CourseManager::get_user_list_from_course_code(
522
                $courseCode,
523
                0,
524
                null,
525
                null,
526
                null,
527
                true
528
            );
529
            $connectionsLastMonth = Tracking::get_course_connections_count(
530
                $courseId,
531
                0,
532
                api_get_utc_datetime(time() - (30 * 86400))
533
            );
534
535
            $courseInfo['point_info'] = CourseManager::get_course_ranking($courseId, 0);
536
            $courseInfo['tutor'] = $courseInfo['tutor_name'];
537
            $courseInfo['registration_code'] = !empty($courseInfo['registration_code']);
538
            $courseInfo['count_users'] = $countUsers;
539
            $courseInfo['count_connections'] = $connectionsLastMonth;
540
541
            $courses[] = $courseInfo;
542
        }
543
544
        return $courses;
545
    }
546
547
    /**
548
     * Gets extra fields listed in configuration option course_catalog_settings/extra_field_sort_options sorting order.
549
     *
550
     * @return array "extra_field_$id" => order (1 = ascending, -1 = descending)
551
     */
552
    public static function courseExtraFieldSortingOrder()
553
    {
554
        $order = [];
555
        $variableOrder = api_get_configuration_sub_value('course_catalog_settings/extra_field_sort_options', []);
556
        foreach (self::getCourseExtraFieldsAvailableForSorting() as $extraField) {
557
            $order['extra_field_'.$extraField->getId()] = $variableOrder[$extraField->getVariable()];
558
        }
559
560
        return $order;
561
    }
562
563
    /**
564
     * Gets the extra fields listed in configuration option course_catalog_settings/extra_field_sort_options.
565
     *
566
     * @return ExtraField[]
567
     */
568
    public static function getCourseExtraFieldsAvailableForSorting()
569
    {
570
        $variables = array_keys(
571
            api_get_configuration_sub_value('course_catalog_settings/extra_field_sort_options', [])
572
        );
573
        if (is_array($variables) && !empty($variables)) {
574
            return ExtraField::getExtraFieldsFromVariablesOrdered($variables, ExtraField::COURSE_FIELD_TYPE);
575
        }
576
577
        return [];
578
    }
579
580
    /**
581
     * Builds the list of possible course standard sort criteria.
582
     *
583
     * @return array option name => order (1 = ascending, -1 = descending)
584
     */
585
    public static function courseStandardSortOrder()
586
    {
587
        return api_get_configuration_sub_value(
588
            'course_catalog_settings/standard_sort_options',
589
            [
590
                'title' => 1,
591
                'creation_date' => -1,
592
                'count_users' => -1, // subscription count
593
                'point_info/point_average' => -1, // average score
594
                'point_info/total_score' => -1, // score sum
595
                'point_info/users' => -1, // vote count
596
            ]
597
        );
598
    }
599
600
    /**
601
     * Builds the list of possible course sort criteria to be used in an HTML select element.
602
     *
603
     * @return array select option name => display text
604
     */
605
    public static function courseSortOptions()
606
    {
607
        /** @var $extraFields ExtraField[] */
608
        $standardLabels = [
609
            'title' => get_lang('Title'),
610
            'creation_date' => get_lang('CreationDate'),
611
            'count_users' => get_lang('SubscriptionCount'),
612
            'point_info/point_average' => get_lang('PointAverage'),
613
            'point_info/total_score' => get_lang('TotalScore'),
614
            'point_info/users' => get_lang('VoteCount'),
615
        ];
616
        $options = [];
617
        foreach (array_keys(self::courseStandardSortOrder()) as $name) {
618
            $options[$name] = $standardLabels[$name] ?: $name;
619
        }
620
        foreach (self::getCourseExtraFieldsAvailableForSorting() as $extraField) {
621
            $options['extra_field_'.$extraField->getId()] = $extraField->getDisplayText();
622
        }
623
624
        return $options;
625
    }
626
627
    public static function courseSortOrder()
628
    {
629
        return self::courseStandardSortOrder() + self::courseExtraFieldSortingOrder();
630
    }
631
632
    /**
633
     * Wrapper for self::searchCourses which locally sorts the results according to $sortKey.
634
     *
635
     * @param string   $categoryCode can be 'ALL', 'NONE' or any existing course category code
636
     * @param string   $keyword      search pattern to be found in course code, title or tutor_name
637
     * @param array    $limit        associative array generated by \CoursesAndSessionsCatalog::getLimitArray()
638
     * @param bool     $justVisible  search only on visible courses in the catalogue
639
     * @param array    $conditions   associative array generated using \ExtraField::parseConditions
640
     * @param string[] $sortKeys     a subset of the keys of the array returned by courseSortOptions()
641
     *
642
     * @return array list of all the courses matching the the search term
643
     */
644
    public static function searchAndSortCourses(
645
        $categoryCode,
646
        $keyword,
647
        $limit,
648
        $justVisible = false,
649
        $conditions = [],
650
        $sortKeys = []
651
    ) {
652
        // Get ALL matching courses (no limit)
653
        $courses = self::searchCourses($categoryCode, $keyword, null, $justVisible, $conditions);
654
        // Do we have extra fields to sort on ?
655
        $extraFieldsToSortOn = [];
656
        foreach (self::getCourseExtraFieldsAvailableForSorting() as $extraField) {
657
            if (in_array('extra_field_'.$extraField->getId(), $sortKeys)) {
658
                $extraFieldsToSortOn[] = $extraField;
659
            }
660
        }
661
        if (!empty($extraFieldsToSortOn)) {
662
            // load extra field values and store them in $courses
663
            $courseIds = [];
664
            foreach ($courses as $course) {
665
                $courseIds[] = $course['real_id'];
666
            }
667
            $values = ExtraField::getValueForEachExtraFieldForEachItem($extraFieldsToSortOn, $courseIds);
668
            foreach ($courses as &$course) {
669
                $courseId = $course['real_id'];
670
                if (array_key_exists($courseId, $values)) {
671
                    foreach ($values[$courseId] as $extraFieldId => $value) {
672
                        $course['extra_field_'.$extraFieldId] = $value;
673
                    }
674
                }
675
            }
676
            unset($course);
677
        }
678
        // do we have $course['groupKey']['subKey'] to sort on, such as 'point_info/users' ?
679
        foreach ($sortKeys as $key) {
680
            if (false !== strpos($key, '/')) {
681
                foreach ($courses as &$course) {
682
                    $subValue = api_array_sub_value($course, $key);
683
                    if (!is_null($subValue)) {
684
                        $course[$key] = $subValue;
685
                    }
686
                }
687
                unset($course);
688
            }
689
        }
690
        $sortOrder = self::courseSortOrder();
691
        usort($courses, function ($a, $b) use ($sortKeys, $sortOrder) {
692
            foreach ($sortKeys as $key) {
693
                $valueA = array_key_exists($key, $a) ? $a[$key] : null;
694
                $valueB = array_key_exists($key, $b) ? $b[$key] : null;
695
                if ($valueA !== $valueB) {
696
                    $aIsLessThanB = (is_string($valueA) && is_string($valueB))
697
                        ? strtolower($valueA) < strtolower($valueB)
698
                        : $valueA < $valueB;
699
                    $reverseOrder = (array_key_exists($key, $sortOrder) && -1 === $sortOrder[$key]);
700
                    $aIsBeforeB = ($aIsLessThanB xor $reverseOrder);
701
702
                    return $aIsBeforeB ? -1 : 1;
703
                }
704
            }
705
706
            return 0;
707
        });
708
709
        return array_slice($courses, $limit['start'], $limit['length']);
710
    }
711
712
    /**
713
     * List the sessions.
714
     *
715
     * @param string $date
716
     * @param array  $limit
717
     * @param bool   $returnQueryBuilder
718
     * @param bool   $getCount
719
     *
720
     * @return array|\Doctrine\ORM\Query The session list
721
     */
722
    public static function browseSessions($date = null, $limit = [], $returnQueryBuilder = false, $getCount = false)
723
    {
724
        $urlId = api_get_current_access_url_id();
725
        $em = Database::getManager();
726
        $qb = $em->createQueryBuilder();
727
        $qb2 = $em->createQueryBuilder();
728
729
        $qb = $qb
730
            ->select('s')
731
            ->from('ChamiloCoreBundle:Session', 's')
732
            ->where(
733
                $qb->expr()->in(
734
                    's',
735
                    $qb2
736
                        ->select('s2')
737
                        ->from('ChamiloCoreBundle:AccessUrlRelSession', 'url')
738
                        ->join('ChamiloCoreBundle:Session', 's2')
739
                        ->where(
740
                            $qb->expr()->eq('url.sessionId ', 's2.id')
741
                        )->andWhere(
742
                            $qb->expr()->eq('url.accessUrlId ', $urlId))
743
                        ->getDQL()
744
                )
745
            )
746
            ->andWhere($qb->expr()->gt('s.nbrCourses', 0))
747
        ;
748
749
        if (!empty($date)) {
750
            $qb->andWhere(
751
                $qb->expr()->orX(
752
                    $qb->expr()->isNull('s.accessEndDate'),
753
                    $qb->expr()->andX(
754
                        $qb->expr()->isNotNull('s.accessStartDate'),
755
                        $qb->expr()->isNotNull('s.accessEndDate'),
756
                        $qb->expr()->lte('s.accessStartDate', $date),
757
                        $qb->expr()->gte('s.accessEndDate', $date)
758
                    ),
759
                    $qb->expr()->andX(
760
                        $qb->expr()->isNull('s.accessStartDate'),
761
                        $qb->expr()->isNotNull('s.accessEndDate'),
762
                        $qb->expr()->gte('s.accessEndDate', $date)
763
                    )
764
                )
765
            );
766
        }
767
768
        if ($getCount) {
769
            $qb->select('count(s)');
770
        }
771
772
        $qb = self::hideFromSessionCatalogCondition($qb);
773
774
        if (!empty($limit)) {
775
            $qb
776
                ->setFirstResult($limit['start'])
777
                ->setMaxResults($limit['length'])
778
            ;
779
        }
780
781
        $query = $qb->getQuery();
782
783
        if ($returnQueryBuilder) {
784
            return $query;
785
        }
786
787
        if ($getCount) {
788
            return $query->getSingleScalarResult();
789
        }
790
791
        return $query->getResult();
792
    }
793
794
    /**
795
     * @param \Doctrine\ORM\QueryBuilder $qb
796
     *
797
     * @return mixed
798
     */
799
    public static function hideFromSessionCatalogCondition($qb)
800
    {
801
        $em = Database::getManager();
802
        $qb3 = $em->createQueryBuilder();
803
804
        $extraField = new \ExtraField('session');
805
        $extraFieldInfo = $extraField->get_handler_field_info_by_field_variable('hide_from_catalog');
806
        if (!empty($extraFieldInfo)) {
807
            $qb->andWhere(
808
                $qb->expr()->notIn(
809
                    's',
810
                    $qb3
811
                        ->select('s3')
812
                        ->from('ChamiloCoreBundle:ExtraFieldValues', 'fv')
813
                        ->innerJoin('ChamiloCoreBundle:Session', 's3', Join::WITH, 'fv.itemId = s3.id')
814
                        ->where(
815
                            $qb->expr()->eq('fv.field', $extraFieldInfo['id'])
816
                        )->andWhere(
817
                            $qb->expr()->eq('fv.fieldValue ', 1)
818
                        )
819
                        ->getDQL()
820
                )
821
            );
822
        }
823
824
        return $qb;
825
    }
826
827
    /**
828
     * Search sessions by the tags in their courses.
829
     *
830
     * @param string $termTag Term for search in tags
831
     * @param array  $limit   Limit info
832
     *
833
     * @return array The sessions
834
     */
835
    public static function browseSessionsByTags($termTag, array $limit, $getCount = false)
836
    {
837
        $em = Database::getManager();
838
        $qb = $em->createQueryBuilder();
839
840
        $urlId = api_get_current_access_url_id();
841
842
        $qb->select('s')
843
            ->distinct()
844
            ->from('ChamiloCoreBundle:Session', 's')
845
            ->innerJoin(
846
                'ChamiloCoreBundle:SessionRelCourse',
847
                'src',
848
                Join::WITH,
849
                's.id = src.session'
850
            )
851
            ->innerJoin(
852
                'ChamiloCoreBundle:AccessUrlRelSession',
853
                'url',
854
                Join::WITH,
855
                'url.sessionId = s.id'
856
            )
857
            ->innerJoin(
858
                'ChamiloCoreBundle:ExtraFieldRelTag',
859
                'frt',
860
                Join::WITH,
861
                'src.course = frt.itemId'
862
            )
863
            ->innerJoin(
864
                'ChamiloCoreBundle:Tag',
865
                't',
866
                Join::WITH,
867
                'frt.tagId = t.id'
868
            )
869
            ->innerJoin(
870
                'ChamiloCoreBundle:ExtraField',
871
                'f',
872
                Join::WITH,
873
                'frt.fieldId = f.id'
874
            )
875
            ->where(
876
                $qb->expr()->like('t.tag', ':tag')
877
            )
878
            ->andWhere(
879
                $qb->expr()->eq('f.itemType', ExtraField::COURSE_FIELD_TYPE)
880
            )
881
            ->andWhere($qb->expr()->gt('s.nbrCourses', 0))
882
            ->andWhere($qb->expr()->eq('url.accessUrlId', $urlId))
883
            ->setParameter('tag', "$termTag%")
884
        ;
885
886
        if (!empty($limit)) {
887
            $qb
888
            ->setFirstResult($limit['start'])
889
            ->setMaxResults($limit['length'])
890
            ;
891
        }
892
        //->setParameter('name', "%$termTag%")
893
        $qb = self::hideFromSessionCatalogCondition($qb);
894
895
        if ($getCount) {
896
            $qb->select('count(s)');
897
898
            return $qb->getQuery()->getSingleScalarResult();
899
        }
900
901
        return $qb->getQuery()->getResult();
902
    }
903
904
    /**
905
     * Search sessions by the title.
906
     *
907
     * @param string $keyword
908
     * @param array  $limit   Limit info
909
     *
910
     * @return array The sessions
911
     */
912
    public static function getSessionsByName($keyword, array $limit, $getCount = false)
913
    {
914
        $em = Database::getManager();
915
        $qb = $em->createQueryBuilder();
916
        $urlId = api_get_current_access_url_id();
917
918
        $qb->select('s')
919
            ->distinct()
920
            ->from('ChamiloCoreBundle:Session', 's')
921
            ->innerJoin(
922
                'ChamiloCoreBundle:SessionRelCourse',
923
                'src',
924
                Join::WITH,
925
                's.id = src.session'
926
            )
927
            ->innerJoin(
928
                'ChamiloCoreBundle:AccessUrlRelSession',
929
                'url',
930
                Join::WITH,
931
                'url.sessionId = s.id'
932
            )
933
            ->andWhere($qb->expr()->eq('url.accessUrlId', $urlId))
934
            ->andWhere('s.title LIKE :keyword')
935
            ->andWhere($qb->expr()->gt('s.nbrCourses', 0))
936
            ->setParameter('keyword', "%$keyword%")
937
        ;
938
939
        if (!empty($limit)) {
940
            $qb
941
                ->setFirstResult($limit['start'])
942
                ->setMaxResults($limit['length']);
943
        }
944
945
        $qb = self::hideFromSessionCatalogCondition($qb);
946
947
        if ($getCount) {
948
            $qb->select('count(s)');
949
950
            return $qb->getQuery()->getSingleScalarResult();
951
        }
952
953
        return $qb->getQuery()->getResult();
954
    }
955
956
    /**
957
     * Build a recursive tree of course categories.
958
     *
959
     * @param array $categories
960
     * @param int   $parentId
961
     * @param int   $level
962
     *
963
     * @return array
964
     */
965
    public static function buildCourseCategoryTree($categories, $parentId = 0, $level = 0)
966
    {
967
        $list = [];
968
        $count = 0;
969
        $level++;
970
        foreach ($categories as $category) {
971
            if (empty($category['parent_id'])) {
972
                continue;
973
            }
974
            if ($category['parent_id'] == $parentId) {
975
                $list[$category['code']] = $category;
976
                $count += $category['number_courses'];
977
                $list[$category['code']]['level'] = $level;
978
                list($subList, $childrenCount) = self::buildCourseCategoryTree(
979
                    $categories,
980
                    $category['code'],
981
                    $level
982
                );
983
                $list[$category['code']]['number_courses'] += $childrenCount;
984
                foreach ($subList as $item) {
985
                    $list[$item['code']] = $item;
986
                }
987
                $count += $childrenCount;
988
            }
989
        }
990
991
        return [$list, $count];
992
    }
993
994
    /**
995
     * List Code Search Category.
996
     *
997
     * @param string $code
998
     *
999
     * @return array
1000
     */
1001
    public static function childrenCategories($code)
1002
    {
1003
        $allCategories = CourseCategory::getAllCategories();
1004
        $list = [];
1005
        $row = [];
1006
1007
        if ('ALL' !== $code && 'NONE' !== $code) {
1008
            foreach ($allCategories as $category) {
1009
                if ($category['code'] === $code) {
1010
                    $list = self::buildCourseCategoryTree($allCategories, $category['code'], 0);
1011
                }
1012
            }
1013
            foreach ($list[0] as $item) {
1014
                $row[] = $item['code'];
1015
            }
1016
        }
1017
1018
        return $row;
1019
    }
1020
1021
    /**
1022
     * Display the course catalog image of a course.
1023
     *
1024
     * @param array $course
1025
     *
1026
     * @return string HTML string
1027
     */
1028
    public static function returnThumbnail($course)
1029
    {
1030
        /*$course_path = api_get_path(SYS_COURSE_PATH).$course['directory'];
1031
1032
        if (file_exists($course_path.'/course-pic.png')) {
1033
            // redimensioned image 85x85
1034
            $courseMediumImage = api_get_path(WEB_COURSE_PATH).$course['directory'].'/course-pic.png';
1035
        } else {
1036
            // without picture
1037
            $courseMediumImage = Display::return_icon(
1038
                'session_default.png',
1039
                null,
1040
                null,
1041
                null,
1042
                null,
1043
                true
1044
            );
1045
        }
1046
1047
        return $courseMediumImage;*/
1048
    }
1049
1050
    /**
1051
     * @param array $courseInfo
1052
     *
1053
     * @return string
1054
     */
1055
    public static function return_teacher($courseInfo)
1056
    {
1057
        $teachers = CourseManager::getTeachersFromCourse($courseInfo['real_id']);
1058
        $length = count($teachers);
1059
1060
        if (!$length) {
1061
            return '';
1062
        }
1063
1064
        $html = '<div class="block-author">';
1065
        if ($length > 6) {
1066
            $html .= '<a
1067
            id="plist"
1068
            data-trigger="focus"
1069
            tabindex="0" role="button"
1070
            class="btn btn--plain panel_popover"
1071
            data-toggle="popover"
1072
            title="'.addslashes(get_lang('CourseTeachers')).'"
1073
            data-html="true"
1074
        >
1075
            <i class="fa fa-graduation-cap" aria-hidden="true"></i>
1076
        </a>';
1077
            $html .= '<div id="popover-content-plist" class="hide">';
1078
            foreach ($teachers as $value) {
1079
                $name = $value['firstname'].' '.$value['lastname'];
1080
                $html .= '<div class="popover-teacher">';
1081
                $html .= '<a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">
1082
                        <img src="'.$value['avatar'].'" title="'.$name.'" alt="'.get_lang('UserPicture').'"/></a>';
1083
                $html .= '<div class="teachers-details"><h5>
1084
                        <a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">'
1085
                    .$name.'</a></h5></div>';
1086
                $html .= '</div>';
1087
            }
1088
            $html .= '</div>';
1089
        } else {
1090
            foreach ($teachers as $value) {
1091
                $name = $value['firstname'].' '.$value['lastname'];
1092
                if ($length > 2) {
1093
                    $html .= '<a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">
1094
                        <img src="'.$value['avatar'].'" title="'.$name.'" alt="'.get_lang('UserPicture').'"/></a>';
1095
                } else {
1096
                    $html .= '<a href="'.$value['url'].'" class="ajax" data-title="'.$name.'" title="'.$name.'">
1097
                        <img src="'.$value['avatar'].'" title="'.$name.'" alt="'.get_lang('UserPicture').'"/></a>';
1098
                    $html .= '<div class="teachers-details"><h5>
1099
                        <a href="'.$value['url'].'" class="ajax" data-title="'.$name.'">'
1100
                        .$name.'</a></h5><p>'.get_lang('Teacher').'</p></div>';
1101
                }
1102
            }
1103
        }
1104
        $html .= '</div>';
1105
1106
        return $html;
1107
    }
1108
1109
    /**
1110
     * Display the already registerd text in a course in the course catalog.
1111
     *
1112
     * @param $status
1113
     *
1114
     * @return string HTML string
1115
     */
1116
    public static function return_already_registered_label($status)
1117
    {
1118
        $icon = '<em class="fa fa-check"></em>';
1119
        $title = get_lang('YouAreATeacherOfThisCourse');
1120
        if ('student' === $status) {
1121
            $icon = '<em class="fa fa-check"></em>';
1122
            $title = get_lang('AlreadySubscribed');
1123
        }
1124
1125
        $html = Display::tag(
1126
            'span',
1127
            $icon.' '.$title,
1128
            [
1129
                'id' => 'register',
1130
                'class' => 'label-subscribed text-success',
1131
                'title' => $title,
1132
                'aria-label' => $title,
1133
            ]
1134
        );
1135
1136
        return $html.PHP_EOL;
1137
    }
1138
1139
    /**
1140
     * Display the register button of a course in the course catalog.
1141
     *
1142
     * @param $course
1143
     * @param $stok
1144
     * @param $categoryCode
1145
     * @param $search_term
1146
     *
1147
     * @return string
1148
     */
1149
    public static function return_register_button($course, $stok, $categoryCode, $search_term)
1150
    {
1151
        $title = get_lang('Subscribe');
1152
        $action = 'subscribe_course';
1153
        if (!empty($course['registration_code'])) {
1154
            $action = 'subscribe_course_validation';
1155
        }
1156
1157
        return Display::url(
1158
            Display::getMdiIcon('check').' '.$title,
1159
            api_get_self().'?action='.$action.'&sec_token='.$stok.
1160
            '&course_code='.$course['code'].'&search_term='.$search_term.'&category_code='.$categoryCode,
1161
            ['class' => 'btn btn--success btn-sm', 'title' => $title, 'aria-label' => $title]
1162
        );
1163
    }
1164
1165
    /**
1166
     * Display the unregister button of a course in the course catalog.
1167
     *
1168
     * @param array  $course
1169
     * @param string $stok
1170
     * @param string $search_term
1171
     * @param string $categoryCode
1172
     * @param int    $sessionId
1173
     *
1174
     * @return string
1175
     */
1176
    public static function return_unregister_button($course, $stok, $search_term, $categoryCode, $sessionId = 0)
1177
    {
1178
        $title = get_lang('Unsubscription');
1179
        $search_term = Security::remove_XSS($search_term);
1180
        $categoryCode = Security::remove_XSS($categoryCode);
1181
        $sessionId = (int) $sessionId;
1182
1183
        $url = api_get_self().'?action=unsubscribe&sec_token='.$stok.'&sid='.$sessionId.'&course_code='.$course['code'].
1184
            '&search_term='.$search_term.'&category_code='.$categoryCode;
1185
1186
        return Display::url(
1187
            Display::getMdiIcon('login').'&nbsp;'.$title,
1188
            $url,
1189
            ['class' => 'btn btn--danger', 'title' => $title, 'aria-label' => $title]
1190
        );
1191
    }
1192
1193
    /**
1194
     * Get a HTML button for subscribe to session.
1195
     *
1196
     * @param int    $sessionId         The session ID
1197
     * @param string $sessionName       The session name
1198
     * @param bool   $checkRequirements Optional.
1199
     *                                  Whether the session has requirement. Default is false
1200
     * @param bool   $includeText       Optional. Whether show the text in button
1201
     * @param bool   $btnBing
1202
     *
1203
     * @return string The button HTML
1204
     */
1205
    public static function getRegisteredInSessionButton(
1206
        $sessionId,
1207
        $sessionName,
1208
        $checkRequirements = false,
1209
        $includeText = false,
1210
        $btnBing = false
1211
    ) {
1212
        $sessionId = (int) $sessionId;
1213
1214
        $class = 'btn-sm';
1215
        if ($btnBing) {
1216
            $class = 'btn-lg btn-block';
1217
        }
1218
1219
        if ($checkRequirements) {
1220
            return self::getRequirements($sessionId, SequenceResource::SESSION_TYPE, $includeText, $class);
1221
        }
1222
1223
        $catalogSessionAutoSubscriptionAllowed = false;
1224
        if ('true' === api_get_setting('catalog_allow_session_auto_subscription')) {
1225
            $catalogSessionAutoSubscriptionAllowed = true;
1226
        }
1227
1228
        $url = api_get_path(WEB_CODE_PATH);
1229
1230
        if ($catalogSessionAutoSubscriptionAllowed) {
1231
            $url .= 'auth/courses.php?';
1232
            $url .= http_build_query([
1233
                'action' => 'subscribe_to_session',
1234
                'session_id' => $sessionId,
1235
            ]);
1236
1237
            $result = Display::toolbarButton(
1238
                get_lang('Subscribe'),
1239
                $url,
1240
                'pencil',
1241
                'primary',
1242
                [
1243
                    'class' => $class.' ajax',
1244
                    'data-title' => get_lang('AreYouSureToSubscribe'),
1245
                    'data-size' => 'md',
1246
                    'title' => get_lang('Subscribe'),
1247
                ],
1248
                $includeText
1249
            );
1250
        } else {
1251
            $url .= 'inc/email_editor.php?';
1252
            $url .= http_build_query([
1253
                'action' => 'subscribe_me_to_session',
1254
                'session' => Security::remove_XSS($sessionName),
1255
            ]);
1256
1257
            $result = Display::toolbarButton(
1258
                get_lang('SubscribeToSessionRequest'),
1259
                $url,
1260
                'pencil',
1261
                'primary',
1262
                ['class' => $class],
1263
                $includeText
1264
            );
1265
        }
1266
1267
        $hook = HookResubscribe::create();
1268
        if (!empty($hook)) {
1269
            $hook->setEventData([
1270
                'session_id' => $sessionId,
1271
            ]);
1272
            try {
1273
                $hook->notifyResubscribe(HOOK_EVENT_TYPE_PRE);
1274
            } catch (Exception $exception) {
1275
                $result = $exception->getMessage();
1276
            }
1277
        }
1278
1279
        return $result;
1280
    }
1281
1282
    public static function getRequirements($id, $type, $includeText, $class, $sessionId = 0)
1283
    {
1284
        $id = (int) $id;
1285
        $type = (int) $type;
1286
1287
        $url = api_get_path(WEB_AJAX_PATH).'sequence.ajax.php?';
1288
        $url .= http_build_query(
1289
            [
1290
                'a' => 'get_requirements',
1291
                'id' => $id,
1292
                'type' => $type,
1293
                'sid' => $sessionId,
1294
            ]
1295
        );
1296
1297
        return Display::toolbarButton(
1298
            get_lang('CheckRequirements'),
1299
            $url,
1300
            'shield',
1301
            'info',
1302
            [
1303
                'class' => $class.' ajax',
1304
                'data-title' => get_lang('CheckRequirements'),
1305
                'data-size' => 'md',
1306
                'title' => get_lang('CheckRequirements'),
1307
            ],
1308
            $includeText
1309
        );
1310
    }
1311
1312
    /**
1313
     * Generate a label if the user has been  registered in session.
1314
     *
1315
     * @return string The label
1316
     */
1317
    public static function getAlreadyRegisteredInSessionLabel()
1318
    {
1319
        $icon = '<em class="fa fa-graduation-cap"></em>';
1320
1321
        return Display::div(
1322
            $icon,
1323
            [
1324
                'class' => 'btn btn--plain btn-sm registered',
1325
                'title' => get_lang("AlreadyRegisteredToSession"),
1326
            ]
1327
        );
1328
    }
1329
1330
    /**
1331
     * Get a icon for a session.
1332
     *
1333
     * @param string $sessionName The session name
1334
     *
1335
     * @return string The icon
1336
     */
1337
    public static function getSessionIcon($sessionName)
1338
    {
1339
        return Display::getMdiIcon(
1340
            ObjectIcon::SESSION,
1341
            'ch-tool-icon',
1342
            null,
1343
            ICON_SIZE_MEDIUM,
1344
            $sessionName
1345
        );
1346
    }
1347
1348
    public static function getSessionPagination($action, $countSessions, $limit)
1349
    {
1350
        $pageTotal = ceil($countSessions / $limit['length']);
1351
        $pagination = '';
1352
        // Do NOT show pagination if only one page or less
1353
        if ($pageTotal > 1) {
1354
            $pagination = self::getCatalogPagination(
1355
                $limit['current'],
1356
                $limit['length'],
1357
                $pageTotal,
1358
                null,
1359
                $action
1360
            );
1361
        }
1362
1363
        return $pagination;
1364
    }
1365
1366
    /**
1367
     * Return Session catalog rendered view.
1368
     *
1369
     * @param array $limit
1370
     */
1371
    public static function sessionList($limit = [])
1372
    {
1373
        $date = isset($_POST['date']) ? $_POST['date'] : '';
1374
        $limit = isset($limit) ? $limit : self::getLimitArray();
1375
1376
        $countSessions = self::browseSessions($date, [], false, true);
1377
        $sessions = self::browseSessions($date, $limit);
1378
1379
        $pagination = self::getSessionPagination('display_sessions', $countSessions, $limit);
1380
        $sessionsBlocks = self::getFormattedSessionsBlock($sessions);
1381
1382
        // Get session search catalogue URL
1383
        $courseUrl = self::getCatalogUrl(
1384
            1,
1385
            $limit['length'],
1386
            null,
1387
            'subscribe'
1388
        );
1389
1390
        $tpl = new Template();
1391
        $tpl->assign('actions', self::getTabList(2));
1392
        $tpl->assign('show_courses', self::showCourses());
1393
        $tpl->assign('show_sessions', self::showSessions());
1394
        $tpl->assign('show_tutor', 'true' === api_get_setting('show_session_coach'));
1395
        $tpl->assign('course_url', $courseUrl);
1396
        $tpl->assign('catalog_pagination', $pagination);
1397
        $tpl->assign('search_token', Security::get_token());
1398
        $tpl->assign('search_date', $date);
1399
        $tpl->assign('web_session_courses_ajax_url', api_get_path(WEB_AJAX_PATH).'course.ajax.php');
1400
        $tpl->assign('sessions', $sessionsBlocks);
1401
        $tpl->assign('already_subscribed_label', self::getAlreadyRegisteredInSessionLabel());
1402
        $tpl->assign('catalog_settings', self::getCatalogSearchSettings());
1403
1404
        $contentTemplate = $tpl->get_template('catalog/session_catalog.tpl');
1405
1406
        $tpl->display($contentTemplate);
1407
    }
1408
1409
    /**
1410
     * Show the Session Catalogue with filtered session by course tags.
1411
     *
1412
     * @param array $limit Limit info
1413
     */
1414
    public static function sessionsListByName(array $limit)
1415
    {
1416
        $keyword = isset($_REQUEST['keyword']) ? $_REQUEST['keyword'] : null;
1417
        $courseUrl = self::getCatalogUrl(
1418
            1,
1419
            $limit['length'],
1420
            null,
1421
            'subscribe'
1422
        );
1423
1424
        $count = self::getSessionsByName($keyword, [], true);
1425
        $sessions = self::getSessionsByName($keyword, $limit);
1426
        $sessionsBlocks = self::getFormattedSessionsBlock($sessions);
1427
        $pagination = self::getSessionPagination('search_session_title', $count, $limit);
1428
1429
        $tpl = new Template();
1430
        $tpl->assign('catalog_pagination', $pagination);
1431
        $tpl->assign('actions', self::getTabList(2));
1432
        $tpl->assign('show_courses', self::showCourses());
1433
        $tpl->assign('show_sessions', self::showSessions());
1434
        $tpl->assign('show_tutor', 'true' === api_get_setting('show_session_coach'));
1435
        $tpl->assign('course_url', $courseUrl);
1436
        $tpl->assign('already_subscribed_label', self::getAlreadyRegisteredInSessionLabel());
1437
        $tpl->assign('search_token', Security::get_token());
1438
        $tpl->assign('keyword', Security::remove_XSS($keyword));
1439
        $tpl->assign('sessions', $sessionsBlocks);
1440
        $tpl->assign('catalog_settings', self::getCatalogSearchSettings());
1441
1442
        $contentTemplate = $tpl->get_template('catalog/session_catalog.tpl');
1443
1444
        $tpl->display($contentTemplate);
1445
    }
1446
1447
    public static function getCatalogSearchSettings()
1448
    {
1449
        $settings = api_get_setting('session.catalog_settings', true);
1450
        if (empty($settings)) {
1451
            // Default everything is visible
1452
            $settings = [
1453
                'sessions' => [
1454
                    'by_title' => true,
1455
                    'by_date' => true,
1456
                    'by_tag' => true,
1457
                    'show_session_info' => true,
1458
                    'show_session_date' => true,
1459
                ],
1460
                'courses' => [
1461
                    'by_title' => true,
1462
                ],
1463
            ];
1464
        }
1465
1466
        return $settings;
1467
    }
1468
1469
    /**
1470
     * @param int $active
1471
     *
1472
     * @return string
1473
     */
1474
    public static function getTabList($active = 1)
1475
    {
1476
        $pageLength = isset($_GET['pageLength']) ? (int) $_GET['pageLength'] : self::PAGE_LENGTH;
1477
1478
        $url = self::getCatalogUrl(1, $pageLength, null, 'display_sessions');
1479
        $headers = [];
1480
        if (self::showCourses()) {
1481
            $headers[] = [
1482
                'url' => api_get_self(),
1483
                'content' => get_lang('CourseManagement'),
1484
            ];
1485
        }
1486
1487
        if (self::showSessions()) {
1488
            $headers[] = [
1489
                'url' => $url,
1490
                'content' => get_lang('SessionList'),
1491
            ];
1492
        }
1493
1494
        // If only one option hide menu.
1495
        if (1 === count($headers)) {
1496
            return '';
1497
        }
1498
1499
        return Display::tabsOnlyLink($headers, $active);
1500
    }
1501
1502
    /**
1503
     * Show the Session Catalogue with filtered session by course tags.
1504
     *
1505
     * @param array $limit Limit info
1506
     */
1507
    public static function sessionsListByCoursesTag(array $limit)
1508
    {
1509
        $searchTag = isset($_REQUEST['search_tag']) ? $_REQUEST['search_tag'] : '';
1510
        $searchDate = isset($_REQUEST['date']) ? $_REQUEST['date'] : date('Y-m-d');
1511
        $courseUrl = self::getCatalogUrl(
1512
            1,
1513
            $limit['length'],
1514
            null,
1515
            'subscribe'
1516
        );
1517
1518
        $sessions = self::browseSessionsByTags($searchTag, $limit);
1519
        $sessionsBlocks = self::getFormattedSessionsBlock($sessions);
1520
1521
        $count = self::browseSessionsByTags($searchTag, [], true);
1522
        $pagination = self::getSessionPagination('search_tag', $count, $limit);
1523
1524
        $tpl = new Template();
1525
        $tpl->assign('catalog_pagination', $pagination);
1526
        $tpl->assign('show_courses', self::showCourses());
1527
        $tpl->assign('show_sessions', self::showSessions());
1528
        $tpl->assign('show_tutor', 'true' === api_get_setting('show_session_coach'));
1529
        $tpl->assign('course_url', $courseUrl);
1530
        $tpl->assign('already_subscribed_label', self::getAlreadyRegisteredInSessionLabel());
1531
        $tpl->assign('search_token', Security::get_token());
1532
        $tpl->assign('search_date', Security::remove_XSS($searchDate));
1533
        $tpl->assign('search_tag', Security::remove_XSS($searchTag));
1534
        $tpl->assign('sessions', $sessionsBlocks);
1535
1536
        $contentTemplate = $tpl->get_template('catalog/session_catalog.tpl');
1537
1538
        $tpl->display($contentTemplate);
1539
    }
1540
1541
    /**
1542
     * @return array
1543
     */
1544
    public static function getLimitArray()
1545
    {
1546
        $pageCurrent = isset($_REQUEST['pageCurrent']) ? (int) $_GET['pageCurrent'] : 1;
1547
        $pageLength = isset($_REQUEST['pageLength']) ? (int) $_GET['pageLength'] : self::PAGE_LENGTH;
1548
1549
        return [
1550
            'start' => ($pageCurrent - 1) * $pageLength,
1551
            'current' => $pageCurrent,
1552
            'length' => $pageLength,
1553
        ];
1554
    }
1555
1556
    /**
1557
     * Get the formatted data for sessions block to be displayed on Session Catalog page.
1558
     *
1559
     * @param array $sessions The session list
1560
     *
1561
     * @return array
1562
     */
1563
    public static function getFormattedSessionsBlock(array $sessions)
1564
    {
1565
        $extraFieldValue = new ExtraFieldValue('session');
1566
        $userId = api_get_user_id();
1567
        $sessionsBlocks = [];
1568
        $entityManager = Database::getManager();
1569
        $sessionRelCourseRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourse');
1570
        $extraFieldRepo = $entityManager->getRepository('ChamiloCoreBundle:ExtraField');
1571
        $tagRepo = \Chamilo\CoreBundle\Framework\Container::getTagRepository();
1572
1573
        $tagsField = $extraFieldRepo->findOneBy([
1574
            'itemType' => Chamilo\CoreBundle\Entity\ExtraField::COURSE_FIELD_TYPE,
1575
            'variable' => 'tags',
1576
        ]);
1577
1578
        /** @var \Chamilo\CoreBundle\Entity\Session $session */
1579
        foreach ($sessions as $session) {
1580
            $sessionDates = SessionManager::parseSessionDates([
1581
                'display_start_date' => $session->getDisplayStartDate(),
1582
                'display_end_date' => $session->getDisplayEndDate(),
1583
                'access_start_date' => $session->getAccessStartDate(),
1584
                'access_end_date' => $session->getAccessEndDate(),
1585
                'coach_access_start_date' => $session->getCoachAccessStartDate(),
1586
                'coach_access_end_date' => $session->getCoachAccessEndDate(),
1587
            ]);
1588
1589
            $imageField = $extraFieldValue->get_values_by_handler_and_field_variable(
1590
                $session->getId(),
1591
                'image'
1592
            );
1593
            $sessionCourseTags = [];
1594
            if (!is_null($tagsField)) {
1595
                $sessionRelCourses = $sessionRelCourseRepo->findBy([
1596
                    'session' => $session,
1597
                ]);
1598
                /** @var SessionRelCourse $sessionRelCourse */
1599
                foreach ($sessionRelCourses as $sessionRelCourse) {
1600
                    $courseTags = $tagRepo->getTagsByItem(
1601
                        $tagsField,
1602
                        $sessionRelCourse->getCourse()->getId()
1603
                    );
1604
                    /** @var Tag $tag */
1605
                    foreach ($courseTags as $tag) {
1606
                        $sessionCourseTags[] = $tag->getTag();
1607
                    }
1608
                }
1609
            }
1610
1611
            if (!empty($sessionCourseTags)) {
1612
                $sessionCourseTags = array_unique($sessionCourseTags);
1613
            }
1614
1615
            /** @var SequenceResourceRepository $repo */
1616
            $repo = $entityManager->getRepository('ChamiloCoreBundle:SequenceResource');
1617
            $sequences = $repo->getRequirementsAndDependenciesWithinSequences(
1618
                $session->getId(),
1619
                SequenceResource::SESSION_TYPE
1620
            );
1621
1622
            $hasRequirements = false;
1623
            foreach ($sequences as $sequence) {
1624
                if (0 === count($sequence['requirements'])) {
1625
                    continue;
1626
                }
1627
                $hasRequirements = true;
1628
                break;
1629
            }
1630
            $cat = $session->getCategory();
1631
            if (empty($cat)) {
1632
                $cat = null;
1633
                $catName = '';
1634
            } else {
1635
                $catName = $cat->getTitle();
1636
            }
1637
1638
            $actions = null;
1639
            if (api_is_platform_admin()) {
1640
                $actions = api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$session->getId();
1641
            }
1642
1643
            $plugin = \BuyCoursesPlugin::create();
1644
            $isThisSessionOnSale = $plugin->getBuyCoursePluginPrice($session);
1645
1646
            $sessionsBlock = [
1647
                'id' => $session->getId(),
1648
                'name' => $session->getTitle(),
1649
                'image' => isset($imageField['value']) ? $imageField['value'] : null,
1650
                'nbr_courses' => $session->getNbrCourses(),
1651
                'nbr_users' => $session->getNbrUsers(),
1652
                'coaches' => $session->getGeneralCoaches()
1653
                    ->map(
1654
                        fn(User $coach) => [
1655
                            'id' => $coach->getId(),
1656
                            'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?'
1657
                                .http_build_query(['a' => 'get_user_popup', 'user_id' => $coach->getId()]),
1658
                            'name' => $coach->getFullname(),
1659
                            'avatar' => UserManager::getUserPicture($coach->getId(), USER_IMAGE_SIZE_SMALL),
1660
                        ]
1661
                    ),
1662
                'is_subscribed' => SessionManager::isUserSubscribedAsStudent(
1663
                    $session->getId(),
1664
                    $userId
1665
                ),
1666
                'icon' => self::getSessionIcon($session->getTitle()),
1667
                'date' => $sessionDates['display'],
1668
                'price' => !empty($isThisSessionOnSale['html']) ? $isThisSessionOnSale['html'] : '',
1669
                'subscribe_button' => isset($isThisSessionOnSale['buy_button']) ? $isThisSessionOnSale['buy_button'] : self::getRegisteredInSessionButton(
1670
                    $session->getId(),
1671
                    $session->getTitle(),
1672
                    $hasRequirements
1673
                ),
1674
                'show_description' => $session->getShowDescription(),
1675
                'description' => $session->getDescription(),
1676
                'category' => $catName,
1677
                'tags' => $sessionCourseTags,
1678
                'edit_actions' => $actions,
1679
                'duration' => SessionManager::getDayLeftInSession(
1680
                    ['id' => $session->getId(), 'duration' => $session->getDuration()],
1681
                    $userId
1682
                ),
1683
            ];
1684
1685
            $sessionsBlocks[] = array_merge($sessionsBlock, $sequences);
1686
        }
1687
1688
        return $sessionsBlocks;
1689
    }
1690
1691
    /**
1692
     * Get Pagination HTML div.
1693
     *
1694
     * @param int    $pageCurrent
1695
     * @param int    $pageLength
1696
     * @param int    $pageTotal
1697
     * @param string $categoryCode
1698
     * @param string $action
1699
     * @param array  $fields
1700
     * @param array  $sortKeys
1701
     *
1702
     * @return string
1703
     */
1704
    public static function getCatalogPagination(
1705
        $pageCurrent,
1706
        $pageLength,
1707
        $pageTotal,
1708
        $categoryCode = '',
1709
        $action = '',
1710
        $fields = [],
1711
        $sortKeys = []
1712
    ) {
1713
        // Start empty html
1714
        $pageDiv = '';
1715
        $html = '';
1716
        $pageBottom = max(1, $pageCurrent - 3);
1717
        $pageTop = min($pageTotal, $pageCurrent + 3);
1718
1719
        if ($pageBottom > 1) {
1720
            $pageDiv .= self::getPageNumberItem(1, $pageLength);
1721
            if ($pageBottom > 2) {
1722
                $pageDiv .= self::getPageNumberItem(
1723
                    $pageBottom - 1,
1724
                    $pageLength,
1725
                    null,
1726
                    '...',
1727
                    $categoryCode,
1728
                    $action,
1729
                    $fields,
1730
                    $sortKeys
1731
                );
1732
            }
1733
        }
1734
1735
        // For each page add its page button to html
1736
        for ($i = $pageBottom; $i <= $pageTop; $i++) {
1737
            if ($i === $pageCurrent) {
1738
                $pageItemAttributes = ['class' => 'active'];
1739
            } else {
1740
                $pageItemAttributes = [];
1741
            }
1742
            $pageDiv .= self::getPageNumberItem(
1743
                $i,
1744
                $pageLength,
1745
                $pageItemAttributes,
1746
                '',
1747
                $categoryCode,
1748
                $action,
1749
                $fields,
1750
                $sortKeys
1751
            );
1752
        }
1753
1754
        // Check if current page is the last page
1755
        if ($pageTop < $pageTotal) {
1756
            if ($pageTop < ($pageTotal - 1)) {
1757
                $pageDiv .= self::getPageNumberItem(
1758
                    $pageTop + 1,
1759
                    $pageLength,
1760
                    null,
1761
                    '...',
1762
                    $categoryCode,
1763
                    $action,
1764
                    $fields,
1765
                    $sortKeys
1766
                );
1767
            }
1768
            $pageDiv .= self::getPageNumberItem(
1769
                $pageTotal,
1770
                $pageLength,
1771
                [],
1772
                '',
1773
                $categoryCode,
1774
                $action,
1775
                $fields,
1776
                $sortKeys
1777
            );
1778
        }
1779
1780
        // Complete pagination html
1781
        $pageDiv = Display::tag('ul', $pageDiv, ['class' => 'pagination']);
1782
        $html .= '<nav>'.$pageDiv.'</nav>';
1783
1784
        return $html;
1785
    }
1786
1787
    /**
1788
     * Get li HTML of page number.
1789
     *
1790
     * @param $pageNumber
1791
     * @param $pageLength
1792
     * @param array  $liAttributes
1793
     * @param string $content
1794
     * @param string $categoryCode
1795
     * @param string $action
1796
     * @param array  $fields
1797
     * @param array  $sortKeys
1798
     *
1799
     * @return string
1800
     */
1801
    public static function getPageNumberItem(
1802
        $pageNumber,
1803
        $pageLength,
1804
        $liAttributes = [],
1805
        $content = '',
1806
        $categoryCode = '',
1807
        $action = '',
1808
        $fields = [],
1809
        $sortKeys = []
1810
    ) {
1811
        // Get page URL
1812
        $url = self::getCatalogUrl($pageNumber, $pageLength, $categoryCode, $action, $fields, $sortKeys);
1813
1814
        // If is current page ('active' class) clear URL
1815
        if (isset($liAttributes) && is_array($liAttributes) && isset($liAttributes['class'])) {
1816
            if (false !== strpos('active', $liAttributes['class'])) {
1817
                $url = '';
1818
            }
1819
        }
1820
1821
        $content = !empty($content) ? $content : $pageNumber;
1822
1823
        return Display::tag(
1824
            'li',
1825
            Display::url(
1826
                $content,
1827
                $url
1828
            ),
1829
            $liAttributes
1830
        );
1831
    }
1832
1833
    /**
1834
     * Return URL to course catalog.
1835
     *
1836
     * @param int    $pageCurrent
1837
     * @param int    $pageLength
1838
     * @param string $categoryCode
1839
     * @param string $action
1840
     * @param array  $extraFields
1841
     * @param array  $sortKeys
1842
     *
1843
     * @return string
1844
     */
1845
    public static function getCatalogUrl(
1846
        $pageCurrent,
1847
        $pageLength,
1848
        $categoryCode = null,
1849
        $action = null,
1850
        $extraFields = [],
1851
        $sortKeys = []
1852
    ) {
1853
        $requestAction = isset($_REQUEST['action']) ? Security::remove_XSS($_REQUEST['action']) : '';
1854
        $action = isset($action) ? Security::remove_XSS($action) : $requestAction;
1855
        $searchTerm = isset($_REQUEST['search_term']) ? Security::remove_XSS($_REQUEST['search_term']) : '';
1856
        $keyword = isset($_REQUEST['keyword']) ? Security::remove_XSS($_REQUEST['keyword']) : '';
1857
        $searchTag = isset($_REQUEST['search_tag']) ? $_REQUEST['search_tag'] : '';
1858
1859
        if ('subscribe_user_with_password' === $action) {
1860
            $action = 'subscribe';
1861
        }
1862
1863
        $categoryCode = !empty($categoryCode) ? Security::remove_XSS($categoryCode) : 'ALL';
1864
1865
        // Start URL with params
1866
        $pageUrl = api_get_self().
1867
            '?action='.$action.
1868
            '&search_term='.$searchTerm.
1869
            '&keyword='.$keyword.
1870
            '&search_tag='.$searchTag.
1871
            '&category_code='.$categoryCode.
1872
            '&pageCurrent='.$pageCurrent.
1873
            '&pageLength='.$pageLength;
1874
1875
        if (!empty($extraFields)) {
1876
            $params = [];
1877
            foreach ($extraFields as $variable => $value) {
1878
                $params[Security::remove_XSS($variable)] = Security::remove_XSS($value);
1879
            }
1880
            if (!empty($params)) {
1881
                $pageUrl .= '&'.http_build_query($params);
1882
            }
1883
        }
1884
1885
        if (!empty($sortKeys)) {
1886
            foreach ($sortKeys as $sortKey) {
1887
                $pageUrl .= '&sortKeys%5B%5D='.Security::remove_XSS($sortKey);
1888
            }
1889
        }
1890
1891
        switch ($action) {
1892
            case 'subscribe':
1893
                // for search
1894
                $pageUrl .=
1895
                    '&sec_token='.Security::getTokenFromSession();
1896
                break;
1897
            case 'display_courses':
1898
            default:
1899
                break;
1900
        }
1901
1902
        return $pageUrl;
1903
    }
1904
}
1905