Passed
Pull Request — 1.11.x (#6950)
by
unknown
10:45
created

Display::page_header_and_translate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
6
use Chamilo\CoreBundle\Entity\ExtraField;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, ExtraField. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use ChamiloSession as Session;
8
9
/**
10
 * Class Display
11
 * Contains several public functions dealing with the display of
12
 * table data, messages, help topics, ...
13
 *
14
 * Include/require it in your code to use its public functionality.
15
 * There are also several display public functions in the main api library.
16
 *
17
 * All public functions static public functions inside a class called Display,
18
 * so you use them like this: e.g.
19
 * Display::return_message($message)
20
 *
21
 * @package chamilo.library
22
 */
23
class Display
24
{
25
    /** @var Template */
26
    public static $global_template;
27
    public static $preview_style = null;
28
29
    /**
30
     * Constructor.
31
     */
32
    public function __construct()
33
    {
34
    }
35
36
    /**
37
     * @return array
38
     */
39
    public static function toolList()
40
    {
41
        return [
42
            'group',
43
            'work',
44
            'glossary',
45
            'forum',
46
            'course_description',
47
            'gradebook',
48
            'attendance',
49
            'course_progress',
50
            'notebook',
51
        ];
52
    }
53
54
    /**
55
     * Displays the page header.
56
     *
57
     * @param string The name of the page (will be showed in the page title)
58
     * @param string Optional help file name
59
     * @param string $page_header
60
     */
61
    public static function display_header(
62
        $tool_name = '',
63
        $help = null,
64
        $page_header = null
65
    ) {
66
        $origin = api_get_origin();
67
        $showHeader = true;
68
        if (isset($origin) && ($origin == 'learnpath' || $origin == 'mobileapp')) {
69
            $showHeader = false;
70
        }
71
72
        if (Session::read('origin') == 'mobileapp') {
73
            $showHeader = false;
74
        }
75
76
        /* USER_IN_ANON_SURVEY is defined in fillsurvey.php when survey is marked as anonymous survey */
77
        $userInAnonSurvey = defined('USER_IN_ANON_SURVEY') && USER_IN_ANON_SURVEY;
78
79
        self::$global_template = new Template($tool_name, $showHeader, $showHeader, false, $userInAnonSurvey);
80
        self::$global_template->assign('user_in_anon_survey', $userInAnonSurvey);
81
82
        // Fixing tools with any help it takes xxx part of main/xxx/index.php
83
        if (empty($help)) {
84
            $currentURL = api_get_self();
85
            preg_match('/main\/([^*\/]+)/', $currentURL, $matches);
86
            $toolList = self::toolList();
87
            if (!empty($matches)) {
88
                foreach ($matches as $match) {
89
                    if (in_array($match, $toolList)) {
90
                        $help = explode('_', $match);
91
                        $help = array_map('ucfirst', $help);
92
                        $help = implode('', $help);
93
                        break;
94
                    }
95
                }
96
            }
97
        }
98
99
        self::$global_template->setHelp($help);
100
101
        if (!empty(self::$preview_style)) {
102
            self::$global_template->preview_theme = self::$preview_style;
103
            self::$global_template->set_system_parameters();
104
            self::$global_template->setCssFiles();
105
            self::$global_template->set_js_files();
106
            self::$global_template->setCssCustomFiles();
107
        }
108
109
        if (!empty($page_header)) {
110
            self::$global_template->assign('header', $page_header);
111
        }
112
113
        echo self::$global_template->show_header_template();
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::global_template->show_header_template() targeting Template::show_header_template() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
114
    }
115
116
    /**
117
     * Displays the reduced page header (without banner).
118
     */
119
    public static function display_reduced_header()
120
    {
121
        global $show_learnpath, $tool_name;
122
        self::$global_template = new Template(
123
            $tool_name,
124
            false,
125
            false,
126
            $show_learnpath
127
        );
128
        echo self::$global_template->show_header_template();
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::global_template->show_header_template() targeting Template::show_header_template() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
129
    }
130
131
    /**
132
     * Display no header.
133
     */
134
    public static function display_no_header()
135
    {
136
        global $tool_name, $show_learnpath;
137
        $disable_js_and_css_files = true;
138
        self::$global_template = new Template(
139
            $tool_name,
140
            false,
141
            false,
142
            $show_learnpath
143
        );
144
    }
145
146
    /**
147
     * Displays the reduced page header (without banner).
148
     */
149
    public static function set_header()
150
    {
151
        global $show_learnpath, $tool_name;
152
        self::$global_template = new Template(
153
            $tool_name,
154
            false,
155
            false,
156
            $show_learnpath
157
        );
158
    }
159
160
    /**
161
     * Display the page footer.
162
     */
163
    public static function display_footer()
164
    {
165
        echo self::$global_template->show_footer_template();
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::global_template->show_footer_template() targeting Template::show_footer_template() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
166
    }
167
168
    /**
169
     * Display the page footer.
170
     */
171
    public static function display_reduced_footer()
172
    {
173
        echo '</body></html>';
174
    }
175
176
    /**
177
     * Displays the tool introduction of a tool.
178
     *
179
     * @author Patrick Cool <[email protected]>, Ghent University
180
     *
181
     * @param string $tool          these are the constants that are used for indicating the tools
182
     * @param array  $editor_config Optional configuration settings for the online editor.
183
     *                              return: $tool return a string array list with the "define" in main_api.lib
184
     *
185
     * @return string html code for adding an introduction
186
     */
187
    public static function display_introduction_section(
188
        $tool,
189
        $editor_config = null
190
    ) {
191
        echo self::return_introduction_section($tool, $editor_config);
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::return_introductio...($tool, $editor_config) targeting Display::return_introduction_section() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
192
    }
193
194
    /**
195
     * @param string $tool
196
     * @param array  $editor_config
197
     */
198
    public static function return_introduction_section(
199
        $tool,
200
        $editor_config = null
201
    ) {
202
        $moduleId = $tool;
203
        if (api_get_setting('enable_tool_introduction') == 'true' || $tool == TOOL_COURSE_HOMEPAGE) {
204
            $introduction_section = null;
205
            require api_get_path(SYS_INC_PATH).'introductionSection.inc.php';
206
207
            return $introduction_section;
208
        }
209
    }
210
211
    /**
212
     * Displays a table.
213
     *
214
     * @param array  $header          Titles for the table header
215
     *                                each item in this array can contain 3 values
216
     *                                - 1st element: the column title
217
     *                                - 2nd element: true or false (column sortable?)
218
     *                                - 3th element: additional attributes for
219
     *                                th-tag (eg for column-width)
220
     *                                - 4the element: additional attributes for the td-tags
221
     * @param array  $content         2D-array with the tables content
222
     * @param array  $sorting_options Keys are:
223
     *                                'column' = The column to use as sort-key
224
     *                                'direction' = SORT_ASC or SORT_DESC
225
     * @param array  $paging_options  Keys are:
226
     *                                'per_page_default' = items per page when switching from
227
     *                                full-    list to per-page-view
228
     *                                'per_page' = number of items to show per page
229
     *                                'page_nr' = The page to display
230
     * @param array  $query_vars      Additional variables to add in the query-string
231
     * @param array  $form_actions
232
     * @param string $style           The style that the table will show. You can set 'table' or 'grid'
233
     * @param string $tableName
234
     * @param string $tableId
235
     *
236
     * @author [email protected]
237
     */
238
    public static function display_sortable_table(
239
        $header,
240
        $content,
241
        $sorting_options = [],
242
        $paging_options = [],
243
        $query_vars = null,
244
        $form_actions = [],
245
        $style = 'table',
246
        $tableName = 'tablename',
247
        $tableId = ''
248
    ) {
249
        $column = isset($sorting_options['column']) ? $sorting_options['column'] : 0;
250
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
251
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $tableName, null, $tableId);
252
        if (is_array($query_vars)) {
253
            $table->set_additional_parameters($query_vars);
254
        }
255
        if ($style === 'table') {
256
            if (is_array($header) && count($header) > 0) {
257
                foreach ($header as $index => $header_item) {
258
                    $table->set_header(
259
                        $index,
260
                        isset($header_item[0]) ? $header_item[0] : null,
261
                        isset($header_item[1]) ? $header_item[1] : null,
262
                        isset($header_item[2]) ? $header_item[2] : null,
263
                        isset($header_item[3]) ? $header_item[3] : null
264
                    );
265
                }
266
            }
267
            $table->set_form_actions($form_actions);
268
            $table->display();
269
        } else {
270
            $table->display_grid();
271
        }
272
    }
273
274
    /**
275
     * Returns an HTML table with sortable column (through complete page refresh).
276
     *
277
     * @param array  $header
278
     * @param array  $content         Array of row arrays
279
     * @param array  $sorting_options
280
     * @param array  $paging_options
281
     * @param array  $query_vars
282
     * @param array  $form_actions
283
     * @param string $style
284
     *
285
     * @return string HTML string for array
286
     */
287
    public static function return_sortable_table(
288
        $header,
289
        $content,
290
        $sorting_options = [],
291
        $paging_options = [],
292
        $query_vars = null,
293
        $form_actions = [],
294
        $style = 'table'
295
    ) {
296
        ob_start();
297
        self::display_sortable_table(
298
            $header,
299
            $content,
300
            $sorting_options,
301
            $paging_options,
302
            $query_vars,
303
            $form_actions,
304
            $style
305
        );
306
        $content = ob_get_contents();
307
        ob_end_clean();
308
309
        return $content;
310
    }
311
312
    /**
313
     * Shows a nice grid.
314
     *
315
     * @param string grid name (important to create css)
316
     * @param array header content
317
     * @param array array with the information to show
318
     * @param array $paging_options Keys are:
319
     *                              'per_page_default' = items per page when switching from
320
     *                              full-    list to per-page-view
321
     *                              'per_page' = number of items to show per page
322
     *                              'page_nr' = The page to display
323
     *                              'hide_navigation' =  true to hide the navigation
324
     * @param array $query_vars     Additional variables to add in the query-string
325
     * @param array $form_actions   actions Additional variables to add in the query-string
326
     * @param mixed An array with bool values to know which columns show.
327
     * i.e: $visibility_options= array(true, false) we will only show the first column
328
     *                Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
329
     */
330
    public static function display_sortable_grid(
331
        $name,
332
        $header,
333
        $content,
334
        $paging_options = [],
335
        $query_vars = null,
336
        $form_actions = [],
337
        $visibility_options = true,
338
        $sort_data = true,
339
        $grid_class = []
340
    ) {
341
        echo self::return_sortable_grid(
342
            $name,
343
            $header,
344
            $content,
345
            $paging_options,
346
            $query_vars,
347
            $form_actions,
348
            $visibility_options,
349
            $sort_data,
350
            $grid_class
351
        );
352
    }
353
354
    /**
355
     * Gets a nice grid in html string.
356
     *
357
     * @param string grid name (important to create css)
358
     * @param array header content
359
     * @param array array with the information to show
360
     * @param array $paging_options Keys are:
361
     *                              'per_page_default' = items per page when switching from
362
     *                              full-    list to per-page-view
363
     *                              'per_page' = number of items to show per page
364
     *                              'page_nr' = The page to display
365
     *                              'hide_navigation' =  true to hide the navigation
366
     * @param array $query_vars     Additional variables to add in the query-string
367
     * @param mixed An array with bool values to know which columns show. i.e:
368
     *  $visibility_options= array(true, false) we will only show the first column
369
     *    Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
370
     * @param bool  true for sorting data or false otherwise
371
     * @param array grid classes
372
     *
373
     * @return string html grid
374
     */
375
    public static function return_sortable_grid(
376
        $name,
377
        $header,
378
        $content,
379
        $paging_options = [],
380
        $query_vars = null,
381
        $form_actions = [],
382
        $visibility_options = true,
383
        $sort_data = true,
384
        $grid_class = [],
385
        $elementCount = 0
386
    ) {
387
        $column = 0;
388
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
389
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $name);
390
        $table->total_number_of_items = intval($elementCount);
391
        if (is_array($query_vars)) {
392
            $table->set_additional_parameters($query_vars);
393
        }
394
395
        return $table->display_simple_grid(
396
            $visibility_options,
397
            $paging_options['hide_navigation'],
398
            $default_items_per_page,
399
            $sort_data,
400
            $grid_class
401
        );
402
    }
403
404
    /**
405
     * Displays a table with a special configuration.
406
     *
407
     * @param array $header          Titles for the table header
408
     *                               each item in this array can contain 3 values
409
     *                               - 1st element: the column title
410
     *                               - 2nd element: true or false (column sortable?)
411
     *                               - 3th element: additional attributes for th-tag (eg for column-width)
412
     *                               - 4the element: additional attributes for the td-tags
413
     * @param array $content         2D-array with the tables content
414
     * @param array $sorting_options Keys are:
415
     *                               'column' = The column to use as sort-key
416
     *                               'direction' = SORT_ASC or SORT_DESC
417
     * @param array $paging_options  Keys are:
418
     *                               'per_page_default' = items per page when switching from full list to per-page-view
419
     *                               'per_page' = number of items to show per page
420
     *                               'page_nr' = The page to display
421
     * @param array $query_vars      Additional variables to add in the query-string
422
     * @param array $column_show     Array of binaries 1= show columns 0. hide a column
423
     * @param array $column_order    An array of integers that let us decide how the columns are going to be sort.
424
     *                               i.e:  $column_order=array('1''4','3','4'); The 2nd column will be order like the 4th column
425
     * @param array $form_actions    Set optional forms actions
426
     *
427
     * @author Julio Montoya
428
     */
