Issues (2037)

main/inc/lib/template.lib.php (1 issue)

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
6
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
7
use Chamilo\UserBundle\Entity\User;
8
9
/**
10
 * Class Template.
11
 *
12
 * @author Julio Montoya <[email protected]>
13
 *
14
 * @todo better organization of the class, methods and variables
15
 */
16
class Template
17
{
18
    /**
19
     * The Template folder name see main/template.
20
     *
21
     * @var string
22
     */
23
    public $templateFolder = 'default';
24
25
    /**
26
     * The theme that will be used: chamilo, public_admin, chamilo_red, etc
27
     * This variable is set from the database.
28
     *
29
     * @var string
30
     */
31
    public $theme = '';
32
33
    /**
34
     * @var string
35
     */
36
    public $preview_theme = '';
37
    public $title = null;
38
    public $show_header;
39
    public $show_footer;
40
    public $help;
41
    public $menu_navigation = []; //Used in the userportal.lib.php function: return_navigation_course_links()
42
    public $show_learnpath = false; // This is a learnpath section or not?
43
    public $plugin = null;
44
    public $course_id = null;
45
    public $user_is_logged_in = false;
46
    public $twig = null;
47
48
    /* Loads chamilo plugins */
49
    public $load_plugins = false;
50
    public $params = [];
51
    public $force_plugin_load = false;
52
    public $responseCode = 0;
53
    private $themeDir;
54
55
    /**
56
     * @param string $title
57
     * @param bool   $show_header
58
     * @param bool   $show_footer
59
     * @param bool   $show_learnpath
60
     * @param bool   $hide_global_chat
61
     * @param bool   $load_plugins
62
     * @param int    $responseCode
63
     * @param bool   $sendHeaders      send http headers or not
64
     */
65
    public function __construct(
66
        $title = '',
67
        $show_header = true,
68
        $show_footer = true,
69
        $show_learnpath = false,
70
        $hide_global_chat = false,
71
        $load_plugins = true,
72
        $sendHeaders = true,
73
        $responseCode = 0
74
    ) {
75
        // Page title
76
        $this->title = $title;
77
        $this->show_learnpath = $show_learnpath;
78
        $this->setResponseCode($responseCode);
79
80
        if (empty($this->show_learnpath)) {
81
            $origin = api_get_origin();
82
            if ($origin === 'learnpath') {
83
                $this->show_learnpath = true;
84
                $show_footer = false;
85
                $show_header = false;
86
            }
87
        }
88
        $this->hide_global_chat = $hide_global_chat;
89
        $this->load_plugins = $load_plugins;
90
91
        $template_paths = [
92
            api_get_path(SYS_CODE_PATH).'template/overrides', // user defined templates
93
            api_get_path(SYS_CODE_PATH).'template', //template folder
94
            api_get_path(SYS_PLUGIN_PATH), // plugin folder
95
        ];
96
97
        $urlId = api_get_current_access_url_id();
98
        $cache_folder = api_get_path(SYS_ARCHIVE_PATH).'twig/'.$urlId.'/';
99
100
        if (!is_dir($cache_folder)) {
101
            mkdir($cache_folder, api_get_permissions_for_new_directories(), true);
102
        }
103
104
        $loader = new Twig_Loader_Filesystem($template_paths);
105
106
        $isTestMode = api_get_setting('server_type') === 'test';
107
108
        //Setting Twig options depending on the server see http://twig.sensiolabs.org/doc/api.html#environment-options
109
        if ($isTestMode) {
110
            $options = [
111
                //'cache' => api_get_path(SYS_ARCHIVE_PATH), //path to the cache folder
112
                'autoescape' => false,
113
                'debug' => true,
114
                'auto_reload' => true,
115
                'optimizations' => 0,
116
                // turn on optimizations with -1
117
                'strict_variables' => false,
118
                //If set to false, Twig will silently ignore invalid variables
119
            ];
120
        } else {
121
            $options = [
122
                'cache' => $cache_folder,
123
                //path to the cache folder
124
                'autoescape' => false,
125
                'debug' => false,
126
                'auto_reload' => false,
127
                'optimizations' => -1,
128
                // turn on optimizations with -1
129
                'strict_variables' => false,
130
                //If set to false, Twig will silently ignore invalid variables
131
            ];
132
        }
133
134
        $this->twig = new Twig_Environment($loader, $options);
135
136
        if ($isTestMode) {
137
            $this->twig->addExtension(new Twig_Extension_Debug());
138
        }
139
140
        // Twig filters setup
141
        $filters = [
142
            'var_dump',
143
            'get_plugin_lang',
144
            'get_lang',
145
            'api_get_path',
146
            'api_get_local_time',
147
            'api_convert_and_format_date',
148
            'api_is_allowed_to_edit',
149
            'api_get_user_info',
150
            'api_get_configuration_value',
151
            'api_get_setting',
152
            'api_get_course_setting',
153
            'api_get_plugin_setting',
154
            [
155
                'name' => 'return_message',
156
                'callable' => 'Display::return_message_and_translate',
157
            ],
158
            [
159
                'name' => 'display_page_header',
160
                'callable' => 'Display::page_header_and_translate',
161
            ],
162
            [
163
                'name' => 'display_page_subheader',
164
                'callable' => 'Display::page_subheader_and_translate',
165
            ],
166
            [
167
                'name' => 'icon',
168
                'callable' => 'Display::get_icon_path',
169
            ],
170
            [
171
                'name' => 'img',
172
                'callable' => 'Display::get_image',
173
            ],
174
            [
175
                'name' => 'format_date',
176
                'callable' => 'api_format_date',
177
            ],
178
            [
179
                'name' => 'get_template',
180
                'callable' => 'api_find_template',
181
            ],
182
            [
183
                'name' => 'date_to_time_ago',
184
                'callable' => 'Display::dateToStringAgoAndLongDate',
185
            ],
186
            [
187
                'name' => 'remove_xss',
188
                'callable' => 'Security::remove_XSS',
189
            ],
190
        ];
191
192
        foreach ($filters as $filter) {
193
            if (is_array($filter)) {
194
                $this->twig->addFilter(new Twig_SimpleFilter($filter['name'], $filter['callable']));
195
            } else {
196
                $this->twig->addFilter(new Twig_SimpleFilter($filter, $filter));
197
            }
198
        }
199
200
        $functions = [
201
            ['name' => 'get_tutors_names', 'callable' => 'Template::returnTutorsNames'],
202
            ['name' => 'get_teachers_names', 'callable' => 'Template::returnTeachersNames'],
203
            ['name' => 'api_is_platform_admin', 'callable' => 'api_is_platform_admin'],
204
        ];
205
206
        foreach ($functions as $function) {
207
            $this->twig->addFunction(new Twig_SimpleFunction($function['name'], $function['callable']));
208
        }
209
210
        // Setting system variables
211
        $this->set_system_parameters();
212
213
        // Setting user variables
214
        $this->set_user_parameters();
215
216
        // Setting course variables
217
        $this->set_course_parameters();
218
219
        // Setting administrator variables
220
        $this->setAdministratorParams();
221
        //$this->setCSSEditor();
222
223
        // Header and footer are showed by default
224
        $this->set_footer($show_footer);
225
        $this->set_header($show_header);
226
227
        // Extra class for the main cm-content div
228
        global $htmlContentExtraClass;
229
        $this->setExtraContentClass($htmlContentExtraClass);
230
231
        $this->set_header_parameters($sendHeaders);
232
        $this->set_footer_parameters();
233
234
        $defaultStyle = api_get_configuration_value('default_template');
235
        if (!empty($defaultStyle)) {
236
            $this->templateFolder = $defaultStyle;
237
        }
238
239
        $this->assign('template', $this->templateFolder);
240
        $this->assign('locale', api_get_language_isocode());
241
        $this->assign('login_class', null);
242
243
        $allow = api_get_configuration_value('show_language_selector_in_menu');
244
        if ($allow) {
245
            $this->assign('language_form', api_display_language_form());
246
        }
247
248
        if (api_get_configuration_value('notification_event')) {
249
            $this->assign('notification_event', '1');
250
        }
251
252
        // Chamilo plugins
253
        if ($this->show_header) {
254
            if ($this->load_plugins) {
255
                $this->plugin = new AppPlugin();
256
257
                //1. Showing installed plugins in regions
258
                $pluginRegions = $this->plugin->get_plugin_regions();
259
                foreach ($pluginRegions as $region) {
260
                    $this->set_plugin_region($region);
261
                }
262
263
                //2. Loading the course plugin info
264
                global $course_plugin;
265
                if (isset($course_plugin) && !empty($course_plugin) && !empty($this->course_id)) {
266
                    //Load plugin get_langs
267
                    $this->plugin->load_plugin_lang_variables($course_plugin);
268
                }
269
            }
270
        }
271
    }
272
273
    /**
274
     * Return the item's url key:.
275
     *
276
     *      c_id=xx&id=xx
277
     *
278
     * @param object $item
279
     *
280
     * @return string
281
     */
282
    public static function key($item)
283
    {
284
        $id = isset($item->id) ? $item->id : null;
285
        $c_id = isset($item->c_id) ? $item->c_id : null;
286
        $result = '';
287
        if ($c_id) {
288
            $result = "c_id=$c_id";
289
        }
290
        if ($id) {
291
            if ($result) {
292
                $result .= "&amp;id=$id";
293
            } else {
294
                $result .= "&amp;id=$id";
295
            }
296
        }
297
298
        return $result;
299
    }
300
301
    /**
302
     * @param string $helpInput
303
     */
304
    public function setHelp($helpInput = null)
305
    {
306
        if (!empty($helpInput)) {
307
            $help = $helpInput;
308
        } else {
309
            $help = $this->help;
310
        }
311
312
        $content = '';
313
        if (api_get_setting('enable_help_link') == 'true') {
314
            if (!empty($help)) {
315
                $help = Security::remove_XSS($help);
316
                $content = '<div class="help">';
317
                $content .= Display::url(
318
                    Display::return_icon('help.large.png', get_lang('Help')),
319
                    api_get_path(WEB_CODE_PATH).'help/help.php?open='.$help,
320
                    [
321
                        'class' => 'ajax',
322
                        'data-title' => get_lang('Help'),
323
                    ]
324
                );
325
                $content .= '</div>';
326
            }
327
        }
328
        $this->assign('help_content', $content);
329
    }
330
331
    /**
332
     * Use template system to parse the actions menu.
333
     *
334
     * @todo finish it!
335
     */
336
    public function set_actions($actions)
337
    {
338
        $action_string = '';
339
        if (!empty($actions)) {
340
            foreach ($actions as $action) {
341
                $action_string .= $action;
342
            }
343
        }
344
        $this->assign('actions', $actions);
345
    }
346
347
    /**
348
     * Shortcut to display a 1 col layout (index.php).
349
     * */
350
    public function display_one_col_template(bool $clearFlashMessages = true)
351
    {
352
        $tpl = $this->get_template('layout/layout_1_col.tpl');
353
        $this->display($tpl, $clearFlashMessages);
354
    }
355
356
    /**
357
     * Shortcut to display a 2 col layout (userportal.php).
358
     */
359
    public function display_two_col_template()
360
    {
361
        $tpl = $this->get_template('layout/layout_2_col.tpl');
362
        $this->display($tpl);
363
    }
364
365
    /**
366
     * Displays an empty template.
367
     */
368
    public function display_blank_template()
369
    {
370
        $tpl = $this->get_template('layout/blank.tpl');
371
        $this->display($tpl);
372
    }
373
374
    /**
375
     * Displays an empty template.
376
     */
377
    public function displayBlankTemplateNoHeader()
378
    {
379
        $tpl = $this->get_template('layout/blank_no_header.tpl');
380
        $this->display($tpl);
381
    }
382
383
    /**
384
     * Displays an empty template.
385
     */
386
    public function display_no_layout_template()
387
    {
388
        $tpl = $this->get_template('layout/no_layout.tpl');
389
        $this->display($tpl);
390
    }
391
392
    /**
393
     * Sets the footer visibility.
394
     *
395
     * @param bool true if we show the footer
396
     */
397
    public function set_footer($status)
398
    {
399
        $this->show_footer = $status;
400
        $this->assign('show_footer', $status);
401
    }
402
403
    /**
404
     * return true if toolbar has to be displayed for user.
405
     *
406
     * @return bool
407
     */
408
    public static function isToolBarDisplayedForUser()
409
    {
410
        //Toolbar
411
        $show_admin_toolbar = api_get_setting('show_admin_toolbar');
412
        $show_toolbar = false;
413
414
        switch ($show_admin_toolbar) {
415
            case 'do_not_show':
416
                break;
417
            case 'show_to_admin':
418
                if (api_is_platform_admin()) {
419
                    $show_toolbar = true;
420
                }
421
                break;
422
            case 'show_to_admin_and_teachers':
423
                if (api_is_platform_admin() || api_is_allowed_to_edit()) {
424
                    $show_toolbar = true;
425
                }
426
                break;
427
            case 'show_to_all':
428
                $show_toolbar = true;
429
                break;
430
        }
431
432
        return $show_toolbar;
433
    }
434
435
    /**
436
     * Sets the header visibility.
437
     *
438
     * @param bool true if we show the header
439
     */
440
    public function set_header($status)
441
    {
442
        $this->show_header = $status;
443
        $this->assign('show_header', $status);
444
445
        $show_toolbar = 0;
446
447
        if (self::isToolBarDisplayedForUser()) {
448
            $show_toolbar = 1;
449
        }
450
451
        $this->assign('show_toolbar', $show_toolbar);
452
453
        // Only if course is available
454
        $courseToolBar = '';
455
        $origin = api_get_origin();
456
        $show_course_navigation_menu = '';
457
        if (!empty($this->course_id) && $this->user_is_logged_in) {
458
            if ($origin !== 'iframe' && $origin !== 'embeddable' && api_get_setting('show_toolshortcuts') !== 'false') {
459
                // Course toolbar
460
                $courseToolBar = CourseHome::show_navigation_tool_shortcuts();
461
            }
462
            if (api_get_setting('show_navigation_menu') != 'false') {
463
                // Course toolbar
464
                $show_course_navigation_menu = CourseHome::show_navigation_menu();
465
            }
466
        }
467
        $this->assign('show_course_shortcut', $courseToolBar);
468
        $this->assign('show_course_navigation_menu', $show_course_navigation_menu);
469
    }
470
471
    /**
472
     * Sets an extra class for the main cm-content div.
473
     * To use, give a new row to $htmlContentExtraClass like so: `$htmlContentExtraClass[] = 'feature-item-user-skill-on';`
474
     * before any Display::display_header() call.
475
     */
476
    public function setExtraContentClass($htmlContentExtraClass): void
477
    {
478
        if (empty($htmlContentExtraClass)) {
479
            $extraClass = '';
480
        } else {
481
            if (is_array($htmlContentExtraClass)) {
482
                $extraClass = implode(' ', $htmlContentExtraClass);
483
            } else {
484
                $extraClass = $htmlContentExtraClass;
485
            }
486
            $extraClass = Security::remove_XSS($extraClass);
487
            $extraClass = trim($extraClass);
488
            $extraClass = ' class="'.$extraClass.'"';
489
        }
490
        $this->assign('html_content_extra_class', $extraClass);
491
    }
492
493
    /**
494
     * Returns the sub-folder and filename for the given tpl file.
495
     *
496
     * If template not found in overrides/ or custom template folder, the default template will be used.
497
     *
498
     * @param string $name
499
     *
500
     * @return string
501
     */
502
    public static function findTemplateFilePath($name)
503
    {
504
        $sysTemplatePath = api_get_path(SYS_TEMPLATE_PATH);
505
506
        // Check if the tpl file is present in the main/template/overrides/ dir
507
        // Overrides is a special directory meant for temporary template
508
        // customization. It must be taken into account before anything else
509
        if (is_readable($sysTemplatePath."overrides/$name")) {
510
            return "overrides/$name";
511
        }
512
513
        $defaultFolder = api_get_configuration_value('default_template');
514
515
        // If a template folder has been manually defined, search for the right
516
        // file, and if not found, go for the same file in the default template
517
        if ($defaultFolder && $defaultFolder != 'default') {
518
            // Avoid missing template error, use the default file.
519
            if (file_exists($sysTemplatePath."$defaultFolder/$name")) {
520
                return "$defaultFolder/$name";
521
            }
522
        }
523
524
        return "default/$name";
525
    }
526
527
    /**
528
     * Call non-static for Template::findTemplateFilePath.
529
     *
530
     * @see Template::findTemplateFilePath()
531
     *
532
     * @param string $name
533
     *
534
     * @return string
535
     */
536
    public function get_template($name)
537
    {
538
        return api_find_template($name);
539
    }
540
541
    /**
542
     * Get CSS themes sub-directory.
543
     *
544
     * @param string $theme
545
     *
546
     * @return string with a trailing slash, e.g. 'themes/chamilo_red/'
547
     */
548
    public static function getThemeDir($theme)
549
    {
550
        $themeDir = 'themes/'.$theme.'/';
551
        $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
552
        if (!empty($virtualTheme)) {
553
            $virtualThemeList = api_get_themes(true);
554
            $isVirtualTheme = in_array($theme, array_keys($virtualThemeList));
555
            if ($isVirtualTheme) {
556
                $themeDir = 'themes/'.$virtualTheme.'/'.$theme.'/';
557
            }
558
        }
559
560
        return $themeDir;
561
    }
562
563
    /**
564
     * Set system parameters from api_get_configuration into _s array for use in TPLs
565
     * Also fills the _p array from getWebPaths().
566
     *
567
     * @uses \self::getWebPaths()
568
     */
569
    public function set_system_parameters()
570
    {
571
        // Get the interface language from global.inc.php
572
        global $language_interface;
573
        $this->theme = api_get_visual_theme();
574
        if (!empty($this->preview_theme)) {
575
            $this->theme = $this->preview_theme;
576
        }
577
578
        $this->themeDir = self::getThemeDir($this->theme);
579
580
        // Setting app paths/URLs
581
        $this->assign('_p', $this->getWebPaths());
582
583
        // Here we can add system parameters that can be use in any template
584
        $_s = [
585
            'software_name' => api_get_configuration_value('software_name'),
586
            'system_version' => api_get_configuration_value('system_version'),
587
            'site_name' => api_get_setting('siteName'),
588
            'institution' => api_get_setting('Institution'),
589
            'institution_url' => api_get_setting('InstitutionUrl'),
590
            'date' => api_format_date('now', DATE_FORMAT_LONG),
591
            'timezone' => api_get_timezone(),
592
            'gamification_mode' => api_get_setting('gamification_mode'),
593
            'language_interface' => $language_interface,
594
        ];
595
        $this->assign('_s', $_s);
596
    }
597
598
    /**
599
     * Set theme, include mainstream CSS files.
600
     *
601
     * @see setCssCustomFiles() for additional CSS sheets
602
     */
603
    public function setCssFiles()
604
    {
605
        global $disable_js_and_css_files;
606
        $css = [];
607
608
        $webPublicPath = api_get_path(WEB_PUBLIC_PATH);
609
        $webJsPath = api_get_path(WEB_LIBRARY_JS_PATH);
610
611
        // Default CSS Bootstrap
612
        $bowerCSSFiles = [
613
            'fontawesome/css/font-awesome.min.css',
614
            'jquery-ui/themes/smoothness/theme.css',
615
            'jquery-ui/themes/smoothness/jquery-ui.min.css',
616
            'mediaelement/build/mediaelementplayer.min.css',
617
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.css',
618
            'bootstrap/dist/css/bootstrap.min.css',
619
            'jquery.scrollbar/jquery.scrollbar.css',
620
            'bootstrap-daterangepicker/daterangepicker.css',
621
            'bootstrap-select/dist/css/bootstrap-select.min.css',
622
            'select2/dist/css/select2.min.css',
623
        ];
624
625
        $hide = api_get_configuration_value('hide_flag_language_switcher');
626
627
        if ($hide === false) {
628
            $bowerCSSFiles[] = 'flag-icon-css/css/flag-icon.min.css';
629
        }
630
631
        foreach ($bowerCSSFiles as $file) {
632
            $css[] = api_get_cdn_path($webPublicPath.'assets/'.$file);
633
        }
634
635
        $isVrViewEnabled = Display::isVrViewEnabled();
636
637
        if ($isVrViewEnabled) {
638
            $css[] = $webJsPath.'mediaelement/plugins/vrview/vrview.css';
639
        }
640
641
        $features = api_get_configuration_value('video_features');
642
        $defaultFeatures = [
643
            'playpause',
644
            'current',
645
            'progress',
646
            'duration',
647
            'tracks',
648
            'volume',
649
            'fullscreen',
650
            'markersrolls',
651
        ];
652
653
        if ($isVrViewEnabled) {
654
            $defaultFeatures[] = 'vrview';
655
        }
656
657
        if (!empty($features) && isset($features['features'])) {
658
            foreach ($features['features'] as $feature) {
659
                if ($feature === 'vrview') {
660
                    continue;
661
                }
662
                $css[] = $webJsPath."mediaelement/plugins/$feature/$feature.min.css";
663
                $defaultFeatures[] = $feature;
664
            }
665
        }
666
667
        $css[] = $webJsPath.'chosen/chosen.css';
668
669
        if (api_is_global_chat_enabled()) {
670
            $css[] = $webJsPath.'chat/css/chat.css';
671
        }
672
        $css_file_to_string = '';
673
        foreach ($css as $file) {
674
            $css_file_to_string .= api_get_css($file);
675
        }
676
677
        if (!$disable_js_and_css_files) {
678
            $this->assign('css_static_file_to_string', $css_file_to_string);
679
        }
680
681
        $defaultFeatures = implode("','", $defaultFeatures);
682
        $this->assign('video_features', $defaultFeatures);
683
    }
684
685
    /**
686
     * Prepare custom CSS to be added at the very end of the <head> section.
687
     *
688
     * @see setCssFiles() for the mainstream CSS files
689
     */
690
    public function setCssCustomFiles()
691
    {
692
        global $disable_js_and_css_files;
693
        // Base CSS
694
        $css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'base.css');
695
696
        if ($this->show_learnpath) {
697
            $css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'scorm.css');
698
            if (is_file(api_get_path(SYS_CSS_PATH).$this->themeDir.'learnpath.css')) {
699
                $css[] = api_get_path(WEB_CSS_PATH).$this->themeDir.'learnpath.css';
700
            }
701
        }
