Completed
Push — master ( ff1873...f20885 )
by Julito
14:04
created

Template::displaySkillLayout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nop 0
dl 0
loc 4
rs 10
nc 1
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
5
use Chamilo\CoreBundle\Framework\Container;
6
use Chamilo\UserBundle\Entity\User;
7
use Symfony\Component\HttpFoundation\Response;
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;
0 ignored issues
show
Bug Best Practice introduced by
The property hide_global_chat does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
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
99
        $cache_folder = api_get_path(SYS_ARCHIVE_PATH).'twig/'.$urlId.'/';
100
101
        if (!is_dir($cache_folder)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
102
            //mkdir($cache_folder, api_get_permissions_for_new_directories(), true);
103
        }
104
105
        $loader = new Twig_Loader_Filesystem($template_paths);
106
107
        $isTestMode = api_get_setting('server_type') === 'test';
108
109
        //Setting Twig options depending on the server see http://twig.sensiolabs.org/doc/api.html#environment-options
110
        if ($isTestMode) {
111
            $options = [
112
                //'cache' => api_get_path(SYS_ARCHIVE_PATH), //path to the cache folder
113
                'autoescape' => false,
114
                'debug' => true,
115
                'auto_reload' => true,
116
                'optimizations' => 0,
117
                // turn on optimizations with -1
118
                'strict_variables' => false,
119
                //If set to false, Twig will silently ignore invalid variables
120
            ];
121
        } else {
122
            $options = [
123
                'cache' => $cache_folder,
124
                //path to the cache folder
125
                'autoescape' => false,
126
                'debug' => false,
127
                'auto_reload' => false,
128
                'optimizations' => -1,
129
                // turn on optimizations with -1
130
                'strict_variables' => false,
131
                //If set to false, Twig will silently ignore invalid variables
132
            ];
133
        }
134
135
        $this->twig = new Twig_Environment($loader, $options);
136
137
        if ($isTestMode) {
138
            $this->twig->addExtension(new Twig_Extension_Debug());
139
        }
140
141
        // Twig filters setup
142
        $filters = [
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
            [
153
                'name' => 'return_message',
154
                'callable' => 'Display::return_message_and_translate',
155
            ],
156
            [
157
                'name' => 'display_page_header',
158
                'callable' => 'Display::page_header_and_translate',
159
            ],
160
            [
161
                'name' => 'display_page_subheader',
162
                'callable' => 'Display::page_subheader_and_translate',
163
            ],
164
            [
165
                'name' => 'icon',
166
                'callable' => 'Template::get_icon_path',
167
            ],
168
            [
169
                'name' => 'img',
170
                'callable' => 'Template::get_image',
171
            ],
172
            [
173
                'name' => 'format_date',
174
                'callable' => 'Template::format_date',
175
            ],
176
            [
177
                'name' => 'get_template',
178
                'callable' => 'Template::findTemplateFilePath',
179
            ],
180
        ];
181
182
        foreach ($filters as $filter) {
183
            if (is_array($filter)) {
184
                $this->twig->addFilter(new Twig_SimpleFilter($filter['name'], $filter['callable']));
185
            } else {
186
                $this->twig->addFilter(new Twig_SimpleFilter($filter, $filter));
187
            }
188
        }
189
190
        $functions = [
191
            ['name' => 'get_tutors_names', 'callable' => 'Template::returnTutorsNames'],
192
            ['name' => 'get_teachers_names', 'callable' => 'Template::returnTeachersNames'],
193
        ];
194
195
        foreach ($functions as $function) {
196
            $this->twig->addFunction(new Twig_SimpleFunction($function['name'], $function['callable']));
197
        }
198
199
        // Setting system variables
200
        $this->set_system_parameters();
201
202
        // Setting user variables
203
        $this->set_user_parameters();
204
205
        // Setting course variables
206
        $this->set_course_parameters();
207
208
        // Setting administrator variables
209
        $this->setAdministratorParams();
210
        //$this->setCSSEditor();
211
212
        // Header and footer are showed by default
213
        $this->set_footer($show_footer);
214
        $this->set_header($show_header);
215
216
        $this->set_header_parameters($sendHeaders);
217
        $this->set_footer_parameters();
218
219
        $defaultStyle = api_get_configuration_value('default_template');
220
        if (!empty($defaultStyle)) {
221
            $this->templateFolder = $defaultStyle;
0 ignored issues
show
Documentation Bug introduced by
It seems like $defaultStyle can also be of type boolean. 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...
222
        }
223
224
        $this->assign('template', $this->templateFolder);
225
        $this->assign('locale', api_get_language_isocode());
226
        $this->assign('login_class', null);
227
228
        $allow = api_get_configuration_value('show_language_selector_in_menu');
229
        if ($allow) {
230
            $this->assign('language_form', api_display_language_form());
231
        }
232
233
        // Chamilo plugins
234
        if ($this->show_header) {
235
            if ($this->load_plugins) {
236
                $this->plugin = new AppPlugin();
237
238
                //1. Showing installed plugins in regions
239
                $pluginRegions = $this->plugin->get_plugin_regions();
240
                foreach ($pluginRegions as $region) {
241
                    $this->set_plugin_region($region);
242
                }
243
244
                //2. Loading the course plugin info
245
                global $course_plugin;
246
                if (isset($course_plugin) && !empty($course_plugin) && !empty($this->course_id)) {
247
                    //Load plugin get_langs
248
                    $this->plugin->load_plugin_lang_variables($course_plugin);
249
                }
250
            }
251
        }
252
    }
253
254
    /**
255
     * @param string $image
256
     * @param int    $size
257
     *
258
     * @return string
259
     */
260
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
261
    {
262
        return Display::return_icon($image, '', [], $size, false, true);
263
    }
264
265
    /**
266
     * @param string $image
267
     * @param int    $size
268
     * @param string $name
269
     *
270
     * @return string
271
     */
272
    public static function get_image($image, $size = ICON_SIZE_SMALL, $name = '')
273
    {
274
        return Display::return_icon($image, $name, [], $size);
275
    }
276
277
    /**
278
     * @param string $timestamp
279
     * @param string $format
280
     *
281
     * @return string
282
     */
283
    public static function format_date($timestamp, $format = null)
284
    {
285
        return api_format_date($timestamp, $format);
286
    }
287
288
    /**
289
     * Return the item's url key:.
290
     *
291
     *      c_id=xx&id=xx
292
     *
293
     * @param object $item
294
     *
295
     * @return string
296
     */
297
    public static function key($item)
298
    {
299
        $id = isset($item->id) ? $item->id : null;
300
        $c_id = isset($item->c_id) ? $item->c_id : null;
301
        $result = '';
302
        if ($c_id) {
303
            $result = "c_id=$c_id";
304
        }
305
        if ($id) {
306
            if ($result) {
307
                $result .= "&amp;id=$id";
308
            } else {
309
                $result .= "&amp;id=$id";
310
            }
311
        }
312
313
        return $result;
314
    }
315
316
    /**
317
     * @param string $helpInput
318
     */
319
    public function setHelp($helpInput = null)
320
    {
321
        if (!empty($helpInput)) {
322
            $help = $helpInput;
323
        } else {
324
            $help = $this->help;
325
        }
326
327
        $content = '';
328
        if (api_get_setting('enable_help_link') == 'true') {
329
            if (!empty($help)) {
330
                $help = Security::remove_XSS($help);
331
                $content = '<div class="help">';
332
                $content .= Display::url(
333
                    Display::return_icon('help.large.png', get_lang('Help')),
334
                    api_get_path(WEB_CODE_PATH).'help/help.php?open='.$help,
335
                    [
336
                        'class' => 'ajax',
337
                        'data-title' => get_lang('Help'),
338
                    ]
339
                );
340
                $content .= '</div>';
341
            }
342
        }
343
        $this->assign('help_content', $content);
344
    }
345
346
    /**
347
     * Use template system to parse the actions menu.
348
     *
349
     * @todo finish it!
350
     */
351
    public function set_actions($actions)
352
    {
353
        $action_string = '';
354
        if (!empty($actions)) {
355
            foreach ($actions as $action) {
356
                $action_string .= $action;
357
            }
358
        }
359
        $this->assign('actions', $actions);
360
    }
