Completed
Push — master ( ae5621...ef667c )
by Julito
13:23
created

Template::setCssFiles()   C

Complexity

Conditions 8
Paths 32

Size

Total Lines 50
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 30
nc 32
nop 0
dl 0
loc 50
rs 6.3636
c 0
b 0
f 0
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
8
/**
9
 * Class Template.
10
 *
11
 * @author Julio Montoya <[email protected]>
12
 *
13
 * @todo better organization of the class, methods and variables
14
 */
15
class Template
16
{
17
    /**
18
     * The Template folder name see main/template.
19
     *
20
     * @var string
21
     */
22
    public $templateFolder = 'default';
23
24
    /**
25
     * The theme that will be used: chamilo, public_admin, chamilo_red, etc
26
     * This variable is set from the database.
27
     *
28
     * @var string
29
     */
30
    public $theme = '';
31
32
    /**
33
     * @var string
34
     */
35
    public $preview_theme = '';
36
    public $title = null;
37
    public $show_header;
38
    public $show_footer;
39
    public $help;
40
    public $menu_navigation = []; //Used in the userportal.lib.php function: return_navigation_course_links()
41
    public $show_learnpath = false; // This is a learnpath section or not?
42
    public $plugin = null;
43
    public $course_id = null;
44
    public $user_is_logged_in = false;
45
    public $twig = null;
46
47
    /* Loads chamilo plugins */
48
    public $load_plugins = false;
49
    public $params = [];
50
    public $force_plugin_load = false;
51
    public $responseCode = 0;
52
    private $themeDir;
53
54
    /**
55
     * @param string $title
56
     * @param bool   $show_header
57
     * @param bool   $show_footer
58
     * @param bool   $show_learnpath
59
     * @param bool   $hide_global_chat
60
     * @param bool   $load_plugins
61
     * @param int    $responseCode
62
     * @param bool   $sendHeaders      send http headers or not
63
     */
64
    public function __construct(
65
        $title = '',
66
        $show_header = true,
67
        $show_footer = true,
68
        $show_learnpath = false,
69
        $hide_global_chat = false,
70
        $load_plugins = true,
71
        $sendHeaders = true,
72
        $responseCode = 0
73
    ) {
74
        // Page title
75
        $this->title = $title;
76
        $this->show_learnpath = $show_learnpath;
77
        $this->setResponseCode($responseCode);
78
79
        if (empty($this->show_learnpath)) {
80
            $origin = api_get_origin();
81
            if ($origin === 'learnpath') {
82
                $this->show_learnpath = true;
83
                $show_footer = false;
84
                $show_header = false;
85
            }
86
        }
87
        $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...
88
        $this->load_plugins = $load_plugins;
89
90
        $template_paths = [
91
            api_get_path(SYS_CODE_PATH).'template/overrides', // user defined templates
92
            api_get_path(SYS_CODE_PATH).'template', //template folder
93
            api_get_path(SYS_PLUGIN_PATH), // plugin folder
94
        ];
95
96
        $urlId = api_get_current_access_url_id();
97
98
        $cache_folder = api_get_path(SYS_ARCHIVE_PATH).'twig/'.$urlId.'/';
99
100
        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...
101
            //mkdir($cache_folder, api_get_permissions_for_new_directories(), true);
102
        }
103
104
        $loader = new Twig_Loader_Filesystem($template_paths);
105
106
        $isTestMode = api_get_setting('server_type') === 'test';
107
108
        //Setting Twig options depending on the server see http://twig.sensiolabs.org/doc/api.html#environment-options
109
        if ($isTestMode) {
110
            $options = [
111
                //'cache' => api_get_path(SYS_ARCHIVE_PATH), //path to the cache folder
112
                'autoescape' => false,
113
                'debug' => true,
114
                'auto_reload' => true,
115
                'optimizations' => 0,
116
                // turn on optimizations with -1
117
                'strict_variables' => false,
118
                //If set to false, Twig will silently ignore invalid variables
119
            ];
120
        } else {
121
            $options = [
122
                'cache' => $cache_folder,
123
                //path to the cache folder
124
                'autoescape' => false,
125
                'debug' => false,
126
                'auto_reload' => false,
127
                'optimizations' => -1,
128
                // turn on optimizations with -1
129
                'strict_variables' => false,
130
                //If set to false, Twig will silently ignore invalid variables
131
            ];
132
        }
133
134
        $this->twig = new Twig_Environment($loader, $options);
135
136
        if ($isTestMode) {
137
            $this->twig->addExtension(new Twig_Extension_Debug());
138
        }
139
140
        // Twig filters setup
141
        $filters = [
142
            'get_plugin_lang',
143
            'get_lang',
144
            'api_get_path',
145
            'api_get_local_time',
146
            'api_convert_and_format_date',
147
            'api_is_allowed_to_edit',
148
            'api_get_user_info',
149
            'api_get_configuration_value',
150
            'api_get_setting',
151
            [
152
                'name' => 'return_message',
153
                'callable' => 'Display::return_message_and_translate',
154
            ],
155
            [
156
                'name' => 'display_page_header',
157
                'callable' => 'Display::page_header_and_translate',
158
            ],
159
            [
160
                'name' => 'display_page_subheader',
161
                'callable' => 'Display::page_subheader_and_translate',
162
            ],
163
            [
164
                'name' => 'icon',
165
                'callable' => 'Template::get_icon_path',
166
            ],
167
            [
168
                'name' => 'img',
169
                'callable' => 'Template::get_image',
170
            ],
171
            [
172
                'name' => 'format_date',
173
                'callable' => 'Template::format_date',
174
            ],
175
            [
176
                'name' => 'get_template',
177
                'callable' => 'Template::findTemplateFilePath',
178
            ],
179
        ];
180
181
        foreach ($filters as $filter) {
182
            if (is_array($filter)) {
183
                $this->twig->addFilter(new Twig_SimpleFilter($filter['name'], $filter['callable']));
184
            } else {
185
                $this->twig->addFilter(new Twig_SimpleFilter($filter, $filter));
186
            }
187
        }
188
189
        $functions = [
190
            ['name' => 'get_tutors_names', 'callable' => 'Template::returnTutorsNames'],
191
            ['name' => 'get_teachers_names', 'callable' => 'Template::returnTeachersNames'],
192
        ];
193
194
        foreach ($functions as $function) {
195
            $this->twig->addFunction(new Twig_SimpleFunction($function['name'], $function['callable']));
196
        }
197
198
        // Setting system variables
199
        $this->set_system_parameters();
200
201
        // Setting user variables
202
        $this->set_user_parameters();
203
204
        // Setting course variables
205
        $this->set_course_parameters();
206
207
        // Setting administrator variables
208
        $this->setAdministratorParams();
209
        //$this->setCSSEditor();
210
211
        // Header and footer are showed by default
212
        $this->set_footer($show_footer);
213
        $this->set_header($show_header);
214
215
        $this->set_header_parameters($sendHeaders);
216
        $this->set_footer_parameters();
217
218
        $defaultStyle = api_get_configuration_value('default_template');
219
        if (!empty($defaultStyle)) {
220
            $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...
221
        }
222
223
        $this->assign('template', $this->templateFolder);
224
        $this->assign('locale', api_get_language_isocode());
225
        $this->assign('login_class', null);
226
227
        $allow = api_get_configuration_value('show_language_selector_in_menu');
228
        if ($allow) {
229
            $this->assign('language_form', api_display_language_form());
230
        }
231
232
        // Chamilo plugins
233
        if ($this->show_header) {
234
            if ($this->load_plugins) {
235
                $this->plugin = new AppPlugin();
236
237
                //1. Showing installed plugins in regions
238
                $pluginRegions = $this->plugin->get_plugin_regions();
239
                foreach ($pluginRegions as $region) {
240
                    $this->set_plugin_region($region);
241
                }
242
243
                //2. Loading the course plugin info
244
                global $course_plugin;
245
                if (isset($course_plugin) && !empty($course_plugin) && !empty($this->course_id)) {
246
                    //Load plugin get_langs
247
                    $this->plugin->load_plugin_lang_variables($course_plugin);
248
                }
249
            }
250
        }
251
    }