702
        if (CustomPages::enabled()) {
703
            $cssCustomPage = api_get_path(SYS_CSS_PATH).$this->themeDir."custompage.css";
704
            if (is_file($cssCustomPage)) {
705
                $css[] = api_get_path(WEB_CSS_PATH).$this->themeDir.'custompage.css';
706
            } else {
707
                $css[] = api_get_path(WEB_CSS_PATH).'custompage.css';
708
            }
709
        }
710
711
        $css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).$this->themeDir.'default.css');
712
        $css[] = api_get_cdn_path(ChamiloApi::getEditorBlockStylePath());
713
714
        $css_file_to_string = null;
715
        foreach ($css as $file) {
716
            $css_file_to_string .= api_get_css($file);
717
        }
718
        // @todo move this somewhere else. Special fix when using tablets in order to see the text near icons
719
        if (SHOW_TEXT_NEAR_ICONS == true) {
720
            //hack in order to fix the actions buttons
721
            $css_file_to_string .= '<style>
722
                .td_actions a {
723
                    float:left;
724
                    width:100%;
725
                }
726
                .forum_message_left a {
727
                    float:left;
728
                    width:100%;
729
                }
730
                </style>';
731
        }
732
733
        $navigator_info = api_get_navigator();
734
        if ($navigator_info['name'] == 'Internet Explorer' && $navigator_info['version'] == '6') {
735
            $css_file_to_string .= 'img, div { behavior: url('.api_get_path(WEB_LIBRARY_PATH).'javascript/iepngfix/iepngfix.htc) } '."\n";
736
        }