361
362
    /**
363
     * Render the template.
364
     *
365
     * @param string $template           The template path
366
     * @param bool   $clearFlashMessages Clear the $_SESSION variables for flash messages
367
     */
368
    public function display($template, $clearFlashMessages = true)
0 ignored issues
show
Unused Code introduced by
The parameter $clearFlashMessages is not used and could be removed. ( Ignorable by Annotation )

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

368
    public function display($template, /** @scrutinizer ignore-unused */ $clearFlashMessages = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
369
    {
370
        $template = str_replace('tpl', 'html.twig', $template);
371
        $templateFile = api_get_path(SYS_PATH).'main/template/'.$template;
372
373
        // Set legacy breadcrumb
374
        global $interbreadcrumb;
375
        $this->params['legacy_breadcrumb'] = $interbreadcrumb;
376
377
        if (!file_exists($templateFile)) {
378
            $e = new \Gaufrette\Exception\FileNotFound($templateFile);
379
            echo $e->getMessage();
380
            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...
381
        }
382
383
        $this->returnResponse($this->params, $template);
384
    }
385
386
    /**
387
     * @param array $params
388
     * @param string $template
389
     *
390
     * @throws \Twig\Error\Error
391
     */
392
    public function returnResponse($params, $template)
393
    {
394
        $response = new Response();
395
        $content = Container::getTemplating()->render($template, $params);
396
        $response->setContent($content);
397
        $response->send();
398
    }
399
400
    /**
401
     * Shortcut to display a 1 col layout (index.php).
402
     * */
403
    public function display_one_col_template()
404
    {
405
        $template = '@ChamiloTheme/Layout/layout_one_col.html.twig';
406
        $this->returnResponse($this->params, $template);
407
    }
408
409
    /**
410
     * Shortcut to display a 2 col layout (userportal.php).
411
     */
412
    public function display_two_col_template()
413
    {
414
        $template = '@ChamiloTheme/Layout/layout_two_col.html.twig';
415
        $this->returnResponse($this->params, $template);
416
    }
417
418
    /**
419
     * Displays an empty template.
420
     */
421
    public function display_blank_template()
422
    {
423
        $template = '@ChamiloTheme/Layout/blank.html.twig';
424
        $this->returnResponse($this->params, $template);
425
    }
426
427
    /**
428
     * Displays an empty template.
429
     */
430
    public function displayBlankTemplateNoHeader()
431
    {
432
        $template = '@ChamiloTheme/Layout/blank_no_header.html.twig';
433
        $this->returnResponse($this->params, $template);
434
    }
435
436
    /**
437
     * Displays an empty template.
438
     */
439
    public function display_no_layout_template()
440
    {
441
        $template = '@ChamiloTheme/Layout/no_layout.html.twig';
442
        $this->returnResponse($this->params, $template);
443
    }
444
445
    /**
446
     * Displays an empty template.
447
     */
448
    public function displaySkillLayout()
449
    {
450
        $template = '@ChamiloTheme/Layout/skill_layout.html.twig';
451
        $this->returnResponse($this->params, $template);
452
    }
453
454
    /**
455
     * Sets the footer visibility.
456
     *
457
     * @param bool true if we show the footer
458
     */
459
    public function set_footer($status)
460
    {
461
        $this->show_footer = $status;
462
        $this->assign('show_footer', $status);
463
    }
464
465
    /**
466
     * return true if toolbar has to be displayed for user.
467
     *
468
     * @return bool
469
     */
470
    public static function isToolBarDisplayedForUser()
471
    {
472
        //Toolbar
473
        $show_admin_toolbar = api_get_setting('show_admin_toolbar');
474
        $show_toolbar = false;
475
476
        switch ($show_admin_toolbar) {
477
            case 'do_not_show':
478
                break;
479
            case 'show_to_admin':
480
                if (api_is_platform_admin()) {
481
                    $show_toolbar = true;
482
                }
483
                break;
484
            case 'show_to_admin_and_teachers':
485
                if (api_is_platform_admin() || api_is_allowed_to_edit()) {
486
                    $show_toolbar = true;
487
                }
488
                break;
489
            case 'show_to_all':
490
                $show_toolbar = true;
491
                break;
492
        }
493
494
        return $show_toolbar;
495
    }
496
497
    /**
498
     * Sets the header visibility.
499
     *
500
     * @param bool true if we show the header
501
     */
502
    public function set_header($status)
503
    {
504
        $this->show_header = $status;
505
        $this->assign('show_header', $status);
506
507
        $show_toolbar = 0;
508
509
        if (self::isToolBarDisplayedForUser()) {
510
            $show_toolbar = 1;
511
        }
512
513
        $this->assign('show_toolbar', $show_toolbar);
514
515
        //Only if course is available
516
        $show_course_shortcut = null;
517
        $show_course_navigation_menu = null;
518
519
        if (!empty($this->course_id) && $this->user_is_logged_in) {
520
            if (api_get_setting('show_toolshortcuts') != 'false') {
521
                //Course toolbar
522
                $show_course_shortcut = CourseHome::show_navigation_tool_shortcuts();
523
            }
524
            if (api_get_setting('show_navigation_menu') != 'false') {
525
                //Course toolbar
526
                $show_course_navigation_menu = CourseHome::show_navigation_menu();
527
            }
528
        }
529
        $this->assign('show_course_shortcut', $show_course_shortcut);
530
        $this->assign('show_course_navigation_menu', $show_course_navigation_menu);
531
    }
532
533
    /**
534
     * Returns the sub-folder and filename for the given tpl file.
535
     * If template not found in overrides/ or custom template folder, the
536
     * default template will be used.
537
     *
538
     * @param string $name
539
     *
540
     * @return string
541
     */
542
    public function get_template($name)
543
    {
544
        // Check if the tpl file is present in the main/template/overrides/ dir
545
        // Overrides is a special directory meant for temporary template
546
        // customization. It must be taken into account before anything else
547
        $file = api_get_path(SYS_CODE_PATH).'template/overrides/'.$name;
548
        if (is_readable($file)) {
549
            return 'overrides/'.$name;
550
        }
551
        // If a template folder has been manually defined, search for the right
552
        // file, and if not found, go for the same file in the default template
553
        if ($this->templateFolder != 'default') {
554
            // Avoid missing template error, use the default file.
555
            $file = api_get_path(SYS_CODE_PATH).'template/'.$this->templateFolder.'/'.$name;
556
            if (!file_exists($file)) {
557
                return 'default/'.$name;
558
            }
559
        }
560
        $name = str_replace('tpl', 'html.twig', $name);
561
562
        return $this->templateFolder.'/'.$name;
563
    }
564
565
    /**
566
     * Get CSS themes sub-directory.
567
     *
568
     * @param string $theme
569
     *
570
     * @return string with a trailing slash, e.g. 'themes/chamilo_red/'
571
     */
572
    public static function getThemeDir($theme)
573
    {
574
        $themeDir = 'themes/'.$theme.'/';
575
        $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
576
        if (!empty($virtualTheme)) {
577
            $virtualThemeList = api_get_themes(true);
578
            $isVirtualTheme = in_array($theme, array_keys($virtualThemeList));
579
            if ($isVirtualTheme) {
580
                $themeDir = 'themes/'.$virtualTheme.'/'.$theme.'/';
581
            }
582
        }
583
584
        return $themeDir;
585
    }
586
587
    /**
588
     * Set system parameters from api_get_configuration into _s array for use in TPLs
589
     * Also fills the _p array from getWebPaths().
590
     *
591
     * @uses \self::getWebPaths()
592
     */
593
    public function set_system_parameters()
594
    {
595
        $this->theme = api_get_visual_theme();
596
        if (!empty($this->preview_theme)) {
597
            $this->theme = $this->preview_theme;
598
        }
599
600
        $this->assign('theme', $this->theme);
601
602
        $this->themeDir = self::getThemeDir($this->theme);
603
604
        // Setting app paths/URLs
605
        $this->assign('_p', $this->getWebPaths());
606
607
        // Here we can add system parameters that can be use in any template
608
        $_s = [
609
            'software_name' => api_get_configuration_value('software_name'),
610
            'system_version' => api_get_configuration_value('system_version'),
611
            'site_name' => api_get_setting('siteName'),
612
            'institution' => api_get_setting('Institution'),
613
            'date' => api_format_date('now', DATE_FORMAT_LONG),
614
            'timezone' => api_get_timezone(),
615
            'gamification_mode' => api_get_setting('gamification_mode'),
616
        ];
617
        $this->assign('_s', $_s);
618
    }
619
620
    /**
621
     * Set legacy twig globals in order to be hook in the LegacyListener.php.
622
     *
623
     * @return array
624
     */
625
    public static function getGlobals()
626
    {
627
        $_p = [
628
            'web' => api_get_path(WEB_PATH),
629
            'web_relative' => api_get_path(REL_PATH),
630
            'web_course' => api_get_path(WEB_COURSE_PATH),
631
            'web_main' => api_get_path(WEB_CODE_PATH),
632
            'web_css' => api_get_path(WEB_CSS_PATH),
633
            //'web_css_theme' => api_get_path(WEB_CSS_PATH) . 'themes/' . $this->theme . '/',
634
            'web_ajax' => api_get_path(WEB_AJAX_PATH),
635
            'web_img' => api_get_path(WEB_IMG_PATH),
636
            'web_plugin' => api_get_path(WEB_PLUGIN_PATH),
637
            'web_plugin_asset' => api_get_path(WEB_PLUGIN_ASSET_PATH),
638
            'web_lib' => api_get_path(WEB_LIBRARY_PATH),
639
            'web_upload' => api_get_path(WEB_UPLOAD_PATH),
640
            'web_self' => api_get_self(),
641
            'web_query_vars' => api_htmlentities($_SERVER['QUERY_STRING']),
642
            'web_self_query_vars' => api_htmlentities($_SERVER['REQUEST_URI']),
643
            'web_cid_query' => api_get_cidreq(),
644
        ];
645
646
        $_s = [
647
            'software_name' => api_get_configuration_value('software_name'),
648
            'system_version' => api_get_configuration_value('system_version'),
649
            'site_name' => api_get_setting('siteName'),
650
            'institution' => api_get_setting('Institution'),
651
            //'date' => api_format_date('now', DATE_FORMAT_LONG),
652
            'date' => '',
653
            'timezone' => '',
654
            //'timezone' => api_get_timezone(),
655
            'gamification_mode' => api_get_setting('gamification_mode'),
656
        ];
657
658
        //$user_info = api_get_user_info();
659
660
        return [
661
            '_p' => $_p,
662
            '_s' => $_s,
663
            //       '_u' => $user_info,
664
            'template' => 'default', // @todo setup template folder in config.yml;
665
        ];
666
    }
667
668
    /**
669
     * Set theme, include mainstream CSS files.
670
     *
671
     * @see setCssCustomFiles() for additional CSS sheets
672
     */
673
    public function setCssFiles()
674
    {
675
        global $disable_js_and_css_files;
676
        $css = [];
677
678
        // Default CSS Bootstrap
679
        $bowerCSSFiles = [
680
            'fontawesome/css/font-awesome.min.css',
681
            'jquery-ui/themes/smoothness/theme.css',
682
            'jquery-ui/themes/smoothness/jquery-ui.min.css',
683
            'mediaelement/build/mediaelementplayer.min.css',
684
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.css',
685
            'bootstrap/dist/css/bootstrap.min.css',
686
            'jquery.scrollbar/jquery.scrollbar.css',
687
            'bootstrap-daterangepicker/daterangepicker.css',
688
            'bootstrap-select/dist/css/bootstrap-select.min.css',
689
            'select2/dist/css/select2.min.css',
690
            'flag-icon-css/css/flag-icon.min.css',
691
        ];
692
693
        $features = api_get_configuration_value('video_features');
694
        $defaultFeatures = ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'];
695
696
        if (!empty($features) && isset($features['features'])) {
697
            foreach ($features['features'] as $feature) {
698
                $bowerCSSFiles[] = "mediaelement/plugins/$feature/$feature.css";
699
                $defaultFeatures[] = $feature;
700
            }
701
        }
702
703
        foreach ($bowerCSSFiles as $file) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
704
            //$css[] = api_get_path(WEB_PUBLIC_PATH).'assets/'.$file;
705
        }
706
707
        //$css[] = api_get_path(WEB_LIBRARY_PATH).'javascript/chosen/chosen.css';
708
709
        if (api_is_global_chat_enabled()) {
710
            $css[] = api_get_path(WEB_LIBRARY_PATH).'javascript/chat/css/chat.css';
711
        }
712
        $css_file_to_string = '';
713
        foreach ($css as $file) {
714
            $css_file_to_string .= api_get_css($file);
715
        }
716
717
        if (!$disable_js_and_css_files) {
718
            $this->assign('css_static_file_to_string', $css_file_to_string);
719
        }
720
721
        $defaultFeatures = implode("','", $defaultFeatures);
722
        $this->assign('video_features', $defaultFeatures);
723
    }