252
253
    /**
254
     * @param string $image
255
     * @param int    $size
256
     *
257
     * @return string
258
     */
259
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
260
    {
261
        return Display::return_icon($image, '', [], $size, false, true);
262
    }
263
264
    /**
265
     * @param string $image
266
     * @param int    $size
267
     * @param string $name
268
     *
269
     * @return string
270
     */
271
    public static function get_image($image, $size = ICON_SIZE_SMALL, $name = '')
272
    {
273
        return Display::return_icon($image, $name, [], $size);
274
    }
275
276
    /**
277
     * @param string $timestamp
278
     * @param string $format
279
     *
280
     * @return string
281
     */
282
    public static function format_date($timestamp, $format = null)
283
    {
284
        return api_format_date($timestamp, $format);
285
    }
286
287
    /**
288
     * Return the item's url key:.
289
     *
290
     *      c_id=xx&id=xx
291
     *
292
     * @param object $item
293
     *
294
     * @return string
295
     */
296
    public static function key($item)
297
    {
298
        $id = isset($item->id) ? $item->id : null;
299
        $c_id = isset($item->c_id) ? $item->c_id : null;
300
        $result = '';
301
        if ($c_id) {
302
            $result = "c_id=$c_id";
303
        }
304
        if ($id) {
305
            if ($result) {
306
                $result .= "&amp;id=$id";
307
            } else {
308
                $result .= "&amp;id=$id";
309
            }
310
        }
311
312
        return $result;
313
    }
314
315
    /**
316
     * @param string $helpInput
317
     */
318
    public function setHelp($helpInput = null)
319
    {
320
        if (!empty($helpInput)) {
321
            $help = $helpInput;
322
        } else {
323
            $help = $this->help;
324
        }
325
326
        $content = '';
327
        if (api_get_setting('enable_help_link') == 'true') {
328
            if (!empty($help)) {
329
                $help = Security::remove_XSS($help);
330
                $content = '<div class="help">';
331
                $content .= Display::url(
332
                    Display::return_icon('help.large.png', get_lang('Help')),
333
                    api_get_path(WEB_CODE_PATH).'help/help.php?open='.$help,
334
                    [
335
                        'class' => 'ajax',
336
                        'data-title' => get_lang('Help'),
337
                    ]
338
                );
339
                $content .= '</div>';
340
            }
341
        }
342
        $this->assign('help_content', $content);
343
    }
344
345
    /**
346
     * Use template system to parse the actions menu.
347
     *
348
     * @todo finish it!
349
     */
350
    public function set_actions($actions)
351
    {
352
        $action_string = '';
353
        if (!empty($actions)) {
354
            foreach ($actions as $action) {
355
                $action_string .= $action;
356
            }
357
        }
358
        $this->assign('actions', $actions);
359
    }
360
361
    /**
362
     * Shortcut to display a 1 col layout (index.php).
363
     * */
364
    public function display_one_col_template()
365
    {
366
        $tpl = $this->get_template('layout/layout_1_col.html.twig');
367
        echo Container::getTemplating()->render($tpl, $this->params);
368
    }
369
370
    /**
371
     * Shortcut to display a 2 col layout (userportal.php).
372
     */
373
    public function display_two_col_template()
374
    {
375
        $tpl = $this->get_template('layout/layout_2_col.html.twig');
376
        echo Container::getTemplating()->render($tpl, $this->params);
377
    }
378
379
    /**
380
     * Displays an empty template.
381
     */
382
    public function display_blank_template()
383
    {
384
        $tpl = $this->get_template('layout/blank.tpl');
385
        $this->display($tpl);
386
    }
387
388
    /**
389
     * Displays an empty template.
390
     */
391
    public function displayBlankTemplateNoHeader()
392
    {
393
        $tpl = $this->get_template('layout/blank_no_header.tpl');
394
        $this->display($tpl);
395
    }
396
397
    /**
398
     * Displays an empty template.
399
     */
400
    public function display_no_layout_template()
401
    {
402
        $tpl = $this->get_template('layout/no_layout.tpl');
403
        $this->display($tpl);
404
    }
405
406
    /**
407
     * Sets the footer visibility.
408
     *
409
     * @param bool true if we show the footer
410
     */
411
    public function set_footer($status)
412
    {
413
        $this->show_footer = $status;
414
        $this->assign('show_footer', $status);
415
    }
416
417
    /**
418
     * return true if toolbar has to be displayed for user.
419
     *
420
     * @return bool
421
     */
422
    public static function isToolBarDisplayedForUser()
423
    {
424
        //Toolbar
425
        $show_admin_toolbar = api_get_setting('show_admin_toolbar');
426
        $show_toolbar = false;
427
428
        switch ($show_admin_toolbar) {
429
            case 'do_not_show':
430
                break;
431
            case 'show_to_admin':
432
                if (api_is_platform_admin()) {
433
                    $show_toolbar = true;
434
                }
435
                break;
436
            case 'show_to_admin_and_teachers':
437
                if (api_is_platform_admin() || api_is_allowed_to_edit()) {
438
                    $show_toolbar = true;
439
                }
440
                break;
441
            case 'show_to_all':
442
                $show_toolbar = true;
443
                break;
444
        }
445
446
        return $show_toolbar;
447
    }
448
449
    /**
450
     * Sets the header visibility.
451
     *
452
     * @param bool true if we show the header
453
     */
454
    public function set_header($status)
455
    {
456
        $this->show_header = $status;
457
        $this->assign('show_header', $status);
458
459
        $show_toolbar = 0;
460
461
        if (self::isToolBarDisplayedForUser()) {
462
            $show_toolbar = 1;
463
        }
464
465
        $this->assign('show_toolbar', $show_toolbar);
466
467
        //Only if course is available
468
        $show_course_shortcut = null;
469
        $show_course_navigation_menu = null;
470
471
        if (!empty($this->course_id) && $this->user_is_logged_in) {
472
            if (api_get_setting('show_toolshortcuts') != 'false') {
473
                //Course toolbar
474
                $show_course_shortcut = CourseHome::show_navigation_tool_shortcuts();
475
            }
476
            if (api_get_setting('show_navigation_menu') != 'false') {
477
                //Course toolbar
478
                $show_course_navigation_menu = CourseHome::show_navigation_menu();
479
            }
480
        }
481
        $this->assign('show_course_shortcut', $show_course_shortcut);
482
        $this->assign('show_course_navigation_menu', $show_course_navigation_menu);
483
    }
484
485
    /**
486
     * Returns the sub-folder and filename for the given tpl file.
487
     * If template not found in overrides/ or custom template folder, the
488
     * default template will be used.
489
     *
490
     * @param string $name
491
     *
492
     * @return string
493
     */
494
    public function get_template($name)
495
    {
496
        // Check if the tpl file is present in the main/template/overrides/ dir
497
        // Overrides is a special directory meant for temporary template
498
        // customization. It must be taken into account before anything else
499
        $file = api_get_path(SYS_CODE_PATH).'template/overrides/'.$name;
500
        if (is_readable($file)) {
501
            return 'overrides/'.$name;
502
        }
503
        // If a template folder has been manually defined, search for the right
504
        // file, and if not found, go for the same file in the default template
505
        if ($this->templateFolder != 'default') {
506
            // Avoid missing template error, use the default file.
507
            $file = api_get_path(SYS_CODE_PATH).'template/'.$this->templateFolder.'/'.$name;
508
            if (!file_exists($file)) {
509
                return 'default/'.$name;
510
            }
511
        }
512
        $name = str_replace('tpl', 'html.twig', $name);
513
514
        return $this->templateFolder.'/'.$name;
515
    }