737
738
        if (!$disable_js_and_css_files) {
739
            $this->assign('css_custom_file_to_string', $css_file_to_string);
740
741
            $style_print = api_get_css(
742
                api_get_print_css(false, true),
743
                'print'
744
            );
745
            $this->assign('css_style_print', $style_print);
746
        }
747
748
        // Logo
749
        $logo = return_logo($this->theme);
750
        $logoPdf = return_logo($this->theme, false);
751
        $this->assign('logo', $logo);
752
        $this->assign('logo_pdf', $logoPdf);
753
        $this->assign('show_media_element', 1);
754
    }
755
756
    /**
757
     * Declare and define the template variable that will be used to load
758
     * javascript libraries in the header.
759
     */
760
    public function set_js_files()
761
    {
762
        global $disable_js_and_css_files, $htmlHeadXtra;
763
        $isoCode = api_get_language_isocode();
764
        $isVrViewEnabled = Display::isVrViewEnabled();
765
        $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_'.strtoupper($isoCode).'.min.js';
766
767
        if ($isoCode == 'en') {
768
            $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_US.min.js';
769
        }
770
        // JS files
771
        $js_files = [];
772
        $js_files[] = 'chosen/chosen.jquery.min.js';
773
774
        if ($isVrViewEnabled) {
775
            $js_files[] = 'mediaelement/plugins/vrview/vrview.js';
776
        }
777
778
        $js_files[] = 'mediaelement/plugins/markersrolls/markersrolls.min.js';
779
780
        if (api_get_setting('accessibility_font_resize') === 'true') {
781
            $js_files[] = 'fontresize.js';
782
        }
783
784
        $js_file_to_string = '';
785
        $bowerJsFiles = [
786
            'modernizr/modernizr.js',
787
            'jquery/dist/jquery.min.js',
788
            'bootstrap/dist/js/bootstrap.min.js',
789
            'jquery-ui/jquery-ui.min.js',
790
            'jqueryui-touch-punch/jquery.ui.touch-punch.min.js',
791
            'moment/min/moment-with-locales.js',
792
            'bootstrap-daterangepicker/daterangepicker.js',
793
            'jquery-timeago/jquery.timeago.js',
794
            'mediaelement/build/mediaelement-and-player.min.js',
795
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.js',
796
            'image-map-resizer/js/imageMapResizer.min.js',
797
            'jquery.scrollbar/jquery.scrollbar.min.js',
798
            'readmore-js/readmore.min.js',
799
            'bootstrap-select/dist/js/bootstrap-select.min.js',
800
            $selectLink,
801
            'select2/dist/js/select2.min.js',
802
            "select2/dist/js/i18n/$isoCode.js",
803
            'js-cookie/src/js.cookie.js',
804
        ];
805
806
        if ($renderers = api_get_configuration_sub_value('video_player_renderers/renderers')) {
807
            foreach ($renderers as $renderName) {
808
                if ('youtube' === $renderName) {
809
                    continue;
810
                }
811
812
                $bowerJsFiles[] = "mediaelement/build/renderers/$renderName.min.js";
813
            }
814
        }
815
816
        $viewBySession = api_get_setting('my_courses_view_by_session') === 'true';
817
818
        if ($viewBySession || api_is_global_chat_enabled()) {
819
            // Do not include the global chat in LP
820
            if ($this->show_learnpath == false &&
821
                $this->show_footer == true &&
822
                $this->hide_global_chat == false
823
            ) {
824
                $js_files[] = 'chat/js/chat.js';
825
                $bowerJsFiles[] = 'linkifyjs/linkify.js';
826
                $bowerJsFiles[] = 'linkifyjs/linkify-jquery.js';
827
            }
828
        }
829
830
        $features = api_get_configuration_value('video_features');
831
        if (!empty($features) && isset($features['features'])) {
832
            foreach ($features['features'] as $feature) {
833
                if ($feature === 'vrview') {
834
                    continue;
835
                }
836
                $js_files[] = "mediaelement/plugins/$feature/$feature.min.js";
837
            }
838
        }
839
840
        if (CHAMILO_LOAD_WYSIWYG === true) {
841
            $bowerJsFiles[] = 'ckeditor/ckeditor.js';
842
        }
843
844
        if (api_get_setting('include_asciimathml_script') === 'true') {
845
            $bowerJsFiles[] = 'MathJax/MathJax.js?config=TeX-MML-AM_HTMLorMML';
846
        }
847
848
        // If not English and the language is supported by timepicker, localize
849
        $assetsPath = api_get_path(SYS_PUBLIC_PATH).'assets/';
850
        if ($isoCode != 'en') {
851
            if (is_file($assetsPath.'jqueryui-timepicker-addon/dist/i18n/jquery-ui-timepicker-'.$isoCode.'.js') && is_file($assetsPath.'jquery-ui/ui/minified/i18n/datepicker-'.$isoCode.'.min.js')) {
852
                $bowerJsFiles[] = 'jqueryui-timepicker-addon/dist/i18n/jquery-ui-timepicker-'.$isoCode.'.js';
853
                $bowerJsFiles[] = 'jquery-ui/ui/minified/i18n/datepicker-'.$isoCode.'.min.js';
854
            }
855
        }
856
857
        foreach ($bowerJsFiles as $file) {
858
            $js_file_to_string .= '<script src="'.api_get_cdn_path(api_get_path(WEB_PUBLIC_PATH).'assets/'.$file).'"></script>'."\n";
859
        }
860
861
        foreach ($js_files as $file) {
862
            $js_file_to_string .= api_get_js($file);
863
        }
864
865
        // Loading email_editor js
866
        if (api_get_setting('allow_email_editor') === 'true') {
867
            $link = 'email_editor.php';
868
            if (!api_is_anonymous()) {
869
                $this->assign('email_editor', $link);
870
                $template = $this->get_template('mail_editor/email_link.js.tpl');
871
                $js_file_to_string .= $this->fetch($template);
872
            } else {
873
                if (api_get_configuration_value('allow_email_editor_for_anonymous')) {
874
                    $link = 'email_editor_external.php';
875
                    $this->assign('email_editor', $link);
876
                    $template = $this->get_template('mail_editor/email_link.js.tpl');
877
                    $js_file_to_string .= $this->fetch($template);
878
                }
879
            }
880
        }
881
882
        if (!$disable_js_and_css_files) {
883
            $this->assign('js_file_to_string', $js_file_to_string);
884
885
            $extraHeaders = '<script>var _p = '.json_encode($this->getWebPaths(), JSON_PRETTY_PRINT).'</script>';
886
            // Adding jquery ui by default
887
            $extraHeaders .= api_get_jquery_ui_js();
888
            if (isset($htmlHeadXtra) && $htmlHeadXtra) {
889
                foreach ($htmlHeadXtra as &$this_html_head) {
890
                    $extraHeaders .= $this_html_head."\n";
891
                }
892
            }
893
894
            $ajax = api_get_path(WEB_AJAX_PATH);
895
            $courseId = api_get_course_id();
896
            if (empty($courseId)) {
897
                $courseLogoutCode = '
898
                <script>
899
                function courseLogout() {
900
                }
901
                </script>';
902
            } else {
903
                $courseLogoutCode = "
904
                <script>
905
                var logOutUrl = '".$ajax."course.ajax.php?a=course_logout&".api_get_cidreq()."';
906
                function courseLogout() {
907
                    $.ajax({
908
                        async : false,
909
                        url: logOutUrl,
910
                        success: function (data) {
911
                            return 1;
912
                        }
913
                    });
914
                }
915
                </script>";
916
            }
917
918
            $extraHeaders .= $courseLogoutCode;
919
            $this->assign('extra_headers', $extraHeaders);
920
        }
921
    }