724
725
    /**
726
     * Sets the "styles" menu in ckEditor.
727
     *
728
     * Reads css/themes/xxx/editor.css if exists and shows it in the menu, otherwise it
729
     * will take the default web/editor.css file
730
     */
731
    public function setStyleMenuInCkEditor()
732
    {
733
        $cssEditor = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'editor.css');
734
        if (is_file(api_get_path(SYS_CSS_PATH).$this->themeDir.'editor.css')) {
735
            $cssEditor = api_get_path(WEB_CSS_PATH).$this->themeDir.'editor.css';
736
        }
737
        $this->assign('css_editor', $cssEditor);
738
    }
739
740
    /**
741
     * Prepare custom CSS to be added at the very end of the <head> section.
742
     *
743
     * @see setCssFiles() for the mainstream CSS files
744
     */
745
    public function setCssCustomFiles()
746
    {
747
        global $disable_js_and_css_files;
748
        // chamilo CSS
749
        //$css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'../chamilo.css');
750
751
        // Base CSS
752
        //$css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'base.css');
753
        $css = [];
754
        if ($this->show_learnpath) {
755
            $css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'scorm.css');
756
            if (is_file(api_get_path(SYS_CSS_PATH).$this->themeDir.'learnpath.css')) {
757
                $css[] = api_get_path(WEB_CSS_PATH).$this->themeDir.'learnpath.css';
758
            }
759
        }
760
761
        //$css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).$this->themeDir.'default.css');
762
        $css_file_to_string = '';
763
        foreach ($css as $file) {
764
            $css_file_to_string .= api_get_css($file);
765
        }
766
767
        // @todo move this somewhere else. Special fix when using tablets in order to see the text near icons
768
        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...
769
            //hack in order to fix the actions buttons
770
            $css_file_to_string .= '<style>
771
                .td_actions a {
772
                    float:left;
773
                    width:100%;
774
                }
775
                .forum_message_left a {
776
                    float:left;
777
                    width:100%;
778
                }
779
                </style>';
780
        }
781
782
        $navigator_info = api_get_navigator();
783
        if ($navigator_info['name'] == 'Internet Explorer' && $navigator_info['version'] == '6') {
784
            $css_file_to_string .= 'img, div { behavior: url('.api_get_path(WEB_LIBRARY_PATH).'javascript/iepngfix/iepngfix.htc) } '."\n";
785
        }
786
787
        if (!$disable_js_and_css_files) {
788
            $this->assign('css_custom_file_to_string', $css_file_to_string);
789
790
            $style_print = '';
791
            if (is_readable(api_get_path(SYS_CSS_PATH).$this->theme.'/print.css')) {
792
                $style_print = api_get_css(
793
                    api_get_cdn_path(api_get_path(WEB_CSS_PATH).$this->theme.'/print.css'),
794
                    'print'
795
                );
796
            }
797
            $this->assign('css_style_print', $style_print);
798
        }
799
800
        // Logo
801
        $logo = return_logo($this->theme);
802
        $this->assign('logo', $logo);
803
        $this->assign('show_media_element', 1);
804
    }
805
806
    /**
807
     * Declare and define the template variable that will be used to load
808
     * javascript libraries in the header.
809
     */
810
    public function set_js_files()