429
    public static function display_sortable_config_table(
430
        $table_name,
431
        $header,
432
        $content,
433
        $sorting_options = [],
434
        $paging_options = [],
435
        $query_vars = null,
436
        $column_show = [],
437
        $column_order = [],
438
        $form_actions = []
439
    ) {
440
        $column = isset($sorting_options['column']) ? $sorting_options['column'] : 0;
441
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
442
443
        $table = new SortableTableFromArrayConfig(
444
            $content,
445
            $column,
446
            $default_items_per_page,
447
            $table_name,
448
            $column_show,
449
            $column_order
450
        );
451
452
        if (is_array($query_vars)) {
453
            $table->set_additional_parameters($query_vars);
454
        }
455
        // Show or hide the columns header
456
        if (is_array($column_show)) {
457
            for ($i = 0; $i < count($column_show); $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...
458
                if (!empty($column_show[$i])) {
459
                    $val0 = isset($header[$i][0]) ? $header[$i][0] : null;
460
                    $val1 = isset($header[$i][1]) ? $header[$i][1] : null;
461
                    $val2 = isset($header[$i][2]) ? $header[$i][2] : null;
462
                    $val3 = isset($header[$i][3]) ? $header[$i][3] : null;
463
                    $table->set_header($i, $val0, $val1, $val2, $val3);
464
                }
465
            }
466
        }
467
        $table->set_form_actions($form_actions);
468
        $table->display();
469
    }
470
471
    /**
472
     * @param string $message
473
     * @param string $type
474
     * @param bool   $filter
475
     */
476
    public static function return_message_and_translate(
477
        $message,
478
        $type = 'normal',
479
        $filter = true
480
    ) {
481
        $message = get_lang($message);
482
        echo self::return_message($message, $type, $filter);
483
    }
484
485
    /**
486
     * Returns a div html string with.
487
     *
488
     * @param string $message
489
     * @param string $type    Example: confirm, normal, warning, error
490
     * @param bool   $filter  Whether to XSS-filter or not
491
     *
492
     * @return string Message wrapped into an HTML div
493
     */
494
    public static function return_message(
495
        $message,
496
        $type = 'normal',
497
        $filter = true
498
    ) {
499
        if (empty($message)) {
500
            return '';
501
        }
502
503
        if ($filter) {
504
            $message = api_htmlentities(
505
                $message,
506
                ENT_QUOTES,
507
                api_is_xml_http_request() ? 'UTF-8' : api_get_system_encoding()
508
            );
509
        }
510
511
        $class = '';
512
        switch ($type) {
513
            case 'warning':
514
                $class .= 'alert alert-warning';
515
                break;
516
            case 'error':
517
                $class .= 'alert alert-danger';
518
                break;
519
            case 'confirmation':
520
            case 'confirm':
521
            case 'success':
522
                $class .= 'alert alert-success';
523
                break;
524
            case 'normal':
525
            default:
526
                $class .= 'alert alert-info';
527
        }
528
529
        return self::div($message, ['class' => $class]);
530
    }
531
532
    /**
533
     * Returns an encrypted mailto hyperlink.
534
     *
535
     * @param string  e-mail
0 ignored issues
show
Documentation Bug introduced by
The doc comment e-mail at position 0 could not be parsed: Unknown type name 'e-mail' at position 0 in e-mail.
Loading history...
536
     * @param string  clickable text
537
     * @param string  optional, class from stylesheet
538
     * @param bool $addExtraContent
539
     *
540
     * @return string encrypted mailto hyperlink
541
     */
542
    public static function encrypted_mailto_link(
543
        $email,
544
        $clickable_text = null,
545
        $style_class = '',
546
        $addExtraContent = false
547
    ) {
548
        if (is_null($clickable_text)) {
549
            $clickable_text = $email;
550
        }
551
552
        // "mailto:" already present?
553
        if (substr($email, 0, 7) !== 'mailto:') {
554
            $email = 'mailto:'.$email;
555
        }
556
557
        // Class (stylesheet) defined?
558
        if ($style_class !== '') {
559
            $style_class = ' class="'.$style_class.'"';
560
        }
561
562
        // Encrypt email
563
        $hmail = '';
564
        for ($i = 0; $i < strlen($email); $i++) {
565
            $hmail .= '&#'.ord($email[$i]).';';
566
        }
567
568
        $value = api_get_configuration_value('add_user_course_information_in_mailto');
569
570
        if ($value) {
571
            if (api_get_setting('allow_email_editor') === 'false') {
572
                $hmail .= '?';
573
            }
574
575
            if (!api_is_anonymous()) {
576
                $hmail .= '&subject='.Security::remove_XSS(api_get_setting('siteName'));
577
            }
578
            if ($addExtraContent) {
579
                $content = '';
580
                if (!api_is_anonymous()) {
581
                    $userInfo = api_get_user_info();
582
                    $content .= get_lang('User').': '.$userInfo['complete_name']."\n";
583
584
                    $courseInfo = api_get_course_info();
585
                    if (!empty($courseInfo)) {
586
                        $content .= get_lang('Course').': ';
587
                        $content .= $courseInfo['name'];
588
                        $sessionInfo = api_get_session_info(api_get_session_id());
589
                        if (!empty($sessionInfo)) {
590
                            $content .= ' '.$sessionInfo['name'].' <br />';
591
                        }
592
                    }
593
                }
594
595
                if (!empty($content)) {
596
                    $hmail .= '&body='.rawurlencode($content);
597
                }
598
            }
599
        }
600
601
        $hclickable_text = '';
602
        // Encrypt clickable text if @ is present
603
        if (strpos($clickable_text, '@')) {
604
            for ($i = 0; $i < strlen($clickable_text); $i++) {
605
                $hclickable_text .= '&#'.ord($clickable_text[$i]).';';
606
            }
607
        } else {
608
            $hclickable_text = @htmlspecialchars(
609
                $clickable_text,
610
                ENT_QUOTES,
611
                api_get_system_encoding()
612
            );
613
        }
614
        // Return encrypted mailto hyperlink
615
        return '<a href="'.$hmail.'"'.$style_class.' class="clickable_email_link">'.$hclickable_text.'</a>';
616
    }
617
618
    /**
619
     * Returns an mailto icon hyperlink.
620
     *
621
     * @param string  e-mail
0 ignored issues
show
Documentation Bug introduced by
The doc comment e-mail at position 0 could not be parsed: Unknown type name 'e-mail' at position 0 in e-mail.
Loading history...
622
     * @param string  icon source file from the icon lib
623
     * @param int  icon size from icon lib
624
     * @param string  optional, class from stylesheet
625
     *
626
     * @return string encrypted mailto hyperlink
627
     */
628
    public static function icon_mailto_link(
629
        $email,
630
        $icon_file = "mail.png",
631
        $icon_size = 22,
632
        $style_class = ''
633
    ) {
634
        // "mailto:" already present?
635
        if (substr($email, 0, 7) != 'mailto:') {
636
            $email = 'mailto:'.$email;
637
        }
638
        // Class (stylesheet) defined?
639
        if ($style_class != '') {
640
            $style_class = ' class="'.$style_class.'"';
641
        }
642
        // Encrypt email
643
        $hmail = '';
644
        for ($i = 0; $i < strlen($email); $i++) {
645
            $hmail .= '&#'.ord($email[
646
            $i]).';';
647
        }
648
        // icon html code
649
        $icon_html_source = self::return_icon(
650
            $icon_file,
651
            $hmail,
652
            '',
653
            $icon_size
654
        );
655
        // Return encrypted mailto hyperlink
656
657
        return '<a href="'.$hmail.'"'.$style_class.' class="clickable_email_link">'.$icon_html_source.'</a>';
658
    }
659
660
    /**
661
     * Prints an <option>-list with all letters (A-Z).
662
     *
663
     * @todo This is English language specific implementation.
664
     * It should be adapted for the other languages.
665
     *
666
     * @return string
667
     */
668
    public static function get_alphabet_options($selectedLetter = '')
669
    {
670
        $result = '';
671
        for ($i = 65; $i <= 90; $i++) {
672
            $letter = chr($i);
673
            $result .= '<option value="'.$letter.'"';
674
            if ($selectedLetter == $letter) {
675
                $result .= ' selected="selected"';
676
            }
677
            $result .= '>'.$letter.'</option>';
678
        }
679
680
        return $result;
681
    }
682
683
    /**
684
     * Get the options withing a select box within the given values.
685
     *
686
     * @param int   Min value
687
     * @param int   Max value
688
     * @param int   Default value
689
     *
690
     * @return string HTML select options
691
     */
692
    public static function get_numeric_options($min, $max, $selected_num = 0)
693
    {
694
        $result = '';
695
        for ($i = $min; $i <= $max; $i++) {
696
            $result .= '<option value="'.$i.'"';
697
            if (is_int($selected_num)) {
698
                if ($selected_num == $i) {
699
                    $result .= ' selected="selected"';
700
                }
701
            }
702
            $result .= '>'.$i.'</option>';
703
        }
704
705
        return $result;
706
    }
707
708
    /**
709
     * This public function displays an icon.
710
     *
711
     * @param string   The filename of the file (in the main/img/ folder
712
     * @param string   The alt text (probably a language variable)
713
     * @param array    additional attributes (for instance height, width, onclick, ...)
714
     * @param int  The wanted width of the icon (to be looked for in the corresponding img/icons/ folder)
715
     */
716
    public static function display_icon(
717
        $image,
718
        $alt_text = '',
719
        $additional_attributes = [],
720
        $size = null
721
    ) {
722
        echo self::return_icon($image, $alt_text, $additional_attributes, $size);
723
    }
724
725
    /**
726
     * Gets the path of an icon.
727
     *
728
     * @param string $icon
729
     * @param int    $size
730
     *
731
     * @return string
732
     */
733
    public static function returnIconPath($icon, $size = ICON_SIZE_SMALL)
734
    {
735
        return self::return_icon($icon, null, null, $size, null, true, false);
736
    }
737
738
    /**
739
     * This public function returns the HTML code for an icon.
740
     *
741
     * @param string $image                 The filename of the file (in the main/img/ folder
742
     * @param string $alt_text              The alt text (probably a language variable)
743
     * @param array  $additional_attributes Additional attributes (for instance height, width, onclick, ...)
744
     * @param int    $size                  The wanted width of the icon (to be looked for in the corresponding img/icons/ folder)
745
     * @param bool   $show_text             Whether to show the text next (usually under) the icon
746
     * @param bool   $return_only_path      Whether we only want the path to the icon or the whole HTML tag
747
     * @param bool   $loadThemeIcon         Whether we want to allow an overloaded theme icon, if it exists, to replace the default icon
748
     *
749
     * @return string An HTML string of the right <img> tag
750
     *
751
     * @author Patrick Cool <[email protected]>, Ghent University 2006
752
     * @author Julio Montoya 2010 Function improved, adding image constants
753
     * @author Yannick Warnier 2011 Added size handler
754
     *
755
     * @version Feb 2011
756
     */
757
    public static function return_icon(
758
        $image,
759
        $alt_text = '',
760
        $additional_attributes = [],
761
        $size = ICON_SIZE_SMALL,
762
        $show_text = true,
763
        $return_only_path = false,
764
        $loadThemeIcon = true
765
    ) {
766
        $code_path = api_get_path(SYS_CODE_PATH);
767
        $w_code_path = api_get_path(WEB_CODE_PATH);
768
        // The following path is checked to see if the file exist. It's
769
        // important to use the public path (i.e. web/css/) rather than the
770
        // internal path (/app/Resource/public/css/) because the path used
771
        // in the end must be the public path
772
        $alternateCssPath = api_get_path(SYS_PUBLIC_PATH).'css/';
773
        $alternateWebCssPath = api_get_path(WEB_PUBLIC_PATH).'css/';
774
775
        // Avoid issues with illegal string offset for legacy calls to this
776
        // method with an empty string rather than null or an empty array
777
        if (empty($additional_attributes)) {
778
            $additional_attributes = [];
779
        }
780
781
        $image = trim($image);
782
783
        if (isset($size)) {
784
            $size = intval($size);
785
        } else {
786
            $size = ICON_SIZE_SMALL;
787
        }
788
789
        $size_extra = $size.'/';
790
        $icon = $w_code_path.'img/'.$image;
791
        $theme = 'themes/chamilo/icons/';
792
793
        if ($loadThemeIcon) {
794
            $theme = 'themes/'.api_get_visual_theme().'/icons/';
795
            if (is_file($alternateCssPath.$theme.$image)) {
796
                $icon = $alternateWebCssPath.$theme.$image;
797
            }
798
            // Checking the theme icons folder example: app/Resources/public/css/themes/chamilo/icons/XXX
799
            if (is_file($alternateCssPath.$theme.$size_extra.$image)) {
800
                $icon = $alternateWebCssPath.$theme.$size_extra.$image;
801
            } elseif (is_file($code_path.'img/icons/'.$size_extra.$image)) {
802
                //Checking the main/img/icons/XXX/ folder
803
                $icon = $w_code_path.'img/icons/'.$size_extra.$image;
804
            }
805
        } else {
806
            if (is_file($code_path.'img/icons/'.$size_extra.$image)) {
807
                // Checking the main/img/icons/XXX/ folder
808
                $icon = $w_code_path.'img/icons/'.$size_extra.$image;
809
            }
810
        }
811
812
        // Special code to enable SVG - refs #7359 - Needs more work
813
        // The code below does something else to "test out" SVG: for each icon,
814
        // it checks if there is an SVG version. If so, it uses it.
815
        // When moving this to production, the return_icon() calls should
816
        // ask for the SVG version directly
817
        $svgIcons = api_get_setting('icons_mode_svg');
818
        if ($svgIcons === 'true' && $return_only_path == 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...
819
            $svgImage = substr($image, 0, -3).'svg';
820
            if (is_file($code_path.$theme.'svg/'.$svgImage)) {
821
                $icon = $w_code_path.$theme.'svg/'.$svgImage;
822
            } elseif (is_file($code_path.'img/icons/svg/'.$svgImage)) {
823
                $icon = $w_code_path.'img/icons/svg/'.$svgImage;
824
            }
825
826
            if (empty($additional_attributes['height'])) {
827
                $additional_attributes['height'] = $size;
828
            }
829
            if (empty($additional_attributes['width'])) {
830
                $additional_attributes['width'] = $size;
831
            }
832
        }
833
834
        $icon = api_get_cdn_path($icon);
835
836
        if ($return_only_path) {
837
            return $icon;
838
        }
839
840
        $img = self::img($icon, $alt_text, $additional_attributes);
841
        if (SHOW_TEXT_NEAR_ICONS == true && !empty($alt_text)) {
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...
842
            if ($show_text) {
843
                $img = "$img $alt_text";
844
            }
845
        }
846
847
        return $img;
848
    }