922
923
    /**
924
     * Special function to declare last-minute JS libraries which depend on
925
     * other things to be declared first. In particular, it might be useful
926
     * under IE9 with compatibility mode, which for some reason is getting
927
     * upset when a variable is used in a function (even if not used yet)
928
     * when this variable hasn't been defined yet.
929
     */
930
    public function set_js_files_post()
931
    {
932
        global $disable_js_and_css_files;
933
        $js_files = [];
934
        $bower = '';
935
        if (api_is_global_chat_enabled()) {
936
            //Do not include the global chat in LP
937
            if ($this->show_learnpath == false && $this->show_footer == true && $this->hide_global_chat == false) {
938
                $js_files[] = 'chat/js/chat.js';
939
                $bower .= '<script src="'.api_get_path(WEB_PUBLIC_PATH).'assets/linkifyjs/linkify.js"></script>';
940
                $bower .= '<script src="'.api_get_path(WEB_PUBLIC_PATH).'assets/linkifyjs/linkify-jquery.js"></script>';
941
            }
942
        }
943
        $js_file_to_string = '';
944
        foreach ($js_files as $js_file) {
945
            $js_file_to_string .= api_get_js($js_file);
946
        }
947
        if (!$disable_js_and_css_files) {
948
            $this->assign('js_file_to_string_post', $js_file_to_string.$bower);
949
        }
950
    }
951
952
    /**
953
     * Show header template.
954
     */
955
    public function show_header_template()
956
    {
957
        $tpl = $this->get_template('layout/show_header.tpl');
958
        $this->display($tpl);
959
    }
960
961
    /**
962
     * Show footer template.
963
     */
964
    public function show_footer_template()
965
    {
966
        $tpl = $this->get_template('layout/show_footer.tpl');
967
        $this->display($tpl);
968
    }
969
970
    /**
971
     * Sets the plugin content in a template variable.
972
     *
973
     * @param string $pluginRegion
974
     */
975
    public function set_plugin_region($pluginRegion)
976
    {
977
        if (!empty($pluginRegion)) {
978
            $regionContent = $this->plugin->load_region(
979
                $pluginRegion,
980
                $this,
981
                $this->force_plugin_load
982
            );
983
984
            $pluginList = $this->plugin->getInstalledPlugins(false);
985
            foreach ($pluginList as $plugin_name) {
986
                // The plugin_info variable is available inside the plugin index
987
                $pluginInfo = $this->plugin->getPluginInfo($plugin_name);
988
989
                if (isset($pluginInfo['is_course_plugin']) && $pluginInfo['is_course_plugin']) {
990
                    $courseInfo = api_get_course_info();
991
                    if (!empty($courseInfo)) {
992
                        if (isset($pluginInfo['obj']) && $pluginInfo['obj'] instanceof Plugin) {
993
                            /** @var Plugin $plugin */
994
                            $plugin = $pluginInfo['obj'];
995
                            $regionContent .= $plugin->renderRegion($pluginRegion);
996
                        }
997
                    }
998
                } else {
999
                    continue;
1000
                }
1001
            }
1002
1003
            if (!empty($regionContent)) {
1004
                $this->assign('plugin_'.$pluginRegion, $regionContent);
1005
            } else {
1006
                $this->assign('plugin_'.$pluginRegion, null);
1007
            }
1008
        }
1009
1010
        return null;
1011
    }
1012
1013
    /**
1014
     * @param string $template
1015
     *
1016
     * @return string
1017
     */
1018
    public function fetch($template = null)
1019
    {
1020
        $template = $this->twig->loadTemplate($template);
1021
1022
        return $template->render($this->params);
1023
    }
1024
1025
    /**
1026
     * @param string $variable
1027
     * @param mixed  $value
1028
     */
1029
    public function assign($variable, $value = '')
1030
    {
1031
        $this->params[$variable] = $value;
1032
    }
1033
1034
    /**
1035
     * Render the template.
1036
     *
1037
     * @param string $template           The template path
1038
     * @param bool   $clearFlashMessages Clear the $_SESSION variables for flash messages
1039
     */
1040
    public function display($template, $clearFlashMessages = true)
1041
    {
1042
        $this->assign('page_origin', api_get_origin());
1043
        $this->assign('flash_messages', Display::getFlashToString());
1044
1045
        if ($clearFlashMessages) {
1046
            Display::cleanFlashMessages();
1047
        }
1048
1049
        echo $this->twig->render($template, $this->params);
1050
    }
1051
1052
    /**
1053
     * Adds a body class for login pages.
1054
     */
1055
    public function setLoginBodyClass()
1056
    {
1057
        $this->assign('login_class', 'section-login');
1058
    }
1059
1060
    /**
1061
     * The theme that will be used if the database is not working.
1062
     *
1063
     * @return string
1064
     */
1065
    public static function getThemeFallback()
1066
    {
1067
        $theme = api_get_configuration_value('theme_fallback');
1068
        if (empty($theme)) {
1069
            $theme = 'chamilo';
1070
        }
1071
1072
        return $theme;
1073
    }
1074
1075
    /**
1076
     * @param bool|true $setLoginForm
1077
     */
1078
    public function setLoginForm($setLoginForm = true)
1079
    {
1080
        global $loginFailed;
1081
        $userId = api_get_user_id();
1082
        if (!($userId) || api_is_anonymous($userId)) {
1083
            // Only display if the user isn't logged in.
1084
            $this->assign(
1085
                'login_language_form',
1086
                api_display_language_form(true, true)
1087
            );
1088
            if ($setLoginForm) {
1089
                $this->assign('login_form', $this->displayLoginForm());
1090
1091
                if ($loginFailed) {
1092
                    $this->assign('login_failed', $this::handleLoginFailed());
1093
                }
1094
            }
1095
        }
1096
    }
1097
1098
    /**
1099
     * @return string
1100
     */
1101
    public function handleLoginFailed()
1102
    {
1103
        $message = get_lang('InvalidId');
1104
1105
        if (!isset($_GET['error'])) {
1106
            if (api_is_self_registration_allowed()) {
1107
                $message = get_lang('InvalidForSelfRegistration');
1108
            }
1109
        } else {
1110
            switch ($_GET['error']) {
1111
                case '':
1112
                    if (api_is_self_registration_allowed()) {
1113
                        $message = get_lang('InvalidForSelfRegistration');
1114
                    }
1115
                    break;
1116
                case 'account_expired':
1117
                    $message = get_lang('AccountExpired');
1118
                    break;
1119
                case 'account_inactive':
1120
                    $message = get_lang('AccountInactive');
1121
1122
                    if (api_get_setting('allow_registration') === 'confirmation') {
1123
                        $message = get_lang('AccountNotConfirmed').PHP_EOL;
1124
                        $message .= Display::url(
1125
                            get_lang('ReSendConfirmationMail'),
1126
                            api_get_path(WEB_PATH).'main/auth/resend_confirmation_mail.php',
1127
                            ['class' => 'alert-link']
1128
                        );
1129
                    }
1130
                    break;
1131
                case 'user_password_incorrect':
1132
                    $message = get_lang('InvalidId');
1133
                    break;
1134
                case 'access_url_inactive':
1135
                    $message = get_lang('AccountURLInactive');
1136
                    break;
1137
                case 'wrong_captcha':
1138
                    $message = get_lang('TheTextYouEnteredDoesNotMatchThePicture');
1139
                    break;
1140
                case 'blocked_by_captcha':
1141
                    $message = get_lang('AccountBlockedByCaptcha');
1142
                    break;
1143
                case 'multiple_connection_not_allowed':
1144
                    $message = get_lang('MultipleConnectionsAreNotAllow');
1145
                    break;
1146
                case 'unrecognize_sso_origin':
1147
                    //$message = get_lang('SSOError');
1148
                    break;
1149
            }
1150
        }
1151
1152
        return Display::return_message($message, 'error', false);
1153
    }
1154
1155
    public static function displayCASLoginButton($label = null)
1156
    {
1157
        $form = new FormValidator(
1158
            'form-cas-login',
1159
            'POST',
1160
            $_SERVER['REQUEST_URI'],
1161
            null,
1162
            null,
1163
            FormValidator::LAYOUT_BOX_NO_LABEL
1164
        );
1165
        $form->addHidden('forceCASAuthentication', 1);
1166
        $form->addButton(
1167
            'casLoginButton',
1168
            is_null($label) ? sprintf(get_lang('LoginWithYourAccount'), api_get_setting("Institution")) : $label,
1169
            api_get_setting("casLogoURL"),
1170
            'primary',
1171
            null,
1172
            'btn-block'
1173
        );
1174
1175
        return $form->returnForm();
1176
    }