811
    {
812
        global $disable_js_and_css_files, $htmlHeadXtra;
813
        $isoCode = api_get_language_isocode();
814
        $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_'.strtoupper($isoCode).'.min.js';
815
816
        if ($isoCode == 'en') {
817
            $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_US.min.js';
818
        }
819
        // JS files
820
        $js_files = [
821
            'chosen/chosen.jquery.min.js',
822
        ];
823
824
        $viewBySession = api_get_setting('my_courses_view_by_session') === 'true';
825
826
        if (api_is_global_chat_enabled() || $viewBySession) {
827
            // Do not include the global chat in LP
828
            if ($this->show_learnpath == false &&
829
                $this->show_footer == 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...
830
                $this->hide_global_chat == false
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...
831
            ) {
832
                $js_files[] = 'chat/js/chat.js';
833
            }
834
        }
835
836
        if (api_get_setting('accessibility_font_resize') == 'true') {
837
            $js_files[] = 'fontresize.js';
838
        }
839
840
        $js_file_to_string = '';
841
        $bowerJsFiles = [
842
            'modernizr/modernizr.js',
843
            'jquery/dist/jquery.min.js',
844
            'bootstrap/dist/js/bootstrap.min.js',
845
            'jquery-ui/jquery-ui.min.js',
846
            'moment/min/moment-with-locales.js',
847
            'bootstrap-daterangepicker/daterangepicker.js',
848
            'jquery-timeago/jquery.timeago.js',
849
            'mediaelement/build/mediaelement-and-player.min.js',
850
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.js',
851
            'image-map-resizer/js/imageMapResizer.min.js',
852
            'jquery.scrollbar/jquery.scrollbar.min.js',
853
            //'readmore-js/readmore.min.js',
854
            'bootstrap-select/dist/js/bootstrap-select.min.js',
855
            $selectLink,
856
            'select2/dist/js/select2.min.js',
857
            "select2/dist/js/i18n/$isoCode.js",
858
        ];
859
860
        $features = api_get_configuration_value('video_features');
861
        if (!empty($features) && isset($features['features'])) {
862
            foreach ($features['features'] as $feature) {
863
                $bowerJsFiles[] = "mediaelement/plugins/$feature/$feature.js";
864
            }
865
        }
866
867
        if (CHAMILO_LOAD_WYSIWYG == 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...
868
            $bowerJsFiles[] = 'ckeditor/ckeditor.js';
869
        }
870
871
        if (api_get_setting('include_asciimathml_script') == 'true') {
872
            $bowerJsFiles[] = 'MathJax/MathJax.js?config=TeX-MML-AM_HTMLorMML';
873
        }
874
875
        if ($isoCode != 'en') {
876
            $bowerJsFiles[] = 'jqueryui-timepicker-addon/dist/i18n/jquery-ui-timepicker-'.$isoCode.'.js';
877
            $bowerJsFiles[] = 'jquery-ui/ui/minified/i18n/datepicker-'.$isoCode.'.min.js';
878
        }
879
880
        foreach ($bowerJsFiles as $file) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
881
            //$js_file_to_string .= '<script type="text/javascript" src="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'"></script>'."\n";
882
        }
883
884
        foreach ($js_files as $file) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
885
            //$js_file_to_string .= api_get_js($file);
886
        }
887
888
        // Loading email_editor js
889
        if (!api_is_anonymous() && api_get_setting('allow_email_editor') == 'true') {
890
            $template = $this->get_template('mail_editor/email_link.js.tpl');
891
            $js_file_to_string .= $this->fetch($template);
892
        }
893
894
        if (!$disable_js_and_css_files) {
895
            $this->assign('js_file_to_string', $js_file_to_string);
896
897
            $extra_headers = '<script>var _p = '.json_encode($this->getWebPaths(), JSON_PRETTY_PRINT).'</script>';
898
            //Adding jquery ui by default
899
            $extra_headers .= api_get_jquery_ui_js();
900
901
            //$extra_headers = '';
902
            if (isset($htmlHeadXtra) && $htmlHeadXtra) {
903
                foreach ($htmlHeadXtra as &$this_html_head) {
904
                    $extra_headers .= $this_html_head."\n";
905
                }
906
            }
907
            $this->assign('extra_headers', $extra_headers);
908
        }
909
    }
910
911
    /**
912
     * Special function to declare last-minute JS libraries which depend on
913
     * other things to be declared first. In particular, it might be useful
914
     * under IE9 with compatibility mode, which for some reason is getting
915
     * upset when a variable is used in a function (even if not used yet)
916
     * when this variable hasn't been defined yet.
917
     */
918
    public function set_js_files_post()
919
    {
920
        global $disable_js_and_css_files;
921
        $js_files = [];
922
        if (api_is_global_chat_enabled()) {
923
            //Do not include the global chat in LP
924
            if ($this->show_learnpath == false && $this->show_footer == true && $this->hide_global_chat == false) {
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...
925
                $js_files[] = 'chat/js/chat.js';
926
            }
927
        }
928
        $js_file_to_string = null;
929
930
        foreach ($js_files as $js_file) {
931
            $js_file_to_string .= api_get_js($js_file);
932
        }
933
        if (!$disable_js_and_css_files) {
934
            $this->assign('js_file_to_string_post', $js_file_to_string);
935
        }
936
    }
937
938
    /**
939
     * @param string $theme
940
     *
941
     * @return string
942
     */
943
    public static function getPortalIcon($theme)
944
    {
945
        // Default root chamilo favicon
946
        $favico = '<link rel="shortcut icon" href="'.api_get_path(WEB_PATH).'favicon.ico" type="image/x-icon" />';
947
948
        // Added to verify if in the current Chamilo Theme exist a favicon
949
        $favicoThemeUrl = api_get_path(SYS_CSS_PATH).'/themes/'.$theme.'/images/';
950
951
        //If exist pick the current chamilo theme favicon
952
        if (is_file($favicoThemeUrl.'favicon.ico')) {
953
            $favico = '<link rel="shortcut icon" href="'.api_get_path(WEB_CSS_PATH).'themes/'.$theme.'/images/favicon.ico" type="image/x-icon" />';
954
        }
955
        if (api_is_multiple_url_enabled()) {
956
            $access_url_id = api_get_current_access_url_id();
957
            if ($access_url_id != -1) {
958
                $url_info = api_get_access_url($access_url_id);
959
                $url = api_remove_trailing_slash(
960
                    preg_replace('/https?:\/\//i', '', $url_info['url'])
961
                );
962
                $clean_url = api_replace_dangerous_char($url);
963
                $clean_url = str_replace('/', '-', $clean_url);
964
                $clean_url .= '/';
965
                $homep = api_get_path(REL_PATH).'home/'.$clean_url; //homep for Home Path
966
                $icon_real_homep = api_get_path(SYS_APP_PATH).'home/'.$clean_url;
967
                //we create the new dir for the new sites
968
                if (is_file($icon_real_homep.'favicon.ico')) {
969
                    $favico = '<link rel="shortcut icon" href="'.$homep.'favicon.ico" type="image/x-icon" />';
970
                }
971
            }
972
        }
973
974
        //var_dump(Container::$container->get('router')->generate('main', ['name' => '1']));
975
        //var_dump(api_get_path(WEB_PATH));
976
977
        return $favico;
978
    }
979
980
    /**
981
     * Show header template.
982
     */
983
    public function show_header_template()
984
    {
985
        $tpl = $this->get_template('layout/show_header.tpl');
986
        $this->display($tpl);
987
    }
988
989
    /**
990
     * Show footer template.
991
     */
992
    public function show_footer_template()
993
    {
994
        $tpl = $this->get_template('layout/show_footer.tpl');
995
        $this->display($tpl);
996
    }
997
998
    /**
999
     * Show footer js template.
1000
     */
1001
    public function show_footer_js_template()
1002
    {
1003
        $tpl = $this->get_template('layout/footer.js.tpl');
1004
        $this->display($tpl);
1005
    }
1006
1007
    /**
1008
     * Sets the plugin content in a template variable.
1009
     *
1010
     * @param string $pluginRegion
1011
     */
1012
    public function set_plugin_region($pluginRegion)
