Completed
Push — master ( 0b38e9...f93a42 )
by Julito
09:18
created

CoursesAndSessionsCatalog::getSessionIcon()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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