1177
1178
    public static function displayCASLogoutButton($label = null)
1179
    {
1180
        $form = new FormValidator(
1181
            'form-cas-logout',
1182
            'GET',
1183
            api_get_path(WEB_PATH),
1184
            null,
1185
            null,
1186
            FormValidator::LAYOUT_BOX_NO_LABEL
1187
        );
1188
        $form->addHidden('logout', 1);
1189
        $form->addButton(
1190
            'casLogoutButton',
1191
            is_null($label) ? sprintf(get_lang('LogoutWithYourAccountFromX'), api_get_setting("Institution")) : $label,
1192
            api_get_setting("casLogoURL"),
1193
            'primary',
1194
            null,
1195
            'btn-block'
1196
        );
1197
1198
        return $form->returnForm();
1199
    }
1200
1201
    /**
1202
     * @throws Exception
1203
     *
1204
     * @return string
1205
     */
1206
    public static function displayLoginForm()
1207
    {
1208
        // Get the $cas array from app/config/auth.conf.php
1209
        global $cas;
1210
1211
        if (is_array($cas) && array_key_exists('replace_login_form', $cas) && $cas['replace_login_form']) {
1212
            return self::displayCASLoginButton();
1213
        }
1214
1215
        $form = new FormValidator(
1216
            'formLogin',
1217
            'POST',
1218
            null,
1219
            null,
1220
            null,
1221
            FormValidator::LAYOUT_BOX_NO_LABEL
1222
        );
1223
        $params = [
1224
            'id' => 'login',
1225
            'autofocus' => 'autofocus',
1226
            'icon' => 'user fa-fw',
1227
            'placeholder' => get_lang('UserName'),
1228
        ];
1229
        if (api_get_configuration_value('security_login_autocomplete_disable') === true) {
1230
            $params['autocomplete'] = 'new-password';
1231
        }
1232
        $browserAutoCapitalize = false;
1233
        // Avoid showing the autocapitalize option if the browser doesn't
1234
        // support it: this attribute is against the HTML5 standard
1235
        if (api_browser_support('autocapitalize')) {
1236
            $browserAutoCapitalize = false;
1237
            $params['autocapitalize'] = 'none';
1238
        }
1239
        $form->addText(
1240
            'login',
1241
            get_lang('UserName'),
1242
            true,
1243
            $params
1244
        );
1245
        $params = [
1246
            'id' => 'password',
1247
            'icon' => 'lock fa-fw',
1248
            'placeholder' => get_lang('Pass'),
1249
        ];
1250
        if (api_get_configuration_value('security_login_autocomplete_disable') === true) {
1251
            $params['autocomplete'] = 'new-password';
1252
        }
1253
        if ($browserAutoCapitalize) {
1254
            $params['autocapitalize'] = 'none';
1255
        }
1256
        $form->addElement(
1257
            'password',
1258
            'password',
1259
            get_lang('Pass'),
1260
            $params
1261
        );
1262
        // Captcha
1263
        $captcha = api_get_setting('allow_captcha');
1264
        $allowCaptcha = $captcha === 'true';
1265
1266
        if ($allowCaptcha) {
1267
            $useCaptcha = isset($_SESSION['loginFailed']) ? $_SESSION['loginFailed'] : null;
1268
            if ($useCaptcha) {
1269
                $ajax = api_get_path(WEB_AJAX_PATH).'form.ajax.php?a=get_captcha';
1270
                $options = [
1271
                    'width' => 250,
1272
                    'height' => 90,
1273
                    'callback' => $ajax.'&var='.basename(__FILE__, '.php'),
1274
                    'sessionVar' => basename(__FILE__, '.php'),
1275
                    'imageOptions' => [
1276
                        'font_size' => 20,
1277
                        'font_path' => api_get_path(SYS_FONTS_PATH).'opensans/',
1278
                        'font_file' => 'OpenSans-Regular.ttf',
1279
                        //'output' => 'gif'
1280
                    ],
1281
                ];
1282
1283
                // Minimum options using all defaults (including defaults for Image_Text):
1284
                //$options = array('callback' => 'qfcaptcha_image.php');
1285
                $captchaQuestion = $form->addElement('CAPTCHA_Image', 'captcha_question', '', $options);
1286
                $form->addHtml(get_lang('ClickOnTheImageForANewOne'));
1287
1288
                $form->addElement(
1289
                    'text',
1290
                    'captcha',
1291
                    get_lang('EnterTheLettersYouSee')
1292
                );
1293
                $form->addRule(
1294
                    'captcha',
1295
                    get_lang('EnterTheCharactersYouReadInTheImage'),
1296
                    'required',
1297
                    null,
1298
                    'client'
1299
                );
1300
                $form->addRule(
1301
                    'captcha',
1302
                    get_lang('TheTextYouEnteredDoesNotMatchThePicture'),
1303
                    'CAPTCHA',
1304
                    $captchaQuestion
1305
                );
1306
            }
1307
        }
1308
1309
        $form->addButton(
1310
            'submitAuth',
1311
            get_lang('LoginEnter'),
1312
            null,
1313
            'primary',
1314
            null,
1315
            'btn-block'
1316
        );
1317
1318
        $html = $form->returnForm();
1319
        if (api_get_setting('openid_authentication') == 'true') {
1320
            include_once api_get_path(SYS_CODE_PATH).'auth/openid/login.php';
1321
            $html .= '<div>'.openid_form().'</div>';
1322
        }
1323
1324
        $pluginKeycloak = api_get_plugin_setting('keycloak', 'tool_enable') === 'true';
1325
        $plugin = null;
1326
        if ($pluginKeycloak) {
1327
            $pluginUrl = api_get_path(WEB_PLUGIN_PATH).'keycloak/start.php?sso';
1328
            $pluginUrl = Display::url('Keycloak', $pluginUrl, ['class' => 'btn btn-block btn-primary']);
1329
            $html .= '<div style="margin-top: 10px">'.$pluginUrl.'</div>';
1330
        }
1331
1332
        $html .= '<div></div>';
1333
1334
        return $html;
1335
    }
1336
1337
    public function enableCookieUsageWarning()
1338
    {
1339
        $form = new FormValidator(
1340
            'cookiewarning',
1341
            'post',
1342
            '',
1343
            '',
1344
            [
1345
                //'onsubmit' => "$(this).toggle('show')",
1346
            ],
1347
            FormValidator::LAYOUT_BOX_NO_LABEL
1348
        );
1349
        $form->addHidden('acceptCookies', '1');
1350
        $form->addHtml(
1351
            '<div class="cookieUsageValidation">
1352
                '.get_lang('YouAcceptCookies').'
1353
                <button class="btn btn-link" onclick="$(this).next().toggle(\'slow\'); $(this).toggle(\'slow\')" type="button">
1354
                    ('.get_lang('More').')
1355
                </button>
1356
                <div style="display:none; margin:20px 0;">
1357
                    '.get_lang('HelpCookieUsageValidation').'
1358
                </div>
1359
                <button class="btn btn-link" onclick="$(this).parents(\'form\').submit()" type="button">
1360
                    ('.get_lang('Accept').')
1361
                </button>
1362
            </div>'
1363
        );
1364
        $form->protect();
1365
1366
        if ($form->validate()) {
1367
            api_set_site_use_cookie_warning_cookie();
1368
        } else {
1369
            $this->assign('frmDisplayCookieUsageWarning', $form->returnForm());
1370
        }
1371
    }
1372
1373
    /**
1374
     * Returns the tutors names for the current course in session
1375
     * Function to use in Twig templates.
1376
     *
1377
     * @return string
1378
     */
1379
    public static function returnTutorsNames()