1013
    {
1014
        if (!empty($pluginRegion)) {
1015
            $regionContent = $this->plugin->load_region(
1016
                $pluginRegion,
1017
                $this,
1018
                $this->force_plugin_load
1019
            );
1020
1021
            $pluginList = $this->plugin->get_installed_plugins();
1022
            foreach ($pluginList as $plugin_name) {
1023
                // The plugin_info variable is available inside the plugin index
1024
                $pluginInfo = $this->plugin->getPluginInfo($plugin_name);
1025
1026
                if (isset($pluginInfo['is_course_plugin']) && $pluginInfo['is_course_plugin']) {
1027
                    $courseInfo = api_get_course_info();
1028
                    if (!empty($courseInfo)) {
1029
                        if (isset($pluginInfo['obj']) && $pluginInfo['obj'] instanceof Plugin) {
1030
                            /** @var Plugin $plugin */
1031
                            $plugin = $pluginInfo['obj'];
1032
                            $regionContent .= $plugin->renderRegion($pluginRegion);
1033
                        }
1034
                    }
1035
                } else {
1036
                    continue;
1037
                }
1038
            }
1039
1040
            if (!empty($regionContent)) {
1041
                $this->assign('plugin_'.$pluginRegion, $regionContent);
1042
            } else {
1043
                $this->assign('plugin_'.$pluginRegion, null);
1044
            }
1045
        }
1046
1047
        return null;
1048
    }
1049
1050
    /**
1051
     * @param string $template
1052
     *
1053
     * @return string
1054
     */
1055
    public function fetch($template = null)
1056
    {
1057
        $template = $this->twig->loadTemplate($template);
1058
1059
        return $template->render($this->params);
1060
    }
1061
1062
    /**
1063
     * @param string $variable
1064
     * @param mixed  $value
1065
     */
1066
    public function assign($variable, $value = '')
1067
    {
1068
        $this->params[$variable] = $value;
1069
    }
1070
1071
    /**
1072
     * Adds a body class for login pages.
1073
     */
1074
    public function setLoginBodyClass()
1075
    {
1076
        $this->assign('login_class', 'section-login');
1077
    }
1078
1079
    /**
1080
     * The theme that will be used if the database is not working.
1081
     *
1082
     * @return string
1083
     */
1084
    public static function getThemeFallback()
1085
    {
1086
        $theme = api_get_configuration_value('theme_fallback');
1087
        if (empty($theme)) {
1088
            $theme = 'chamilo';
1089
        }
1090
1091
        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...
1092
    }
1093
1094
    /**
1095
     * @param bool|true $setLoginForm
1096
     */
1097
    public function setLoginForm($setLoginForm = true)
1098
    {
1099
        global $loginFailed;
1100
        $userId = api_get_user_id();
1101
        if (!($userId) || api_is_anonymous($userId)) {
1102
            // Only display if the user isn't logged in.
1103
            $this->assign(
1104
                'login_language_form',
1105
                api_display_language_form(true, true)
1106
            );
1107
            if ($setLoginForm) {
1108
                $this->assign('login_form', $this->displayLoginForm());
1109
1110
                if ($loginFailed) {
1111
                    $this->assign('login_failed', $this::handleLoginFailed());
0 ignored issues
show
Bug Best Practice introduced by
The method Template::handleLoginFailed() is not static, but was called statically. ( Ignorable by Annotation )

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

1111
                    $this->assign('login_failed', $this::/** @scrutinizer ignore-call */ handleLoginFailed());
Loading history...
1112
                }
1113
            }
1114
        }
1115
    }
1116
1117
    /**
1118
     * @return string
1119
     */
1120
    public function handleLoginFailed()
1121
    {
1122
        $message = get_lang('InvalidId');
1123
1124
        if (!isset($_GET['error'])) {
1125
            if (api_is_self_registration_allowed()) {
1126
                $message = get_lang('InvalidForSelfRegistration');
1127
            }
1128
        } else {
1129
            switch ($_GET['error']) {
1130
                case '':
1131
                    if (api_is_self_registration_allowed()) {
1132
                        $message = get_lang('InvalidForSelfRegistration');
1133
                    }
1134
                    break;
1135
                case 'account_expired':
1136
                    $message = get_lang('AccountExpired');
1137
                    break;
1138
                case 'account_inactive':
1139
                    $message = get_lang('AccountInactive');
1140
1141
                    if (api_get_setting('allow_registration') === 'confirmation') {
1142
                        $message = get_lang('AccountNotConfirmed').PHP_EOL;
1143
                        $message .= Display::url(
1144
                            get_lang('ReSendConfirmationMail'),
1145
                            api_get_path(WEB_PATH).'main/auth/resend_confirmation_mail.php',
1146
                            ['class' => 'alert-link']
1147
                        );
1148
                    }
1149
                    break;
1150
                case 'user_password_incorrect':
1151
                    $message = get_lang('InvalidId');
1152
                    break;
1153
                case 'access_url_inactive':
1154
                    $message = get_lang('AccountURLInactive');
1155
                    break;
1156
                case 'wrong_captcha':
1157
                    $message = get_lang('TheTextYouEnteredDoesNotMatchThePicture');
1158
                    break;
1159
                case 'blocked_by_captcha':
1160
                    $message = get_lang('AccountBlockedByCaptcha');
1161
                    break;
1162
                case 'multiple_connection_not_allowed':
1163
                    $message = get_lang('MultipleConnectionsAreNotAllow');
1164
                    break;
1165
                case 'unrecognize_sso_origin':
1166
                    //$message = get_lang('SSOError');
1167
                    break;
1168
            }
1169
        }
1170
1171
        return Display::return_message($message, 'error', false);
1172
    }
1173
1174
    /**
1175
     * @return string
1176
     */
1177
    public function displayLoginForm()
1178
    {
1179
        $form = new FormValidator(
1180
            'form-login',
1181
            'POST',
1182
            api_get_path(WEB_PUBLIC_PATH).'login_check',
1183
            null,
1184
            null,
1185
            FormValidator::LAYOUT_BOX_NO_LABEL
1186
        );
1187
        $params = [
1188
            'id' => '_username',
1189
            'autofocus' => 'autofocus',
1190
            'icon' => 'user fa-fw',
1191
            'placeholder' => get_lang('UserName'),
1192
        ];
1193
        $browserAutoCapitalize = false;
1194
        // Avoid showing the autocapitalize option if the browser doesn't
1195
        // support it: this attribute is against the HTML5 standard
1196
        if (api_browser_support('autocapitalize')) {
1197
            $browserAutoCapitalize = false;
1198
            $params['autocapitalize'] = 'none';
1199
        }
1200
        $form->addText(
1201
            '_username',
1202
            get_lang('UserName'),
1203
            true,
1204
            $params
1205
        );
1206
        $params = [
1207
            'id' => '_password',
1208
            'icon' => 'lock fa-fw',
1209
            'placeholder' => get_lang('Pass'),
1210
        ];
1211
        if ($browserAutoCapitalize) {
1212
            $params['autocapitalize'] = 'none';
1213
        }
1214
        $form->addElement(
1215
            'password',
1216
            '_password',
1217
            get_lang('Pass'),
1218
            $params
1219
        );
1220
1221
        $token = Chamilo\CoreBundle\Framework\Container::$container->get('security.csrf.token_manager')->getToken('authenticate');
1222
        $form->addHidden('_csrf_token', $token->getValue());
1223
1224
        // Captcha
1225
        $captcha = api_get_setting('allow_captcha');
1226
        $allowCaptcha = $captcha === 'true';
1227
1228
        if ($allowCaptcha) {
1229
            $useCaptcha = isset($_SESSION['loginFailed']) ? $_SESSION['loginFailed'] : null;
1230
            if ($useCaptcha) {
1231
                $ajax = api_get_path(WEB_AJAX_PATH).'form.ajax.php?a=get_captcha';
1232
                $options = [
1233
                    'width' => 250,
1234
                    'height' => 90,
1235
                    'callback' => $ajax.'&var='.basename(__FILE__, '.php'),
1236
                    'sessionVar' => basename(__FILE__, '.php'),
1237
                    'imageOptions' => [
1238
                        'font_size' => 20,
1239
                        'font_path' => api_get_path(SYS_FONTS_PATH).'opensans/',
1240
                        'font_file' => 'OpenSans-Regular.ttf',
1241
                        //'output' => 'gif'
1242
                    ],
1243
                ];
1244
1245
                // Minimum options using all defaults (including defaults for Image_Text):
1246
                //$options = array('callback' => 'qfcaptcha_image.php');
1247
                $captcha_question = $form->addElement('CAPTCHA_Image', 'captcha_question', '', $options);
1248
                $form->addHtml(get_lang('ClickOnTheImageForANewOne'));
1249
1250
                $form->addElement(
1251
                    'text',
1252
                    'captcha',
1253
                    get_lang('EnterTheLettersYouSee')
1254
                );
1255
                $form->addRule(
1256
                    'captcha',
1257
                    get_lang('EnterTheCharactersYouReadInTheImage'),
1258
                    'required',
1259
                    null,
1260
                    'client'
1261
                );
1262
                $form->addRule(
1263
                    'captcha',
1264
                    get_lang('TheTextYouEnteredDoesNotMatchThePicture'),
1265
                    'CAPTCHA',
1266
                    $captcha_question
1267
                );
1268
            }
1269
        }
1270
1271
        $form->addButton(
1272
            'submitAuth',
1273
            get_lang('LoginEnter'),
1274
            null,
1275
            'primary',
1276
            null,
1277
            'btn-block'
1278
        );
1279
1280
        $html = $form->returnForm();
1281
        if (api_get_setting('openid_authentication') == 'true') {
1282
            include_once api_get_path(SYS_CODE_PATH).'auth/openid/login.php';
1283
            $html .= '<div>'.openid_form().'</div>';
0 ignored issues
show
Bug introduced by
The function openid_form was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1283
            $html .= '<div>'./** @scrutinizer ignore-call */ openid_form().'</div>';
Loading history...
1284
        }
1285
1286
        return $html;
1287
    }