516
517
    /**
518
     * Get CSS themes sub-directory.
519
     *
520
     * @param string $theme
521
     *
522
     * @return string with a trailing slash, e.g. 'themes/chamilo_red/'
523
     */
524
    public static function getThemeDir($theme)
525
    {
526
        $themeDir = 'themes/'.$theme.'/';
527
        $virtualTheme = api_get_configuration_value('virtual_css_theme_folder');
528
        if (!empty($virtualTheme)) {
529
            $virtualThemeList = api_get_themes(true);
530
            $isVirtualTheme = in_array($theme, array_keys($virtualThemeList));
531
            if ($isVirtualTheme) {
532
                $themeDir = 'themes/'.$virtualTheme.'/'.$theme.'/';
533
            }
534
        }
535
536
        return $themeDir;
537
    }
538
539
    /**
540
     * Set system parameters from api_get_configuration into _s array for use in TPLs
541
     * Also fills the _p array from getWebPaths().
542
     *
543
     * @uses \self::getWebPaths()
544
     */
545
    public function set_system_parameters()
546
    {
547
        $this->theme = api_get_visual_theme();
548
        if (!empty($this->preview_theme)) {
549
            $this->theme = $this->preview_theme;
550
        }
551
552
        $this->assign('theme', $this->theme);
553
554
        $this->themeDir = self::getThemeDir($this->theme);
555
556
        // Setting app paths/URLs
557
        $this->assign('_p', $this->getWebPaths());
558
559
        // Here we can add system parameters that can be use in any template
560
        $_s = [
561
            'software_name' => api_get_configuration_value('software_name'),
562
            'system_version' => api_get_configuration_value('system_version'),
563
            'site_name' => api_get_setting('siteName'),
564
            'institution' => api_get_setting('Institution'),
565
            'date' => api_format_date('now', DATE_FORMAT_LONG),
566
            'timezone' => api_get_timezone(),
567
            'gamification_mode' => api_get_setting('gamification_mode'),
568
        ];
569
        $this->assign('_s', $_s);
570
    }
571
572
    /**
573
     * Set legacy twig globals in order to be hook in the LegacyListener.php.
574
     *
575
     * @return array
576
     */
577
    public static function getGlobals()
578
    {
579
        $_p = [
580
            'web' => api_get_path(WEB_PATH),
581
            'web_relative' => api_get_path(REL_PATH),
582
            'web_course' => api_get_path(WEB_COURSE_PATH),
583
            'web_main' => api_get_path(WEB_CODE_PATH),
584
            'web_css' => api_get_path(WEB_CSS_PATH),
585
            //'web_css_theme' => api_get_path(WEB_CSS_PATH) . 'themes/' . $this->theme . '/',
586
            'web_ajax' => api_get_path(WEB_AJAX_PATH),
587
            'web_img' => api_get_path(WEB_IMG_PATH),
588
            'web_plugin' => api_get_path(WEB_PLUGIN_PATH),
589
            'web_plugin_asset' => api_get_path(WEB_PLUGIN_ASSET_PATH),
590
            'web_lib' => api_get_path(WEB_LIBRARY_PATH),
591
            'web_upload' => api_get_path(WEB_UPLOAD_PATH),
592
            'web_self' => api_get_self(),
593
            'web_query_vars' => api_htmlentities($_SERVER['QUERY_STRING']),
594
            'web_self_query_vars' => api_htmlentities($_SERVER['REQUEST_URI']),
595
            'web_cid_query' => api_get_cidreq(),
596
        ];
597
598
        $_s = [
599
            'software_name' => api_get_configuration_value('software_name'),
600
            'system_version' => api_get_configuration_value('system_version'),
601
            'site_name' => api_get_setting('siteName'),
602
            'institution' => api_get_setting('Institution'),
603
            'date' => api_format_date('now', DATE_FORMAT_LONG),
604
            'timezone' => api_get_timezone(),
605
            'gamification_mode' => api_get_setting('gamification_mode'),
606
        ];
607
608
        //$user_info = api_get_user_info();
609
610
        return [
611
            '_p' => $_p,
612
            '_s' => $_s,
613
            //       '_u' => $user_info,
614
            'template' => 'default', // @todo setup template folder in config.yml;
615
        ];
616
    }
617
618
    /**
619
     * Set theme, include mainstream CSS files.
620
     *
621
     * @see setCssCustomFiles() for additional CSS sheets
622
     */
623
    public function setCssFiles()
624
    {
625
        global $disable_js_and_css_files;
626
        $css = [];
627
628
        // Default CSS Bootstrap
629
        $bowerCSSFiles = [
630
            'fontawesome/css/font-awesome.min.css',
631
            'jquery-ui/themes/smoothness/theme.css',
632
            'jquery-ui/themes/smoothness/jquery-ui.min.css',
633
            'mediaelement/build/mediaelementplayer.min.css',
634
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.css',
635
            'bootstrap/dist/css/bootstrap.min.css',
636
            'jquery.scrollbar/jquery.scrollbar.css',
637
            'bootstrap-daterangepicker/daterangepicker.css',
638
            'bootstrap-select/dist/css/bootstrap-select.min.css',
639
            'select2/dist/css/select2.min.css',
640
            'flag-icon-css/css/flag-icon.min.css',
641
        ];
642
643
        $features = api_get_configuration_value('video_features');
644
        $defaultFeatures = ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'];
645
646
        if (!empty($features) && isset($features['features'])) {
647
            foreach ($features['features'] as $feature) {
648
                $bowerCSSFiles[] = "mediaelement/plugins/$feature/$feature.css";
649
                $defaultFeatures[] = $feature;
650
            }
651
        }
652
653
        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...
654
            //$css[] = api_get_path(WEB_PUBLIC_PATH).'assets/'.$file;
655
        }
656
657
        //$css[] = api_get_path(WEB_LIBRARY_PATH).'javascript/chosen/chosen.css';
658
659
        if (api_is_global_chat_enabled()) {
660
            $css[] = api_get_path(WEB_LIBRARY_PATH).'javascript/chat/css/chat.css';
661
        }
662
        $css_file_to_string = '';
663
        foreach ($css as $file) {
664
            $css_file_to_string .= api_get_css($file);
665
        }
666
667
        if (!$disable_js_and_css_files) {
668
            $this->assign('css_static_file_to_string', $css_file_to_string);
669
        }
670
671
        $defaultFeatures = implode("','", $defaultFeatures);
672
        $this->assign('video_features', $defaultFeatures);
673
    }
674
675
    /**
676
     * Sets the "styles" menu in ckEditor.
677
     *
678
     * Reads css/themes/xxx/editor.css if exists and shows it in the menu, otherwise it
679
     * will take the default web/editor.css file
680
     */
681
    public function setStyleMenuInCkEditor()
682
    {
683
        $cssEditor = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'editor.css');
684
        if (is_file(api_get_path(SYS_CSS_PATH).$this->themeDir.'editor.css')) {
685
            $cssEditor = api_get_path(WEB_CSS_PATH).$this->themeDir.'editor.css';
686
        }
687
        $this->assign('css_editor', $cssEditor);
688
    }
689
690
    /**
691
     * Prepare custom CSS to be added at the very end of the <head> section.
692
     *
693
     * @see setCssFiles() for the mainstream CSS files
694
     */
695
    public function setCssCustomFiles()