849
850
    /**
851
     * Returns the HTML code for an image.
852
     *
853
     * @param string $image_path            the filename of the file (in the main/img/ folder
854
     * @param string $alt_text              the alt text (probably a language variable)
855
     * @param array  $additional_attributes (for instance height, width, onclick, ...)
856
     * @param bool   $filterPath            Optional. Whether filter the image path. Default is true
857
     *
858
     * @return string
859
     *
860
     * @author Julio Montoya 2010
861
     */
862
    public static function img(
863
        $image_path,
864
        $alt_text = '',
865
        $additional_attributes = null,
866
        $filterPath = true
867
    ) {
868
        if (empty($image_path)) {
869
            // For some reason, the call to img() happened without a proper
870
            // image. Log the error and return an empty string to avoid
871
            // breaking the HTML
872
            $trace = debug_backtrace();
873
            $caller = $trace[1];
874
            error_log('No image provided in Display::img(). Caller info: '.print_r($caller, 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($caller, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

874
            error_log('No image provided in Display::img(). Caller info: './** @scrutinizer ignore-type */ print_r($caller, 1));
Loading history...
875
876
            return '';
877
        }
878
        // Sanitizing the parameter $image_path
879
        if ($filterPath) {
880
            $image_path = Security::filter_img_path($image_path);
881
        }
882
883
        // alt text = the image name if there is none provided (for XHTML compliance)
884
        if ($alt_text == '') {
885
            $alt_text = basename($image_path);
886
        }
887
888
        if (empty($additional_attributes)) {
889
            $additional_attributes = [];
890
        }
891
892
        $additional_attributes['src'] = $image_path;
893
894
        if (empty($additional_attributes['alt'])) {
895
            $additional_attributes['alt'] = $alt_text;
896
        }
897
        if (empty($additional_attributes['title'])) {
898
            $additional_attributes['title'] = $alt_text;
899
        }
900
901
        return self::tag('img', '', $additional_attributes);
902
    }
903
904
    /**
905
     * Returns the htmlcode for a tag (h3, h1, div, a, button), etc.
906
     *
907
     * @param string $tag                   the tag name
908
     * @param string $content               the tag's content
909
     * @param array  $additional_attributes (for instance height, width, onclick, ...)
910
     *
911
     * @return string
912
     *
913
     * @author Julio Montoya 2010
914
     */
915
    public static function tag($tag, $content, $additional_attributes = [])
916
    {
917
        $attribute_list = '';
918
        // Managing the additional attributes
919
        if (!empty($additional_attributes) && is_array($additional_attributes)) {
920
            foreach ($additional_attributes as $key => &$value) {
921
                $sanitized_key = htmlspecialchars($key, ENT_QUOTES, api_get_system_encoding());
922
                $sanitized_value = htmlspecialchars($value, ENT_QUOTES, api_get_system_encoding());
923
                $attribute_list .= $sanitized_key.'="'.$sanitized_value.'" ';
924
            }
925
        }
926
        //some tags don't have this </XXX>
927
        if (in_array($tag, ['img', 'input', 'br'])) {
928
            $return_value = '<'.$tag.' '.$attribute_list.' />';
929
        } else {
930
            $return_value = '<'.$tag.' '.$attribute_list.' >'.$content.'</'.$tag.'>';
931
        }
932
933
        return $return_value;
934
    }
935
936
    /**
937
     * Creates a URL anchor.
938
     *
939
     * @param string $name
940
     * @param string $url
941
     * @param array  $attributes
942
     *
943
     * @return string
944
     */
945
    public static function url($name, $url, $attributes = [])
946
    {
947
        if (!empty($url)) {
948
            $url = preg_replace('#&amp;#', '&', $url);
949
            $attributes['href'] = $url;
950
        }
951
952
        return self::tag('a', $name, $attributes);
953
    }
954
955
    /**
956
     * Creates a div tag.
957
     *
958
     * @param string $content
959
     * @param array  $attributes
960
     *
961
     * @return string
962
     */
963
    public static function div($content, $attributes = [])
964
    {
965
        return self::tag('div', $content, $attributes);
966
    }
967
968
    /**
969
     * Creates a span tag.
970
     */
971
    public static function span($content, $attributes = [])
972
    {
973
        return self::tag('span', $content, $attributes);
974
    }
975
976
    /**
977
     * Displays an HTML input tag.
978
     */
979
    public static function input($type, $name, $value, $attributes = [])
980
    {
981
        if (isset($type)) {
982
            $attributes['type'] = $type;
983
        }
984
        if (isset($name)) {
985
            $attributes['name'] = $name;
986
        }
987
        if (isset($value)) {
988
            $attributes['value'] = $value;
989
        }
990
991
        return self::tag('input', '', $attributes);
992
    }
993
994
    /**
995
     * @param $name
996
     * @param $value
997
     * @param array $attributes
998
     *
999
     * @return string
1000
     */
1001
    public static function button($name, $value, $attributes = [])
1002
    {
1003
        if (!empty($name)) {
1004
            $attributes['name'] = $name;
1005
        }
1006
1007
        return self::tag('button', $value, $attributes);
1008
    }
1009
1010
    /**
1011
     * Displays an HTML select tag.
1012
     *
1013
     * @param string $name
1014
     * @param array  $values
1015
     * @param int    $default
1016
     * @param array  $extra_attributes
1017
     * @param bool   $show_blank_item
1018
     * @param null   $blank_item_text
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $blank_item_text is correct as it would always require null to be passed?
Loading history...
1019
     *
1020
     * @return string
1021
     */
1022
    public static function select(
1023
        $name,
1024
        $values,
1025
        $default = -1,
1026
        $extra_attributes = [],
1027
        $show_blank_item = true,
1028
        $blank_item_text = ''
1029
    ) {
1030
        $html = '';
1031
        $extra = '';
1032
        $default_id = 'id="'.$name.'" ';
1033
        $extra_attributes = array_merge(['class' => 'form-control'], $extra_attributes);
1034
        foreach ($extra_attributes as $key => $parameter) {
1035
            if ($key == 'id') {
1036
                $default_id = '';
1037
            }
1038
            $extra .= $key.'="'.$parameter.'" ';
1039
        }
1040
        $html .= '<select name="'.$name.'" '.$default_id.' '.$extra.'>';
1041
1042
        if ($show_blank_item) {
1043
            if (empty($blank_item_text)) {
1044
                $blank_item_text = get_lang('Select');
1045
            } else {
1046
                $blank_item_text = Security::remove_XSS($blank_item_text);
1047
            }
1048
            $html .= self::tag(
1049
                'option',
1050
                '-- '.$blank_item_text.' --',
1051
                ['value' => '-1']
1052
            );
1053
        }
1054
        if ($values) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1055
            foreach ($values as $key => $value) {
1056
                if (is_array($value) && isset($value['name'])) {
1057
                    $value = $value['name'];
1058
                }
1059
                $html .= '<option value="'.$key.'"';
1060
1061
                if (is_array($default)) {
1062
                    foreach ($default as $item) {
1063
                        if ($item == $key) {
1064
                            $html .= ' selected="selected"';
1065
                            break;
1066
                        }
1067
                    }
1068
                } else {
1069
                    if ($default == $key) {
1070
                        $html .= ' selected="selected"';
1071
                    }
1072
                }
1073
1074
                $html .= '>'.$value.'</option>';
1075
            }
1076
        }
1077
        $html .= '</select>';
1078
1079
        return $html;
1080
    }
1081
1082
    /**
1083
     * Creates a tab menu
1084
     * Requirements: declare the jquery, jquery-ui libraries + the jquery-ui.css
1085
     * in the $htmlHeadXtra variable before the display_header
1086
     * Add this script.
1087
     *
1088
     * @example
1089
     * <script>
1090
                </script>
1091
     * @param array  $headers       list of the tab titles
1092
     * @param array  $items
1093
     * @param string $id            id of the container of the tab in the example "tabs"
1094
     * @param array  $attributes    for the ul
1095
     * @param array  $ul_attributes
1096
     * @param int    $selected
1097
     *
1098
     * @return string
1099
     */
1100
    public static function tabs(
1101
        $headers,
1102
        $items,
1103
        $id = 'tabs',
1104
        $attributes = [],
1105
        $ul_attributes = [],
1106
        $selected = ''
1107
    ) {
1108
        if (empty($headers) || count($headers) == 0) {
1109
            return '';
1110
        }
1111
1112
        $lis = '';
1113
        $i = 1;
1114
        foreach ($headers as $item) {
1115
            $active = '';
1116
            if ($i == 1) {
1117
                $active = ' active';
1118
            }
1119
1120
            if (!empty($selected)) {
1121
                $active = '';
1122
                if ($selected == $i) {
1123
                    $active = ' active';
1124
                }
1125
            }
1126
1127
            $item = self::tag(
1128
                'a',
1129
                $item,
1130
                [
1131
                    'href' => '#'.$id.'-'.$i,
1132
                    'role' => 'tab',
1133
                    'data-toggle' => 'tab',
1134
                    'id' => $id.$i,
1135
                ]
1136
            );
1137
            $ul_attributes['role'] = 'presentation';
1138
            $ul_attributes['class'] = $active;
1139
            $lis .= self::tag('li', $item, $ul_attributes);
1140
            $i++;
1141
        }
1142
1143
        $ul = self::tag(
1144
            'ul',
1145
            $lis,
1146
            [
1147
                'class' => 'nav nav-tabs tabs-margin',
1148
                'role' => 'tablist',
1149
                'id' => 'ul_'.$id,
1150
            ]
1151
        );
1152
1153
        $i = 1;
1154
        $divs = '';
1155
        foreach ($items as $content) {
1156
            $active = '';
1157
            if ($i == 1) {
1158
                $active = ' active';
1159
            }
1160
1161
            if (!empty($selected)) {
1162
                $active = '';
1163
                if ($selected == $i) {
1164
                    $active = ' active';
1165
                }
1166
            }
1167
1168
            $divs .= self::tag(
1169
                'div',
1170
                $content,
1171
                ['id' => $id.'-'.$i, 'class' => 'tab-pane '.$active, 'role' => 'tabpanel']
1172
            );
1173
            $i++;
1174
        }
1175
1176
        $attributes['id'] = $id;
1177
        $attributes['role'] = 'tabpanel';
1178
        $attributes['class'] = 'tab-wrapper';
1179
1180
        $main_div = self::tag(
1181
            'div',
1182
            $ul.self::tag('div', $divs, ['class' => 'tab-content']),
1183
            $attributes
1184
        );
1185
1186
        return $main_div;
1187
    }
1188
1189
    /**
1190
     * @param $headers
1191
     * @param null $selected
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $selected is correct as it would always require null to be passed?
Loading history...
1192
     *
1193
     * @return string
1194
     */
1195
    public static function tabsOnlyLink($headers, $selected = null)
1196
    {
1197
        $id = uniqid();
1198
        $i = 1;
1199
        $lis = null;
1200
        foreach ($headers as $item) {
1201
            $class = null;
1202
            if ($i == $selected) {
1203
                $class = 'active';
1204
            }
1205
            $item = self::tag(
1206
                'a',
1207
                $item['content'],
1208
                ['id' => $id.'-'.$i, 'href' => $item['url']]
1209
            );
1210
            $lis .= self::tag('li', $item, ['class' => $class]);
1211
            $i++;
1212
        }
1213
1214
        return self::tag(
1215
            'ul',
1216
            $lis,
1217
            ['class' => 'nav nav-tabs tabs-margin']
1218
        );
1219
    }
1220
1221
    /**
1222
     * In order to display a grid using jqgrid you have to:.
1223
     *
1224
     * @example
1225
     * After your Display::display_header function you have to add the nex javascript code:
1226
     * <script>
1227
     *   echo Display::grid_js('my_grid_name', $url,$columns, $column_model, $extra_params,[]);
1228
     *   // for more information of this function check the grid_js() function
1229
     * </script>
1230
     * //Then you have to call the grid_html
1231
     * echo Display::grid_html('my_grid_name');
1232
     * As you can see both function use the same "my_grid_name" this is very important otherwise nothing will work
1233
     *
1234
     * @param   string  the div id, this value must be the same with the first parameter of Display::grid_js()
1235
     *
1236
     * @return string html
1237
     */
1238
    public static function grid_html($div_id)
1239
    {
1240
        $table = self::tag('table', '', ['id' => $div_id]);
1241
        $table .= self::tag('div', '', ['id' => $div_id.'_pager']);
1242
1243
        return $table;
1244
    }
1245
1246
    /**
1247
     * @param string $label
1248
     * @param string $form_item
1249
     *
1250
     * @return string
1251
     */
1252
    public static function form_row($label, $form_item)
1253
    {
1254
        $label = self::tag('label', $label, ['class' => 'col-sm-2 control-label']);
1255
        $form_item = self::div($form_item, ['class' => 'col-sm-10']);
1256
1257
        return self::div($label.$form_item, ['class' => 'form-group']);
1258
    }
1259
1260
    /**
1261
     * This is a wrapper to use the jqgrid in Chamilo.
1262
     * For the other jqgrid options visit http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options
1263
     * This function need to be in the ready jquery function
1264
     * example --> $(function() { <?php echo Display::grid_js('grid' ...); ?> }
1265
     * In order to work this function needs the Display::grid_html function with the same div id.
1266
     *
1267
     * @param string $div_id       div id
1268
     * @param string $url          url where the jqgrid will ask for data (if datatype = json)
1269
     * @param array  $column_names Visible columns (you should use get_lang).
1270
     *                             An array in which we place the names of the columns.
1271
     *                             This is the text that appears in the head of the grid (Header layer).
1272
     *                             Example: colname   {name:'date',     index:'date',   width:120, align:'right'},
1273
     * @param array  $column_model the column model :  Array which describes the parameters of the columns.
1274
     *                             This is the most important part of the grid.
1275
     *                             For a full description of all valid values see colModel API. See the url above.
1276
     * @param array  $extra_params extra parameters
1277
     * @param array  $data         data that will be loaded
1278
     * @param string $formatter    A string that will be appended to the JSON returned
1279
     * @param bool   $fixed_width  not implemented yet
1280
     *
1281
     * @return string the js code
1282
     */
1283
    public static function grid_js(
1284
        $div_id,
1285
        $url,
1286
        $column_names,
1287
        $column_model,
1288
        $extra_params,
1289
        $data = [],
1290
        $formatter = '',
1291
        $fixed_width = false
1292
    ) {
1293
        $obj = new stdClass();
1294
        $obj->first = 'first';
1295
1296
        if (!empty($url)) {
1297
            $obj->url = $url;
1298
        }
1299
1300
        //This line should only be used/modified in case of having characters
1301
        // encoding problems - see #6159
1302
        //$column_names = array_map("utf8_encode", $column_names);
1303
        $obj->colNames = $column_names;
1304
        $obj->colModel = $column_model;
1305
        $obj->pager = '#'.$div_id.'_pager';
1306
        $obj->datatype = 'json';
1307
        $obj->viewrecords = 'true';
1308
        $all_value = 10000000;
1309
1310
        // Sets how many records we want to view in the grid
1311
        $obj->rowNum = 20;
1312
1313
        // Default row quantity
1314
        if (!isset($extra_params['rowList'])) {
1315
            $extra_params['rowList'] = [20, 50, 100, 500, 1000, $all_value];
1316
            $rowList = api_get_configuration_value('table_row_list');
1317
            if (!empty($rowList) && isset($rowList['options'])) {
1318
                $rowList = $rowList['options'];
1319
                $rowList[] = $all_value;
1320
                $extra_params['rowList'] = $rowList;
1321
            }
1322
        }
1323
1324
        $defaultRow = api_get_configuration_value('table_default_row');
1325
        if (!empty($defaultRow)) {
1326
            $obj->rowNum = (int) $defaultRow;
1327
        }
1328
1329
        $json = '';
1330
        if (!empty($extra_params['datatype'])) {
1331
            $obj->datatype = $extra_params['datatype'];
1332
        }
1333
1334
        // Row even odd style.
1335
        $obj->altRows = true;
1336
        if (!empty($extra_params['altRows'])) {
1337
            $obj->altRows = $extra_params['altRows'];
1338
        }
1339
1340
        if (!empty($extra_params['sortname'])) {
1341
            $obj->sortname = $extra_params['sortname'];
1342
        }
1343
1344
        if (!empty($extra_params['sortorder'])) {
1345
            $obj->sortorder = $extra_params['sortorder'];
1346
        }
1347
1348
        if (!empty($extra_params['rowList'])) {
1349
            $obj->rowList = $extra_params['rowList'];
1350
        }
1351
1352
        if (!empty($extra_params['rowNum'])) {
1353
            $obj->rowNum = $extra_params['rowNum'];
1354
        } else {
1355
            // Try to load max rows from Session
1356
            $urlInfo = parse_url($url);
1357
            if (isset($urlInfo['query'])) {
1358
                parse_str($urlInfo['query'], $query);
1359
                if (isset($query['a'])) {
1360
                    $action = $query['a'];
1361
                    // This value is set in model.ajax.php
1362
                    $savedRows = Session::read('max_rows_'.$action);
1363
                    if (!empty($savedRows)) {
1364
                        $obj->rowNum = $savedRows;
1365
                    }
1366
                }
1367
            }
1368
        }
1369
1370
        if (!empty($extra_params['viewrecords'])) {
1371
            $obj->viewrecords = $extra_params['viewrecords'];
1372
        }
1373
1374
        $beforeSelectRow = null;
1375
        if (isset($extra_params['beforeSelectRow'])) {
1376
            $beforeSelectRow = 'beforeSelectRow: '.$extra_params['beforeSelectRow'].', ';
1377
            unset($extra_params['beforeSelectRow']);
1378
        }
1379
1380
        $beforeProcessing = '';
1381
        if (isset($extra_params['beforeProcessing'])) {
1382
            $beforeProcessing = 'beforeProcessing : function() { '.$extra_params['beforeProcessing'].' },';
1383
            unset($extra_params['beforeProcessing']);
1384
        }
1385
1386
        $beforeRequest = '';
1387
        if (isset($extra_params['beforeRequest'])) {
1388
            $beforeRequest = 'beforeRequest : function() { '.$extra_params['beforeRequest'].' },';
1389
            unset($extra_params['beforeRequest']);
1390
        }
1391
1392
        $gridComplete = '';
1393
        if (isset($extra_params['gridComplete'])) {
1394
            $gridComplete = 'gridComplete : function() { '.$extra_params['gridComplete'].' },';
1395
            unset($extra_params['gridComplete']);
1396
        }
1397
1398
        // Adding extra params
1399
        if (!empty($extra_params)) {
1400
            foreach ($extra_params as $key => $element) {
1401
                // the groupHeaders key gets a special treatment
1402
                if ($key != 'groupHeaders') {
1403
                    $obj->$key = $element;
1404
                }
1405
            }
1406
        }
1407
1408
        // Adding static data.
1409
        if (!empty($data)) {
1410
            $data_var = $div_id.'_data';
1411
            $json .= ' var '.$data_var.' = '.json_encode($data).';';
1412
            $obj->data = $data_var;
1413
            $obj->datatype = 'local';
1414
            $json .= "\n";
1415
        }
1416
1417
        $obj->end = 'end';
1418
1419
        $json_encode = json_encode($obj);
1420
1421
        if (!empty($data)) {
1422
            //Converts the "data":"js_variable" to "data":js_variable,
1423
            // otherwise it will not work
1424
            $json_encode = str_replace('"data":"'.$data_var.'"', '"data":'.$data_var.'', $json_encode);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data_var does not seem to be defined for all execution paths leading up to this point.
Loading history...
1425
        }
1426
1427
        // Fixing true/false js values that doesn't need the ""
1428
        $json_encode = str_replace(':"true"', ':true', $json_encode);
1429
        // wrap_cell is not a valid jqgrid attributes is a hack to wrap a text
1430
        $json_encode = str_replace('"wrap_cell":true', 'cellattr : function(rowId, value, rowObject, colModel, arrData) { return \'class = "jqgrid_whitespace"\'; }', $json_encode);
1431
        $json_encode = str_replace(':"false"', ':false', $json_encode);
1432
        $json_encode = str_replace('"formatter":"action_formatter"', 'formatter:action_formatter', $json_encode);
1433
        $json_encode = str_replace('"formatter":"extra_formatter"', 'formatter:extra_formatter', $json_encode);
1434
        $json_encode = str_replace(['{"first":"first",', '"end":"end"}'], '', $json_encode);
1435
1436
        if (api_get_configuration_value('allow_compilatio_tool') &&
1437
            (strpos($_SERVER['REQUEST_URI'], 'work/work.php') !== false ||
1438
             strpos($_SERVER['REQUEST_URI'], 'work/work_list_all.php') != false
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($_SERVER['REQUEST...ork/work_list_all.php') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
1439
            )
1440
        ) {
1441
            $json_encode = str_replace('"function () { compilatioInit() }"',
1442
                'function () { compilatioInit() }',
1443
                $json_encode
1444
            );
1445
        }
1446
        // Creating the jqgrid element.
1447
        $json .= '$("#'.$div_id.'").jqGrid({';
1448
        //$json .= $beforeSelectRow;
1449
        $json .= $gridComplete;
1450
        $json .= $beforeProcessing;
1451
        $json .= $beforeRequest;
1452
        $json .= $json_encode;
1453
        $json .= '});';
1454
1455
        // Grouping headers option
1456
        if (isset($extra_params['groupHeaders'])) {
1457
            $groups = '';
1458
            foreach ($extra_params['groupHeaders'] as $group) {
1459
                //{ "startColumnName" : "courses", "numberOfColumns" : 1, "titleText" : "Order Info" },
1460
                $groups .= '{ "startColumnName" : "'.$group['startColumnName'].'", "numberOfColumns" : '.$group['numberOfColumns'].', "titleText" : "'.$group['titleText'].'" },';
1461
            }
1462
            $json .= '$("#'.$div_id.'").jqGrid("setGroupHeaders", {
1463
                "useColSpanStyle" : false,
1464
                "groupHeaders"    : [
1465
                    '.$groups.'
1466
                ]
1467
            });';
1468
        }
1469
1470
        $all_text = addslashes(get_lang('All'));
1471
        $json .= '$("'.$obj->pager.' option[value='.$all_value.']").text("'.$all_text.'");';
1472
        $json .= "\n";
1473
        // Adding edit/delete icons.
1474
        $json .= $formatter;
1475
1476
        return $json;
1477
    }
1478
1479
    /**
1480
     * @param array $headers
1481
     * @param array $rows
1482
     * @param array $attributes
1483
     *
1484
     * @return string
1485
     */
1486
    public static function table($headers, $rows, $attributes = [])
1487
    {
1488
        if (empty($attributes)) {
1489
            $attributes['class'] = 'table table-hover table-striped data_table';
1490
        }
1491
        $table = new HTML_Table($attributes);
1492
        $row = 0;
1493
        $column = 0;
1494
1495
        // Course headers
1496
        if (!empty($headers)) {
1497
            foreach ($headers as $item) {
1498
                $table->setHeaderContents($row, $column, $item);
1499
                $column++;
1500
            }
1501
            $row = 1;
1502
            $column = 0;
1503
        }
1504
1505
        if (!empty($rows)) {
1506
            foreach ($rows as $content) {
1507
                $table->setCellContents($row, $column, $content);
1508
                $row++;
1509
            }
1510
        }
1511
1512
        return $table->toHtml();
1513
    }
1514
1515
    /**
1516
     * Returns the "what's new" icon notifications.
1517
     *
1518
     * The general logic of this function is to track the last time the user
1519
     * entered the course and compare to what has changed inside this course
1520
     * since then, based on the item_property table inside this course. Note that,
1521
     * if the user never entered the course before, he will not see notification
1522
     * icons. This function takes session ID into account (if any) and only shows
1523
     * the corresponding notifications.
1524
     *
1525
     * @param array $courseInfo Course information array, containing at least elements 'db' and 'k'
1526
     * @param bool  $loadAjax
1527
     *
1528
     * @return string The HTML link to be shown next to the course
1529
     */
1530
    public static function show_notification($courseInfo, $loadAjax = true)
1531
    {
1532
        if (empty($courseInfo)) {
1533
            return '';
1534
        }
1535
1536
        $t_track_e_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
1537
        $course_tool_table = Database::get_course_table(TABLE_TOOL_LIST);
1538
        $tool_edit_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
1539
        $course_code = Database::escape_string($courseInfo['code']);
1540
1541
        $user_id = api_get_user_id();
1542
        $course_id = (int) $courseInfo['real_id'];
1543
        $sessionId = (int) $courseInfo['id_session'];
1544
        $status = (int) $courseInfo['status'];
1545
1546
        $loadNotificationsByAjax = api_get_configuration_value('user_portal_load_notification_by_ajax');
1547
1548
        if ($loadNotificationsByAjax) {
1549
            if ($loadAjax) {
1550
                $id = 'notification_'.$course_id.'_'.$sessionId.'_'.$status;
1551
                Session::write($id, true);
1552
1553
                return '<span id ="'.$id.'" class="course_notification"></span>';
1554
            }
1555
        }
1556
1557
        // Get the user's last access dates to all tools of this course
1558
        $sql = "SELECT *
1559
                FROM $t_track_e_access
1560
                WHERE
1561
                    c_id = $course_id AND
1562
                    access_user_id = '$user_id' AND
1563
                    access_session_id ='".$sessionId."'
1564
                ORDER BY access_date DESC
1565
                LIMIT 1
1566
                ";
1567
        $result = Database::query($sql);
1568
1569
        // latest date by default is the creation date
1570
        $latestDate = $courseInfo['creation_date'];
1571
        if (Database::num_rows($result)) {
1572
            $row = Database::fetch_array($result, 'ASSOC');
1573
            $latestDate = $row['access_date'];
1574
        }
1575
        // Get a timestamp format copy, for use in c_lp_item_view below
1576
        $originalTimeZone = date_default_timezone_get();
1577
        date_default_timezone_set('UTC');
1578
        $latestTimestamp = strtotime($latestDate);
1579
        date_default_timezone_set($originalTimeZone);
1580
1581
        $sessionCondition = api_get_session_condition(
1582
            $sessionId,
1583
            true,
1584
            false,
1585
            'session_id'
1586
        );
1587
1588
        $hideTools = [TOOL_NOTEBOOK, TOOL_CHAT];
1589
        // Get current tools in course
1590
        $sql = "SELECT name, link, image
1591
                FROM $course_tool_table
1592
                WHERE
1593
                    c_id = $course_id AND
1594
                    visibility = '1' AND
1595
                    name NOT IN ('".implode("','", $hideTools)."')
1596
                ";
1597
        $result = Database::query($sql);
1598
        $tools = Database::store_result($result);
1599
1600
        $group_ids = GroupManager::get_group_ids($courseInfo['real_id'], $user_id);
1601
        $group_ids[] = 0; //add group 'everyone'
1602
        $notifications = [];
1603
        if ($tools) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tools of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1604
            $latestLocalDate = $latestDate;
1605
            foreach ($tools as $tool) {
1606
                $toolName = $tool['name'];
1607
                $toolName = Database::escape_string($toolName);
1608
                // Fix to get student publications
1609
                $toolCondition = " tool = '$toolName' AND ";
1610
                if ($toolName == 'student_publication' || $toolName == 'work') {
1611
                    $toolCondition = " (tool = 'work' OR tool = 'student_publication') AND ";
1612
                }
1613
                if ($toolName == 'learnpath') {
1614
                    // Make sure c_lp_item_view is considered in the latestDate calculation for LPs
1615
                    $lpLatest = self::getLatestLpView($course_id, $user_id, $sessionId, $latestTimestamp);
1616
                    if (!empty($lpLatest)) {
1617
                        $latestLocalDate = $lpLatest;
1618
                    }
1619
                }
1620
1621
                $toolName = addslashes($toolName);
1622
1623
                $sql = "SELECT * FROM $tool_edit_table
1624
                        WHERE
1625
                            c_id = $course_id AND
1626
                            $toolCondition
1627
                            lastedit_type NOT LIKE '%Deleted%' AND
1628
                            lastedit_type NOT LIKE '%deleted%' AND
1629
                            lastedit_type NOT LIKE '%DocumentInvisible%' AND
1630
                            lastedit_date > '$latestLocalDate' AND
1631
                            lastedit_user_id != $user_id $sessionCondition AND
1632
                            visibility != 2 AND
1633
                            (to_user_id IN ('$user_id', '0') OR to_user_id IS NULL) AND
1634
                            (to_group_id IN ('".implode("','", $group_ids)."') OR to_group_id IS NULL)
1635
                        ORDER BY lastedit_date DESC
1636
                        LIMIT 1";
1637
                $result = Database::query($sql);
1638
1639
                $latestChange = Database::fetch_array($result, 'ASSOC');
1640
1641
                if ($latestChange) {
1642
                    $latestChange['link'] = $tool['link'];
1643
                    $latestChange['image'] = $tool['image'];
1644
                    $latestChange['tool'] = $tool['name'];
1645
                    $notifications[$toolName] = $latestChange;
1646
                }
1647
            }
1648
        }
1649
1650
        // Show all tool icons where there is something new.
1651
        $return = '';
1652
        foreach ($notifications as $notification) {
1653
            $toolName = $notification['tool'];
1654
            if (!(
1655
                    $notification['visibility'] == '1' ||
1656
                    ($status == '1' && $notification['visibility'] == '0') ||
1657
                    !isset($notification['visibility'])
1658
                )
1659
            ) {
1660
                continue;
1661
            }
1662
1663
            if ($toolName == TOOL_SURVEY) {
1664
                $survey_info = SurveyManager::get_survey($notification['ref'], 0, $course_code);
1665
                if (!empty($survey_info)) {
1666
                    $invited_users = SurveyUtil::get_invited_users(
1667
                        $survey_info['code'],
1668
                        $course_code
1669
                    );
1670
                    if (!in_array($user_id, $invited_users['course_users'])) {
1671
                        continue;
1672
                    }
1673
                }
1674
            }
1675
1676
            if ($notification['tool'] == TOOL_LEARNPATH) {
1677
                if (!learnpath::is_lp_visible_for_student($notification['ref'], $user_id, $courseInfo)) {
1678
                    continue;
1679
                }
1680
            }
1681
1682
            if ($notification['tool'] == TOOL_DROPBOX) {
1683
                $notification['link'] = 'dropbox/dropbox_download.php?id='.$notification['ref'];
1684
            }
1685
1686
            if ($notification['tool'] == 'work' &&
1687
                $notification['lastedit_type'] == 'DirectoryCreated'
1688
            ) {
1689
                $notification['lastedit_type'] = 'WorkAdded';
1690
            }
1691
1692
            $lastDate = api_get_local_time($notification['lastedit_date']);
1693
            $type = $notification['lastedit_type'];
1694
            if ($type == 'CalendareventVisible') {
1695
                $type = 'Visible';
1696
            }
1697
            $label = get_lang('TitleNotification').": ".get_lang($type)." ($lastDate)";
1698
1699
            if (strpos($notification['link'], '?') === false) {
1700
                $notification['link'] = $notification['link'].'?notification=1';
1701
            } else {
1702
                $notification['link'] = $notification['link'].'&notification=1';
1703
            }
1704
1705
            $image = substr($notification['image'], 0, -4).'.png';
1706
1707
            $return .= self::url(
1708
                self::return_icon($image, $label),
1709
                api_get_path(WEB_CODE_PATH).
1710
                $notification['link'].'&cidReq='.$course_code.
1711
                '&ref='.$notification['ref'].
1712
                '&gidReq='.$notification['to_group_id'].
1713
                '&id_session='.$sessionId
1714
            ).PHP_EOL;
1715
        }
1716
1717
        return $return;
1718
    }