1288
1289
    /**
1290
     * Returns the tutors names for the current course in session
1291
     * Function to use in Twig templates.
1292
     *
1293
     * @return string
1294
     */
1295
    public static function returnTutorsNames()
1296
    {
1297
        $em = Database::getManager();
1298
        $tutors = $em
1299
            ->createQuery('
1300
                SELECT u FROM ChamiloUserBundle:User u
1301
                INNER JOIN ChamiloCoreBundle:SessionRelCourseRelUser scu WITH u.id = scu.user
1302
                WHERE scu.status = :teacher_status AND scu.session = :session AND scu.course = :course
1303
            ')
1304
            ->setParameters([
1305
                'teacher_status' => SessionRelCourseRelUser::STATUS_COURSE_COACH,
1306
                'session' => api_get_session_id(),
1307
                'course' => api_get_course_int_id(),
1308
            ])
1309
            ->getResult();
1310
1311
        $names = [];
1312
1313
        /** @var User $tutor */
1314
        foreach ($tutors as $tutor) {
1315
            $names[] = $tutor->getCompleteName();
1316
        }
1317
1318
        return implode(CourseManager::USER_SEPARATOR, $names);
1319
    }
1320
1321
    /*s
1322
     * Returns the teachers name for the current course
1323
     * Function to use in Twig templates
1324
     * @return string
1325
     */
1326
    public static function returnTeachersNames()
1327
    {
1328
        $em = Database::getManager();
1329
        $teachers = $em
1330
            ->createQuery('
1331
                SELECT u FROM ChamiloUserBundle:User u
1332
                INNER JOIN ChamiloCoreBundle:CourseRelUser cu WITH u.id = cu.user
1333
                WHERE cu.status = :teacher_status AND cu.course = :course
1334
            ')
1335
            ->setParameters([
1336
                'teacher_status' => User::COURSE_MANAGER,
1337
                'course' => api_get_course_int_id(),
1338
            ])
1339
            ->getResult();
1340
1341
        $names = [];
1342
1343
        /** @var User $teacher */
1344
        foreach ($teachers as $teacher) {
1345
            $names[] = $teacher->getCompleteName();
1346
        }
1347
1348
        return implode(CourseManager::USER_SEPARATOR, $names);
1349
    }
1350
1351
    /**
1352
     * @param int $code
1353
     */
1354
    public function setResponseCode($code)
1355
    {
1356
        $this->responseCode = $code;
1357
    }
1358
1359
    /**
1360
     * @param string $code
1361
     */
1362
    public function getResponseCode()
1363
    {
1364
        return $this->responseCode;
1365
    }
1366
1367
    /**
1368
     * Prepare the _c array for template files. The _c array contains
1369
     * information about the current course.
1370
     */
1371
    private function set_course_parameters()
1372
    {
1373
        //Setting course id
1374
        $course = api_get_course_info();
1375
        if (empty($course)) {
1376
            $this->assign('course_is_set', false);
1377
1378
            return;
1379
        }
1380
        $this->assign('course_is_set', true);
1381
        $this->course_id = $course['id'];
1382
        $_c = [
1383
            'id' => $course['real_id'],
1384
            'code' => $course['code'],
1385
            'title' => $course['name'],
1386
            'visibility' => $course['visibility'],
1387
            'language' => $course['language'],
1388
            'directory' => $course['directory'],
1389
            'session_id' => api_get_session_id(),
1390
            'user_is_teacher' => api_is_course_admin(),
1391
            'student_view' => (!empty($_GET['isStudentView']) && $_GET['isStudentView'] == 'true'),
1392
        ];
1393
        $this->assign('course_code', $course['code']);
1394
        $this->assign('_c', $_c);
1395
    }
1396
1397
    /**
1398
     * Prepare the _u array for template files. The _u array contains
1399
     * information about the current user, as returned by
1400
     * api_get_user_info().
1401
     */
1402
    private function set_user_parameters()
1403
    {
1404
        $user_info = [];
1405
        $user_info['logged'] = 0;
1406
        $this->user_is_logged_in = false;
1407
        if (api_user_is_login()) {
1408
            $user_info = api_get_user_info(api_get_user_id(), true);
1409
            $user_info['logged'] = 1;
1410
1411
            $user_info['is_admin'] = 0;
1412
            if (api_is_platform_admin()) {
1413
                $user_info['is_admin'] = 1;
1414
            }
1415
1416
            $user_info['messages_count'] = MessageManager::getCountNewMessages();
1417
            $this->user_is_logged_in = true;
1418
        }
1419
        // Setting the $_u array that could be use in any template
1420
        $this->assign('_u', $user_info);
1421
    }
1422
1423
    /**
1424
     * Get an array of all the web paths available (e.g. 'web' => 'https://my.chamilo.site/').
1425
     *
1426
     * @return array
1427
     */
1428
    private function getWebPaths()
1429
    {
1430
        return [
1431
            'web' => api_get_path(WEB_PATH),
1432
            'web_url' => api_get_web_url(),
1433
            'web_relative' => api_get_path(REL_PATH),
1434
            'web_course' => api_get_path(WEB_COURSE_PATH),
1435
            'web_main' => api_get_path(WEB_CODE_PATH),
1436
            'web_css' => api_get_path(WEB_CSS_PATH),
1437
            'web_css_theme' => api_get_path(WEB_CSS_PATH).$this->themeDir,
1438
            'web_ajax' => api_get_path(WEB_AJAX_PATH),
1439
            'web_img' => api_get_path(WEB_IMG_PATH),
1440
            'web_plugin' => api_get_path(WEB_PLUGIN_PATH),
1441
            'web_lib' => api_get_path(WEB_LIBRARY_PATH),
1442
            'web_upload' => api_get_path(WEB_UPLOAD_PATH),
1443
            'web_self' => api_get_self(),
1444
            'web_query_vars' => api_htmlentities($_SERVER['QUERY_STRING']),
1445
            'web_self_query_vars' => api_htmlentities($_SERVER['REQUEST_URI']),
1446
            'web_cid_query' => api_get_cidreq(),
1447
        ];
1448
    }
1449
1450
    /**
1451
     * Set header parameters.
1452
     *
1453
     * @param bool $sendHeaders send headers
1454
     */
1455
    private function set_header_parameters($sendHeaders)
1456
    {
1457
        global $httpHeadXtra, $interbreadcrumb, $language_file, $_configuration, $this_section;
1458
        $_course = api_get_course_info();
1459
        $nameTools = $this->title;
1460
        $navigation = return_navigation_array();
1461
        $this->menu_navigation = $navigation['menu_navigation'];
1462
1463
        $this->assign('system_charset', api_get_system_encoding());
1464
1465
        if (isset($httpHeadXtra) && $httpHeadXtra) {
1466
            foreach ($httpHeadXtra as &$thisHttpHead) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
1467
                //header($thisHttpHead);
1468
            }
1469
        }
1470
1471
        $this->assign(
1472
            'online_button',
1473
            Display::return_icon('statusonline.png', null, [], ICON_SIZE_ATOM)
1474
        );
1475
        $this->assign(
1476
            'offline_button',
1477
            Display::return_icon('statusoffline.png', null, [], ICON_SIZE_ATOM)
1478
        );
1479
1480
        // Get language iso-code for this page - ignore errors
1481
        $this->assign('document_language', api_get_language_isocode());
1482
1483
        $course_title = isset($_course['name']) ? $_course['name'] : null;
1484
1485
        $title_list = [];
1486
1487
        $title_list[] = api_get_setting('Institution');
1488
        $title_list[] = api_get_setting('siteName');
1489
1490
        if (!empty($course_title)) {
1491
            $title_list[] = $course_title;
1492
        }
1493
        if ($nameTools != '') {
1494
            $title_list[] = $nameTools;
1495
        }
1496
1497
        $title_string = '';
1498
        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...
1499
            $title_string .= $title_list[$i];
1500
            if (isset($title_list[$i + 1])) {
1501
                $item = trim($title_list[$i + 1]);
1502
                if (!empty($item)) {
1503
                    $title_string .= ' - ';
1504
                }
1505
            }
1506
        }
1507
1508
        $this->assign('title_string', $title_string);
1509
1510
        // Setting the theme and CSS files
1511
        $this->setCssFiles();
1512
        $this->set_js_files();
1513
        $this->setCssCustomFiles();
1514
1515
        $browser = api_browser_support('check_browser');
1516
        if ($browser[0] == 'Internet Explorer' && $browser[1] >= '11') {
1517
            $browser_head = '<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />';
1518
            $this->assign('browser_specific_head', $browser_head);
1519
        }
1520
1521
        // Implementation of prefetch.
1522
        // See http://cdn.chamilo.org/main/img/online.png for details
1523
        $prefetch = '';
1524
        if (!empty($_configuration['cdn_enable'])) {
1525
            $prefetch .= '<meta http-equiv="x-dns-prefetch-control" content="on">';
1526
            foreach ($_configuration['cdn'] as $host => $exts) {
1527
                $prefetch .= '<link rel="dns-prefetch" href="'.$host.'">';
1528
            }
1529
        }
1530
1531
        $this->assign('prefetch', $prefetch);
1532
        $this->assign('text_direction', api_get_text_direction());
1533
        $this->assign('section_name', 'section-'.$this_section);
1534
        $this->assign('favico', $this->getPortalIcon($this->theme));
1535
        $this->setHelp();
1536
1537
        //@todo move this in the template
1538
        $rightFloatMenu = '';
1539
        $iconBug = Display::return_icon(
1540
            'bug.png',
1541
            get_lang('ReportABug'),
1542
            [],
1543
            ICON_SIZE_LARGE
1544
        );
1545
        if (api_get_setting('show_link_bug_notification') == 'true' && $this->user_is_logged_in) {
1546
            $rightFloatMenu = '<div class="report">
1547
		        <a href="https://github.com/chamilo/chamilo-lms/wiki/How-to-report-issues" target="_blank">
1548
                    '.$iconBug.'
1549
                </a>
1550
		        </div>';
1551
        }
1552
1553
        if (api_get_setting('show_link_ticket_notification') == 'true' && $this->user_is_logged_in) {
1554
            // by default is project_id = 1
1555
            $iconTicket = Display::return_icon(
1556
                'help.png',
1557
                get_lang('Ticket'),
1558
                [],
1559
                ICON_SIZE_LARGE
1560
            );
1561
            $courseInfo = api_get_course_info();
1562
            $courseParams = '';
1563
            if (!empty($courseInfo)) {
1564
                $courseParams = api_get_cidreq();
1565
            }
1566
            $url = api_get_path(WEB_CODE_PATH).'ticket/tickets.php?project_id=1&'.$courseParams;
1567
            $rightFloatMenu .= '<div class="help">
1568
		        <a href="'.$url.'" target="_blank">
1569
                    '.$iconTicket.'
1570
                </a>
1571
		    </div>';
1572
        }
1573
1574
        $this->assign('bug_notification', $rightFloatMenu);
1575
1576
        $resize = '';
1577
        if (api_get_setting('accessibility_font_resize') == 'true') {
1578
            $resize .= '<div class="resize_font">';
1579
            $resize .= '<div class="btn-group">';
1580
            $resize .= '<a title="'.get_lang('DecreaseFontSize').'" href="#" class="decrease_font btn btn-default"><em class="fa fa-font"></em></a>';
1581
            $resize .= '<a title="'.get_lang('ResetFontSize').'" href="#" class="reset_font btn btn-default"><em class="fa fa-font"></em></a>';
1582
            $resize .= '<a title="'.get_lang('IncreaseFontSize').'" href="#" class="increase_font btn btn-default"><em class="fa fa-font"></em></a>';
1583
            $resize .= '</div>';
1584
            $resize .= '</div>';
1585
        }
1586
        $this->assign('accessibility', $resize);
1587
1588
        // Preparing values for the menu
1589
1590
        // Logout link
1591
        $hideLogout = api_get_setting('hide_logout_button');
1592
        if ($hideLogout === 'true') {
1593
            $this->assign('logout_link', null);
1594
        } else {
1595
            $this->assign('logout_link', api_get_path(WEB_PATH).'index.php?logout=logout&uid='.api_get_user_id());
1596
        }
1597
1598
        // Profile link
1599
        if (api_get_setting('allow_social_tool') == 'true') {
1600
            $profile_url = api_get_path(WEB_CODE_PATH).'social/home.php';
1601
        } else {
1602
            $profile_url = api_get_path(WEB_CODE_PATH).'auth/profile.php';
1603
        }
1604
1605
        $this->assign('profile_url', $profile_url);
1606
1607
        //Message link
1608
        $message_link = null;
1609
        $message_url = null;
1610
        if (api_get_setting('allow_message_tool') == 'true') {
1611
            $message_url = api_get_path(WEB_CODE_PATH).'messages/inbox.php';
1612
            $message_link = '<a href="'.api_get_path(WEB_CODE_PATH).'messages/inbox.php">'.get_lang('Inbox').'</a>';
1613
        }
1614
        $this->assign('message_link', $message_link);
1615
        $this->assign('message_url', $message_url);
1616
1617
        // Certificate Link
1618
1619
        $allow = api_get_configuration_value('hide_my_certificate_link');
1620
        if ($allow === false) {
1621
            $certificateUrl = api_get_path(WEB_CODE_PATH).'gradebook/my_certificates.php';
1622
            $certificateLink = Display::url(
1623
                get_lang('MyCertificates'),
1624
                $certificateUrl
1625
            );
1626
            $this->assign('certificate_link', $certificateLink);
1627
            $this->assign('certificate_url', $certificateUrl);
1628
        }
1629
1630
        $institution = api_get_setting('Institution');
1631
        $portal_name = empty($institution) ? api_get_setting('siteName') : $institution;
1632
1633
        $this->assign('portal_name', $portal_name);
1634
1635
        //Menu
1636
        $menu = menuArray();
1637
        $this->assign('menu', $menu);
1638
1639
        $breadcrumb = '';
1640
        // Hide breadcrumb in LP
1641
        if ($this->show_learnpath == false) {
1642
            $breadcrumb = return_breadcrumb(
1643
                $interbreadcrumb,
1644
                $language_file,
1645
                $nameTools
1646
            );
1647
        }
1648
        $this->assign('breadcrumb', $breadcrumb);
1649
1650
        //Extra content
1651
        $extra_header = null;
1652
        if (!api_is_platform_admin()) {
1653
            $extra_header = trim(api_get_setting('header_extra_content'));
1654
        }
1655
        $this->assign('header_extra_content', $extra_header);
1656
1657
        if ($sendHeaders) {
1658
            /*header('Content-Type: text/html; charset='.api_get_system_encoding());
1659
            header(
1660
                'X-Powered-By: '.$_configuration['software_name'].' '.substr($_configuration['system_version'], 0, 1)
1661
            );
1662
            self::addHTTPSecurityHeaders();*/
1663
1664
            $responseCode = $this->getResponseCode();
1665
            if (!empty($responseCode)) {
1666
                switch ($responseCode) {
1667
                    case '404':
1668
                        header("HTTP/1.0 404 Not Found");
1669
                        break;
1670
                }
1671
            }
1672
        }
1673
1674
        $socialMeta = '';
1675
        $metaTitle = api_get_setting('meta_title');
1676
        if (!empty($metaTitle)) {
1677
            $socialMeta .= '<meta name="twitter:card" content="summary" />'."\n";
1678
            $metaSite = api_get_setting('meta_twitter_site');
1679
            if (!empty($metaSite)) {
1680
                $socialMeta .= '<meta name="twitter:site" content="'.$metaSite.'" />'."\n";
1681
                $metaCreator = api_get_setting('meta_twitter_creator');
1682
                if (!empty($metaCreator)) {
1683
                    $socialMeta .= '<meta name="twitter:creator" content="'.$metaCreator.'" />'."\n";
1684
                }
1685
            }
1686
1687
            // The user badge page emits its own meta tags, so if this is
1688
            // enabled, ignore the global ones
1689
            $userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
1690
            $skillId = isset($_GET['skill']) ? intval($_GET['skill']) : 0;
1691
1692
            if (!$userId && !$skillId) {
1693
                // no combination of user and skill ID has been defined,
1694
                // so print the normal OpenGraph meta tags
1695
                $socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n";
1696
                $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n";
1697
1698
                $metaDescription = api_get_setting('meta_description');
1699
                if (!empty($metaDescription)) {
1700
                    $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
1701
                }
1702
1703
                $metaImage = api_get_setting('meta_image_path');
1704
                if (!empty($metaImage)) {
1705
                    if (is_file(api_get_path(SYS_PATH).$metaImage)) {
1706
                        $path = api_get_path(WEB_PATH).$metaImage;
1707
                        $socialMeta .= '<meta property="og:image" content="'.$path.'" />'."\n";
1708
                    }
1709
                }
1710
            }
1711
        }
1712
1713
        $this->assign('social_meta', $socialMeta);
1714
    }