696
    {
697
        global $disable_js_and_css_files;
698
        // chamilo CSS
699
        //$css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'../chamilo.css');
700
701
        // Base CSS
702
        //$css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'base.css');
703
        $css = [];
704
        if ($this->show_learnpath) {
705
            $css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).'scorm.css');
706
            if (is_file(api_get_path(SYS_CSS_PATH).$this->themeDir.'learnpath.css')) {
707
                $css[] = api_get_path(WEB_CSS_PATH).$this->themeDir.'learnpath.css';
708
            }
709
        }
710
711
        //$css[] = api_get_cdn_path(api_get_path(WEB_CSS_PATH).$this->themeDir.'default.css');
712
        $css_file_to_string = '';
713
        foreach ($css as $file) {
714
            $css_file_to_string .= api_get_css($file);
715
        }
716
717
        // @todo move this somewhere else. Special fix when using tablets in order to see the text near icons
718
        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...
719
            //hack in order to fix the actions buttons
720
            $css_file_to_string .= '<style>
721
                .td_actions a {
722
                    float:left;
723
                    width:100%;
724
                }
725
                .forum_message_left a {
726
                    float:left;
727
                    width:100%;
728
                }
729
                </style>';
730
        }
731
732
        $navigator_info = api_get_navigator();
733
        if ($navigator_info['name'] == 'Internet Explorer' && $navigator_info['version'] == '6') {
734
            $css_file_to_string .= 'img, div { behavior: url('.api_get_path(WEB_LIBRARY_PATH).'javascript/iepngfix/iepngfix.htc) } '."\n";
735
        }
736
737
        if (!$disable_js_and_css_files) {
738
            $this->assign('css_custom_file_to_string', $css_file_to_string);
739
740
            $style_print = '';
741
            if (is_readable(api_get_path(SYS_CSS_PATH).$this->theme.'/print.css')) {
742
                $style_print = api_get_css(
743
                    api_get_cdn_path(api_get_path(WEB_CSS_PATH).$this->theme.'/print.css'),
744
                    'print'
745
                );
746
            }
747
            $this->assign('css_style_print', $style_print);
748
        }
749
750
        // Logo
751
        $logo = return_logo($this->theme);
752
        $this->assign('logo', $logo);
753
        $this->assign('show_media_element', 1);
754
    }
755
756
    /**
757
     * Declare and define the template variable that will be used to load
758
     * javascript libraries in the header.
759
     */
760
    public function set_js_files()
761
    {
762
        global $disable_js_and_css_files, $htmlHeadXtra;
763
        $isoCode = api_get_language_isocode();
764
        $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_'.strtoupper($isoCode).'.min.js';
765
766
        if ($isoCode == 'en') {
767
            $selectLink = 'bootstrap-select/dist/js/i18n/defaults-'.$isoCode.'_US.min.js';
768
        }
769
        // JS files
770
        $js_files = [
771
            'chosen/chosen.jquery.min.js',
772
        ];
773
774
        $viewBySession = api_get_setting('my_courses_view_by_session') === 'true';
775
776
        if (api_is_global_chat_enabled() || $viewBySession) {
777
            // Do not include the global chat in LP
778
            if ($this->show_learnpath == false &&
779
                $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...
780
                $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...
781
            ) {
782
                $js_files[] = 'chat/js/chat.js';
783
            }
784
        }
785
786
        if (api_get_setting('accessibility_font_resize') == 'true') {
787
            $js_files[] = 'fontresize.js';
788
        }
789
790
        $js_file_to_string = '';
791
        $bowerJsFiles = [
792
            'modernizr/modernizr.js',
793
            'jquery/dist/jquery.min.js',
794
            'bootstrap/dist/js/bootstrap.min.js',
795
            'jquery-ui/jquery-ui.min.js',
796
            'moment/min/moment-with-locales.js',
797
            'bootstrap-daterangepicker/daterangepicker.js',
798
            'jquery-timeago/jquery.timeago.js',
799
            'mediaelement/build/mediaelement-and-player.min.js',
800
            'jqueryui-timepicker-addon/dist/jquery-ui-timepicker-addon.min.js',
801
            'image-map-resizer/js/imageMapResizer.min.js',
802
            'jquery.scrollbar/jquery.scrollbar.min.js',
803
            'readmore-js/readmore.min.js',
804
            'bootstrap-select/dist/js/bootstrap-select.min.js',
805
            $selectLink,
806
            'select2/dist/js/select2.min.js',
807
            "select2/dist/js/i18n/$isoCode.js",
808
        ];
809
810
        $features = api_get_configuration_value('video_features');
811
        if (!empty($features) && isset($features['features'])) {
812
            foreach ($features['features'] as $feature) {
813
                $bowerJsFiles[] = "mediaelement/plugins/$feature/$feature.js";
814
            }
815
        }
816
817
        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...
818
            $bowerJsFiles[] = 'ckeditor/ckeditor.js';
819
        }
820
821
        if (api_get_setting('include_asciimathml_script') == 'true') {
822
            $bowerJsFiles[] = 'MathJax/MathJax.js?config=TeX-MML-AM_HTMLorMML';
823
        }
824
825
        if ($isoCode != 'en') {
826
            $bowerJsFiles[] = 'jqueryui-timepicker-addon/dist/i18n/jquery-ui-timepicker-'.$isoCode.'.js';
827
            $bowerJsFiles[] = 'jquery-ui/ui/minified/i18n/datepicker-'.$isoCode.'.min.js';
828
        }
829
830
        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...
831
            //$js_file_to_string .= '<script type="text/javascript" src="'.api_get_path(WEB_PUBLIC_PATH).'assets/'.$file.'"></script>'."\n";
832
        }
833
834
        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...
835
            //$js_file_to_string .= api_get_js($file);
836
        }
837
838
        // Loading email_editor js
839
        if (!api_is_anonymous() && api_get_setting('allow_email_editor') == 'true') {
840
            $template = $this->get_template('mail_editor/email_link.js.tpl');
841
            $js_file_to_string .= $this->fetch($template);
842
        }
843
844
        if (!$disable_js_and_css_files) {
845
            $this->assign('js_file_to_string', $js_file_to_string);
846
847
            $extra_headers = '<script>var _p = '.json_encode($this->getWebPaths(), JSON_PRETTY_PRINT).'</script>';
848
            //Adding jquery ui by default
849
            $extra_headers .= api_get_jquery_ui_js();
850
851
            //$extra_headers = '';
852
            if (isset($htmlHeadXtra) && $htmlHeadXtra) {
853
                foreach ($htmlHeadXtra as &$this_html_head) {
854
                    $extra_headers .= $this_html_head."\n";
855
                }
856
            }
857
            $this->assign('extra_headers', $extra_headers);
858
        }
859
    }
860
861
    /**
862
     * Special function to declare last-minute JS libraries which depend on
863
     * other things to be declared first. In particular, it might be useful
864
     * under IE9 with compatibility mode, which for some reason is getting
865
     * upset when a variable is used in a function (even if not used yet)
866
     * when this variable hasn't been defined yet.
867
     */
868
    public function set_js_files_post()
869
    {
870
        global $disable_js_and_css_files;
871
        $js_files = [];
872
        if (api_is_global_chat_enabled()) {
873
            //Do not include the global chat in LP
874
            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...
875
                $js_files[] = 'chat/js/chat.js';
876
            }
877
        }
878
        $js_file_to_string = null;
879
880
        foreach ($js_files as $js_file) {
881
            $js_file_to_string .= api_get_js($js_file);
882
        }
883
        if (!$disable_js_and_css_files) {
884
            $this->assign('js_file_to_string_post', $js_file_to_string);
885
        }
886
    }
887
888
    /**
889
     * @param string $theme
890
     *
891
     * @return string
892
     */
893
    public static function getPortalIcon($theme)
