Passed
Push — master ( 4ed902...760dff )
by Julito
11:06 queued 11s
created

Template::assign()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\CoreBundle\Entity\User;
8
use Chamilo\CoreBundle\Framework\Container;
9
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
10
use Symfony\Component\HttpFoundation\Response;
11
12
/**
13
 * Class Template.
14
 *
15
 * @author Julio Montoya <[email protected]>
16
 *
17
 * @todo better organization of the class, methods and variables
18
 */
19
class Template
20
{
21
    /**
22
     * The Template folder name see main/template.
23
     *
24
     * @var string
25
     */
26
    public $templateFolder = 'default';
27
28
    /**
29
     * The theme that will be used: chamilo, public_admin, chamilo_red, etc
30
     * This variable is set from the database.
31
     *
32
     * @var string
33
     */
34
    public $theme = '';
35
36
    /**
37
     * @var string
38
     */
39
    public $preview_theme = '';
40
    public $title = null;
41
    public $show_header;
42
    public $show_footer;
43
    public $help;
44
    public $menu_navigation = []; //Used in the userportal.lib.php function: return_navigation_course_links()
45
    public $show_learnpath = false; // This is a learnpath section or not?
46
    public $plugin = null;
47
    public $course_id = null;
48
    public $user_is_logged_in = false;
49
    public $twig = null;
50
51
    /* Loads chamilo plugins */
52
    public $load_plugins = false;
53
    public $params = [];
54
    public $force_plugin_load = false;
55
    public $responseCode = 0;
56
    private $themeDir;
57
58
    /**
59
     * @param string $title
60
     * @param bool   $show_header
61
     * @param bool   $show_footer
62
     * @param bool   $show_learnpath
63
     * @param bool   $hide_global_chat
64
     * @param bool   $load_plugins
65
     * @param int    $responseCode
66
     * @param bool   $sendHeaders      send http headers or not
67
     */
68
    public function __construct(
69
        $title = '',
70
        $show_header = true,
71
        $show_footer = true,
72
        $show_learnpath = false,
73
        $hide_global_chat = false,
74
        $load_plugins = true,
75
        $sendHeaders = true,
76
        $responseCode = 0
77
    ) {
78
        // Page title
79
        $this->title = $title;
80
        global $interbreadcrumb;
81
        $interbreadcrumb[] = ['url' => '#', 'name' => $title];
82
        $this->show_learnpath = $show_learnpath;
83
        //$this->setResponseCode($responseCode);
84
85
        if (empty($this->show_learnpath)) {
86
            $origin = api_get_origin();
87
            if ('learnpath' === $origin) {
88
                $this->show_learnpath = true;
89
                $show_footer = false;
90
                $show_header = false;
91
            }
92
        }
93
        $this->hide_global_chat = $hide_global_chat;
94
        $this->load_plugins = $load_plugins;
95
96
        /*$template_paths = [
97
            api_get_path(SYS_CODE_PATH).'template/overrides', // user defined templates
98
            api_get_path(SYS_CODE_PATH).'template', //template folder
99
            api_get_path(SYS_PLUGIN_PATH), // plugin folder
100
            api_get_path(SYS_PATH).'src/ThemeBundle/Resources/views',
101
        ];*/
102
103
        $this->twig = Container::getTwig();
104
105
        // Setting system variables
106
        //$this->set_system_parameters();
107
108
        // Setting user variables
109
        //$this->set_user_parameters();
110
111
        // Setting course variables
112
        //$this->set_course_parameters();
113
114
        // Setting administrator variables
115
        //$this->setAdministratorParams();
116
        //$this->setCSSEditor();
117
118
        // Header and footer are showed by default
119
        //$this->set_footer($show_footer);
120
        //$this->set_header($show_header);
121
122
        //$this->set_header_parameters($sendHeaders);
123
        //$this->set_footer_parameters();
124
125
        $defaultStyle = api_get_setting('display.default_template');
126
        if (!empty($defaultStyle)) {
127
            $this->templateFolder = $defaultStyle;
0 ignored issues
show
Documentation Bug introduced by
It seems like $defaultStyle can also be of type array. However, the property $templateFolder is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
128
        }
129
    }
130
131
    /**
132
     * @param string $helpInput
133
     */
134
    public function setHelp($helpInput = null)
135
    {
136
        if (!empty($helpInput)) {
137
            $help = $helpInput;
138
        } else {
139
            $help = $this->help;
140
        }
141
142
        $content = '';
143
        if ('true' == api_get_setting('enable_help_link')) {
144
            if (!empty($help)) {
145
                $help = Security::remove_XSS($help);
146
                $content = '<div class="help">';
147
                $content .= Display::url(
148
                    Display::return_icon('help.png', get_lang('Help'), null, ICON_SIZE_LARGE),
149
                    api_get_path(WEB_CODE_PATH).'help/help.php?open='.$help,
0 ignored issues
show
Bug introduced by
Are you sure $help of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

149
                    api_get_path(WEB_CODE_PATH).'help/help.php?open='./** @scrutinizer ignore-type */ $help,
Loading history...
150
                    [
151
                        'class' => 'ajax',
152
                        'data-title' => get_lang('Help'),
153
                    ]
154
                );
155
                $content .= '</div>';
156
            }
157
        }
158
        $this->assign('help_content', $content);
159
    }
160
161
    /**
162
     * Use template system to parse the actions menu.
163
     *
164
     * @todo finish it!
165
     */
166
    public function set_actions($actions)
167
    {
168
        $action_string = '';
169
        if (!empty($actions)) {
170
            foreach ($actions as $action) {
171
                $action_string .= $action;
172
            }
173
        }
174
        $this->assign('actions', $actions);
175
    }
176
177
    /**
178
     * Render the template.
179
     *
180
     * @param string $template The template path
181
     */
182
    public function display($template)
183
    {
184
        $template = str_replace('tpl', 'html.twig', $template);
185
        $templateFile = api_get_path(SYS_CODE_PATH).'template/'.$template;
186
187
        $this->loadLegacyParams();
188
189
        if (!file_exists($templateFile)) {
190
            $e = new FileNotFoundException($templateFile);
191
            echo $e->getMessage();
192
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
193
        }
194
195
        $this->setVueParams($this->params);
196
        $this->returnResponse($this->params, $template);
197
    }
198
199
    /**
200
     * @param string $template
201
     *
202
     * @throws \Twig\Error\Error
203
     */
204
    public function displayTemplate($template)
205
    {
206
        $this->loadLegacyParams();
207
        $this->returnResponse($this->params, $template);
208
    }
209
210
    /**
211
     * Shortcut to display a 1 col layout (index.php).
212
     * */
213
    public function display_one_col_template()
214
    {
215
        $this->loadLegacyParams();
216
        $template = '@ChamiloCore/Layout/layout_one_col.html.twig';
217
        $origin = api_get_origin();
218
        if ('learnpath' === $origin) {
219
            $template = '@ChamiloCore/Layout/no_layout.html.twig';
220
        }
221
        $this->setVueParams($this->params);
222
        $this->returnResponse($this->params, $template);
223
    }
224
225
    public function displayTwoColTemplate()
226
    {
227
        $this->loadLegacyParams();
228
        $template = '@ChamiloCore/Layout/layout_two_col.html.twig';
229
        $this->setVueParams($this->params);
230
        $this->returnResponse($this->params, $template);
231
    }
232
233
    /**
234
     * Displays an empty template.
235
     */
236
    public function display_blank_template()
237
    {
238
        $this->loadLegacyParams();
239
        $template = '@ChamiloCore/Layout/blank.html.twig';
240
        $this->returnResponse($this->params, $template);
241
    }
242
243
    /**
244
     * Displays an empty template.
245
     */
246
    public function displayBlankTemplateNoHeader()
247
    {
248
        $this->loadLegacyParams();
249
        $template = '@ChamiloCore/Layout/blank_no_header.html.twig';
250
        $this->returnResponse($this->params, $template);
251
    }
252
253
    /**
254
     * Displays an empty template.
255
     */
256
    public function display_no_layout_template()
257
    {
258
        $this->loadLegacyParams();
259
        $template = '@ChamiloCore/Layout/no_layout.html.twig';
260
        $this->returnResponse($this->params, $template);
261
    }
262
263
    /**
264
     * Displays an empty template.
265
     */
266
    public function displaySkillLayout()
267
    {
268
        $this->loadLegacyParams();
269
        $template = '@ChamiloCore/Layout/skill_layout.html.twig';
270
        $this->returnResponse($this->params, $template);
271
    }
272
273
    /**
274
     * return true if toolbar has to be displayed for user.
275
     *
276
     * @return bool
277
     */
278
    public static function isToolBarDisplayedForUser()
279
    {
280
        //Toolbar
281
        $show_admin_toolbar = api_get_setting('show_admin_toolbar');
282
        $show_toolbar = false;
283
284
        switch ($show_admin_toolbar) {
285
            case 'do_not_show':
286
                break;
287
            case 'show_to_admin':
288
                if (api_is_platform_admin()) {
289
                    $show_toolbar = true;
290
                }
291
                break;
292
            case 'show_to_admin_and_teachers':
293
                if (api_is_platform_admin() || api_is_allowed_to_edit()) {
294
                    $show_toolbar = true;
295
                }
296
                break;
297
            case 'show_to_all':
298
                $show_toolbar = true;
299
                break;
300
        }
301
302
        return $show_toolbar;
303
    }
304
305
    /**
306
     * Sets the header visibility.
307
     *
308
     * @param bool true if we show the header
309
     */
310
    public function set_header($status)
311
    {
312
        $this->show_header = $status;
313
        $this->assign('show_header', $status);
314
315
        $show_toolbar = 0;
316
317
        if (self::isToolBarDisplayedForUser()) {
318
            $show_toolbar = 1;
319
        }
320
321
        $this->assign('show_toolbar', $show_toolbar);
322
323
        // Only if course is available
324
        $courseToolBar = '';
325
        $show_course_navigation_menu = '';
326
        if (!empty($this->course_id) && $this->user_is_logged_in) {
327
            if ('false' !== api_get_setting('show_toolshortcuts')) {
328
                // Course toolbar
329
                $courseToolBar = CourseHome::show_navigation_tool_shortcuts();
330
            }
331
            if ('false' !== api_get_setting('show_navigation_menu')) {
332
                //Course toolbar
333
                $show_course_navigation_menu = CourseHome::show_navigation_menu();
334
            }
335
        }
336
        $this->assign('show_course_shortcut', $courseToolBar);
337
        $this->assign('show_course_navigation_menu', $show_course_navigation_menu);
338
    }
339
340
    /**
341
     * Returns the sub-folder and filename for the given tpl file.
342
     *
343
     * If template not found in overrides/ or custom template folder, the default template will be used.
344
     *
345
     * @param string $name
346
     *
347
     * @return string
348
     */
349
    public static function findTemplateFilePath($name)
350
    {
351
        $sysTemplatePath = api_get_path(SYS_TEMPLATE_PATH);
352
353
        // Check if the tpl file is present in the main/template/overrides/ dir
354
        // Overrides is a special directory meant for temporary template
355
        // customization. It must be taken into account before anything else
356
        if (is_readable($sysTemplatePath."overrides/$name")) {
357
            return "overrides/$name";
358
        }
359
360
        $defaultFolder = api_get_configuration_value('default_template');
361
362
        // If a template folder has been manually defined, search for the right
363
        // file, and if not found, go for the same file in the default template
364
        if ($defaultFolder && 'default' != $defaultFolder) {
365
            // Avoid missing template error, use the default file.
366
            if (file_exists($sysTemplatePath."$defaultFolder/$name")) {
367
                return "$defaultFolder/$name";
368
            }
369
        }
370
371
        $name = str_replace('tpl', 'html.twig', $name);
372
373
        return "default/$name";
374
    }
375
376
    /**
377
     * Call non-static for Template::findTemplateFilePath.
378
     *
379
     * @see Template::findTemplateFilePath()
380
     *
381
     * @param string $name
382
     *
383
     * @return string
384
     */
385
    public function get_template($name)
386
    {
387
        return api_find_template($name);
388
    }
389
390
    /**
391
     * Get CSS themes sub-directory.
392
     *
393
     * @param string $theme
394
     *
395
     * @return string with a trailing slash, e.g. 'themes/chamilo_red/'
396
     */
397
    public static function getThemeDir($theme)
398
    {
399
        $themeDir = 'themes/'.$theme.'/';
400
        $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
401
        if (!empty($virtualTheme)) {
402
            $virtualThemeList = api_get_themes(true);
403
            $isVirtualTheme = in_array($theme, array_keys($virtualThemeList));
404
            if ($isVirtualTheme) {
405
                $themeDir = 'themes/'.$virtualTheme.'/'.$theme.'/';
406
            }
407
        }
408
409
        return $themeDir;
410
    }
411
412
    /**
413
     * Set legacy twig globals in order to be hook in the LegacyListener.php.
414
     *
415
     * @return array
416
     */
417
    public static function getGlobals()
418
    {
419
        return [
420
            '_s' => [
421
                'software_name' => api_get_configuration_value('software_name'),
422
                'system_version' => api_get_configuration_value('system_version'),
423
                'site_name' => api_get_setting('siteName'),
424
                'institution' => api_get_setting('Institution'),
425
                'gamification_mode' => api_get_setting('gamification_mode'),
426
            ],
427
            'template' => 'default', // @todo setup template folder in config.yml;
428
        ];
429
    }
430
431
    /**
432
     * Set theme, include mainstream CSS files.
433
     *
434
     * @deprecated
435
     * @see setCssCustomFiles() for additional CSS sheets
436
     */
437
    public function setCssFiles()
438
    {
439
        global $disable_js_and_css_files;
440
        $css = [];
441
442
        // Default CSS Bootstrap
443
        $bowerCSSFiles = [
444
            'fontawesome/css/font-awesome.min.css',
445
            'jquery-ui/themes/smoothness/theme.css',
446
            'jquery-ui/themes/smoothness/jquery-ui.min.css',
447
            'mediaelement/build/mediaelementplayer.min.css',
448
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.css',
449
            'bootstrap/dist/css/bootstrap.min.css',
450
            'jquery.scrollbar/jquery.scrollbar.css',
451
            //'bootstrap-daterangepicker/daterangepicker.css',
452
            'bootstrap-select/dist/css/bootstrap-select.min.css',
453
            'select2/dist/css/select2.min.css',
454
            'flag-icon-css/css/flag-icon.min.css',
455
            'mediaelement/plugins/vrview/vrview.css',
456
        ];
457
458
        $features = api_get_configuration_value('video_features');
459
        $defaultFeatures = ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen', 'vrview'];
460
461
        if (!empty($features) && isset($features['features'])) {
462
            foreach ($features['features'] as $feature) {
463
                if ('vrview' === $feature) {
464
                    continue;
465
                }
466
                $bowerCSSFiles[] = "mediaelement/plugins/$feature/$feature.css";
467
                $defaultFeatures[] = $feature;
468
            }
469
        }
470
471
        foreach ($bowerCSSFiles as $file) {
472
            //$css[] = api_get_path(WEB_PUBLIC_PATH).'assets/'.$file;
473
        }
474
475
        //$css[] = api_get_path(WEB_LIBRARY_PATH).'javascript/chosen/chosen.css';
476
477
        if (api_is_global_chat_enabled()) {
478
            $css[] = api_get_path(WEB_LIBRARY_PATH).'javascript/chat/css/chat.css';
479
        }
480
        $css_file_to_string = '';
481
        foreach ($css as $file) {
482
            $css_file_to_string .= api_get_css($file);
483
        }
484
485
        if (!$disable_js_and_css_files) {
486
            $this->assign('css_static_file_to_string', $css_file_to_string);
487
        }
488
489
        $defaultFeatures = implode("','", $defaultFeatures);
490
        $this->assign('video_features', $defaultFeatures);
491
    }
492
493
    /**
494
     * Prepare custom CSS to be added at the very end of the <head> section.
495
     *
496
     * @see setCssFiles() for the mainstream CSS files
497
     */
498
    public function setCssCustomFiles()
499
    {
500
        global $disable_js_and_css_files;
501
        $css = [];
502
        if ($this->show_learnpath) {
503
            if (is_file(api_get_path(SYS_CSS_PATH).$this->themeDir.'learnpath.css')) {
504
                $css[] = api_get_path(WEB_CSS_PATH).$this->themeDir.'learnpath.css';
505
            }
506
        }
507
508
        $css_file_to_string = '';
509
        foreach ($css as $file) {
510
            $css_file_to_string .= api_get_css($file);
511
        }
512
513
        // @todo move this somewhere else. Special fix when using tablets in order to see the text near icons
514
        if (SHOW_TEXT_NEAR_ICONS == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
515
            //hack in order to fix the actions buttons
516
            $css_file_to_string .= '<style>
517
                .td_actions a {
518
                    float:left;
519
                    width:100%;
520
                }
521
                .forum_message_left a {
522
                    float:left;
523
                    width:100%;
524
                }
525
                </style>';
526
        }
527
528
        $navigator_info = api_get_navigator();
529
        if ('Internet Explorer' == $navigator_info['name'] && '6' == $navigator_info['version']) {
530
            $css_file_to_string .= 'img, div { behavior: url('.api_get_path(WEB_LIBRARY_PATH).'javascript/iepngfix/iepngfix.htc) } '."\n";
531
        }
532
533
        if (!$disable_js_and_css_files) {
534
            $this->assign('css_custom_file_to_string', $css_file_to_string);
535
536
            $style_print = '';
537
            if (is_readable(api_get_path(SYS_CSS_PATH).$this->theme.'/print.css')) {
538
                $style_print = api_get_css(
539
                    api_get_path(WEB_CSS_PATH).$this->theme.'/print.css',
540
                    'print'
541
                );
542
            }
543
            $this->assign('css_style_print', $style_print);
544
        }
545
546
        // Logo
547
        $logo = return_logo($this->theme);
548
        $logoPdf = return_logo($this->theme, false);
549
        $this->assign('logo', $logo);
550
        $this->assign('logo_pdf', $logoPdf);
551
        $this->assign('show_media_element', 1);
552
    }
553
554
    /**
555
     * Declare and define the template variable that will be used to load
556
     * javascript libraries in the header.
557
     */
558
    public function set_js_files()
559
    {
560
        global $disable_js_and_css_files, $htmlHeadXtra;
561
        $isoCode = api_get_language_isocode();
562
        $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_'.strtoupper($isoCode).'.min.js';
563
564
        if ('en' == $isoCode) {
565
            $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_US.min.js';
566
        }
567
        // JS files
568
        $js_files = [
569
            'chosen/chosen.jquery.min.js',
570
        ];
571
572
        $viewBySession = 'true' === api_get_setting('my_courses_view_by_session');
573
574
        if (api_is_global_chat_enabled() || $viewBySession) {
575
            // Do not include the global chat in LP
576
            if (false == $this->show_learnpath &&
577
                true == $this->show_footer &&
578
                false == $this->hide_global_chat
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
579
            ) {
580
                $js_files[] = 'chat/js/chat.js';
581
            }
582
        }
583
584
        if ('true' === api_get_setting('accessibility_font_resize')) {
585
            $js_files[] = 'fontresize.js';
586
        }
587
588
        $js_file_to_string = '';
589
        $bowerJsFiles = [
590
            'modernizr/modernizr.js',
591
            'jquery/query.min.js',
592
            'bootstrap/dist/js/bootstrap.min.js',
593
            'jquery-ui/jquery-ui.min.js',
594
            'jqueryui-touch-punch/jquery.ui.touch-punch.min.js',
595
            'moment/min/moment-with-locales.js',
596
            //'bootstrap-daterangepicker/daterangepicker.js',
597
            'jquery-timeago/jquery.timeago.js',
598
            'mediaelement/mediaelement-and-player.min.js',
599
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.js',
600
            'image-map-resizer/js/imageMapResizer.min.js',
601
            'jquery.scrollbar/jquery.scrollbar.min.js',
602
            //'readmore-js/readmore.min.js',
603
            'bootstrap-select/dist/js/bootstrap-select.min.js',
604
            $selectLink,
605
            'select2/dist/js/select2.min.js',
606
            "select2/dist/js/i18n/$isoCode.js",
607
            'mediaelement/plugins/vrview/vrview.js',
608
        ];
609
610
        $features = api_get_configuration_value('video_features');
611
        if (!empty($features) && isset($features['features'])) {
612
            foreach ($features['features'] as $feature) {
613
                if ('vrview' === $feature) {
614
                    continue;
615
                }
616
                $bowerJsFiles[] = "mediaelement/plugins/$feature/$feature.js";
617
            }
618
        }
619
620
        if ('true' === api_get_setting('include_asciimathml_script')) {
621
            $bowerJsFiles[] = 'MathJax/MathJax.js?config=TeX-MML-AM_HTMLorMML';
622
        }
623
624
        if ('en' != $isoCode) {
625
            $bowerJsFiles[] = 'jqueryui-timepicker-addon/dist/i18n/jquery-ui-timepicker-'.$isoCode.'.js';
626
            $bowerJsFiles[] = 'jquery-ui/ui/minified/i18n/datepicker-'.$isoCode.'.min.js';
627
        }
628
629
        foreach ($bowerJsFiles as $file) {
630
            //$js_file_to_string .= '<script type="text/javascript" src="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'"></script>'."\n";
631
        }
632
633
        foreach ($js_files as $file) {
634
            //$js_file_to_string .= api_get_js($file);
635
        }
636
637
        // Loading email_editor js
638
        if (!api_is_anonymous() && 'true' == api_get_setting('allow_email_editor')) {
639
            $template = $this->get_template('mail_editor/email_link.js.tpl');
640
            $js_file_to_string .= $this->fetch($template);
641
        }
642
643
        if (!$disable_js_and_css_files) {
644
            $this->assign('js_file_to_string', $js_file_to_string);
645
            $extraHeaders = '';
646
            //$extraHeaders = '<script>var _p = '.json_encode($this->getWebPaths(), JSON_PRETTY_PRINT).'</script>';
647
            //Adding jquery ui by default
648
            $extraHeaders .= api_get_jquery_ui_js();
649
            if (isset($htmlHeadXtra) && $htmlHeadXtra) {
650
                foreach ($htmlHeadXtra as &$this_html_head) {
651
                    $extraHeaders .= $this_html_head."\n";
652
                }
653
            }
654
655
            $ajax = api_get_path(WEB_AJAX_PATH);
656
            $courseId = api_get_course_id();
657
            if (empty($courseId)) {
658
                $courseLogoutCode = '
659
                <script>
660
                function courseLogout() {
661
                }
662
                </script>';
663
            } else {
664
                $courseLogoutCode = "
665
                <script>
666
                var logOutUrl = '".$ajax."course.ajax.php?a=course_logout&".api_get_cidreq()."';
667
                function courseLogout() {
668
                    $.ajax({
669
                        async : false,
670
                        url: logOutUrl,
671
                        success: function (data) {
672
                            return 1;
673
                        }
674
                    });
675
                }
676
                </script>";
677
            }
678
679
            $extraHeaders .= $courseLogoutCode;
680
            $this->assign('extra_headers', $extraHeaders);
681
        }
682
    }
683
684
    public static function setVueParams(&$params)
685
    {
686
        $params['is_authenticated'] = !api_is_anonymous();
687
        /*$user = api_get_user_entity(api_get_user_id());
688
        $encoded = '';
689
        if ($user) {
690
            $encoded = json_encode($user);
691
        }
692
        $params['user'] = $encoded;*/
693
        $params['from_vue'] = isset($_REQUEST['from_vue']) ? 1 : 0;
694
    }
695
696
    /**
697
     * @param array  $params
698
     * @param string $template
699
     *
700
     * @throws \Twig\Error\Error
701
     */
702
    public function returnResponse($params, $template)
703
    {
704
        $response = new Response();
705
        $content = Container::getTwig()->render($template, $params);
706
        $response->setContent($content);
707
        $response->send();
708
    }
709
710
    /**
711
     * Special function to declare last-minute JS libraries which depend on
712
     * other things to be declared first. In particular, it might be useful
713
     * under IE9 with compatibility mode, which for some reason is getting
714
     * upset when a variable is used in a function (even if not used yet)
715
     * when this variable hasn't been defined yet.
716
     */
717
    public function set_js_files_post()
718
    {
719
        global $disable_js_and_css_files;
720
        $js_files = [];
721
        if (api_is_global_chat_enabled()) {
722
            //Do not include the global chat in LP
723
            if (false == $this->show_learnpath && true == $this->show_footer && false == $this->hide_global_chat) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
724
                $js_files[] = 'chat/js/chat.js';
725
            }
726
        }
727
        $js_file_to_string = '';
728
        foreach ($js_files as $js_file) {
729
            $js_file_to_string .= api_get_js($js_file);
730
        }
731
        if (!$disable_js_and_css_files) {
732
            $this->assign('js_file_to_string_post', $js_file_to_string);
733
        }
734
    }