1715
1716
    /**
1717
     * Set footer parameters.
1718
     */
1719
    private function set_footer_parameters()
1720
    {
1721
        if (api_get_setting('show_administrator_data') === 'true') {
1722
            $firstName = api_get_setting('administratorName');
1723
            $lastName = api_get_setting('administratorSurname');
1724
1725
            if (!empty($firstName) && !empty($lastName)) {
1726
                $name = api_get_person_name($firstName, $lastName);
1727
            } else {
1728
                $name = $lastName;
1729
                if (empty($lastName)) {
1730
                    $name = $firstName;
1731
                }
1732
            }
1733
1734
            $adminName = '';
1735
            // Administrator name
1736
            if (!empty($name)) {
1737
                $adminName = get_lang('Manager').' : '.
1738
                    Display::encrypted_mailto_link(
1739
                        api_get_setting('emailAdministrator'),
1740
                        $name
1741
                    );
1742
            }
1743
            $this->assign('administrator_name', $adminName);
1744
        }
1745
1746
        // Loading footer extra content
1747
        if (!api_is_platform_admin()) {
1748
            $extra_footer = trim(api_get_setting('footer_extra_content'));
1749
            if (!empty($extra_footer)) {
1750
                $this->assign('footer_extra_content', $extra_footer);
1751
            }
1752
        }
1753
1754
        // Tutor name
1755
        if (api_get_setting('show_tutor_data') == 'true') {
1756
            // Course manager
1757
            $courseId = api_get_course_int_id();
1758
            $id_session = api_get_session_id();
1759
            if (!empty($courseId)) {
1760
                $tutor_data = '';
1761
                if ($id_session != 0) {
1762
                    $coachs_email = CourseManager::get_email_of_tutor_to_session(
1763
                        $id_session,
1764
                        $courseId
1765
                    );
1766
                    $email_link = [];
1767
                    foreach ($coachs_email as $coach) {
1768
                        $email_link[] = Display::encrypted_mailto_link($coach['email'], $coach['complete_name']);
1769
                    }
1770
                    if (count($coachs_email) > 1) {
1771
                        $tutor_data .= get_lang('Coachs').' : ';
1772
                        $tutor_data .= array_to_string($email_link, CourseManager::USER_SEPARATOR);
1773
                    } elseif (count($coachs_email) == 1) {
1774
                        $tutor_data .= get_lang('Coach').' : ';
1775
                        $tutor_data .= array_to_string($email_link, CourseManager::USER_SEPARATOR);
1776
                    } elseif (count($coachs_email) == 0) {
1777
                        $tutor_data .= '';
1778
                    }
1779
                }
1780
                $this->assign('session_teachers', $tutor_data);
1781
            }
1782
        }
1783
1784
        if (api_get_setting('show_teacher_data') == 'true') {
1785
            // course manager
1786
            $courseId = api_get_course_int_id();
1787
            if (!empty($courseId)) {
1788
                $teacher_data = '';
1789
                $mail = CourseManager::get_emails_of_tutors_to_course($courseId);
1790
                if (!empty($mail)) {
1791
                    $teachers_parsed = [];
1792
                    foreach ($mail as $value) {
1793
                        foreach ($value as $email => $name) {
1794
                            $teachers_parsed[] = Display::encrypted_mailto_link($email, $name);
1795
                        }
1796
                    }
1797
                    $label = get_lang('Teacher');
1798
                    if (count($mail) > 1) {
1799
                        $label = get_lang('Teachers');
1800
                    }
1801
                    $teacher_data .= $label.' : '.array_to_string($teachers_parsed, CourseManager::USER_SEPARATOR);
1802
                }
1803
                $this->assign('teachers', $teacher_data);
1804
            }
1805
        }
1806
    }