894
    {
895
        // Default root chamilo favicon
896
        $favico = '<link rel="shortcut icon" href="'.api_get_path(WEB_PATH).'favicon.ico" type="image/x-icon" />';
897
898
        // Added to verify if in the current Chamilo Theme exist a favicon
899
        $favicoThemeUrl = api_get_path(SYS_CSS_PATH).'/themes/'.$theme.'/images/';
900
901
        //If exist pick the current chamilo theme favicon
902
        if (is_file($favicoThemeUrl.'favicon.ico')) {
903
            $favico = '<link rel="shortcut icon" href="'.api_get_path(WEB_CSS_PATH).'themes/'.$theme.'/images/favicon.ico" type="image/x-icon" />';
904
        }
905
        if (api_is_multiple_url_enabled()) {
906
            $access_url_id = api_get_current_access_url_id();
907
            if ($access_url_id != -1) {
908
                $url_info = api_get_access_url($access_url_id);
909
                $url = api_remove_trailing_slash(
910
                    preg_replace('/https?:\/\//i', '', $url_info['url'])
911
                );
912
                $clean_url = api_replace_dangerous_char($url);
913
                $clean_url = str_replace('/', '-', $clean_url);
914
                $clean_url .= '/';
915
                $homep = api_get_path(REL_PATH).'home/'.$clean_url; //homep for Home Path
916
                $icon_real_homep = api_get_path(SYS_APP_PATH).'home/'.$clean_url;
917
                //we create the new dir for the new sites
918
                if (is_file($icon_real_homep.'favicon.ico')) {
919
                    $favico = '<link rel="shortcut icon" href="'.$homep.'favicon.ico" type="image/x-icon" />';
920
                }
921
            }
922
        }
923
924
        //var_dump(Container::$container->get('router')->generate('legacy_index'));
925
        //var_dump(api_get_path(WEB_PATH));
926
927
        return $favico;
928
    }
929
930
    /**
931
     * Show header template.
932
     */
933
    public function show_header_template()
934
    {
935
        $tpl = $this->get_template('layout/show_header.tpl');
936
        $this->display($tpl);
937
    }
938
939
    /**
940
     * Show footer template.
941
     */
942
    public function show_footer_template()
943
    {
944
        $tpl = $this->get_template('layout/show_footer.tpl');
945
        $this->display($tpl);
946
    }
947
948
    /**
949
     * Show footer js template.
950
     */
951
    public function show_footer_js_template()
952
    {
953
        $tpl = $this->get_template('layout/footer.js.tpl');
954
        $this->display($tpl);
955
    }
956
957
    /**
958
     * Sets the plugin content in a template variable.
959
     *
960
     * @param string $pluginRegion
961
     */
962
    public function set_plugin_region($pluginRegion)
963
    {
964
        if (!empty($pluginRegion)) {
965
            $regionContent = $this->plugin->load_region(
966
                $pluginRegion,
967
                $this,
968
                $this->force_plugin_load
969
            );
970
971
            $pluginList = $this->plugin->get_installed_plugins();
972
            foreach ($pluginList as $plugin_name) {
973
                // The plugin_info variable is available inside the plugin index
974
                $pluginInfo = $this->plugin->getPluginInfo($plugin_name);
975
976
                if (isset($pluginInfo['is_course_plugin']) && $pluginInfo['is_course_plugin']) {
977
                    $courseInfo = api_get_course_info();
978
                    if (!empty($courseInfo)) {
979
                        if (isset($pluginInfo['obj']) && $pluginInfo['obj'] instanceof Plugin) {
980
                            /** @var Plugin $plugin */
981
                            $plugin = $pluginInfo['obj'];
982
                            $regionContent .= $plugin->renderRegion($pluginRegion);
983
                        }
984
                    }
985
                } else {
986
                    continue;
987
                }
988
            }
989
990
            if (!empty($regionContent)) {
991
                $this->assign('plugin_'.$pluginRegion, $regionContent);
992
            } else {
993
                $this->assign('plugin_'.$pluginRegion, null);
994
            }
995
        }
996
997
        return null;
998
    }
999
1000
    /**
1001
     * @param string $template
1002
     *
1003
     * @return string
1004
     */
1005
    public function fetch($template = null)
1006
    {
1007
        $template = $this->twig->loadTemplate($template);
1008
1009
        return $template->render($this->params);
1010
    }
1011
1012
    /**
1013
     * @param string $variable
1014
     * @param mixed  $value
1015
     */
1016
    public function assign($variable, $value = '')
1017
    {
1018
        $this->params[$variable] = $value;
1019
    }
1020
1021
    /**
1022
     * Render the template.
1023
     *
1024
     * @param string $template           The template path
1025
     * @param bool   $clearFlashMessages Clear the $_SESSION variables for flash messages
1026
     */
1027
    public function display($template, $clearFlashMessages = true)
1028
    {
1029
        $this->assign('flash_messages', Display::getFlashToString());
1030
1031
        if ($clearFlashMessages) {
1032
            Display::cleanFlashMessages();
1033
        }
1034
        $template = str_replace('tpl', 'html.twig', $template);
1035
        $templateFile = api_get_path(SYS_PATH).'main/template/'.$template;
1036
1037
        if (!file_exists($templateFile)) {
1038
            $e = new \Gaufrette\Exception\FileNotFound($templateFile);
1039
            echo $e->getMessage();
1040
            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...
1041
        }
1042
        echo Container::getTemplating()->render($template, $this->params);
1043
    }
1044
1045
    /**
1046
     * Adds a body class for login pages.
1047
     */
1048
    public function setLoginBodyClass()
1049
    {
1050
        $this->assign('login_class', 'section-login');
1051
    }
1052
1053
    /**
1054
     * The theme that will be used if the database is not working.
1055
     *
1056
     * @return string
1057
     */
1058
    public static function getThemeFallback()
1059
    {
1060
        $theme = api_get_configuration_value('theme_fallback');
1061
        if (empty($theme)) {
1062
            $theme = 'chamilo';
1063
        }
1064
1065
        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...
1066
    }
1067
1068
    /**
1069
     * @param bool|true $setLoginForm
1070
     */
1071
    public function setLoginForm($setLoginForm = true)