735
736
    /**
737
     * @param string $theme
738
     *
739
     * @return string
740
     */
741
    public static function getPortalIcon($theme)
742
    {
743
        // Default root chamilo favicon
744
        $icon = 'favicon.ico';
745
746
        // Added to verify if in the current Chamilo Theme exist a favicon
747
        $themeUrl = api_get_path(SYS_CSS_PATH).'themes/'.$theme.'/images/';
748
749
        // If exist pick the current chamilo theme favicon.
750
        if (is_file($themeUrl.'favicon.ico')) {
751
            $icon = 'build/css/themes/'.$theme.'/images/favicon.ico';
752
        }
753
754
        return $icon;
755
    }
756
757
    /**
758
     * Show footer js template.
759
     */
760
    public function show_footer_js_template()
761
    {
762
        $tpl = $this->get_template('layout/footer.js.tpl');
763
        $this->display($tpl);
764
    }
765
766
    /**
767
     * @param string $template
768
     *
769
     * @return string
770
     */
771
    public function fetch($template = null)
772
    {
773
        return $this->twig->render($template, $this->params);
774
    }
775
776
    /**
777
     * @param string $variable
778
     * @param mixed  $value
779
     */
780
    public function assign($variable, $value = '')
781
    {
782
        $this->params[$variable] = $value;
783
    }