1380
    {
1381
        $em = Database::getManager();
1382
        $tutors = $em
1383
            ->createQuery('
1384
                SELECT u FROM ChamiloUserBundle:User u
1385
                INNER JOIN ChamiloCoreBundle:SessionRelCourseRelUser scu WITH u.id = scu.user
1386
                WHERE scu.status = :teacher_status AND scu.session = :session AND scu.course = :course
1387
            ')
1388
            ->setParameters([
1389
                'teacher_status' => SessionRelCourseRelUser::STATUS_COURSE_COACH,
1390
                'session' => api_get_session_id(),
1391
                'course' => api_get_course_int_id(),
1392
            ])
1393
            ->getResult();
1394
1395
        $names = [];
1396
1397
        /** @var User $tutor */
1398
        foreach ($tutors as $tutor) {
1399
            $names[] = UserManager::formatUserFullName($tutor);
1400
        }
1401
1402
        return implode(CourseManager::USER_SEPARATOR, $names);
1403
    }
1404
1405
    /**
1406
     * Returns the teachers name for the current course
1407
     * Function to use in Twig templates.
1408
     *
1409
     * @return string
1410
     */
1411
    public static function returnTeachersNames()
1412
    {
1413
        $em = Database::getManager();
1414
        $teachers = $em
1415
            ->createQuery('
1416
                SELECT u FROM ChamiloUserBundle:User u
1417
                INNER JOIN ChamiloCoreBundle:CourseRelUser cu WITH u.id = cu.user
1418
                WHERE cu.status = :teacher_status AND cu.course = :course
1419
            ')
1420
            ->setParameters([
1421
                'teacher_status' => User::COURSE_MANAGER,
1422
                'course' => api_get_course_int_id(),
1423
            ])
1424
            ->getResult();
1425
1426
        $names = [];
1427
1428
        /** @var User $teacher */
1429
        foreach ($teachers as $teacher) {
1430
            $names[] = UserManager::formatUserFullName($teacher);
1431
        }
1432
1433
        return implode(CourseManager::USER_SEPARATOR, $names);
1434
    }
1435
1436
    /**
1437
     * @param int $code
1438
     */
1439
    public function setResponseCode($code)
1440
    {
1441
        $this->responseCode = $code;
1442
    }
1443
1444
    public function getResponseCode()
1445
    {
1446
        return $this->responseCode;
1447
    }
1448
1449
    /**
1450
     * Assign HTML code to the 'bug_notification' template variable for the side tabs to report issues.
1451
     *
1452
     * @return bool Always return true because there is always a string, even if empty
1453
     */
1454
    public function assignBugNotification()
1455
    {
1456
        //@todo move this in the template
1457
        $rightFloatMenu = '';
1458
        $iconBug = Display::return_icon(
1459
            'bug.png',
1460
            get_lang('ReportABug'),
1461
            [],
1462
            ICON_SIZE_LARGE
1463
        );
1464
        if (api_get_setting('show_link_bug_notification') === 'true' && $this->user_is_logged_in) {
1465
            $rightFloatMenu = '<div class="report">
1466
		        <a href="https://github.com/chamilo/chamilo-lms/wiki/How-to-report-issues" target="_blank">
1467
                    '.$iconBug.'
1468
                </a>
1469
		        </div>';
1470
        }
1471
1472
        if (api_get_setting('show_link_ticket_notification') === 'true' &&
1473
            $this->user_is_logged_in
1474
        ) {
1475
            // by default is project_id = 1
1476
            $defaultProjectId = 1;
1477
            $iconTicket = Display::return_icon(
1478
                'help.png',
1479
                get_lang('Ticket'),
1480
                [],
1481
                ICON_SIZE_LARGE
1482
            );
1483
            $courseInfo = api_get_course_info();
1484
            $courseParams = '';
1485
            if (!empty($courseInfo)) {
1486
                $courseParams = api_get_cidreq();
1487
            }
1488
1489
            $extraParams = '';
1490
            if (api_get_configuration_value('ticket_lp_quiz_info_add')) {
1491
                if (isset($_GET['exerciseId']) && !empty($_GET['exerciseId'])) {
1492
                    $extraParams = '&exerciseId='.(int) $_GET['exerciseId'];
1493
                }
1494
1495
                if (isset($_GET['lp_id']) && !empty($_GET['lp_id'])) {
1496
                    $extraParams .= '&lpId='.(int) $_GET['lp_id'];
1497
                }
1498
            }
1499
            $url = api_get_path(WEB_CODE_PATH).'ticket/tickets.php?project_id='.$defaultProjectId.'&'.$courseParams.$extraParams;
1500
            $allow = TicketManager::userIsAllowInProject(api_get_user_info(), $defaultProjectId);
1501
1502
            if ($allow) {
1503
                $rightFloatMenu .= '<div class="help">
1504
                    <a href="'.$url.'" target="_blank">
1505
                        '.$iconTicket.'
1506
                    </a>
1507
                </div>';
1508
            }
1509
        }
1510
1511
        $this->assign('bug_notification', $rightFloatMenu);
1512
1513
        return true;
1514
    }
1515
1516
    /**
1517
     * Prepare the _c array for template files. The _c array contains
1518
     * information about the current course.
1519
     */
1520
    private function set_course_parameters()
1521
    {
1522
        //Setting course id
1523
        $course = api_get_course_info();
1524
        if (empty($course)) {
1525
            $this->assign('course_is_set', false);
1526
1527
            return;
1528
        }
1529
        $this->assign('course_is_set', true);
1530
        $this->course_id = $course['id'];
1531
        $_c = [
1532
            'id' => $course['real_id'],
1533
            'code' => $course['code'],
1534
            'title' => $course['name'],
1535
            'visibility' => $course['visibility'],
1536
            'language' => $course['language'],
1537
            'directory' => $course['directory'],
1538
            'session_id' => api_get_session_id(),
1539
            'user_is_teacher' => api_is_course_admin(),
1540
            'student_view' => (!empty($_GET['isStudentView']) && $_GET['isStudentView'] == 'true'),
1541
        ];
1542
        $this->assign('course_code', $course['code']);
1543
        $this->assign('_c', $_c);
1544
    }
1545
1546
    /**
1547
     * Prepare the _u array for template files. The _u array contains
1548
     * information about the current user, as returned by
1549
     * api_get_user_info().
1550
     */
1551
    private function set_user_parameters()
1552
    {
1553
        $user_info = [];
1554
        $user_info['logged'] = 0;
1555
        $this->user_is_logged_in = false;
1556
        if (api_user_is_login()) {
1557
            $user_info = api_get_user_info(api_get_user_id(), true);
1558
            $user_info['logged'] = 1;
1559
1560
            $user_info['is_admin'] = 0;
1561
            if (api_is_platform_admin()) {
1562
                $user_info['is_admin'] = 1;
1563
            }
1564
1565
            $user_info['messages_count'] = MessageManager::getCountNewMessages();
1566
            $this->user_is_logged_in = true;
1567
        }
1568
        // Setting the $_u array that could be use in any template
1569
        $this->assign('_u', $user_info);
1570
    }
1571
1572
    /**
1573
     * Get an array of all the web paths available (e.g. 'web' => 'https://my.chamilo.site/').
1574
     *
1575
     * @return array
1576
     */
1577
    private function getWebPaths()
1578
    {
1579
        $queryString = empty($_SERVER['QUERY_STRING']) ? '' : $_SERVER['QUERY_STRING'];
1580
        $requestURI = empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'];
1581
1582
        return [
1583
            'web' => api_get_path(WEB_PATH),
1584
            'web_url' => api_get_web_url(),
1585
            'web_relative' => api_get_path(REL_PATH),
1586
            'web_course' => api_get_path(WEB_COURSE_PATH),
1587
            'web_main' => api_get_path(WEB_CODE_PATH),
1588
            'web_css' => api_get_path(WEB_CSS_PATH),
1589
            'web_css_theme' => api_get_path(WEB_CSS_PATH).$this->themeDir,
1590
            'web_ajax' => api_get_path(WEB_AJAX_PATH),
1591
            'web_img' => api_get_path(WEB_IMG_PATH),
1592
            'web_plugin' => api_get_path(WEB_PLUGIN_PATH),
1593
            'web_lib' => api_get_path(WEB_LIBRARY_PATH),
1594
            'web_upload' => api_get_path(WEB_UPLOAD_PATH),
1595
            'web_self' => api_get_self(),
1596
            'self_basename' => basename(api_get_self()),
1597
            'web_query_vars' => api_htmlentities($queryString),
1598
            'web_self_query_vars' => api_htmlentities($requestURI),
1599
            'web_cid_query' => api_get_cidreq(),
1600
            'web_rel_code' => api_get_path(REL_CODE_PATH),
1601
        ];
1602
    }
1603
1604
    /**
1605
     * Set header parameters.
1606
     *
1607
     * @param bool $sendHeaders send headers
1608
     */
1609
    private function set_header_parameters($sendHeaders)
1610
    {
1611
        global $httpHeadXtra, $interbreadcrumb, $language_file, $_configuration, $this_section;
1612
        $_course = api_get_course_info();
1613
        $nameTools = $this->title;
1614
        $navigation = return_navigation_array();
1615
        $this->menu_navigation = $navigation['menu_navigation'];
1616
1617
        $this->assign('system_charset', api_get_system_encoding());
1618
1619
        if (isset($httpHeadXtra) && $httpHeadXtra) {
1620
            foreach ($httpHeadXtra as &$thisHttpHead) {
1621
                header($thisHttpHead);
1622
            }
1623
        }
1624
1625
        // Get language iso-code for this page - ignore errors
1626
        $this->assign('document_language', api_get_language_isocode());
1627
1628
        $course_title = isset($_course['name']) ? $_course['name'] : null;
1629
1630
        $title_list = [];
1631
1632
        $title_list[] = api_get_setting('Institution');
1633
        $title_list[] = api_get_setting('siteName');
1634
1635
        if (!empty($course_title)) {
1636
            $title_list[] = $course_title;
1637
        }
1638
        if ($nameTools != '') {
1639
            $title_list[] = $nameTools;
1640
        }
1641
1642
        $title_string = '';
1643
        for ($i = 0; $i < count($title_list); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1644
            $title_string .= $title_list[$i];
1645
            if (isset($title_list[$i + 1])) {
1646
                $item = trim($title_list[$i + 1]);
1647
                if (!empty($item)) {
1648
                    $title_string .= ' - ';
1649
                }
1650
            }
1651
        }
1652
1653
        $this->assign('title_string', $title_string);
1654
1655
        // Setting the theme and CSS files
1656
        $this->setCssFiles();
1657
        $this->set_js_files();
1658
        $this->setCssCustomFiles();
1659
1660
        $browser = api_browser_support('check_browser');
1661
        if ($browser[0] == 'Internet Explorer' && $browser[1] >= '11') {
1662
            $browser_head = '<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />';
1663
            $this->assign('browser_specific_head', $browser_head);
1664
        }
1665
1666
        // Implementation of prefetch.
1667
        // See http://cdn.chamilo.org/main/img/online.png for details
1668
        $prefetch = '';
1669
        if (!empty($_configuration['cdn_enable'])) {
1670
            $prefetch .= '<meta http-equiv="x-dns-prefetch-control" content="on">';
1671
            foreach ($_configuration['cdn'] as $host => $exts) {
1672
                $prefetch .= '<link rel="dns-prefetch" href="'.$host.'">';
1673
            }
1674
        }
1675
1676
        $this->assign('prefetch', $prefetch);
1677
        $this->assign('text_direction', api_get_text_direction());
1678
        $this->assign('section_name', 'section-'.$this_section);
1679
        $this->assignFavIcon();
1680
        $this->setHelp();
1681
1682
        $this->assignBugNotification(); //Prepare the 'bug_notification' var for the template
1683
1684
        $this->assignAccessibilityBlock(); //Prepare the 'accessibility' var for the template
1685
1686
        // Preparing values for the menu
1687
1688
        // Logout link
1689
        $hideLogout = api_get_setting('hide_logout_button');
1690
        if ($hideLogout === 'true') {
1691
            $this->assign('logout_link', null);
1692
        } else {
1693
            $this->assign('logout_link', api_get_path(WEB_PATH).'index.php?logout=logout&uid='.api_get_user_id());
1694
        }
1695
1696
        // Profile link
1697
        if (api_get_setting('allow_social_tool') == 'true') {
1698
            $profile_url = api_get_path(WEB_CODE_PATH).'social/home.php';
1699
        } else {
1700
            $profile_url = api_get_path(WEB_CODE_PATH).'auth/profile.php';
1701
        }
1702
1703
        $this->assign('profile_url', $profile_url);
1704
1705
        //Message link
1706
        $message_link = null;
1707
        $message_url = null;
1708
        if (api_get_setting('allow_message_tool') == 'true') {
1709
            $message_url = api_get_path(WEB_CODE_PATH).'messages/inbox.php';
1710
            $message_link = '<a href="'.api_get_path(WEB_CODE_PATH).'messages/inbox.php">'.get_lang('Inbox').'</a>';
1711
        }
1712
        $this->assign('message_link', $message_link);
1713
        $this->assign('message_url', $message_url);
1714
1715
        $pendingSurveyLink = '';
1716
        $show = api_get_configuration_value('show_pending_survey_in_menu');
1717
        if ($show) {
1718
            $pendingSurveyLink = api_get_path(WEB_CODE_PATH).'survey/pending.php';
1719
        }
1720
        $this->assign('pending_survey_url', $pendingSurveyLink);
1721
1722
        // Certificate Link
1723
        $allow = api_get_configuration_value('hide_my_certificate_link');
1724
        if ($allow === false) {
1725
            $certificateUrl = api_get_path(WEB_CODE_PATH).'gradebook/my_certificates.php';
1726
            $certificateLink = Display::url(
1727
                get_lang('MyCertificates'),
1728
                $certificateUrl
1729
            );
1730
            $this->assign('certificate_link', $certificateLink);
1731
            $this->assign('certificate_url', $certificateUrl);
1732
        }
1733
1734
        $institution = api_get_setting('Institution');
1735
        $portal_name = empty($institution) ? api_get_setting('siteName') : $institution;
1736
1737
        $this->assign('portal_name', $portal_name);
1738
1739
        //Menu
1740
        $menu = menuArray();
1741
        $this->assign('menu', $menu);
1742
1743
        $breadcrumb = '';
1744
        // Hide breadcrumb in LP
1745
        if ($this->show_learnpath == false) {
1746
            $breadcrumb = return_breadcrumb(
1747
                $interbreadcrumb,
1748
                $language_file,
1749
                $nameTools
1750
            );
1751
        }
1752
        $this->assign('breadcrumb', $breadcrumb);
1753
1754
        //Extra content
1755
        $extra_header = null;
1756
        if (!api_is_platform_admin()) {
1757
            $extra_header = trim(api_get_setting('header_extra_content'));
1758
        }
1759
        $this->assign('header_extra_content', $extra_header);
1760
1761
        if ($sendHeaders) {
1762
            header('Content-Type: text/html; charset='.api_get_system_encoding());
1763
            header(
1764
                'X-Powered-By: '.$_configuration['software_name'].' '.substr($_configuration['system_version'], 0, 1)
1765
            );
1766
            self::addHTTPSecurityHeaders();
1767
1768
            $responseCode = $this->getResponseCode();
1769
            if (!empty($responseCode)) {
1770
                switch ($responseCode) {
1771
                    case '404':
1772
                        header("HTTP/1.0 404 Not Found");
1773
                        break;
1774
                }
1775
            }
1776
        }
1777
1778
        $this->assignSocialMeta();
1779
    }
1780
1781
    /**
1782
     * Set footer parameters.
1783
     */
1784
    private function set_footer_parameters()
1785
    {
1786
        if (api_get_setting('show_administrator_data') === 'true') {
1787
            $firstName = api_get_setting('administratorName');
1788
            $lastName = api_get_setting('administratorSurname');
1789
1790
            if (!empty($firstName) && !empty($lastName)) {
1791
                $name = api_get_person_name($firstName, $lastName);
1792
            } else {
1793
                $name = $lastName;
1794
                if (empty($lastName)) {
1795
                    $name = $firstName;
1796
                }
1797
            }
1798
1799
            $adminName = '';
1800
            // Administrator name
1801
            if (!empty($name)) {
1802
                $adminName = get_lang('Manager').' : ';
1803
                $adminName .= Display::encrypted_mailto_link(
1804
                    api_get_setting('emailAdministrator'),
1805
                    $name,
1806
                    '',
1807
                    true
1808
                );
1809
            }
1810
            $this->assign('administrator_name', $adminName);
1811
        }
1812
1813
        // Loading footer extra content
1814
        if (!api_is_platform_admin()) {
1815
            $extra_footer = trim(api_get_setting('footer_extra_content'));
1816
            if (!empty($extra_footer)) {
1817
                $this->assign('footer_extra_content', $extra_footer);
1818
            }
1819
        }
1820
1821
        // Tutor name
1822
        if (api_get_setting('show_tutor_data') == 'true') {
1823
            // Course manager
1824
            $courseId = api_get_course_int_id();
1825
            $id_session = api_get_session_id();
1826
            if (!empty($courseId)) {
1827
                $tutor_data = '';
1828
                if ($id_session != 0) {
1829
                    $users = SessionManager::getCoachesByCourseSession($id_session, $courseId);
1830
                    $links = [];
1831
                    if (!empty($users)) {
1832
                        $coaches = [];
1833
                        foreach ($users as $userId) {
1834
                            $coaches[] = api_get_user_info($userId);
1835
                        }
1836
                        $links = array_column($coaches, 'complete_name_with_message_link');
1837
                    }
1838
                    $count = count($links);
1839
                    if ($count > 1) {
1840
                        $tutor_data .= get_lang('Coachs').' : ';
1841
                        $tutor_data .= array_to_string($links, CourseManager::USER_SEPARATOR);
1842
                    } elseif ($count === 1) {
1843
                        $tutor_data .= get_lang('Coach').' : ';
1844
                        $tutor_data .= array_to_string($links, CourseManager::USER_SEPARATOR);
1845
                    } elseif ($count === 0) {
1846
                        $tutor_data .= '';
1847
                    }
1848
                }
1849
                $this->assign('session_teachers', $tutor_data);
1850
            }
1851
        }
1852
1853
        if (api_get_setting('show_teacher_data') == 'true') {
1854
            // course manager
1855
            $courseId = api_get_course_int_id();
1856
            if (!empty($courseId)) {
1857
                $teacher_data = '';
1858
                $teachers = CourseManager::getTeachersFromCourse($courseId);
1859
                if (!empty($teachers)) {
1860
                    $teachersParsed = [];
1861
                    foreach ($teachers as $teacher) {
1862
                        $userId = $teacher['id'];
1863
                        $teachersParsed[] = api_get_user_info($userId);
1864
                    }
1865
                    $links = array_column($teachersParsed, 'complete_name_with_message_link');
1866
                    $label = get_lang('Teacher');
1867
                    if (count($links) > 1) {
1868
                        $label = get_lang('Teachers');
1869
                    }
1870
                    $teacher_data .= $label.' : '.array_to_string($links, CourseManager::USER_SEPARATOR);
1871
                }
1872
                $this->assign('teachers', $teacher_data);
1873
            }
1874
        }
1875
    }
1876
1877
    /**
1878
     * Set administrator variables.
1879
     */
1880
    private function setAdministratorParams()
1881
    {
1882
        $_admin = [
1883
            'email' => api_get_setting('emailAdministrator'),
1884
            'surname' => api_get_setting('administratorSurname'),
1885
            'name' => api_get_setting('administratorName'),
1886
            'telephone' => api_get_setting('administratorTelephone'),
1887
        ];
1888
1889
        $this->assign('_admin', $_admin);
1890
    }
1891
1892
    /**
1893
     * Manage specific HTTP headers security.
1894
     */
1895
    private function addHTTPSecurityHeaders()
1896
    {
1897
        // Implementation of HTTP headers security, as suggested and checked
1898
        // by https://securityheaders.io/
1899
        // Enable these settings in configuration.php to use them on your site
1900
        // Strict-Transport-Security
1901
        $setting = api_get_configuration_value('security_strict_transport');
1902
        if (!empty($setting)) {
1903
            header('Strict-Transport-Security: '.$setting);
1904
        }
1905
        // Content-Security-Policy
1906
        $setting = api_get_configuration_value('security_content_policy');
1907
        if (!empty($setting)) {
1908
            header('Content-Security-Policy: '.$setting);
1909
        }
1910
        $setting = api_get_configuration_value('security_content_policy_report_only');
1911
        if (!empty($setting)) {
1912
            header('Content-Security-Policy-Report-Only: '.$setting);
1913
        }
1914
        // Public-Key-Pins
1915
        $setting = api_get_configuration_value('security_public_key_pins');
1916
        if (!empty($setting)) {
1917
            header('Public-Key-Pins: '.$setting);
1918
        }
1919
        $setting = api_get_configuration_value('security_public_key_pins_report_only');
1920
        if (!empty($setting)) {
1921
            header('Public-Key-Pins-Report-Only: '.$setting);
1922
        }
1923
        // X-Frame-Options
1924
        $setting = api_get_configuration_value('security_x_frame_options');
1925
        if (!empty($setting)) {
1926
            header('X-Frame-Options: '.$setting);
1927
        }
1928
        // X-XSS-Protection
1929
        $setting = api_get_configuration_value('security_xss_protection');
1930
        if (!empty($setting)) {
1931
            header('X-XSS-Protection: '.$setting);
1932
        }
1933
        // X-Content-Type-Options
1934
        $setting = api_get_configuration_value('security_x_content_type_options');
1935
        if (!empty($setting)) {
1936
            header('X-Content-Type-Options: '.$setting);
1937
        }
1938
        // Referrer-Policy
1939
        $setting = api_get_configuration_value('security_referrer_policy');
1940
        if (!empty($setting)) {
1941
            header('Referrer-Policy: '.$setting);
1942
        }
1943
        // end of HTTP headers security block
1944
    }
1945
1946
    /**
1947
     * Assign favicon to the 'favico' template variable.
1948
     *
1949
     * @return bool Always return true because there is always at least one correct favicon.ico
1950
     */
1951
    private function assignFavIcon()
1952
    {
1953
        // Default root chamilo favicon
1954
        $favico = '<link rel="icon" href="'.api_get_path(WEB_PATH).'favicon.png" type="image/png" />';
1955
1956
        //Added to verify if in the current Chamilo Theme exist a favicon
1957
        $favicoThemeUrl = api_get_path(SYS_CSS_PATH).$this->themeDir.'images/';
1958
1959
        //If exist pick the current chamilo theme favicon
1960
        if (is_file($favicoThemeUrl.'favicon.png')) {
1961
            $favico = '<link rel="icon" href="'.api_get_path(WEB_CSS_PATH).$this->themeDir.'images/favicon.png" type="image/png" />';
1962
        }
1963
1964
        if (api_is_multiple_url_enabled()) {
1965
            $access_url_id = api_get_current_access_url_id();
1966
            if ($access_url_id != -1) {
1967
                $url_info = api_get_access_url($access_url_id);
1968
                $url = api_remove_trailing_slash(
1969
                    preg_replace('/https?:\/\//i', '', $url_info['url'])
1970
                );
1971
                $clean_url = api_replace_dangerous_char($url);
1972
                $clean_url = str_replace('/', '-', $clean_url);
1973
                $clean_url .= '/';
1974
                $homep = api_get_path(WEB_HOME_PATH).$clean_url; //homep for Home Path
1975
                $icon_real_homep = api_get_path(SYS_HOME_PATH).$clean_url;
1976
                //we create the new dir for the new sites
1977
                if (is_file($icon_real_homep.'favicon.ico')) {
1978
                    $favico = '<link rel="icon" href="'.$homep.'favicon.png" type="image/png" />';
1979
                }
1980
            }
1981
        }
1982
1983
        $this->assign('favico', $favico);
1984
1985
        return true;
1986
    }
1987
1988
    /**
1989
     * Assign HTML code to the 'accessibility' template variable (usually shown above top menu).
1990
     *
1991
     * @return bool Always return true (even if empty string)
1992
     */
1993
    private function assignAccessibilityBlock()
1994
    {
1995
        $resize = '';
1996
        if (api_get_setting('accessibility_font_resize') == 'true') {
1997
            $resize .= '<div class="resize_font">';
1998
            $resize .= '<div class="btn-group">';
1999
            $resize .= '<a title="'.get_lang('DecreaseFontSize').'" href="#" class="decrease_font btn btn-default"><em class="fa fa-font"></em></a>';
2000
            $resize .= '<a title="'.get_lang('ResetFontSize').'" href="#" class="reset_font btn btn-default"><em class="fa fa-font"></em></a>';
2001
            $resize .= '<a title="'.get_lang('IncreaseFontSize').'" href="#" class="increase_font btn btn-default"><em class="fa fa-font"></em></a>';
2002
            $resize .= '</div>';
2003
            $resize .= '</div>';
2004
        }
2005
        $this->assign('accessibility', $resize);
2006
2007
        return true;
2008
    }
2009
2010
    /**
2011
     * Assign HTML code to the 'social_meta' template variable (usually shown above top menu).
2012
     *
2013
     * @return bool Always return true (even if empty string)
2014
     */
2015
    private function assignSocialMeta()
2016
    {
2017
        $socialMeta = '';
2018
        $metaTitle = api_get_setting('meta_title');
2019
        if (!empty($metaTitle)) {
2020
            $socialMeta .= '<meta name="twitter:card" content="summary" />'."\n";
2021
            $metaSite = api_get_setting('meta_twitter_site');
2022
            if (!empty($metaSite)) {
2023
                $socialMeta .= '<meta name="twitter:site" content="'.$metaSite.'" />'."\n";
2024
                $metaCreator = api_get_setting('meta_twitter_creator');
2025
                if (!empty($metaCreator)) {
2026
                    $socialMeta .= '<meta name="twitter:creator" content="'.$metaCreator.'" />'."\n";
2027
                }
2028
            }
2029
2030
            // The user badge page emits its own meta tags, so if this is
2031
            // enabled, ignore the global ones
2032
            $userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
2033
            $skillId = isset($_GET['skill']) ? intval($_GET['skill']) : 0;
2034
2035
            if (!$userId && !$skillId) {
2036
                // no combination of user and skill ID has been defined,
2037
                // so print the normal or course-specific OpenGraph meta tags
2038
                // Check for a course ID
2039
                $courseId = api_get_course_int_id();
2040
                // Check session ID from session/id/about (see .htaccess)
2041
                $sessionId = isset($_GET['session_id']) ? intval($_GET['session_id']) : 0;
2042
2043
                if ($courseId != false) {
2044
                    // If we are inside a course (even if within a session), publish info about the course
2045
                    $course = api_get_course_entity($courseId);
2046
                    // @TODO: support right-to-left in title
2047
                    $socialMeta .= '<meta property="og:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
2048
                    $socialMeta .= '<meta property="twitter:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
2049
                    $socialMeta .= '<meta property="og:url" content="'.api_get_course_url($course->getCode()).'" />'."\n";
2050
2051
                    $metaDescription = api_get_setting('meta_description');
2052
                    if (!empty($course->getDescription())) {
2053
                        $socialMeta .= '<meta property="og:description" content="'.strip_tags($course->getDescription()).'" />'."\n";
2054
                        $socialMeta .= '<meta property="twitter:description" content="'.strip_tags($course->getDescription()).'" />'."\n";
2055
                    } elseif (!empty($metaDescription)) {
2056
                        $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
2057
                        $socialMeta .= '<meta property="twitter:description" content="'.$metaDescription.'" />'."\n";
2058
                    }
2059
2060
                    $picture = CourseManager::getPicturePath($course, true);
2061
                    if (!empty($picture)) {
2062
                        $socialMeta .= '<meta property="og:image" content="'.$picture.'" />'."\n";
2063
                        $socialMeta .= '<meta property="twitter:image" content="'.$picture.'" />'."\n";
2064
                        $socialMeta .= '<meta property="twitter:image:alt" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
2065
                    } else {
2066
                        $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
2067
                    }
2068
                } elseif ($sessionId !== 0) {
2069
                    // If we are on a session "about" screen, publish info about the session
2070
                    $em = Database::getManager();
2071
                    $session = $em->find('ChamiloCoreBundle:Session', $sessionId);
2072
2073
                    $socialMeta .= '<meta property="og:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
2074
                    $socialMeta .= '<meta property="twitter:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
2075
                    $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH)."session/{$session->getId()}/about/".'" />'."\n";
2076
2077
                    $sessionValues = new ExtraFieldValue('session');
2078
                    $sessionImage = $sessionValues->get_values_by_handler_and_field_variable($session->getId(), 'image')['value'];
2079
                    $sessionImageSysPath = api_get_path(SYS_UPLOAD_PATH).$sessionImage;
2080
2081
                    if (!empty($sessionImage) && is_file($sessionImageSysPath)) {
2082
                        $sessionImagePath = api_get_path(WEB_UPLOAD_PATH).$sessionImage;
2083
                        $socialMeta .= '<meta property="og:image" content="'.$sessionImagePath.'" />'."\n";
2084
                        $socialMeta .= '<meta property="twitter:image" content="'.$sessionImagePath.'" />'."\n";
2085
                        $socialMeta .= '<meta property="twitter:image:alt" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
2086
                    } else {
2087
                        $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
2088
                    }
2089
                } else {
2090
                    // Otherwise (not a course nor a session, nor a user, nor a badge), publish portal info
2091
                    $socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n";
2092
                    $socialMeta .= '<meta property="twitter:title" content="'.$metaTitle.'" />'."\n";
2093
                    $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n";
2094
2095
                    $metaDescription = api_get_setting('meta_description');
2096
                    if (!empty($metaDescription)) {
2097
                        $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
2098
                        $socialMeta .= '<meta property="twitter:description" content="'.$metaDescription.'" />'."\n";
2099
                    }
2100
                    $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
2101
                }
2102
            }
2103
        }
2104
2105
        $this->assign('social_meta', $socialMeta);
2106
2107
        return true;
2108
    }
2109
2110
    /**
2111
     * Get platform meta image tag (check meta_image_path setting, then use the logo).
2112
     *
2113
     * @param string $imageAlt The alt attribute for the image
2114
     *
2115
     * @return string The meta image HTML tag, or empty
2116
     */
2117
    private function getMetaPortalImagePath($imageAlt = '')
2118
    {
2119
        // Load portal meta image if defined
2120
        $metaImage = api_get_setting('meta_image_path');
2121
        $metaImageSysPath = api_get_path(SYS_PATH).$metaImage;
2122
        $metaImageWebPath = api_get_path(WEB_PATH).$metaImage;
2123
        $portalImageMeta = '';
2124
        if (!empty($metaImage)) {
2125
            if (is_file($metaImageSysPath)) {
2126
                $portalImageMeta = '<meta property="og:image" content="'.$metaImageWebPath.'" />'."\n";
2127
                $portalImageMeta .= '<meta property="twitter:image" content="'.$metaImageWebPath.'" />'."\n";
2128
                $portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
2129
            }
2130
        } else {
2131
            if (api_get_configuration_value('mail_header_from_custom_course_logo') == true) {
2132
                // check if current page is a course page
2133
                $courseId = api_get_course_int_id();
2134
2135
                if (!empty($courseId)) {
2136
                    $course = api_get_course_info_by_id($courseId);
2137
                    if (!empty($course) && !empty($course['course_email_image_large'])) {
2138
                        $portalImageMeta = '<meta property="og:image" content="'.$course['course_email_image_large'].'" />'."\n";
2139
                        $portalImageMeta .= '<meta property="twitter:image" content="'.$course['course_email_image_large'].'" />'."\n";
2140
                        $portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
2141
                    }
2142
                }
2143
            }
2144
            if (empty($portalImageMeta)) {
2145
                $logo = ChamiloApi::getPlatformLogoPath($this->theme);
2146
                if (!empty($logo)) {
2147
                    $portalImageMeta = '<meta property="og:image" content="'.$logo.'" />'."\n";
2148
                    $portalImageMeta .= '<meta property="twitter:image" content="'.$logo.'" />'."\n";
2149
                    $portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
2150
                }
2151
            }
2152
        }
2153
2154
        return $portalImageMeta;
2155
    }
2156
}
2157