1719
1720
    /**
1721
     * Get the session box details as an array.
1722
     *
1723
     * @param int       Session ID
1724
     *
1725
     * @return array Empty array or session array
1726
     *               ['title'=>'...','category'=>'','dates'=>'...','coach'=>'...','active'=>true/false,'session_category_id'=>int]
1727
     */
1728
    public static function getSessionTitleBox($session_id)
1729
    {
1730
        global $nosession;
1731
1732
        if (!$nosession) {
1733
            global $now, $date_start, $date_end;
1734
        }
1735
        $output = [];
1736
        if (!$nosession) {
1737
            $session_info = api_get_session_info($session_id);
1738
            $coachInfo = [];
1739
            if (!empty($session_info['id_coach'])) {
1740
                $coachInfo = api_get_user_info($session_info['id_coach']);
1741
            }
1742
1743
            $session = [];
1744
            $session['category_id'] = $session_info['session_category_id'];
1745
            $session['title'] = $session_info['name'];
1746
            $session['coach_id'] = $session['id_coach'] = $session_info['id_coach'];
1747
            $session['dates'] = '';
1748
            $session['coach'] = '';
1749
            if (api_get_setting('show_session_coach') === 'true' && isset($coachInfo['complete_name'])) {
1750
                $session['coach'] = get_lang('GeneralCoach').': '.$coachInfo['complete_name'];
1751
            }
1752
1753
            if (($session_info['access_end_date'] == '0000-00-00 00:00:00' &&
1754
                $session_info['access_start_date'] == '0000-00-00 00:00:00') ||
1755
                (empty($session_info['access_end_date']) && empty($session_info['access_start_date']))
1756
            ) {
1757
                if (isset($session_info['duration']) && !empty($session_info['duration'])) {
1758
                    $daysLeft = SessionManager::getDayLeftInSession($session_info, api_get_user_id());
1759
                    $session['duration'] = $daysLeft >= 0
1760
                        ? sprintf(get_lang('SessionDurationXDaysLeft'), $daysLeft)
1761
                        : get_lang('YourSessionTimeHasExpired');
1762
                }
1763
                $active = true;
1764
            } else {
1765
                $dates = SessionManager::parseSessionDates($session_info, true);
1766
                $session['dates'] = $dates['access'];
1767
                if (api_get_setting('show_session_coach') === 'true' && isset($coachInfo['complete_name'])) {
1768
                    $session['coach'] = $coachInfo['complete_name'];
1769
                }
1770
                $active = $date_start <= $now && $date_end >= $now;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $date_end does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $date_start does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $now does not seem to be defined for all execution paths leading up to this point.
Loading history...
1771
            }
1772
            $session['active'] = $active;
1773
            $session['session_category_id'] = $session_info['session_category_id'];
1774
            $session['visibility'] = $session_info['visibility'];
1775
            $session['num_users'] = $session_info['nbr_users'];
1776
            $session['num_courses'] = $session_info['nbr_courses'];
1777
            $session['description'] = $session_info['description'];
1778
            $session['show_description'] = $session_info['show_description'];
1779
1780
            $entityManager = Database::getManager();
1781
            $fieldValuesRepo = $entityManager->getRepository('ChamiloCoreBundle:ExtraFieldValues');
1782
            $extraFieldValues = $fieldValuesRepo->getVisibleValues(
1783
                ExtraField::SESSION_FIELD_TYPE,
1784
                $session_id
1785
            );
1786
1787
            $session['extra_fields'] = [];
1788
            foreach ($extraFieldValues as $value) {
1789
                $session['extra_fields'][] = [
1790
                    'field' => [
1791
                        'variable' => $value->getField()->getVariable(),
1792
                        'display_text' => $value->getField()->getDisplayText(),
1793
                    ],
1794
                    'value' => $value->getValue(),
1795
                ];
1796
            }
1797
1798
            $output = $session;
1799
        }
1800
1801
        return $output;
1802
    }