784
785
    /**
786
     * Adds a body class for login pages.
787
     */
788
    public function setLoginBodyClass()
789
    {
790
        $this->assign('login_class', 'section-login');
791
    }
792
793
    /**
794
     * The theme that will be used if the database is not working.
795
     *
796
     * @return string
797
     */
798
    public static function getThemeFallback()
799
    {
800
        $theme = api_get_configuration_value('theme_fallback');
801
        if (empty($theme)) {
802
            $theme = 'chamilo';
803
        }
804
805
        return $theme;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $theme also could return the type boolean which is incompatible with the documented return type string.
Loading history...
806
    }
807
808
    /**
809
     * @return string
810
     */
811
    public function displayLoginForm()
812
    {
813
        $form = new FormValidator(
814
            'form-login',
815
            'POST',
816
            api_get_path(WEB_PUBLIC_PATH).'login_check',
817
            null,
818
            null,
819
            FormValidator::LAYOUT_BOX_NO_LABEL
820
        );
821
        $params = [
822
            'id' => '_username',
823
            'autofocus' => 'autofocus',
824
            'icon' => 'user fa-fw',
825
            'placeholder' => get_lang('Username'),
826
        ];
827
828
        // Avoid showing the autocapitalize option if the browser doesn't
829
        // support it: this attribute is against the HTML5 standard
830
        /*$browserAutoCapitalize = false;
831
        if (api_browser_support('autocapitalize')) {
832
            $browserAutoCapitalize = false;
833
            $params['autocapitalize'] = 'none';
834
        }*/
835
        $form->addText(
836
            '_username',
837
            get_lang('Username'),
838
            true,
839
            $params
840
        );
841
        $params = [
842
            'id' => '_password',
843
            'icon' => 'lock fa-fw',
844
            'placeholder' => get_lang('Pass'),
845
        ];
846
        /*if ($browserAutoCapitalize) {
847
            $params['autocapitalize'] = 'none';
848
        }*/
849
        $form->addElement(
850
            'password',
851
            '_password',
852
            get_lang('Pass'),
853
            $params
854
        );
855
856
        $token = Chamilo\CoreBundle\Framework\Container::$container->get('security.csrf.token_manager')->getToken('authenticate');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

856
        $token = Chamilo\CoreBundle\Framework\Container::$container->/** @scrutinizer ignore-call */ get('security.csrf.token_manager')->getToken('authenticate');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
857
        $form->addHidden('_csrf_token', $token->getValue());
858
859
        // Captcha
860
        $captcha = api_get_setting('allow_captcha');
861
        $allowCaptcha = 'true' === $captcha;
862
863
        if ($allowCaptcha) {
864
            $useCaptcha = isset($_SESSION['loginFailed']) ? $_SESSION['loginFailed'] : null;
865
            if ($useCaptcha) {
866
                $ajax = api_get_path(WEB_AJAX_PATH).'form.ajax.php?a=get_captcha';
867
                $options = [
868
                    'width' => 250,
869
                    'height' => 90,
870
                    'callback' => $ajax.'&var='.basename(__FILE__, '.php'),
871
                    'sessionVar' => basename(__FILE__, '.php'),
872
                    'imageOptions' => [
873
                        'font_size' => 20,
874
                        'font_path' => api_get_path(SYS_FONTS_PATH).'opensans/',
875
                        'font_file' => 'OpenSans-Regular.ttf',
876
                        //'output' => 'gif'
877
                    ],
878
                ];
879
880
                // Minimum options using all defaults (including defaults for Image_Text):
881
                //$options = array('callback' => 'qfcaptcha_image.php');
882
                $captcha_question = $form->addElement('CAPTCHA_Image', 'captcha_question', '', $options);
883
                $form->addHtml(get_lang('Click on the image to load a new one.'));
884
885
                $form->addElement(
886
                    'text',
887
                    'captcha',
888
                    get_lang('Enter the letters you see.')
889
                );
890
                $form->addRule(
891
                    'captcha',
892
                    get_lang('Enter the characters you see on the image'),
893
                    'required',
894
                    null,
895
                    'client'
896
                );
897
                $form->addRule(
898
                    'captcha',
899
                    get_lang('The text you entered doesn\'t match the picture.'),
900
                    'CAPTCHA',
901
                    $captcha_question
902
                );
903
            }
904
        }
905
906
        $form->addButton(
907
            'submitAuth',
908
            get_lang('Login'),
909
            null,
910
            'primary',
911
            null,
912
            'btn-block'
913
        );
914
915
        $html = $form->returnForm();
916
917
        return $html;
918
    }
