Passed
Push — master ( f534c7...28463a )
by Angel Fernando Quiroz
12:52 queued 06:03
created

CoursesAndSessionsCatalog::getPageNumberItem()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* 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