1803
1804
    /**
1805
     * Return the five star HTML.
1806
     *
1807
     * @param string $id              of the rating ul element
1808
     * @param string $url             that will be added (for jquery see hot_courses.tpl)
1809
     * @param array  $point_info      point info array see function CourseManager::get_course_ranking()
1810
     * @param bool   $add_div_wrapper add a div wrapper
1811
     *
1812
     * @return string
1813
     */
1814
    public static function return_rating_system(
1815
        $id,
1816
        $url,
1817
        $point_info = [],
1818
        $add_div_wrapper = true
1819
    ) {
1820
        $number_of_users_who_voted = isset($point_info['users_who_voted']) ? $point_info['users_who_voted'] : null;
1821
        $percentage = isset($point_info['point_average']) ? $point_info['point_average'] : 0;
1822
1823
        if (!empty($percentage)) {
1824
            $percentage = $percentage * 125 / 100;
1825
        }
1826
        $accesses = isset($point_info['accesses']) ? $point_info['accesses'] : 0;
1827
        $star_label = sprintf(get_lang('XStarsOutOf5'), $point_info['point_average_star']);
1828
1829
        $html = '<ul id="'.$id.'" class="star-rating">
1830
                    <li class="current-rating" style="width:'.$percentage.'px;"></li>
1831
                    <li><a href="javascript:void(0);" data-link="'.$url.'&amp;star=1" title="'.$star_label.'" class="one-star">1</a></li>
1832
                    <li><a href="javascript:void(0);" data-link="'.$url.'&amp;star=2" title="'.$star_label.'" class="two-stars">2</a></li>
1833
                    <li><a href="javascript:void(0);" data-link="'.$url.'&amp;star=3" title="'.$star_label.'" class="three-stars">3</a></li>
1834
                    <li><a href="javascript:void(0);" data-link="'.$url.'&amp;star=4" title="'.$star_label.'" class="four-stars">4</a></li>
1835
                    <li><a href="javascript:void(0);" data-link="'.$url.'&amp;star=5" title="'.$star_label.'" class="five-stars">5</a></li>
1836
                </ul>';
1837
1838
        $labels = [];
1839
1840
        $labels[] = $number_of_users_who_voted == 1 ? $number_of_users_who_voted.' '.get_lang('Vote') : $number_of_users_who_voted.' '.get_lang('Votes');
1841
        $labels[] = $accesses == 1 ? $accesses.' '.get_lang('Visit') : $accesses.' '.get_lang('Visits');
1842
        $labels[] = $point_info['user_vote'] ? get_lang('YourVote').' ['.$point_info['user_vote'].']' : get_lang('YourVote').' [?] ';
1843
1844
        if (!$add_div_wrapper && api_is_anonymous()) {
1845
            $labels[] = self::tag('span', get_lang('LoginToVote'), ['class' => 'error']);
1846
        }
1847
1848
        $html .= self::div(implode(' | ', $labels), ['id' => 'vote_label_'.$id, 'class' => 'vote_label_info']);
1849
        $html .= ' '.self::span(' ', ['id' => 'vote_label2_'.$id]);
1850
1851
        if ($add_div_wrapper) {
1852
            $html = self::div($html, ['id' => 'rating_wrapper_'.$id]);
1853
        }
1854
1855
        return $html;
1856
    }
1857
1858
    /**
1859
     * @param string $title
1860
     * @param string $second_title
1861
     * @param string $size
1862
     * @param bool   $filter
1863
     *
1864
     * @return string
1865
     */
1866
    public static function page_header($title, $second_title = null, $size = 'h2', $filter = true)
1867
    {
1868
        if ($filter) {
1869
            $title = Security::remove_XSS($title);
1870
        }
1871
1872
        if (!empty($second_title)) {
1873
            if ($filter) {
1874
                $second_title = Security::remove_XSS($second_title);
1875
            }
1876
            $title .= "<small> $second_title</small>";
1877
        }
1878
1879
        return '<'.$size.' class="page-header">'.$title.'</'.$size.'>';
1880
    }
1881
1882
    public static function page_header_and_translate($title, $second_title = null)
1883
    {
1884
        $title = get_lang($title);
1885
1886
        return self::page_header($title, $second_title);
1887
    }
1888
1889
    public static function page_subheader_and_translate($title, $second_title = null)
1890
    {
1891
        $title = get_lang($title);
1892
1893
        return self::page_subheader($title, $second_title);
1894
    }
1895
1896
    public static function page_subheader($title, $second_title = null, $size = 'h3', $attributes = [])
1897
    {
1898
        if (!empty($second_title)) {
1899
            $second_title = Security::remove_XSS($second_title);
1900
            $title .= "<small> $second_title<small>";
1901
        }
1902
        $attributes['class'] = 'page-header';
1903
1904
        return self::tag($size, Security::remove_XSS($title), $attributes);
1905
    }
1906
1907
    public static function page_subheader2($title, $second_title = null)