919
920
    /**
921
     * Returns the tutors names for the current course in session
922
     * Function to use in Twig templates.
923
     *
924
     * @return string
925
     */
926
    public static function returnTutorsNames()
927
    {
928
        $em = Database::getManager();
929
        $tutors = $em
930
            ->createQuery('
931
                SELECT u FROM ChamiloCoreBundle:User u
932
                INNER JOIN ChamiloCoreBundle:SessionRelCourseRelUser scu WITH u.id = scu.user
933
                WHERE scu.status = :teacher_status AND scu.session = :session AND scu.course = :course
934
            ')
935
            ->setParameters([
936
                'teacher_status' => SessionRelCourseRelUser::STATUS_COURSE_COACH,
937
                'session' => api_get_session_id(),
938
                'course' => api_get_course_int_id(),
939
            ])
940
            ->getResult();
941
942
        $names = [];
943
944
        /** @var User $tutor */
945
        foreach ($tutors as $tutor) {
946
            $names[] = UserManager::formatUserFullName($tutor);
947
        }
948
949
        return implode(CourseManager::USER_SEPARATOR, $names);
950
    }
951
952
    /*s
953
     * Returns the teachers name for the current course
954
     * Function to use in Twig templates
955
     * @return string
956
     */
957
    public static function returnTeachersNames()
958
    {
959
        $em = Database::getManager();
960
        $teachers = $em
961
            ->createQuery('
962
                SELECT u FROM ChamiloCoreBundle:User u
963
                INNER JOIN ChamiloCoreBundle:CourseRelUser cu WITH u.id = cu.user
964
                WHERE cu.status = :teacher_status AND cu.course = :course
965
            ')
966
            ->setParameters([
967
                'teacher_status' => User::COURSE_MANAGER,
968
                'course' => api_get_course_int_id(),
969
            ])
970
            ->getResult();
971
972
        $names = [];
973
974
        /** @var User $teacher */
975
        foreach ($teachers as $teacher) {
976
            $names[] = UserManager::formatUserFullName($teacher);
977
        }
978
979
        return implode(CourseManager::USER_SEPARATOR, $names);
980
    }
981
982
    /**
983
     * @param int $code
984
     */
985
    public function setResponseCode($code)
986
    {
987
        $this->responseCode = $code;
988
    }
989
990
    public function getResponseCode()
991
    {
992
        return $this->responseCode;
993
    }
994
995
    /**
996
     * Assign HTML code to the 'bug_notification' template variable for the side tabs to report issues.
997
     *
998
     * @return bool Always return true because there is always a string, even if empty
999
     */
1000
    public function assignBugNotification()
1001
    {
1002
        //@todo move this in the template
1003
        $rightFloatMenu = '';
1004
        $iconBug = Display::return_icon(
1005
            'bug.png',
1006
            get_lang('Report a bug'),
1007
            [],
1008
            ICON_SIZE_LARGE
1009
        );
1010
        if ('true' === api_get_setting('show_link_bug_notification') && $this->user_is_logged_in) {
1011
            $rightFloatMenu = '<div class="report">
1012
		        <a href="https://github.com/chamilo/chamilo-lms/wiki/How-to-report-issues" target="_blank">
1013
                    '.$iconBug.'
1014
                </a>
1015
		        </div>';
1016
        }
1017
1018
        if ('true' === api_get_setting('show_link_ticket_notification') &&
1019
            $this->user_is_logged_in
1020
        ) {
1021
            // by default is project_id = 1
1022
            $defaultProjectId = 1;
1023
            $iconTicket = Display::return_icon(
1024
                'help.png',
1025
                get_lang('Ticket'),
1026
                [],
1027
                ICON_SIZE_LARGE
1028
            );
1029
            $courseInfo = api_get_course_info();
1030
            $courseParams = '';
1031
            if (!empty($courseInfo)) {
1032
                $courseParams = api_get_cidreq();
1033
            }
1034
            $url = api_get_path(WEB_CODE_PATH).'ticket/tickets.php?project_id='.$defaultProjectId.'&'.$courseParams;
1035
1036
            $allow = TicketManager::userIsAllowInProject(api_get_user_info(), $defaultProjectId);
1037
1038
            if ($allow) {
1039
                $rightFloatMenu .= '<div class="help">
1040
                    <a href="'.$url.'" target="_blank">
1041
                        '.$iconTicket.'
1042
                    </a>
1043
                </div>';
1044
            }
1045
        }
1046
1047
        $this->assign('bug_notification', $rightFloatMenu);
1048
1049
        return true;
1050
    }
1051
1052
    /**
1053
     * Load legacy params.
1054
     */
1055
    private function loadLegacyParams()
1056
    {
1057
        // Set legacy breadcrumb
1058
        global $interbreadcrumb;
1059
1060
        $courseInfo = api_get_course_info();
1061
        if (!empty($courseInfo)) {
1062
            $url = $courseInfo['course_public_url'];
1063
            $sessionId = api_get_session_id();
1064
            if (!empty($sessionId)) {
1065
                $url .= '?sid='.$sessionId;
1066
            }
1067
            array_unshift(
1068
                $interbreadcrumb,
1069
                ['name' => $courseInfo['title'], 'url' => $url]
1070
            );
1071
        }
1072
1073
        $this->params['legacy_breadcrumb'] = json_encode($interbreadcrumb);
1074
1075
        global $htmlHeadXtra;
1076
        $this->params['legacy_javascript'] = $htmlHeadXtra;
1077
    }
1078
1079
    /**
1080
     * Prepare the _c array for template files. The _c array contains
1081
     * information about the current course.
1082
     */
1083
    private function set_course_parameters()
1084
    {
1085
        //Setting course id
1086
        $course = api_get_course_info();
1087
        if (empty($course)) {
1088
            $this->assign('course_is_set', false);
1089
1090
            return;
1091
        }
1092
        $this->assign('course_is_set', true);
1093
        $this->course_id = $course['id'];
1094
        $_c = [
1095
            'id' => $course['real_id'],
1096
            'code' => $course['code'],
1097
            'title' => $course['name'],
1098
            'visibility' => $course['visibility'],
1099
            'language' => $course['language'],
1100
            'directory' => $course['directory'],
1101
            'session_id' => api_get_session_id(),
1102
            'user_is_teacher' => api_is_course_admin(),
1103
            'student_view' => (!empty($_GET['isStudentView']) && 'true' == $_GET['isStudentView']),
1104
        ];
1105
        $this->assign('course_code', $course['code']);
1106
        $this->assign('_c', $_c);
1107
    }
1108
1109
    /**
1110
     * Set header parameters.
1111
     *
1112
     * @deprecated
1113
     *
1114
     * @param bool $sendHeaders send headers
1115
     */
1116
    private function set_header_parameters($sendHeaders)
1117
    {
1118
        global $httpHeadXtra, $interbreadcrumb, $language_file, $_configuration, $this_section;
1119
        $_course = api_get_course_info();
1120
        $nameTools = $this->title;
1121
        $navigation = return_navigation_array();
1122
        $this->menu_navigation = $navigation['menu_navigation'];
1123
1124
        $this->assign('system_charset', api_get_system_encoding());
1125
1126
        if (isset($httpHeadXtra) && $httpHeadXtra) {
1127
            foreach ($httpHeadXtra as &$thisHttpHead) {
1128
                //header($thisHttpHead);
1129
            }
1130
        }
1131
1132
        $this->assign(
1133
            'online_button',
1134
            Display::return_icon('statusonline.png', null, [], ICON_SIZE_ATOM)
1135
        );
1136
        $this->assign(
1137
            'offline_button',
1138
            Display::return_icon('statusoffline.png', null, [], ICON_SIZE_ATOM)
1139
        );
1140
1141
        // Get language iso-code for this page - ignore errors
1142
        $this->assign('document_language', api_get_language_isocode());
1143
1144
        $course_title = isset($_course['name']) ? $_course['name'] : null;
1145
1146
        $title_list = [];
1147
1148
        $title_list[] = api_get_setting('Institution');
1149
        $title_list[] = api_get_setting('siteName');
1150
1151
        if (!empty($course_title)) {
1152
            $title_list[] = $course_title;
1153
        }
1154
        if ('' != $nameTools) {
1155
            $title_list[] = $nameTools;
1156
        }
1157
1158
        $title_string = '';
1159
        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...
1160
            $title_string .= $title_list[$i];
1161
            if (isset($title_list[$i + 1])) {
1162
                $item = trim($title_list[$i + 1]);
1163
                if (!empty($item)) {
1164
                    $title_string .= ' - ';
1165
                }
1166
            }
1167
        }
1168
1169
        $this->assign('title_string', $title_string);
1170
1171
        // Setting the theme and CSS files
1172
        //$this->setCssFiles();
1173
        $this->set_js_files();
1174
        $this->setCssCustomFiles();
1175
1176
        /*$browser = api_browser_support('check_browser');
1177
        if ('Internet Explorer' == $browser[0] && $browser[1] >= '11') {
1178
            $browser_head = '<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />';
1179
            $this->assign('browser_specific_head', $browser_head);
1180
        }*/
1181
1182
        // Implementation of prefetch.
1183
        // See http://cdn.chamilo.org/main/img/online.png for details
1184
        $prefetch = '';
1185
        if (!empty($_configuration['cdn_enable'])) {
1186
            $prefetch .= '<meta http-equiv="x-dns-prefetch-control" content="on">';
1187
            foreach ($_configuration['cdn'] as $host => $exts) {
1188
                $prefetch .= '<link rel="dns-prefetch" href="'.$host.'">';
1189
            }
1190
        }
1191
1192
        $this->assign('prefetch', $prefetch);
1193
        $this->assign('text_direction', api_get_text_direction());
1194
        $this->assign('section_name', 'section-'.$this_section);
1195
        $this->assignFavIcon();
1196
        $this->setHelp();
1197
1198
        $this->assignBugNotification(); //Prepare the 'bug_notification' var for the template
1199
1200
        $this->assignAccessibilityBlock(); //Prepare the 'accessibility' var for the template
1201
1202
        // Preparing values for the menu
1203
1204
        // Logout link
1205
        $hideLogout = api_get_setting('hide_logout_button');
1206
        if ('true' === $hideLogout) {
1207
            $this->assign('logout_link', null);
1208
        } else {
1209
            $this->assign('logout_link', api_get_path(WEB_PATH).'index.php?logout=logout&uid='.api_get_user_id());
1210
        }
1211
1212
        // Profile link
1213
        if ('true' == api_get_setting('allow_social_tool')) {
1214
            $profile_url = api_get_path(WEB_CODE_PATH).'social/home.php';
1215
        } else {
1216
            $profile_url = api_get_path(WEB_CODE_PATH).'auth/profile.php';
1217
        }
1218
1219
        $this->assign('profile_url', $profile_url);
1220
1221
        //Message link
1222
        $message_link = null;
1223
        $message_url = null;
1224
        if ('true' == api_get_setting('allow_message_tool')) {
1225
            $message_url = api_get_path(WEB_CODE_PATH).'messages/inbox.php';
1226
            $message_link = '<a href="'.api_get_path(WEB_CODE_PATH).'messages/inbox.php">'.get_lang('Inbox').'</a>';
1227
        }
1228
        $this->assign('message_link', $message_link);
1229
        $this->assign('message_url', $message_url);
1230
1231
        $pendingSurveyLink = '';
1232
        $show = api_get_configuration_value('show_pending_survey_in_menu');
1233
        if ($show) {
1234
            $pendingSurveyLink = api_get_path(WEB_CODE_PATH).'survey/pending.php';
1235
        }
1236
        $this->assign('pending_survey_url', $pendingSurveyLink);
1237
1238
        // Certificate Link
1239
        $allow = api_get_configuration_value('certificate.hide_my_certificate_link');
1240
        if (false === $allow) {
1241
            $certificateUrl = api_get_path(WEB_CODE_PATH).'gradebook/my_certificates.php';
1242
            $certificateLink = Display::url(
1243
                get_lang('My certificates'),
1244
                $certificateUrl
1245
            );
1246
            $this->assign('certificate_link', $certificateLink);
1247
            $this->assign('certificate_url', $certificateUrl);
1248
        }
1249
1250
        $institution = api_get_setting('Institution');
1251
        $portal_name = empty($institution) ? api_get_setting('siteName') : $institution;
1252
1253
        $this->assign('portal_name', $portal_name);
1254
1255
        //Menu
1256
        //$menu = menuArray();
1257
        //$this->assign('menu', $menu);
1258
1259
        $breadcrumb = '';
1260
        // Hide breadcrumb in LP
1261
        if (false == $this->show_learnpath) {
1262
            $breadcrumb = return_breadcrumb(
1263
                $interbreadcrumb,
1264
                $language_file,
1265
                $nameTools
1266
            );
1267
        }
1268
        $this->assign('breadcrumb', $breadcrumb);
1269
1270
        //Extra content
1271
        $extra_header = null;
1272
        if (!api_is_platform_admin()) {
1273
            $extra_header = trim(api_get_setting('header_extra_content'));
1274
        }
1275
        $this->assign('header_extra_content', $extra_header);
1276
1277
        if ($sendHeaders) {
1278
            /*header('Content-Type: text/html; charset='.api_get_system_encoding());
1279
            header(
1280
                'X-Powered-By: '.$_configuration['software_name'].' '.substr($_configuration['system_version'], 0, 1)
1281
            );
1282
            self::addHTTPSecurityHeaders();*/
1283
1284
            $responseCode = $this->getResponseCode();
1285
            if (!empty($responseCode)) {
1286
                switch ($responseCode) {
1287
                    case '404':
1288
                        header("HTTP/1.0 404 Not Found");
1289
                        break;
1290
                }
1291
            }
1292
        }
1293
1294
        $socialMeta = '';
1295
        $metaTitle = api_get_setting('meta_title');
1296
        if (!empty($metaTitle)) {
1297
            $socialMeta .= '<meta name="twitter:card" content="summary" />'."\n";
1298
            $metaSite = api_get_setting('meta_twitter_site');
1299
            if (!empty($metaSite)) {
1300
                $socialMeta .= '<meta name="twitter:site" content="'.$metaSite.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaSite of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1300
                $socialMeta .= '<meta name="twitter:site" content="'./** @scrutinizer ignore-type */ $metaSite.'" />'."\n";
Loading history...
1301
                $metaCreator = api_get_setting('meta_twitter_creator');
1302
                if (!empty($metaCreator)) {
1303
                    $socialMeta .= '<meta name="twitter:creator" content="'.$metaCreator.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaCreator of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1303
                    $socialMeta .= '<meta name="twitter:creator" content="'./** @scrutinizer ignore-type */ $metaCreator.'" />'."\n";
Loading history...
1304
                }
1305
            }
1306
1307
            // The user badge page emits its own meta tags, so if this is
1308
            // enabled, ignore the global ones
1309
            $userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
1310
            $skillId = isset($_GET['skill']) ? intval($_GET['skill']) : 0;
1311
1312
            if (!$userId && !$skillId) {
1313
                // no combination of user and skill ID has been defined,
1314
                // so print the normal OpenGraph meta tags
1315
                $socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaTitle of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1315
                $socialMeta .= '<meta property="og:title" content="'./** @scrutinizer ignore-type */ $metaTitle.'" />'."\n";
Loading history...
1316
                $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n";
1317
1318
                $metaDescription = api_get_setting('meta_description');
1319
                if (!empty($metaDescription)) {
1320
                    $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaDescription of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1320
                    $socialMeta .= '<meta property="og:description" content="'./** @scrutinizer ignore-type */ $metaDescription.'" />'."\n";
Loading history...
1321
                }
1322
1323
                $metaImage = api_get_setting('meta_image_path');
1324
                if (!empty($metaImage)) {
1325
                    if (is_file(api_get_path(SYS_PATH).$metaImage)) {
0 ignored issues
show
Bug introduced by
Are you sure $metaImage of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1325
                    if (is_file(api_get_path(SYS_PATH)./** @scrutinizer ignore-type */ $metaImage)) {
Loading history...
1326
                        $path = api_get_path(WEB_PATH).$metaImage;
1327
                        $socialMeta .= '<meta property="og:image" content="'.$path.'" />'."\n";
1328
                    }
1329
                }
1330
            }
1331
        }
1332
1333
        $this->assign('social_meta', $socialMeta);
1334
    }
1335
1336
    /**
1337
     * Set footer parameters.
1338
     */
1339
    private function set_footer_parameters()
1340
    {
1341
        // Loading footer extra content
1342
        if (!api_is_platform_admin()) {
1343
            $extra_footer = trim(api_get_setting('footer_extra_content'));
1344
            if (!empty($extra_footer)) {
1345
                $this->assign('footer_extra_content', $extra_footer);
1346
            }
1347
        }
1348
    }
1349
1350
    /**
1351
     * Manage specific HTTP headers security.
1352
     */
1353
    private function addHTTPSecurityHeaders()
1354
    {
1355
        // Implementation of HTTP headers security, as suggested and checked
1356
        // by https://securityheaders.io/
1357
        // Enable these settings in configuration.php to use them on your site
1358
        // Strict-Transport-Security
1359
        $setting = api_get_configuration_value('security_strict_transport');
1360
        if (!empty($setting)) {
1361
            header('Strict-Transport-Security: '.$setting);
1362
        }
1363
        // Content-Security-Policy
1364
        $setting = api_get_configuration_value('security_content_policy');
1365
        if (!empty($setting)) {
1366
            header('Content-Security-Policy: '.$setting);
1367
        }
1368
        $setting = api_get_configuration_value('security_content_policy_report_only');
1369
        if (!empty($setting)) {
1370
            header('Content-Security-Policy-Report-Only: '.$setting);
1371
        }
1372
        // Public-Key-Pins
1373
        $setting = api_get_configuration_value('security_public_key_pins');
1374
        if (!empty($setting)) {
1375
            header('Public-Key-Pins: '.$setting);
1376
        }
1377
        $setting = api_get_configuration_value('security_public_key_pins_report_only');
1378
        if (!empty($setting)) {
1379
            header('Public-Key-Pins-Report-Only: '.$setting);
1380
        }
1381
        // X-Frame-Options
1382
        $setting = api_get_configuration_value('security_x_frame_options');
1383
        if (!empty($setting)) {
1384
            header('X-Frame-Options: '.$setting);
1385
        }
1386
        // X-XSS-Protection
1387
        $setting = api_get_configuration_value('security_xss_protection');
1388
        if (!empty($setting)) {
1389
            header('X-XSS-Protection: '.$setting);
1390
        }
1391
        // X-Content-Type-Options
1392
        $setting = api_get_configuration_value('security_x_content_type_options');
1393
        if (!empty($setting)) {
1394
            header('X-Content-Type-Options: '.$setting);
1395
        }
1396
        // Referrer-Policy
1397
        $setting = api_get_configuration_value('security_referrer_policy');
1398
        if (!empty($setting)) {
1399
            header('Referrer-Policy: '.$setting);
1400
        }
1401
        // end of HTTP headers security block
1402
    }
1403
1404
    /**
1405
     * Assign favicon to the 'favico' template variable.
1406
     *
1407
     * @return bool Always return true because there is always at least one correct favicon.ico
1408
     */
1409
    private function assignFavIcon()
1410
    {
1411
        // Default root chamilo favicon
1412
        $favico = '<link rel="shortcut icon" href="'.api_get_path(WEB_PATH).'favicon.ico" type="image/x-icon" />';
1413
1414
        //Added to verify if in the current Chamilo Theme exist a favicon
1415
        $favicoThemeUrl = api_get_path(SYS_CSS_PATH).$this->themeDir.'images/';
1416
1417
        //If exist pick the current chamilo theme favicon
1418
        if (is_file($favicoThemeUrl.'favicon.ico')) {
1419
            $favico = '<link rel="shortcut icon" href="'.api_get_path(WEB_CSS_PATH).$this->themeDir.'images/favicon.ico" type="image/x-icon" />';
1420
        }
1421
1422
        if (api_is_multiple_url_enabled()) {
1423
            /*$access_url_id = api_get_current_access_url_id();
1424
            if ($access_url_id != -1) {
1425
                $url_info = api_get_access_url($access_url_id);
1426
                $url = api_remove_trailing_slash(
1427
                    preg_replace('/https?:\/\//i', '', $url_info['url'])
1428
                );
1429
                $clean_url = api_replace_dangerous_char($url);
1430
                $clean_url = str_replace('/', '-', $clean_url);
1431
                $clean_url .= '/';
1432
                $homep = api_get_path(REL_PATH).'home/'.$clean_url; //homep for Home Path
1433
                $icon_real_homep = api_get_path(SYS_APP_PATH).'home/'.$clean_url;
1434
                //we create the new dir for the new sites
1435
                if (is_file($icon_real_homep.'favicon.ico')) {
1436
                    $favico = '<link rel="shortcut icon" href="'.$homep.'favicon.ico" type="image/x-icon" />';
1437
                }
1438
            }*/
1439
        }
1440
1441
        $this->assign('favico', $favico);
1442
1443
        return true;
1444
    }
1445
1446
    /**
1447
     * Assign HTML code to the 'accessibility' template variable (usually shown above top menu).
1448
     *
1449
     * @return bool Always return true (even if empty string)
1450
     */
1451
    private function assignAccessibilityBlock()
1452
    {
1453
        $resize = '';
1454
        if ('true' == api_get_setting('accessibility_font_resize')) {
1455
            $resize .= '<div class="resize_font">';
1456
            $resize .= '<div class="btn-group">';
1457
            $resize .= '<a title="'.get_lang('Decrease the font size').'" href="#" class="decrease_font btn btn-default"><em class="fa fa-font"></em></a>';
1458
            $resize .= '<a title="'.get_lang('Reset the font size').'" href="#" class="reset_font btn btn-default"><em class="fa fa-font"></em></a>';
1459
            $resize .= '<a title="'.get_lang('Increase the font size').'" href="#" class="increase_font btn btn-default"><em class="fa fa-font"></em></a>';
1460
            $resize .= '</div>';
1461
            $resize .= '</div>';
1462
        }
1463
        $this->assign('accessibility', $resize);
1464
1465
        return true;
1466
    }
1467
1468
    /**
1469
     * Assign HTML code to the 'social_meta' template variable (usually shown above top menu).
1470
     *
1471
     * @return bool Always return true (even if empty string)
1472
     */
1473
    private function assignSocialMeta()
1474
    {
1475
        $socialMeta = '';
1476
        $metaTitle = api_get_setting('meta_title');
1477
        if (!empty($metaTitle)) {
1478
            $socialMeta .= '<meta name="twitter:card" content="summary" />'."\n";
1479
            $metaSite = api_get_setting('meta_twitter_site');
1480
            if (!empty($metaSite)) {
1481
                $socialMeta .= '<meta name="twitter:site" content="'.$metaSite.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaSite of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1481
                $socialMeta .= '<meta name="twitter:site" content="'./** @scrutinizer ignore-type */ $metaSite.'" />'."\n";
Loading history...
1482
                $metaCreator = api_get_setting('meta_twitter_creator');
1483
                if (!empty($metaCreator)) {
1484
                    $socialMeta .= '<meta name="twitter:creator" content="'.$metaCreator.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaCreator of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1484
                    $socialMeta .= '<meta name="twitter:creator" content="'./** @scrutinizer ignore-type */ $metaCreator.'" />'."\n";
Loading history...
1485
                }
1486
            }
1487
1488
            // The user badge page emits its own meta tags, so if this is
1489
            // enabled, ignore the global ones
1490
            $userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
1491
            $skillId = isset($_GET['skill']) ? intval($_GET['skill']) : 0;
1492
1493
            if (!$userId && !$skillId) {
1494
                // no combination of user and skill ID has been defined,
1495
                // so print the normal or course-specific OpenGraph meta tags
1496
                // Check for a course ID
1497
                $courseId = api_get_course_int_id();
1498
                // Check session ID from session/id/about (see .htaccess)
1499
                $sessionId = isset($_GET['session_id']) ? intval($_GET['session_id']) : 0;
1500
1501
                if (false != $courseId) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $courseId of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
1502
                    // If we are inside a course (even if within a session), publish info about the course
1503
                    $course = api_get_course_entity($courseId);
1504
                    // @TODO: support right-to-left in title
1505
                    $socialMeta .= '<meta property="og:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaTitle of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1505
                    $socialMeta .= '<meta property="og:title" content="'.$course->getTitle().' - './** @scrutinizer ignore-type */ $metaTitle.'" />'."\n";
Loading history...
1506
                    $socialMeta .= '<meta property="twitter:title" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
1507
                    $socialMeta .= '<meta property="og:url" content="'.api_get_course_url($course->getCode()).'" />'."\n";
1508
1509
                    $metaDescription = api_get_setting('meta_description');
1510
                    if (!empty($course->getDescription())) {
1511
                        $socialMeta .= '<meta property="og:description" content="'.strip_tags($course->getDescription()).'" />'."\n";
1512
                        $socialMeta .= '<meta property="twitter:description" content="'.strip_tags($course->getDescription()).'" />'."\n";
1513
                    } elseif (!empty($metaDescription)) {
1514
                        $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
0 ignored issues
show
Bug introduced by
Are you sure $metaDescription of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1514
                        $socialMeta .= '<meta property="og:description" content="'./** @scrutinizer ignore-type */ $metaDescription.'" />'."\n";
Loading history...
1515
                        $socialMeta .= '<meta property="twitter:description" content="'.$metaDescription.'" />'."\n";
1516
                    }
1517
1518
                    $picture = Container::getIllustrationRepository()->getIllustrationUrl($course);
1519
                    if (!empty($picture)) {
1520
                        $socialMeta .= '<meta property="og:image" content="'.$picture.'" />'."\n";
1521
                        $socialMeta .= '<meta property="twitter:image" content="'.$picture.'" />'."\n";
1522
                        $socialMeta .= '<meta property="twitter:image:alt" content="'.$course->getTitle().' - '.$metaTitle.'" />'."\n";
1523
                    } else {
1524
                        $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
1525
                    }
1526
                } elseif (0 !== $sessionId) {
1527
                    // If we are on a session "about" screen, publish info about the session
1528
                    $session = api_get_session_entity($sessionId);
1529
1530
                    $socialMeta .= '<meta property="og:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
1531
                    $socialMeta .= '<meta property="twitter:title" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
1532
                    $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH)."sessions/{$session->getId()}/about/".'" />'."\n";
1533
1534
                    $sessionValues = new ExtraFieldValue('session');
1535
                    $sessionImage = $sessionValues->get_values_by_handler_and_field_variable($session->getId(), 'image')['value'];
1536
                    //$sessionImageSysPath = api_get_path(SYS_UPLOAD_PATH).$sessionImage;
1537
                    if (!empty($sessionImage)) {
1538
                        $asset = Container::getAssetRepository()->find($sessionImage);
1539
                        $sessionImagePath = Container::getAssetRepository()->getAssetUrl($asset);
1540
                        if (!empty($sessionImagePath)) {
1541
                            $socialMeta .= '<meta property="og:image" content="'.$sessionImagePath.'" />'."\n";
1542
                            $socialMeta .= '<meta property="twitter:image" content="'.$sessionImagePath.'" />'."\n";
1543
                            $socialMeta .= '<meta property="twitter:image:alt" content="'.$session->getName().' - '.$metaTitle.'" />'."\n";
1544
                        }
1545
                    } else {
1546
                        $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
1547
                    }
1548
                } else {
1549
                    // Otherwise (not a course nor a session, nor a user, nor a badge), publish portal info
1550
                    $socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n";
1551
                    $socialMeta .= '<meta property="twitter:title" content="'.$metaTitle.'" />'."\n";
1552
                    $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n";
1553
1554
                    $metaDescription = api_get_setting('meta_description');
1555
                    if (!empty($metaDescription)) {
1556
                        $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
1557
                        $socialMeta .= '<meta property="twitter:description" content="'.$metaDescription.'" />'."\n";
1558
                    }
1559
                    $socialMeta .= $this->getMetaPortalImagePath($metaTitle);
1560
                }
1561
            }
1562
        }
1563
1564
        $this->assign('social_meta', $socialMeta);
1565
1566
        return true;
1567
    }