1807
1808
    /**
1809
     * Set administrator variables.
1810
     */
1811
    private function setAdministratorParams()
1812
    {
1813
        $_admin = [
1814
            'email' => api_get_setting('emailAdministrator'),
1815
            'surname' => api_get_setting('administratorSurname'),
1816
            'name' => api_get_setting('administratorName'),
1817
            'telephone' => api_get_setting('administratorTelephone'),
1818
        ];
1819
1820
        $this->assign('_admin', $_admin);
1821
    }
1822
1823
    /**
1824
     * Manage specific HTTP headers security.
1825
     */
1826
    private function addHTTPSecurityHeaders()
0 ignored issues
show
Unused Code introduced by
The method addHTTPSecurityHeaders() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
1827
    {
1828
        // Implementation of HTTP headers security, as suggested and checked
1829
        // by https://securityheaders.io/
1830
        // Enable these settings in configuration.php to use them on your site
1831
        // Strict-Transport-Security
1832
        $setting = api_get_configuration_value('security_strict_transport');
1833
        if (!empty($setting)) {
1834
            header('Strict-Transport-Security: '.$setting);
1835
        }
1836
        // Content-Security-Policy
1837
        $setting = api_get_configuration_value('security_content_policy');
1838
        if (!empty($setting)) {
1839
            header('Content-Security-Policy: '.$setting);
1840
        }
1841
        $setting = api_get_configuration_value('security_content_policy_report_only');
1842
        if (!empty($setting)) {
1843
            header('Content-Security-Policy-Report-Only: '.$setting);
1844
        }
1845
        // Public-Key-Pins
1846
        $setting = api_get_configuration_value('security_public_key_pins');
1847
        if (!empty($setting)) {
1848
            header('Public-Key-Pins: '.$setting);
1849
        }
1850
        $setting = api_get_configuration_value('security_public_key_pins_report_only');
1851
        if (!empty($setting)) {
1852
            header('Public-Key-Pins-Report-Only: '.$setting);
1853
        }
1854
        // X-Frame-Options
1855
        $setting = api_get_configuration_value('security_x_frame_options');
1856
        if (!empty($setting)) {
1857
            header('X-Frame-Options: '.$setting);
1858
        }
1859
        // X-XSS-Protection
1860
        $setting = api_get_configuration_value('security_xss_protection');
1861
        if (!empty($setting)) {
1862
            header('X-XSS-Protection: '.$setting);
1863
        }
1864
        // X-Content-Type-Options
1865
        $setting = api_get_configuration_value('security_x_content_type_options');
1866
        if (!empty($setting)) {
1867
            header('X-Content-Type-Options: '.$setting);
1868
        }
1869
        // Referrer-Policy
1870
        $setting = api_get_configuration_value('security_referrer_policy');
1871
        if (!empty($setting)) {
1872
            header('Referrer-Policy: '.$setting);
1873
        }
1874
        // end of HTTP headers security block
1875
    }
1876
}
1877