1908
    {
1909
        return self::page_header($title, $second_title, 'h4');
1910
    }
1911
1912
    public static function page_subheader3($title, $second_title = null)
1913
    {
1914
        return self::page_header($title, $second_title, 'h5');
1915
    }
1916
1917
    /**
1918
     * @param array $list
1919
     *
1920
     * @return string|null
1921
     */
1922
    public static function description($list)
1923
    {
1924
        $html = null;
1925
        if (!empty($list)) {
1926
            $html = '<dl class="dl-horizontal">';
1927
            foreach ($list as $item) {
1928
                $html .= '<dt>'.$item['title'].'</dt>';
1929
                $html .= '<dd>'.$item['content'].'</dd>';
1930
            }
1931
            $html .= '</dl>';
1932
        }
1933
1934
        return $html;
1935
    }
1936
1937
    /**
1938
     * @param int    $percentage      int value between 0 and 100
1939
     * @param bool   $show_percentage
1940
     * @param string $extra_info
1941
     * @param string $class           danger/success/infowarning
1942
     *
1943
     * @return string
1944
     */
1945
    public static function bar_progress($percentage, $show_percentage = true, $extra_info = '', $class = '')
1946
    {
1947
        $percentage = (int) $percentage;
1948
        $class = empty($class) ? '' : "progress-bar-$class";
1949
1950
        $div = '<div class="progress">
1951
                <div
1952
                    class="progress-bar progress-bar-striped '.$class.'"
1953
                    role="progressbar"
1954
                    aria-valuenow="'.$percentage.'"
1955
                    aria-valuemin="0"
1956
                    aria-valuemax="100"
1957
                    style="width: '.$percentage.'%;"
1958
                >';
1959
        if ($show_percentage) {
1960
            $div .= $percentage.'%';
1961
        } else {
1962
            if (!empty($extra_info)) {
1963
                $div .= $extra_info;
1964
            }
1965
        }
1966
        $div .= '</div></div>';
1967
1968
        return $div;
1969
    }
1970
1971
    /**
1972
     * @param string $count
1973
     * @param string $type
1974
     *
1975
     * @return string|null
1976
     */
1977
    public static function badge($count, $type = "warning")
1978
    {
1979
        $class = '';
1980
1981
        switch ($type) {
1982
            case 'success':
1983
                $class = 'badge-success';
1984
                break;
1985
            case 'warning':
1986
                $class = 'badge-warning';
1987
                break;
1988
            case 'important':
1989
                $class = 'badge-important';
1990
                break;
1991
            case 'info':
1992
                $class = 'badge-info';
1993
                break;
1994
            case 'inverse':
1995
                $class = 'badge-inverse';
1996
                break;
1997
        }
1998
1999
        if (!empty($count)) {
2000
            return ' <span class="badge '.$class.'">'.$count.'</span>';
2001
        }
2002
2003
        return null;
2004
    }
2005
2006
    /**
2007
     * @param array $badge_list
2008
     *
2009
     * @return string
2010
     */
2011
    public static function badge_group($badge_list)
2012
    {
2013
        $html = '<div class="badge-group">';
2014
        foreach ($badge_list as $badge) {
2015
            $html .= $badge;
2016
        }
2017
        $html .= '</div>';
2018
2019
        return $html;
2020
    }
2021
2022
    /**
2023
     * @param string $content
2024
     * @param string $type
2025
     *
2026
     * @return string
2027
     */
2028
    public static function label($content, $type = 'default')
2029
    {
2030
        switch ($type) {
2031
            case 'success':
2032
                $class = 'label-success';
2033
                break;
2034
            case 'warning':
2035
                $class = 'label-warning';
2036
                break;
2037
            case 'important':
2038
            case 'danger':
2039
                $class = 'label-danger';
2040
                break;
2041
            case 'info':
2042
                $class = 'label-info';
2043
                break;
2044
            case 'primary':
2045
                $class = 'label-primary';
2046
                break;
2047
            default:
2048
                $class = 'label-default';
2049
                break;
2050
        }
2051
2052
        $html = '';
2053
        if (!empty($content)) {
2054
            $html = '<span class="label '.$class.'">';
2055
            $html .= $content;
2056
            $html .= '</span>';
2057
        }
2058
2059
        return $html;
2060
    }
2061
2062
    /**
2063
     * @param array  $items
2064
     * @param string $class
2065
     *
2066
     * @return string|null
2067
     */
2068
    public static function actions($items, $class = 'new_actions')
2069
    {
2070
        $html = null;
2071
        if (!empty($items)) {
2072
            $html = '<div class="'.$class.'"><ul class="nav nav-pills">';
2073
            foreach ($items as $value) {
2074
                $class = null;
2075
                if (isset($value['active']) && $value['active']) {
2076
                    $class = 'class ="active"';
2077
                }
2078
2079
                if (basename($_SERVER['REQUEST_URI']) == basename($value['url'])) {
2080
                    $class = 'class ="active"';
2081
                }
2082
                $html .= "<li $class >";
2083
                $attributes = isset($value['url_attributes']) ? $value['url_attributes'] : [];
2084
                $html .= self::url($value['content'], $value['url'], $attributes);
2085
                $html .= '</li>';
2086
            }
2087
            $html .= '</ul></div>';
2088
            $html .= '<br />';
2089
        }
2090
2091
        return $html;
2092
    }
2093
2094
    /**
2095
     * Prints a tooltip.
2096
     *
2097
     * @param string $text
2098
     * @param string $tip
2099
     *
2100
     * @return string
2101
     */
2102
    public static function tip($text, $tip, string $tag = 'span')
2103
    {
2104
        if (empty($tip)) {
2105
            return $text;
2106
        }
2107
2108
        return self::tag(
2109
            $tag,
2110
            $text,
2111
            ['class' => 'boot-tooltip', 'title' => strip_tags($tip)]
2112
        );
2113
    }
2114
2115
    /**
2116
     * @param array  $items
2117
     * @param string $type
2118
     * @param null   $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
2119
     *
2120
     * @return string|null
2121
     */
2122
    public static function generate_accordion($items, $type = 'jquery', $id = null)
2123
    {
2124
        $html = null;
2125
        if (!empty($items)) {
2126
            if (empty($id)) {
2127
                $id = api_get_unique_id();
2128
            }
2129
            if ($type == 'jquery') {
2130
                $html = '<div class="accordion_jquery" id="'.$id.'">'; //using jquery
2131
            } else {
2132
                $html = '<div class="accordion" id="'.$id.'">'; //using bootstrap
2133
            }
2134
2135
            $count = 1;
2136
            foreach ($items as $item) {
2137
                $html .= '<div class="accordion-my-group">';
2138
                $html .= '<div class="accordion-heading">
2139
                            <a class="accordion-toggle" data-toggle="collapse" data-parent="#'.$id.'" href="#collapse'.$count.'">
2140
                            '.$item['title'].'
2141
                            </a>
2142
                          </div>';
2143
2144
                $html .= '<div id="collapse'.$count.'" class="accordion-body">';
2145
                $html .= '<div class="accordion-my-inner">
2146
                            '.$item['content'].'
2147
                            </div>
2148
                          </div>';
2149
            }
2150
            $html .= '</div>';
2151
        }
2152
2153
        return $html;
2154
    }
2155
2156
    /**
2157
     * @param array $buttons
2158
     *
2159
     * @return string
2160
     */
2161
    public static function groupButton($buttons)
2162
    {
2163
        $html = '<div class="btn-group" role="group">';
2164
        foreach ($buttons as $button) {
2165
            $html .= $button;
2166
        }
2167
        $html .= '</div>';
2168
2169
        return $html;
2170
    }
2171
2172
    /**
2173
     * @todo use twig
2174
     *
2175
     * @param string $title
2176
     * @param array  $elements
2177
     * @param bool   $alignToRight
2178
     *
2179
     * @return string
2180
     */
2181
    public static function groupButtonWithDropDown($title, $elements, $alignToRight = false)
2182
    {
2183
        $html = '<div class="btn-group">
2184
                <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
2185
                '.$title.'
2186
                <span class="caret"></span></button>
2187
                <ul class="dropdown-menu '.($alignToRight ? 'dropdown-menu-right' : '').'">';
2188
        foreach ($elements as $item) {
2189
            $html .= self::tag('li', self::url($item['title'], $item['href']));
2190
        }
2191
        $html .= '</ul>
2192
            </div>';
2193
2194
        return $html;
2195
    }
2196
2197
    /**
2198
     * @param string $file
2199
     * @param array  $params
2200
     *
2201
     * @return string|null
2202
     */
2203
    public static function getMediaPlayer($file, $params = [])
2204
    {
2205
        $fileInfo = pathinfo($file);
2206
2207
        $autoplay = isset($params['autoplay']) && 'true' === $params['autoplay'] ? 'autoplay' : '';
2208
        $id = isset($params['id']) ? $params['id'] : $fileInfo['basename'];
2209
        $width = isset($params['width']) ? 'width="'.$params['width'].'"' : null;
2210
        $class = isset($params['class']) ? ' class="'.$params['class'].'"' : null;
2211
2212
        switch ($fileInfo['extension']) {
2213
            case 'mp3':
2214
            case 'webm':
2215
                $html = '<audio id="'.$id.'" '.$class.' controls '.$autoplay.' '.$width.' src="'.$params['url'].'">';
2216
                $html .= '<object width="'.$width.'" height="50" type="application/x-shockwave-flash" data="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf">
2217
                            <param name="movie" value="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf" />
2218
                            <param name="flashvars" value="controls=true&file='.$params['url'].'" />
2219
                          </object>';
2220
                $html .= '</audio>';
2221
2222
                return $html;
2223
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2224
            case 'm4a':  // for ipad ios26
2225
                $html = '<audio id="'.$id.'" '.$class.' controls '.$autoplay.' '.$width.' src="'.$params['url'].'">';
2226
            case 'wav':
2227
            case 'ogg':
2228
                $html = '<audio width="300px" controls id="'.$id.'" '.$autoplay.' src="'.$params['url'].'"></audio>';
2229
2230
                return $html;
2231
                break;
2232
        }
2233
2234
        return null;
2235
    }
2236
2237
    /**
2238
     * @param int    $nextValue
2239
     * @param array  $list
2240
     * @param int    $current
2241
     * @param int    $fixedValue
2242
     * @param array  $conditions
2243
     * @param string $link
2244
     * @param bool   $isMedia
2245
     * @param bool   $addHeaders
2246
     * @param array  $linkAttributes
2247
     *
2248
     * @return string
2249
     */
2250
    public static function progressPaginationBar(
2251
        $nextValue,
2252
        $list,
2253
        $current,
2254
        $fixedValue = null,
2255
        $conditions = [],
2256
        $link = null,
2257
        $isMedia = false,
2258
        $addHeaders = true,
2259
        $linkAttributes = []
2260
    ) {
2261
        if ($addHeaders) {
2262
            $pagination_size = 'pagination-mini';
2263
            $html = '<div class="exercise_pagination pagination '.$pagination_size.'"><ul>';
2264
        } else {
2265
            $html = null;
2266
        }
2267
        $affectAllItems = false;
2268
        if ($isMedia && isset($fixedValue) && ($nextValue + 1 == $current)) {
2269
            $affectAllItems = true;
2270
        }
2271
        $localCounter = 0;
2272
        foreach ($list as $itemId) {
2273
            $isCurrent = false;
2274
            if ($affectAllItems) {
2275
                $isCurrent = true;
2276
            } else {
2277
                if (!$isMedia) {
2278
                    $isCurrent = $current == ($localCounter + $nextValue + 1) ? true : false;
2279
                }
2280
            }
2281
            $html .= self::parsePaginationItem(
2282
                $itemId,
2283
                $isCurrent,
2284
                $conditions,
2285
                $link,
2286
                $nextValue,
2287
                $isMedia,
2288
                $localCounter,
2289
                $fixedValue,
2290
                $linkAttributes
2291
            );
2292
            $localCounter++;
2293
        }
2294
        if ($addHeaders) {
2295
            $html .= '</ul></div>';
2296
        }
2297
2298
        return $html;
2299
    }
2300
2301
    /**
2302
     * @param int    $itemId
2303
     * @param bool   $isCurrent
2304
     * @param array  $conditions
2305
     * @param string $link
2306
     * @param int    $nextValue
2307
     * @param bool   $isMedia
2308
     * @param int    $localCounter
2309
     * @param int    $fixedValue
2310
     * @param array  $linkAttributes
2311
     *
2312
     * @return string
2313
     */
2314
    public static function parsePaginationItem(
2315
        $itemId,
2316
        $isCurrent,
2317
        $conditions,
2318
        $link,
2319
        $nextValue = 0,
2320
        $isMedia = false,
2321
        $localCounter = null,
2322
        $fixedValue = null,
2323
        $linkAttributes = []
2324
    ) {
2325
        $defaultClass = 'before';
2326
        $class = $defaultClass;
2327
        foreach ($conditions as $condition) {
2328
            $array = isset($condition['items']) ? $condition['items'] : [];
2329
            $class_to_applied = $condition['class'];
2330
            $type = isset($condition['type']) ? $condition['type'] : 'positive';
2331
            $mode = isset($condition['mode']) ? $condition['mode'] : 'add';
2332
            switch ($type) {
2333
                case 'positive':
2334
                    if (in_array($itemId, $array)) {
2335
                        if ($mode == 'overwrite') {
2336
                            $class = " $defaultClass $class_to_applied";
2337
                        } else {
2338
                            $class .= " $class_to_applied";
2339
                        }
2340
                    }
2341
                    break;
2342
                case 'negative':
2343
                    if (!in_array($itemId, $array)) {
2344
                        if ($mode == 'overwrite') {
2345
                            $class = " $defaultClass $class_to_applied";
2346
                        } else {
2347
                            $class .= " $class_to_applied";
2348
                        }
2349
                    }
2350
                    break;
2351
            }
2352
        }
2353
        if ($isCurrent) {
2354
            $class = 'before current';
2355
        }
2356
        if ($isMedia && $isCurrent) {
2357
            $class = 'before current';
2358
        }
2359
        if (empty($link)) {
2360
            $link_to_show = '#';
2361
        } else {
2362
            $link_to_show = $link.($nextValue + $localCounter);
2363
        }
2364
        $label = $nextValue + $localCounter + 1;
2365
        if ($isMedia) {
2366
            $label = ($fixedValue + 1).' '.chr(97 + $localCounter);
2367
            $link_to_show = $link.$fixedValue.'#questionanchor'.$itemId;
2368
        }
2369
        $link = self::url($label.' ', $link_to_show, $linkAttributes);
2370
2371
        return '<li class = "'.$class.'">'.$link.'</li>';
2372
    }