1568
1569
    /**
1570
     * Get platform meta image tag (check meta_image_path setting, then use the logo).
1571
     *
1572
     * @param string $imageAlt The alt attribute for the image
1573
     *
1574
     * @return string The meta image HTML tag, or empty
1575
     */
1576
    private function getMetaPortalImagePath($imageAlt = '')
1577
    {
1578
        // Load portal meta image if defined
1579
        $metaImage = api_get_setting('meta_image_path');
1580
        $metaImageSysPath = api_get_path(SYS_PATH).$metaImage;
0 ignored issues
show
Bug introduced by
Are you sure $metaImage of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1580
        $metaImageSysPath = api_get_path(SYS_PATH)./** @scrutinizer ignore-type */ $metaImage;
Loading history...
1581
        $metaImageWebPath = api_get_path(WEB_PATH).$metaImage;
1582
        $portalImageMeta = '';
1583
        if (!empty($metaImage)) {
1584
            if (is_file($metaImageSysPath)) {
1585
                $portalImageMeta = '<meta property="og:image" content="'.$metaImageWebPath.'" />'."\n";
1586
                $portalImageMeta .= '<meta property="twitter:image" content="'.$metaImageWebPath.'" />'."\n";
1587
                $portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
1588
            }
1589
        } else {
1590
            $logo = ChamiloApi::getPlatformLogoPath($this->theme);
1591
            if (!empty($logo)) {
1592
                $portalImageMeta = '<meta property="og:image" content="'.$logo.'" />'."\n";
1593
                $portalImageMeta .= '<meta property="twitter:image" content="'.$logo.'" />'."\n";
1594
                $portalImageMeta .= '<meta property="twitter:image:alt" content="'.$imageAlt.'" />'."\n";
1595
            }
1596
        }
1597
1598
        return $portalImageMeta;
1599
    }
1600
}
1601