Passed
Pull Request — master (#5678)
by Angel Fernando Quiroz
13:02 queued 06:11
created

CoursesAndSessionsCatalog::sessionsListByName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 31
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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