2373
2374
    /**
2375
     * @param int $current
2376
     * @param int $total
2377
     *
2378
     * @return string
2379
     */
2380
    public static function paginationIndicator($current, $total)
2381
    {
2382
        $html = null;
2383
        if (!empty($current) && !empty($total)) {
2384
            $label = sprintf(get_lang('PaginationXofY'), $current, $total);
2385
            $html = self::url($label, '#', ['class' => 'btn disabled']);
2386
        }
2387
2388
        return $html;
2389
    }
2390
2391
    /**
2392
     * @param $url
2393
     * @param $currentPage
2394
     * @param $pagesCount
2395
     * @param $totalItems
2396
     *
2397
     * @return string
2398
     */
2399
    public static function getPagination($url, $currentPage, $pagesCount, $totalItems)
2400
    {
2401
        $pagination = '';
2402
        if ($totalItems > 1 && $pagesCount > 1) {
2403
            $pagination .= '<ul class="pagination">';
2404
            for ($i = 0; $i < $pagesCount; $i++) {
2405
                $newPage = $i + 1;
2406
                if ($currentPage == $newPage) {
2407
                    $pagination .= '<li class="active"><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2408
                } else {
2409
                    $pagination .= '<li><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2410
                }
2411
            }
2412
            $pagination .= '</ul>';
2413
        }
2414
2415
        return $pagination;
2416
    }
2417
2418
    /**
2419
     * Adds a message in the queue.
2420
     *
2421
     * @param string $message
2422
     */
2423
    public static function addFlash($message)
2424
    {
2425
        $messages = Session::read('flash_messages');
2426
        if (empty($messages)) {
2427
            $messages[] = $message;
2428
        } else {
2429
            array_push($messages, $message);
2430
        }
2431
        Session::write('flash_messages', $messages);
2432
    }
2433
2434
    /**
2435
     * @return string
2436
     */
2437
    public static function getFlashToString()
2438
    {
2439
        $messages = Session::read('flash_messages');
2440
        $messageToString = '';
2441
        if (!empty($messages)) {
2442
            foreach ($messages as $message) {
2443
                $messageToString .= $message;
2444
            }
2445
        }
2446
2447
        return $messageToString;
2448
    }
2449
2450
    /**
2451
     * Shows the message from the session.
2452
     */
2453
    public static function showFlash()
2454
    {
2455
        echo self::getFlashToString();
2456
    }
2457
2458
    /**
2459
     * Destroys the message session.
2460
     */
2461
    public static function cleanFlashMessages()
2462
    {
2463
        Session::erase('flash_messages');
2464
    }
2465
2466
    /**
2467
     * Get the profile edition link for a user.
2468
     *
2469
     * @param int  $userId  The user id
2470
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
2471
     *
2472
     * @return string The link
2473
     */
2474
    public static function getProfileEditionLink($userId, $asAdmin = false)
2475
    {
2476
        $editProfileUrl = api_get_path(WEB_CODE_PATH).'auth/profile.php';
2477
        if ($asAdmin) {
2478
            $editProfileUrl = api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=".intval($userId);
2479
        }
2480
2481
        if (api_get_setting('sso_authentication') === 'true') {
2482
            $subSSOClass = api_get_setting('sso_authentication_subclass');
2483
            $objSSO = null;
2484
2485
            if (!empty($subSSOClass)) {
2486
                $file = api_get_path(SYS_CODE_PATH)."auth/sso/sso.$subSSOClass.class.php";
2487
                if (file_exists($file)) {
2488
                    require_once $file;
2489
                    $subSSOClass = 'sso'.$subSSOClass;
2490
                    $objSSO = new $subSSOClass();
2491
                } else {
2492
                    throw new Exception("$subSSOClass file not set");
2493
                }
2494
            } else {
2495
                $objSSO = new sso();
2496
            }
2497
2498
            $editProfileUrl = $objSSO->generateProfileEditingURL(
2499
                $userId,
2500
                $asAdmin
2501
            );
2502
        }
2503
2504
        return $editProfileUrl;
2505
    }
2506
2507
    /**
2508
     * Get the vCard for a user.
2509
     *
2510
     * @param int $userId The user id
2511
     *
2512
     * @return string *.*vcf file
2513
     */
2514
    public static function getVCardUserLink($userId)
2515
    {
2516
        $vCardUrl = api_get_path(WEB_PATH).'main/social/vcard_export.php?userId='.intval($userId);
2517
2518
        return $vCardUrl;
2519
    }
2520
2521
    /**
2522
     * @param string $content
2523
     * @param string $title
2524
     * @param string $footer
2525
     * @param string $type            primary|success|info|warning|danger
2526
     * @param string $extra
2527
     * @param string $id
2528
     * @param string $backgroundColor
2529
     * @param string $extraClass
2530
     *
2531
     * @return string
2532
     */
2533
    public static function panel(
2534
        $content,
2535
        $title = '',
2536
        $footer = '',
2537
        $type = 'default',
2538
        $extra = '',
2539
        $id = '',
2540
        $backgroundColor = '',
2541
        $extraClass = ''
2542
    ) {
2543
        $headerStyle = '';
2544
        if (!empty($backgroundColor)) {
2545
            $headerStyle = 'style = "color: white; background-color: '.$backgroundColor.'" ';
2546
        }
2547
2548
        $title = !empty($title) ? '<div class="panel-heading" '.$headerStyle.' ><h3 class="panel-title">'.$title.'</h3>'.$extra.'</div>' : '';
2549
2550
        if (empty($title) && !empty($extra)) {
2551
            $title = '<div class="panel-heading" '.$headerStyle.' >'.$extra.'</div>';
2552
        }
2553
2554
        $footer = !empty($footer) ? '<div class="panel-footer">'.$footer.'</div>' : '';
2555
        $typeList = ['primary', 'success', 'info', 'warning', 'danger'];
2556
        $style = !in_array($type, $typeList) ? 'default' : $type;
2557
2558
        if (!empty($id)) {
2559
            $id = " id='$id'";
2560
        }
2561
2562
        return '
2563
            <div '.$id.' class="panel panel-'.$style.' '.$extraClass.' ">
2564
                '.$title.'
2565
                '.self::contentPanel($content).'
2566
                '.$footer.'
2567
            </div>'
2568
        ;
2569
    }
2570
2571
    /**
2572
     * @param string $content
2573
     *
2574
     * @return string
2575
     */
2576
    public static function contentPanel($content)
2577
    {
2578
        if (empty($content)) {
2579
            return '';
2580
        }
2581
2582
        return '<div class="panel-body">'.$content.'</div>';
2583
    }
2584
2585
    /**
2586
     * Get the button HTML with an Awesome Font icon.
2587
     *
2588
     * @param string $text        The button content
2589
     * @param string $url         The url to button
2590
     * @param string $icon        The Awesome Font class for icon
2591
     * @param string $type        Optional. The button Bootstrap class. Default 'default' class
2592
     * @param array  $attributes  The additional attributes
2593
     * @param bool   $includeText
2594
     *
2595
     * @return string The button HTML
2596
     */
2597
    public static function toolbarButton(
2598
        $text,
2599
        $url,
2600
        $icon = 'check',
2601
        $type = 'default',
2602
        array $attributes = [],
2603
        $includeText = true
2604
    ) {
2605
        $buttonClass = "btn btn-$type";
2606
        $icon = self::tag('i', null, ['class' => "fa fa-$icon fa-fw", 'aria-hidden' => 'true']);
2607
        $attributes['class'] = isset($attributes['class']) ? "$buttonClass {$attributes['class']}" : $buttonClass;
2608
        $attributes['title'] = isset($attributes['title']) ? $attributes['title'] : $text;
2609
2610
        if (!$includeText) {
2611
            $text = '<span class="sr-only">'.$text.'</span>';
2612
        }
2613
2614
        return self::url("$icon $text", $url, $attributes);
2615
    }
2616
2617
    /**
2618
     * @param string $id
2619
     * @param array  $content
2620
     * @param array  $colsWidth Optional. Columns width
2621
     *
2622
     * @return string
2623
     */
2624
    public static function toolbarAction($id, $content, $colsWidth = [])
2625
    {
2626
        $col = count($content);
2627
2628
        if (!$colsWidth) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $colsWidth of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2629
            $width = 12 / $col;
2630
            array_walk($content, function () use ($width, &$colsWidth) {
2631
                $colsWidth[] = $width;
2632
            });
2633
        }
2634
2635
        $html = '<div id="'.$id.'" class="actions">';
2636
        $html .= '<div class="row">';
2637
2638
        for ($i = 0; $i < $col; $i++) {
2639
            $class = 'col-sm-'.$colsWidth[$i];
2640
2641
            if ($col > 1) {
2642
                if ($i > 0 && $i < count($content) - 1) {
2643
                    $class .= ' text-center';
2644
                } elseif ($i === count($content) - 1) {
2645
                    $class .= ' text-right';
2646
                }
2647
            }
2648
2649
            $html .= '<div class="'.$class.'">'.$content[$i].'</div>';
2650
        }
2651
2652
        $html .= '</div>';
2653
        $html .= '</div>';
2654
2655
        return $html;
2656
    }
2657
2658
    /**
2659
     * Get a HTML code for a icon by Font Awesome.
2660
     *
2661
     * @param string     $name            The icon name. Example: "mail-reply"
2662
     * @param int|string $size            Optional. The size for the icon. (Example: lg, 2, 3, 4, 5)
2663
     * @param bool       $fixWidth        Optional. Whether add the fw class
2664
     * @param string     $additionalClass
2665
     * @param string     $title
2666
     *
2667
     * @return string
2668
     */
2669
    public static function returnFontAwesomeIcon(
2670
        $name,
2671
        $size = '',
2672
        $fixWidth = false,
2673
        $additionalClass = '',
2674
        $title = ''
2675
    ) {
2676
        $className = "fa fa-$name";
2677
2678
        if ($fixWidth) {
2679
            $className .= ' fa-fw';
2680
        }
2681
2682
        switch ($size) {
2683
            case 'lg':
2684
                $className .= ' fa-lg';
2685
                break;
2686
            case 2:
2687
            case 3:
2688
            case 4:
2689
            case 5:
2690
                $className .= " fa-{$size}x";
2691
                break;
2692
        }
2693
2694
        if (!empty($additionalClass)) {
2695
            $className .= " $additionalClass";
2696
        }
2697
2698
        $icon = self::tag('em', null, ['class' => $className, 'title' => $title]);
2699
2700
        return "$icon ";
2701
    }
2702
2703
    /**
2704
     * @param string     $title
2705
     * @param string     $content
2706
     * @param null       $id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $id is correct as it would always require null to be passed?
Loading history...
2707
     * @param array      $params
2708
     * @param null       $idAccordion
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $idAccordion is correct as it would always require null to be passed?
Loading history...
2709
     * @param null       $idCollapse
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $idCollapse is correct as it would always require null to be passed?
Loading history...
2710
     * @param bool|true  $open
2711
     * @param bool|false $fullClickable
2712
     *
2713
     * @return string|null
2714
     *
2715
     * @todo rework function to easy use
2716
     */
2717
    public static function panelCollapse(
2718
        $title,
2719
        $content,
2720
        $id = null,
2721
        $params = [],
2722
        $idAccordion = null,
2723
        $idCollapse = null,
2724
        $open = true,
2725
        $fullClickable = false
2726
    ) {
2727
        if (!empty($idAccordion)) {
2728
            $headerClass = '';
2729
            $headerClass .= $fullClickable ? 'center-block ' : '';
2730
            $headerClass .= $open ? '' : 'collapsed';
2731
            $contentClass = 'panel-collapse collapse ';
2732
            $contentClass .= $open ? 'in ' : '';
2733
            $contentClass .= $params['class'] ?? '';
2734
            $ariaExpanded = $open ? 'true' : 'false';
2735
2736
            $attributes = [
2737
                'id' => $idCollapse,
2738
                'class' => $contentClass,
2739
                'role' => 'tabpanel',
2740
            ];
2741
2742
            if (!empty($params)) {
2743
                $attributes = array_merge(
2744
                    $params,
2745
                    $attributes
2746
                );
2747
            }
2748
2749
            $collapseDiv = self::div('<div class="panel-body">'.$content.'</div>', $attributes);
2750
2751
            $html = <<<HTML
2752
                <div class="panel-group" id="$idAccordion" role="tablist" aria-multiselectable="true">
2753
                    <div class="panel panel-default" id="$id">
2754
                        <div class="panel-heading" role="tab">
2755
                            <h4 class="panel-title">
2756
                                <a
2757
                                    class="$headerClass"
2758
                                    role="button" data-toggle="collapse"
2759
                                    data-parent="#$idAccordion" href="#$idCollapse"
2760
                                    aria-expanded="$ariaExpanded"
2761
                                    aria-controls="$idCollapse">$title</a>
2762
                            </h4>
2763
                        </div>
2764
                        $collapseDiv
2765
                    </div>
2766
                </div>
2767
HTML;
2768
        } else {
2769
            if (!empty($id)) {
2770
                $params['id'] = $id;
2771
            }
2772
            $params['class'] = 'panel panel-default';
2773
            $html = null;
2774
            if (!empty($title)) {
2775
                $html .= '<div class="panel-heading">'.$title.'</div>'.PHP_EOL;
2776
            }
2777
            $html .= '<div class="panel-body">'.$content.'</div>'.PHP_EOL;
2778
            $html = self::div($html, $params);
2779
        }
2780
2781
        return $html;
2782
    }