1072
    {
1073
        global $loginFailed;
1074
        $userId = api_get_user_id();
1075
        if (!($userId) || api_is_anonymous($userId)) {
1076
            // Only display if the user isn't logged in.
1077
            $this->assign(
1078
                'login_language_form',
1079
                api_display_language_form(true, true)
1080
            );
1081
            if ($setLoginForm) {
1082
                $this->assign('login_form', $this->displayLoginForm());
1083
1084
                if ($loginFailed) {
1085
                    $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

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

1257
            $html .= '<div>'./** @scrutinizer ignore-call */ openid_form().'</div>';
Loading history...
1258
        }
1259
1260
        return $html;
1261
    }
1262
1263
    /**
1264
     * Returns the tutors names for the current course in session
1265
     * Function to use in Twig templates.
1266
     *
1267
     * @return string
1268
     */
1269
    public static function returnTutorsNames()
1270
    {
1271
        $em = Database::getManager();
1272
        $tutors = $em
1273
            ->createQuery('
1274
                SELECT u FROM ChamiloUserBundle:User u
1275
                INNER JOIN ChamiloCoreBundle:SessionRelCourseRelUser scu WITH u.id = scu.user
1276
                WHERE scu.status = :teacher_status AND scu.session = :session AND scu.course = :course
1277
            ')
1278
            ->setParameters([
1279
                'teacher_status' => SessionRelCourseRelUser::STATUS_COURSE_COACH,
1280
                'session' => api_get_session_id(),
1281
                'course' => api_get_course_int_id(),
1282
            ])
1283
            ->getResult();
1284
1285
        $names = [];
1286
1287
        /** @var User $tutor */
1288
        foreach ($tutors as $tutor) {
1289
            $names[] = $tutor->getCompleteName();
1290
        }
1291
1292
        return implode(CourseManager::USER_SEPARATOR, $names);
1293
    }
1294
1295
    /*s
1296
     * Returns the teachers name for the current course
1297
     * Function to use in Twig templates
1298
     * @return string
1299
     */
1300
    public static function returnTeachersNames()
1301
    {
1302
        $em = Database::getManager();
1303
        $teachers = $em
1304
            ->createQuery('
1305
                SELECT u FROM ChamiloUserBundle:User u
1306
                INNER JOIN ChamiloCoreBundle:CourseRelUser cu WITH u.id = cu.user
1307
                WHERE cu.status = :teacher_status AND cu.course = :course
1308
            ')
1309
            ->setParameters([
1310
                'teacher_status' => User::COURSE_MANAGER,
1311
                'course' => api_get_course_int_id(),
1312
            ])
1313
            ->getResult();
1314
1315
        $names = [];
1316
1317
        /** @var User $teacher */
1318
        foreach ($teachers as $teacher) {
1319
            $names[] = $teacher->getCompleteName();
1320
        }
1321
1322
        return implode(CourseManager::USER_SEPARATOR, $names);
1323
    }
1324
1325
    /**
1326
     * @param int $code
1327
     */
1328
    public function setResponseCode($code)
1329
    {
1330
        $this->responseCode = $code;
1331
    }
1332
1333
    /**
1334
     * @param string $code
1335
     */
1336
    public function getResponseCode()
1337
    {
1338
        return $this->responseCode;
1339
    }
1340
1341
    /**
1342
     * Prepare the _c array for template files. The _c array contains
1343
     * information about the current course.
1344
     */
1345
    private function set_course_parameters()
1346
    {
1347
        //Setting course id
1348
        $course = api_get_course_info();
1349
        if (empty($course)) {
1350
            $this->assign('course_is_set', false);
1351
1352
            return;
1353
        }
1354
        $this->assign('course_is_set', true);
1355
        $this->course_id = $course['id'];
1356
        $_c = [
1357
            'id' => $course['real_id'],
1358
            'code' => $course['code'],
1359
            'title' => $course['name'],
1360
            'visibility' => $course['visibility'],
1361
            'language' => $course['language'],
1362
            'directory' => $course['directory'],
1363
            'session_id' => api_get_session_id(),
1364
            'user_is_teacher' => api_is_course_admin(),
1365
            'student_view' => (!empty($_GET['isStudentView']) && $_GET['isStudentView'] == 'true'),
1366
        ];
1367
        $this->assign('course_code', $course['code']);
1368
        $this->assign('_c', $_c);
1369
    }
1370
1371
    /**
1372
     * Prepare the _u array for template files. The _u array contains
1373
     * information about the current user, as returned by
1374
     * api_get_user_info().
1375
     */
1376
    private function set_user_parameters()
1377
    {
1378
        $user_info = [];
1379
        $user_info['logged'] = 0;
1380
        $this->user_is_logged_in = false;
1381
        if (api_user_is_login()) {
1382
            $user_info = api_get_user_info(api_get_user_id(), true);
1383
            $user_info['logged'] = 1;
1384
1385
            $user_info['is_admin'] = 0;
1386
            if (api_is_platform_admin()) {
1387
                $user_info['is_admin'] = 1;
1388
            }
1389
1390
            $user_info['messages_count'] = MessageManager::getCountNewMessages();
1391
            $this->user_is_logged_in = true;
1392
        }
1393
        // Setting the $_u array that could be use in any template
1394
        $this->assign('_u', $user_info);
1395
    }
1396
1397
    /**
1398
     * Get an array of all the web paths available (e.g. 'web' => 'https://my.chamilo.site/').
1399
     *
1400
     * @return array
1401
     */
1402
    private function getWebPaths()
1403
    {
1404
        return [
1405
            'web' => api_get_path(WEB_PATH),
1406
            'web_url' => api_get_web_url(),
1407
            'web_relative' => api_get_path(REL_PATH),
1408
            'web_course' => api_get_path(WEB_COURSE_PATH),
1409
            'web_main' => api_get_path(WEB_CODE_PATH),
1410
            'web_css' => api_get_path(WEB_CSS_PATH),
1411
            'web_css_theme' => api_get_path(WEB_CSS_PATH).$this->themeDir,
1412
            'web_ajax' => api_get_path(WEB_AJAX_PATH),
1413
            'web_img' => api_get_path(WEB_IMG_PATH),
1414
            'web_plugin' => api_get_path(WEB_PLUGIN_PATH),
1415
            'web_lib' => api_get_path(WEB_LIBRARY_PATH),
1416
            'web_upload' => api_get_path(WEB_UPLOAD_PATH),
1417
            'web_self' => api_get_self(),
1418
            'web_query_vars' => api_htmlentities($_SERVER['QUERY_STRING']),
1419
            'web_self_query_vars' => api_htmlentities($_SERVER['REQUEST_URI']),
1420
            'web_cid_query' => api_get_cidreq(),
1421
        ];
1422
    }
1423
1424
    /**
1425
     * Set header parameters.
1426
     *
1427
     * @param bool $sendHeaders send headers
1428
     */
1429
    private function set_header_parameters($sendHeaders)
1430
    {
1431
        global $httpHeadXtra, $interbreadcrumb, $language_file, $_configuration, $this_section;
1432
        $_course = api_get_course_info();
1433
        $nameTools = $this->title;
1434
        $navigation = return_navigation_array();
1435
        $this->menu_navigation = $navigation['menu_navigation'];
1436
1437
        $this->assign('system_charset', api_get_system_encoding());
1438
1439
        if (isset($httpHeadXtra) && $httpHeadXtra) {
1440
            foreach ($httpHeadXtra as &$thisHttpHead) {
1441
                header($thisHttpHead);
1442
            }
1443
        }
1444
1445
        $this->assign(
1446
            'online_button',
1447
            Display::return_icon('statusonline.png', null, [], ICON_SIZE_ATOM)
1448
        );
1449
        $this->assign(
1450
            'offline_button',
1451
            Display::return_icon('statusoffline.png', null, [], ICON_SIZE_ATOM)
1452
        );
1453
1454
        // Get language iso-code for this page - ignore errors
1455
        $this->assign('document_language', api_get_language_isocode());
1456
1457
        $course_title = isset($_course['name']) ? $_course['name'] : null;
1458
1459
        $title_list = [];
1460
1461
        $title_list[] = api_get_setting('Institution');
1462
        $title_list[] = api_get_setting('siteName');
1463
1464
        if (!empty($course_title)) {
1465
            $title_list[] = $course_title;
1466
        }
1467
        if ($nameTools != '') {
1468
            $title_list[] = $nameTools;
1469
        }
1470
1471
        $title_string = '';
1472
        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...
1473
            $title_string .= $title_list[$i];
1474
            if (isset($title_list[$i + 1])) {
1475
                $item = trim($title_list[$i + 1]);
1476
                if (!empty($item)) {
1477
                    $title_string .= ' - ';
1478
                }
1479
            }
1480
        }
1481
1482
        $this->assign('title_string', $title_string);
1483
1484
        // Setting the theme and CSS files
1485
        $this->setCssFiles();
1486
        $this->set_js_files();
1487
        $this->setCssCustomFiles();
1488
1489
        $browser = api_browser_support('check_browser');
1490
        if ($browser[0] == 'Internet Explorer' && $browser[1] >= '11') {
1491
            $browser_head = '<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" />';
1492
            $this->assign('browser_specific_head', $browser_head);
1493
        }
1494
1495
        // Implementation of prefetch.
1496
        // See http://cdn.chamilo.org/main/img/online.png for details
1497
        $prefetch = '';
1498
        if (!empty($_configuration['cdn_enable'])) {
1499
            $prefetch .= '<meta http-equiv="x-dns-prefetch-control" content="on">';
1500
            foreach ($_configuration['cdn'] as $host => $exts) {
1501
                $prefetch .= '<link rel="dns-prefetch" href="'.$host.'">';
1502
            }
1503
        }
1504
1505
        $this->assign('prefetch', $prefetch);
1506
        $this->assign('text_direction', api_get_text_direction());
1507
        $this->assign('section_name', 'section-'.$this_section);
1508
        $this->assign('favico', $this->getPortalIcon($this->theme));
1509
        $this->setHelp();
1510
1511
        //@todo move this in the template
1512
        $rightFloatMenu = '';
1513
        $iconBug = Display::return_icon(
1514
            'bug.png',
1515
            get_lang('ReportABug'),
1516
            [],
1517
            ICON_SIZE_LARGE
1518
        );
1519
        if (api_get_setting('show_link_bug_notification') == 'true' && $this->user_is_logged_in) {
1520
            $rightFloatMenu = '<div class="report">
1521
		        <a href="https://github.com/chamilo/chamilo-lms/wiki/How-to-report-issues" target="_blank">
1522
                    '.$iconBug.'
1523
                </a>
1524
		        </div>';
1525
        }
1526
1527
        if (api_get_setting('show_link_ticket_notification') == 'true' && $this->user_is_logged_in) {
1528
            // by default is project_id = 1
1529
            $iconTicket = Display::return_icon(
1530
                'help.png',
1531
                get_lang('Ticket'),
1532
                [],
1533
                ICON_SIZE_LARGE
1534
            );
1535
            $courseInfo = api_get_course_info();
1536
            $courseParams = '';
1537
            if (!empty($courseInfo)) {
1538
                $courseParams = api_get_cidreq();
1539
            }
1540
            $url = api_get_path(WEB_CODE_PATH).'ticket/tickets.php?project_id=1&'.$courseParams;
1541
            $rightFloatMenu .= '<div class="help">
1542
		        <a href="'.$url.'" target="_blank">
1543
                    '.$iconTicket.'
1544
                </a>
1545
		    </div>';
1546
        }
1547
1548
        $this->assign('bug_notification', $rightFloatMenu);
1549
1550
        $resize = '';
1551
        if (api_get_setting('accessibility_font_resize') == 'true') {
1552
            $resize .= '<div class="resize_font">';
1553
            $resize .= '<div class="btn-group">';
1554
            $resize .= '<a title="'.get_lang('DecreaseFontSize').'" href="#" class="decrease_font btn btn-default"><em class="fa fa-font"></em></a>';
1555
            $resize .= '<a title="'.get_lang('ResetFontSize').'" href="#" class="reset_font btn btn-default"><em class="fa fa-font"></em></a>';
1556
            $resize .= '<a title="'.get_lang('IncreaseFontSize').'" href="#" class="increase_font btn btn-default"><em class="fa fa-font"></em></a>';
1557
            $resize .= '</div>';
1558
            $resize .= '</div>';
1559
        }
1560
        $this->assign('accessibility', $resize);
1561
1562
        // Preparing values for the menu
1563
1564
        // Logout link
1565
        $hideLogout = api_get_setting('hide_logout_button');
1566
        if ($hideLogout === 'true') {
1567
            $this->assign('logout_link', null);
1568
        } else {
1569
            $this->assign('logout_link', api_get_path(WEB_PATH).'index.php?logout=logout&uid='.api_get_user_id());
1570
        }
1571
1572
        // Profile link
1573
        if (api_get_setting('allow_social_tool') == 'true') {
1574
            $profile_url = api_get_path(WEB_CODE_PATH).'social/home.php';
1575
        } else {
1576
            $profile_url = api_get_path(WEB_CODE_PATH).'auth/profile.php';
1577
        }
1578
1579
        $this->assign('profile_url', $profile_url);
1580
1581
        //Message link
1582
        $message_link = null;
1583
        $message_url = null;
1584
        if (api_get_setting('allow_message_tool') == 'true') {
1585
            $message_url = api_get_path(WEB_CODE_PATH).'messages/inbox.php';
1586
            $message_link = '<a href="'.api_get_path(WEB_CODE_PATH).'messages/inbox.php">'.get_lang('Inbox').'</a>';
1587
        }
1588
        $this->assign('message_link', $message_link);
1589
        $this->assign('message_url', $message_url);
1590
1591
        // Certificate Link
1592
1593
        $allow = api_get_configuration_value('hide_my_certificate_link');
1594
        if ($allow === false) {
1595
            $certificateUrl = api_get_path(WEB_CODE_PATH).'gradebook/my_certificates.php';
1596
            $certificateLink = Display::url(
1597
                get_lang('MyCertificates'),
1598
                $certificateUrl
1599
            );
1600
            $this->assign('certificate_link', $certificateLink);
1601
            $this->assign('certificate_url', $certificateUrl);
1602
        }
1603
1604
        $institution = api_get_setting('Institution');
1605
        $portal_name = empty($institution) ? api_get_setting('siteName') : $institution;
1606
1607
        $this->assign('portal_name', $portal_name);
1608
1609
        //Menu
1610
        $menu = menuArray();
1611
        $this->assign('menu', $menu);
1612
1613
        $breadcrumb = '';
1614
        // Hide breadcrumb in LP
1615
        if ($this->show_learnpath == false) {
1616
            $breadcrumb = return_breadcrumb(
1617
                $interbreadcrumb,
1618
                $language_file,
1619
                $nameTools
1620
            );
1621
        }
1622
        $this->assign('breadcrumb', $breadcrumb);
1623
1624
        //Extra content
1625
        $extra_header = null;
1626
        if (!api_is_platform_admin()) {
1627
            $extra_header = trim(api_get_setting('header_extra_content'));
1628
        }
1629
        $this->assign('header_extra_content', $extra_header);
1630
1631
        if ($sendHeaders) {
1632
            header('Content-Type: text/html; charset='.api_get_system_encoding());
1633
            header(
1634
                'X-Powered-By: '.$_configuration['software_name'].' '.substr($_configuration['system_version'], 0, 1)
1635
            );
1636
            self::addHTTPSecurityHeaders();
0 ignored issues
show
Bug Best Practice introduced by
The method Template::addHTTPSecurityHeaders() 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

1636
            self::/** @scrutinizer ignore-call */ 
1637
                  addHTTPSecurityHeaders();
Loading history...
1637
1638
            $responseCode = $this->getResponseCode();
1639
            if (!empty($responseCode)) {
1640
                switch ($responseCode) {
1641
                    case '404':
1642
                        header("HTTP/1.0 404 Not Found");
1643
                        break;
1644
                }
1645
            }
1646
        }
1647
1648
        $socialMeta = '';
1649
        $metaTitle = api_get_setting('meta_title');
1650
        if (!empty($metaTitle)) {
1651
            $socialMeta .= '<meta name="twitter:card" content="summary" />'."\n";
1652
            $metaSite = api_get_setting('meta_twitter_site');
1653
            if (!empty($metaSite)) {
1654
                $socialMeta .= '<meta name="twitter:site" content="'.$metaSite.'" />'."\n";
1655
                $metaCreator = api_get_setting('meta_twitter_creator');
1656
                if (!empty($metaCreator)) {
1657
                    $socialMeta .= '<meta name="twitter:creator" content="'.$metaCreator.'" />'."\n";
1658
                }
1659
            }
1660
1661
            // The user badge page emits its own meta tags, so if this is
1662
            // enabled, ignore the global ones
1663
            $userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
1664
            $skillId = isset($_GET['skill']) ? intval($_GET['skill']) : 0;
1665
1666
            if (!$userId && !$skillId) {
1667
                // no combination of user and skill ID has been defined,
1668
                // so print the normal OpenGraph meta tags
1669
                $socialMeta .= '<meta property="og:title" content="'.$metaTitle.'" />'."\n";
1670
                $socialMeta .= '<meta property="og:url" content="'.api_get_path(WEB_PATH).'" />'."\n";
1671
1672
                $metaDescription = api_get_setting('meta_description');
1673
                if (!empty($metaDescription)) {
1674
                    $socialMeta .= '<meta property="og:description" content="'.$metaDescription.'" />'."\n";
1675
                }
1676
1677
                $metaImage = api_get_setting('meta_image_path');
1678
                if (!empty($metaImage)) {
1679
                    if (is_file(api_get_path(SYS_PATH).$metaImage)) {
1680
                        $path = api_get_path(WEB_PATH).$metaImage;
1681
                        $socialMeta .= '<meta property="og:image" content="'.$path.'" />'."\n";
1682
                    }
1683
                }
1684
            }
1685
        }
1686
1687
        $this->assign('social_meta', $socialMeta);
1688
    }
1689
1690
    /**
1691
     * Set footer parameters.
1692
     */
1693
    private function set_footer_parameters()
1694
    {
1695
        if (api_get_setting('show_administrator_data') === 'true') {
1696
            $firstName = api_get_setting('administratorName');
1697
            $lastName = api_get_setting('administratorSurname');
1698
1699
            if (!empty($firstName) && !empty($lastName)) {
1700
                $name = api_get_person_name($firstName, $lastName);
1701
            } else {
1702
                $name = $lastName;
1703
                if (empty($lastName)) {
1704
                    $name = $firstName;
1705
                }
1706
            }
1707
1708
            $adminName = '';
1709
            // Administrator name
1710
            if (!empty($name)) {
1711
                $adminName = get_lang('Manager').' : '.
1712
                    Display::encrypted_mailto_link(
1713
                        api_get_setting('emailAdministrator'),
1714
                        $name
1715
                    );
1716
            }
1717
            $this->assign('administrator_name', $adminName);
1718
        }
1719
1720
        // Loading footer extra content
1721
        if (!api_is_platform_admin()) {
1722
            $extra_footer = trim(api_get_setting('footer_extra_content'));
1723
            if (!empty($extra_footer)) {
1724
                $this->assign('footer_extra_content', $extra_footer);
1725
            }
1726
        }
1727
1728
        // Tutor name
1729
        if (api_get_setting('show_tutor_data') == 'true') {
1730
            // Course manager
1731
            $courseId = api_get_course_int_id();
1732
            $id_session = api_get_session_id();
1733
            if (!empty($courseId)) {
1734
                $tutor_data = '';
1735
                if ($id_session != 0) {
1736
                    $coachs_email = CourseManager::get_email_of_tutor_to_session(
1737
                        $id_session,
1738
                        $courseId
1739
                    );
1740
                    $email_link = [];
1741
                    foreach ($coachs_email as $coach) {
1742
                        $email_link[] = Display::encrypted_mailto_link($coach['email'], $coach['complete_name']);
1743
                    }
1744
                    if (count($coachs_email) > 1) {
1745
                        $tutor_data .= get_lang('Coachs').' : ';
1746
                        $tutor_data .= array_to_string($email_link, CourseManager::USER_SEPARATOR);
1747
                    } elseif (count($coachs_email) == 1) {
1748
                        $tutor_data .= get_lang('Coach').' : ';
1749
                        $tutor_data .= array_to_string($email_link, CourseManager::USER_SEPARATOR);
1750
                    } elseif (count($coachs_email) == 0) {
1751
                        $tutor_data .= '';
1752
                    }
1753
                }
1754
                $this->assign('session_teachers', $tutor_data);
1755
            }
1756
        }
1757
1758
        if (api_get_setting('show_teacher_data') == 'true') {
1759
            // course manager
1760
            $courseId = api_get_course_int_id();
1761
            if (!empty($courseId)) {
1762
                $teacher_data = '';
1763
                $mail = CourseManager::get_emails_of_tutors_to_course($courseId);
1764
                if (!empty($mail)) {
1765
                    $teachers_parsed = [];
1766
                    foreach ($mail as $value) {
1767
                        foreach ($value as $email => $name) {
1768
                            $teachers_parsed[] = Display::encrypted_mailto_link($email, $name);
1769
                        }
1770
                    }
1771
                    $label = get_lang('Teacher');
1772
                    if (count($mail) > 1) {
1773
                        $label = get_lang('Teachers');
1774
                    }
1775
                    $teacher_data .= $label.' : '.array_to_string($teachers_parsed, CourseManager::USER_SEPARATOR);
1776
                }
1777
                $this->assign('teachers', $teacher_data);
1778
            }
1779
        }
1780
    }
1781
1782
    /**
1783
     * Set administrator variables.
1784
     */
1785
    private function setAdministratorParams()
1786
    {
1787
        $_admin = [
1788
            'email' => api_get_setting('emailAdministrator'),
1789
            'surname' => api_get_setting('administratorSurname'),
1790
            'name' => api_get_setting('administratorName'),
1791
            'telephone' => api_get_setting('administratorTelephone'),
1792
        ];
1793
1794
        $this->assign('_admin', $_admin);
1795
    }
1796
1797
    /**
1798
     * Manage specific HTTP headers security.
1799
     */
1800
    private function addHTTPSecurityHeaders()
1801
    {
1802
        // Implementation of HTTP headers security, as suggested and checked
1803
        // by https://securityheaders.io/
1804
        // Enable these settings in configuration.php to use them on your site
1805
        // Strict-Transport-Security
1806
        $setting = api_get_configuration_value('security_strict_transport');
1807
        if (!empty($setting)) {
1808
            header('Strict-Transport-Security: '.$setting);
1809
        }
1810
        // Content-Security-Policy
1811
        $setting = api_get_configuration_value('security_content_policy');
1812
        if (!empty($setting)) {
1813
            header('Content-Security-Policy: '.$setting);
1814
        }
1815
        $setting = api_get_configuration_value('security_content_policy_report_only');
1816
        if (!empty($setting)) {
1817
            header('Content-Security-Policy-Report-Only: '.$setting);
1818
        }
1819
        // Public-Key-Pins
1820
        $setting = api_get_configuration_value('security_public_key_pins');
1821
        if (!empty($setting)) {
1822
            header('Public-Key-Pins: '.$setting);
1823
        }
1824
        $setting = api_get_configuration_value('security_public_key_pins_report_only');
1825
        if (!empty($setting)) {
1826
            header('Public-Key-Pins-Report-Only: '.$setting);
1827
        }
1828
        // X-Frame-Options
1829
        $setting = api_get_configuration_value('security_x_frame_options');
1830
        if (!empty($setting)) {
1831
            header('X-Frame-Options: '.$setting);
1832
        }
1833
        // X-XSS-Protection
1834
        $setting = api_get_configuration_value('security_xss_protection');
1835
        if (!empty($setting)) {
1836
            header('X-XSS-Protection: '.$setting);
1837
        }
1838
        // X-Content-Type-Options
1839
        $setting = api_get_configuration_value('security_x_content_type_options');
1840
        if (!empty($setting)) {
1841
            header('X-Content-Type-Options: '.$setting);
1842
        }
1843
        // Referrer-Policy
1844
        $setting = api_get_configuration_value('security_referrer_policy');
1845
        if (!empty($setting)) {
1846
            header('Referrer-Policy: '.$setting);
1847
        }
1848
        // end of HTTP headers security block
1849
    }
1850
}
1851