2783
2784
    /**
2785
     * Returns the string "1 day ago" with a link showing the exact date time.
2786
     *
2787
     * @param string|DateTime $dateTime in UTC or a DateTime in UTC
2788
     *
2789
     * @return string
2790
     */
2791
    public static function dateToStringAgoAndLongDate($dateTime)
2792
    {
2793
        if (empty($dateTime) || $dateTime === '0000-00-00 00:00:00') {
2794
            return '';
2795
        }
2796
2797
        if ($dateTime instanceof \DateTime) {
2798
            $dateTime = $dateTime->format('Y-m-d H:i:s');
2799
        }
2800
2801
        return self::tip(
2802
            date_to_str_ago($dateTime),
2803
            api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG)
2804
            //api_get_local_time($dateTime)
2805
        );
2806
    }
2807
2808
    /**
2809
     * @param array  $userInfo
2810
     * @param string $status
2811
     * @param string $toolbar
2812
     *
2813
     * @return string
2814
     */
2815
    public static function getUserCard($userInfo, $status = '', $toolbar = '')
2816
    {
2817
        if (empty($userInfo)) {
2818
            return '';
2819
        }
2820
2821
        if (!empty($status)) {
2822
            $status = '<div class="items-user-status">'.$status.'</div>';
2823
        }
2824
2825
        if (!empty($toolbar)) {
2826
            $toolbar = '<div class="btn-group pull-right">'.$toolbar.'</div>';
2827
        }
2828
2829
        return '<div id="user_card_'.$userInfo['id'].'" class="col-md-12">
2830
                    <div class="row">
2831
                        <div class="col-md-2">
2832
                            <img src="'.$userInfo['avatar'].'" class="img-responsive img-circle">
2833
                        </div>
2834
                        <div class="col-md-10">
2835
                           <p>'.$userInfo['complete_name'].'</p>
2836
                           <div class="row">
2837
                           <div class="col-md-2">
2838
                           '.$status.'
2839
                           </div>
2840
                           <div class="col-md-10">
2841
                           '.$toolbar.'
2842
                           </div>
2843
                           </div>
2844
                        </div>
2845
                    </div>
2846
                    <hr />
2847
              </div>';
2848
    }
2849
2850
    /**
2851
     * @param string $fileName
2852
     * @param string $fileUrl
2853
     *
2854
     * @return string
2855
     */
2856
    public static function fileHtmlGuesser($fileName, $fileUrl)
2857
    {
2858
        $data = pathinfo($fileName);
2859
2860
        //$content = self::url($data['basename'], $fileUrl);
2861
        $content = '';
2862
        switch ($data['extension']) {
2863
            case 'webm':
2864
            case 'mp4':
2865
            case 'ogg':
2866
                $content = '<video style="width: 400px; height:100%;" src="'.$fileUrl.'"></video>';
2867
                // Allows video to play when loading during an ajax call
2868
                $content .= "<script>jQuery('video:not(.skip), audio:not(.skip)').mediaelementplayer();</script>";
2869
                break;
2870
            case 'jpg':
2871
            case 'jpeg':
2872
            case 'gif':
2873
            case 'png':
2874
                $content = '<img class="img-responsive" src="'.$fileUrl.'" />';
2875
                break;
2876
            default:
2877
                //$html = self::url($data['basename'], $fileUrl);
2878
                break;
2879
        }
2880
        //$html = self::url($content, $fileUrl, ['ajax']);
2881
2882
        return $content;
2883
    }
2884
2885
    public static function isVrViewEnabled(): bool
2886
    {
2887
        $featuresConf = api_get_configuration_value('video_features');
2888
2889
        if (!isset($featuresConf['features'])) {
2890
            return false;
2891
        }
2892
2893
        if (in_array('vrview', $featuresConf['features'])) {
2894
            return true;
2895
        }
2896
2897
        return false;
2898
    }
2899
2900
    public static function getFrameReadyBlock(
2901
        string $frameName,
2902
        string $itemType = '',
2903
        string $jsConditionalFunction = 'function () { return false; }'
2904
    ): string {
2905
        $webPublicPath = api_get_path(WEB_PUBLIC_PATH);
2906
        $webJsPath = api_get_path(WEB_LIBRARY_JS_PATH);
2907
2908
        $isVrViewEnabled = self::isVrViewEnabled();
2909
2910
        $videoFeatures = [
2911
            'playpause',
2912
            'current',
2913
            'progress',
2914
            'duration',
2915
            'tracks',
2916
            'volume',
2917
            'fullscreen',
2918
            'markersrolls',
2919
        ];
2920
2921
        if ($isVrViewEnabled) {
2922
            $videoFeatures[] = 'vrview';
2923
        }
2924
2925
        $features = api_get_configuration_value('video_features');
2926
        $videoPluginsJS = [];
2927
        $videoPluginCSS = [];
2928
        if (!empty($features) && isset($features['features'])) {
2929
            foreach ($features['features'] as $feature) {
2930
                if ($feature === 'vrview') {
2931
                    continue;
2932
                }
2933
                $defaultFeatures[] = $feature;
2934
                $videoPluginsJS[] = "mediaelement/plugins/$feature/$feature.min.js";
2935
                $videoPluginCSS[] = "mediaelement/plugins/$feature/$feature.min.css";
2936
            }
2937
        }
2938
2939
        $videoPluginFiles = '';
2940
        foreach ($videoPluginsJS as $file) {
2941
            $videoPluginFiles .= '{type: "script", src: "'.$webJsPath.$file.'"},';
2942
        }
2943
2944
        $videoPluginCssFiles = '';
2945
        foreach ($videoPluginCSS as $file) {
2946
            $videoPluginCssFiles .= '{type: "stylesheet", src: "'.$webJsPath.$file.'"},';
2947
        }
2948
2949
        if ($renderers = api_get_configuration_sub_value('video_player_renderers/renderers')) {
2950
            foreach ($renderers as $renderName) {
2951
                if ('youtube' === $renderName) {
2952
                    continue;
2953
                }
2954
2955
                $file = $webPublicPath."assets/mediaelement/build/renderers/$renderName.min.js";
2956
                $videoPluginFiles .= '{type: "script", src: "'.$file.'"},';
2957
            }
2958
        }
2959
2960
        $translateHtml = '';
2961
        $translate = api_get_configuration_value('translate_html');
2962
        if ($translate) {
2963
            $translateHtml = '{type:"script", src:"'.api_get_path(WEB_AJAX_PATH).'lang.ajax.php?a=translate_html&'.api_get_cidreq().'"},';
2964
        }
2965
2966
        $fixLinkSetting = api_get_configuration_value('lp_fix_embed_content');
2967
        $fixLink = '';
2968
        if ($fixLinkSetting) {
2969
            $fixLink = '{type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'fixlinks.js"},';
2970
        }
2971
2972
        $videoContextMenyHiddenNative = '';
2973
        $videoContextMenyHiddenMejs = '';
2974
        if (api_get_configuration_value('video_context_menu_hidden')) {
2975
            $videoContextMenyHiddenNative = '$("video").on("contextmenu", function(e) {
2976
                e.preventDefault();
2977
            });';
2978
            $videoContextMenyHiddenMejs = '$(".mejs__container").on("contextmenu", function(e) {
2979
                e.preventDefault();
2980
            });';
2981
        }
2982
2983
        $strMediaElementAdditionalConf = '';
2984
        $strMediaElementJsDeps = '';
2985
        $strMediaElementCssDeps = '';
2986
2987
        if ($isVrViewEnabled) {
2988
            $strMediaElementAdditionalConf = ', vrPath: "'.$webPublicPath.'assets/vrview/build/vrview.js"';
2989
            $strMediaElementJsDeps = '{type:"script", src: "'.$webJsPath.'mediaelement/plugins/vrview/vrview.js"},';
2990
            $strMediaElementCssDeps = '{type:"stylesheet", src: "'.$webJsPath.'mediaelement/plugins/vrview/vrview.css"},';
2991
        }
2992
2993
        $videoFeatures = implode("','", $videoFeatures);
2994
        $frameReady = '
2995
        $.frameReady(function() {
2996
             $(function () {
2997
                '.$videoContextMenyHiddenNative.'
2998
2999
                $("video:not(.skip), audio:not(.skip)").mediaelementplayer({
3000
                    pluginPath: "'.$webJsPath.'mediaelement/plugins/",
3001
                    features: [\''.$videoFeatures.'\'],
3002
                    success: function(mediaElement, originalNode, instance) {
3003
                        '.$videoContextMenyHiddenMejs.PHP_EOL.ChamiloApi::getQuizMarkersRollsJS().'
3004
                    }
3005
                    '.$strMediaElementAdditionalConf.'
3006
                });
3007
            });
3008
        },
3009
        "'.$frameName.'",
3010
        ';
3011
3012
        if ('quiz' === $itemType) {
3013
            $jquery = '
3014
                '.$fixLink.'
3015
                {type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
3016
                {type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
3017
                {type:"script", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelement-and-player.min.js",
3018
                    deps: [
3019
                    '.$strMediaElementJsDeps.'
3020
                    {type:"script", src: "'.$webJsPath.'mediaelement/plugins/markersrolls/markersrolls.min.js"},
3021
                    '.$videoPluginFiles.'
3022
                ]},
3023
                '.$translateHtml.'
3024
            ';
3025
        } else {
3026
            $jquery = '
3027
                {type:"script", src:"'.api_get_jquery_web_path().'", deps: [
3028
                '.$fixLink.'
3029
                {type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
3030
                {type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
3031
                {type:"script", src:"'.$webPublicPath.'assets/jquery-ui/jquery-ui.min.js"},
3032
                {type:"script", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelement-and-player.min.js",
3033
                    deps: [
3034
                    '.$strMediaElementJsDeps.'
3035
                    {type:"script", src: "'.$webJsPath.'mediaelement/plugins/markersrolls/markersrolls.min.js"},
3036
                    '.$videoPluginFiles.'
3037
                ]},
3038
                '.$translateHtml.'
3039
            ]},';
3040
        }
3041
3042
        $frameReady .= '
3043
        [
3044
            '.$jquery.'
3045
            '.$videoPluginCssFiles.'
3046
            {type:"script", src:"'.$webPublicPath.'assets/MathJax/MathJax.js?config=AM_HTMLorMML"},
3047
            {type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/jquery-ui.min.css"},
3048
            {type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/theme.css"},
3049
            {type:"stylesheet", src:"'.$webPublicPath.'css/dialog.css"},
3050
            {type:"stylesheet", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelementplayer.min.css"},
3051
            '.$strMediaElementCssDeps.'
3052
        ], '.$jsConditionalFunction.');';
3053
3054
        return $frameReady;
3055
    }
3056
3057
    /**
3058
     * @param string $image
3059
     * @param int    $size
3060
     *
3061
     * @return string
3062
     */
3063
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
3064
    {
3065
        return self::return_icon($image, '', [], $size, false, true);
3066
    }
3067
3068
    /**
3069
     * @param string $image
3070
     * @param int    $size
3071
     * @param string $name
3072
     *
3073
     * @return string
3074
     */
3075
    public static function get_image($image, $size = ICON_SIZE_SMALL, $name = '')
3076
    {
3077
        return self::return_icon($image, $name, [], $size);
3078
    }
3079
3080
    public static function returnHeaderWithPercentage($header, $percentage)
3081
    {
3082
        $percentHtml = sprintf(
3083
            get_lang('XPercent'),
3084
            round($percentage, 2)
3085
        );
3086
3087
        return "$header<br><small>$percentHtml</small>";
3088
    }
3089
3090
    /**
3091
     * Get the latest view (later than given date) in any LP in this course/session
3092
     * as datetime format, or null.
3093
     *
3094
     * @param int $latestTimestamp The latest time for the tool in general, as obtained through track_e_access
3095
     *
3096
     * @return string|null The latest view if later than $latestTimestamp, or null otherwise
3097
     */
3098
    public static function getLatestLpView(int $courseId, int $userId, int $sessionId, int $latestTimestamp): ?string
3099
    {
3100
        // Control if the latest view in c_lp_view is more recent than in track_e_access
3101
        // Use case: a user skipped the course home page by following a direct link to a LP in an email
3102
        // $latestDate is in datetime format, while c_lp_item_view.start_time is in EPOCH
3103
        $t_lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3104
        $t_lp_view = Database::get_course_table(TABLE_LP_VIEW);
3105
        $sql = "SELECT cliv.start_time FROM $t_lp_item_view cliv
3106
        INNER JOIN $t_lp_view clv ON cliv.lp_view_id = clv.id
3107
        WHERE
3108
        clv.c_id = $courseId AND
3109
        clv.user_id = $userId AND
3110
        clv.session_id = $sessionId AND
3111
        cliv.start_time > $latestTimestamp
3112
        ORDER BY cliv.start_time DESC
3113
        LIMIT 1";
3114
        $resultItems = Database::query($sql);
3115
        if (Database::num_rows($resultItems)) {
3116
            $rowItems = Database::fetch_assoc($resultItems);
3117
            $controlDate = $rowItems['start_time'];
3118
            // convert to date
3119
            return date('Y-m-d H:i:s', $controlDate);
3120
        }
3121
3122
        return null;
3123
    }
3124
}
3125