Display::page_subheader_and_translate()   A
last analyzed

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;
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 'wav':
2225
            case 'ogg':
2226
                $html = '<audio width="300px" controls id="'.$id.'" '.$autoplay.' src="'.$params['url'].'"></audio>';
2227
2228
                return $html;
2229
                break;
2230
        }
2231
2232
        return null;
2233
    }
2234
2235
    /**
2236
     * @param int    $nextValue
2237
     * @param array  $list
2238
     * @param int    $current
2239
     * @param int    $fixedValue
2240
     * @param array  $conditions
2241
     * @param string $link
2242
     * @param bool   $isMedia
2243
     * @param bool   $addHeaders
2244
     * @param array  $linkAttributes
2245
     *
2246
     * @return string
2247
     */
2248
    public static function progressPaginationBar(
2249
        $nextValue,
2250
        $list,
2251
        $current,
2252
        $fixedValue = null,
2253
        $conditions = [],
2254
        $link = null,
2255
        $isMedia = false,
2256
        $addHeaders = true,
2257
        $linkAttributes = []
2258
    ) {
2259
        if ($addHeaders) {
2260
            $pagination_size = 'pagination-mini';
2261
            $html = '<div class="exercise_pagination pagination '.$pagination_size.'"><ul>';
2262
        } else {
2263
            $html = null;
2264
        }
2265
        $affectAllItems = false;
2266
        if ($isMedia && isset($fixedValue) && ($nextValue + 1 == $current)) {
2267
            $affectAllItems = true;
2268
        }
2269
        $localCounter = 0;
2270
        foreach ($list as $itemId) {
2271
            $isCurrent = false;
2272
            if ($affectAllItems) {
2273
                $isCurrent = true;
2274
            } else {
2275
                if (!$isMedia) {
2276
                    $isCurrent = $current == ($localCounter + $nextValue + 1) ? true : false;
2277
                }
2278
            }
2279
            $html .= self::parsePaginationItem(
2280
                $itemId,
2281
                $isCurrent,
2282
                $conditions,
2283
                $link,
2284
                $nextValue,
2285
                $isMedia,
2286
                $localCounter,
2287
                $fixedValue,
2288
                $linkAttributes
2289
            );
2290
            $localCounter++;
2291
        }
2292
        if ($addHeaders) {
2293
            $html .= '</ul></div>';
2294
        }
2295
2296
        return $html;
2297
    }
2298
2299
    /**
2300
     * @param int    $itemId
2301
     * @param bool   $isCurrent
2302
     * @param array  $conditions
2303
     * @param string $link
2304
     * @param int    $nextValue
2305
     * @param bool   $isMedia
2306
     * @param int    $localCounter
2307
     * @param int    $fixedValue
2308
     * @param array  $linkAttributes
2309
     *
2310
     * @return string
2311
     */
2312
    public static function parsePaginationItem(
2313
        $itemId,
2314
        $isCurrent,
2315
        $conditions,
2316
        $link,
2317
        $nextValue = 0,
2318
        $isMedia = false,
2319
        $localCounter = null,
2320
        $fixedValue = null,
2321
        $linkAttributes = []
2322
    ) {
2323
        $defaultClass = 'before';
2324
        $class = $defaultClass;
2325
        foreach ($conditions as $condition) {
2326
            $array = isset($condition['items']) ? $condition['items'] : [];
2327
            $class_to_applied = $condition['class'];
2328
            $type = isset($condition['type']) ? $condition['type'] : 'positive';
2329
            $mode = isset($condition['mode']) ? $condition['mode'] : 'add';
2330
            switch ($type) {
2331
                case 'positive':
2332
                    if (in_array($itemId, $array)) {
2333
                        if ($mode == 'overwrite') {
2334
                            $class = " $defaultClass $class_to_applied";
2335
                        } else {
2336
                            $class .= " $class_to_applied";
2337
                        }
2338
                    }
2339
                    break;
2340
                case 'negative':
2341
                    if (!in_array($itemId, $array)) {
2342
                        if ($mode == 'overwrite') {
2343
                            $class = " $defaultClass $class_to_applied";
2344
                        } else {
2345
                            $class .= " $class_to_applied";
2346
                        }
2347
                    }
2348
                    break;
2349
            }
2350
        }
2351
        if ($isCurrent) {
2352
            $class = 'before current';
2353
        }
2354
        if ($isMedia && $isCurrent) {
2355
            $class = 'before current';
2356
        }
2357
        if (empty($link)) {
2358
            $link_to_show = '#';
2359
        } else {
2360
            $link_to_show = $link.($nextValue + $localCounter);
2361
        }
2362
        $label = $nextValue + $localCounter + 1;
2363
        if ($isMedia) {
2364
            $label = ($fixedValue + 1).' '.chr(97 + $localCounter);
2365
            $link_to_show = $link.$fixedValue.'#questionanchor'.$itemId;
2366
        }
2367
        $link = self::url($label.' ', $link_to_show, $linkAttributes);
2368
2369
        return '<li class = "'.$class.'">'.$link.'</li>';
2370
    }
2371
2372
    /**
2373
     * @param int $current
2374
     * @param int $total
2375
     *
2376
     * @return string
2377
     */
2378
    public static function paginationIndicator($current, $total)
2379
    {
2380
        $html = null;
2381
        if (!empty($current) && !empty($total)) {
2382
            $label = sprintf(get_lang('PaginationXofY'), $current, $total);
2383
            $html = self::url($label, '#', ['class' => 'btn disabled']);
2384
        }
2385
2386
        return $html;
2387
    }
2388
2389
    /**
2390
     * @param $url
2391
     * @param $currentPage
2392
     * @param $pagesCount
2393
     * @param $totalItems
2394
     *
2395
     * @return string
2396
     */
2397
    public static function getPagination($url, $currentPage, $pagesCount, $totalItems)
2398
    {
2399
        $pagination = '';
2400
        if ($totalItems > 1 && $pagesCount > 1) {
2401
            $pagination .= '<ul class="pagination">';
2402
            for ($i = 0; $i < $pagesCount; $i++) {
2403
                $newPage = $i + 1;
2404
                if ($currentPage == $newPage) {
2405
                    $pagination .= '<li class="active"><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2406
                } else {
2407
                    $pagination .= '<li><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2408
                }
2409
            }
2410
            $pagination .= '</ul>';
2411
        }
2412
2413
        return $pagination;
2414
    }
2415
2416
    /**
2417
     * Adds a message in the queue.
2418
     *
2419
     * @param string $message
2420
     */
2421
    public static function addFlash($message)
2422
    {
2423
        $messages = Session::read('flash_messages');
2424
        if (empty($messages)) {
2425
            $messages[] = $message;
2426
        } else {
2427
            array_push($messages, $message);
2428
        }
2429
        Session::write('flash_messages', $messages);
2430
    }
2431
2432
    /**
2433
     * @return string
2434
     */
2435
    public static function getFlashToString()
2436
    {
2437
        $messages = Session::read('flash_messages');
2438
        $messageToString = '';
2439
        if (!empty($messages)) {
2440
            foreach ($messages as $message) {
2441
                $messageToString .= $message;
2442
            }
2443
        }
2444
2445
        return $messageToString;
2446
    }
2447
2448
    /**
2449
     * Shows the message from the session.
2450
     */
2451
    public static function showFlash()
2452
    {
2453
        echo self::getFlashToString();
2454
    }
2455
2456
    /**
2457
     * Destroys the message session.
2458
     */
2459
    public static function cleanFlashMessages()
2460
    {
2461
        Session::erase('flash_messages');
2462
    }
2463
2464
    /**
2465
     * Get the profile edition link for a user.
2466
     *
2467
     * @param int  $userId  The user id
2468
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
2469
     *
2470
     * @return string The link
2471
     */
2472
    public static function getProfileEditionLink($userId, $asAdmin = false)
2473
    {
2474
        $editProfileUrl = api_get_path(WEB_CODE_PATH).'auth/profile.php';
2475
        if ($asAdmin) {
2476
            $editProfileUrl = api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=".intval($userId);
2477
        }
2478
2479
        if (api_get_setting('sso_authentication') === 'true') {
2480
            $subSSOClass = api_get_setting('sso_authentication_subclass');
2481
            $objSSO = null;
2482
2483
            if (!empty($subSSOClass)) {
2484
                $file = api_get_path(SYS_CODE_PATH)."auth/sso/sso.$subSSOClass.class.php";
2485
                if (file_exists($file)) {
2486
                    require_once $file;
2487
                    $subSSOClass = 'sso'.$subSSOClass;
2488
                    $objSSO = new $subSSOClass();
2489
                } else {
2490
                    throw new Exception("$subSSOClass file not set");
2491
                }
2492
            } else {
2493
                $objSSO = new sso();
2494
            }
2495
2496
            $editProfileUrl = $objSSO->generateProfileEditingURL(
2497
                $userId,
2498
                $asAdmin
2499
            );
2500
        }
2501
2502
        return $editProfileUrl;
2503
    }
2504
2505
    /**
2506
     * Get the vCard for a user.
2507
     *
2508
     * @param int $userId The user id
2509
     *
2510
     * @return string *.*vcf file
2511
     */
2512
    public static function getVCardUserLink($userId)
2513
    {
2514
        $vCardUrl = api_get_path(WEB_PATH).'main/social/vcard_export.php?userId='.intval($userId);
2515
2516
        return $vCardUrl;
2517
    }
2518
2519
    /**
2520
     * @param string $content
2521
     * @param string $title
2522
     * @param string $footer
2523
     * @param string $type            primary|success|info|warning|danger
2524
     * @param string $extra
2525
     * @param string $id
2526
     * @param string $backgroundColor
2527
     * @param string $extraClass
2528
     *
2529
     * @return string
2530
     */
2531
    public static function panel(
2532
        $content,
2533
        $title = '',
2534
        $footer = '',
2535
        $type = 'default',
2536
        $extra = '',
2537
        $id = '',
2538
        $backgroundColor = '',
2539
        $extraClass = ''
2540
    ) {
2541
        $headerStyle = '';
2542
        if (!empty($backgroundColor)) {
2543
            $headerStyle = 'style = "color: white; background-color: '.$backgroundColor.'" ';
2544
        }
2545
2546
        $title = !empty($title) ? '<div class="panel-heading" '.$headerStyle.' ><h3 class="panel-title">'.$title.'</h3>'.$extra.'</div>' : '';
2547
2548
        if (empty($title) && !empty($extra)) {
2549
            $title = '<div class="panel-heading" '.$headerStyle.' >'.$extra.'</div>';
2550
        }
2551
2552
        $footer = !empty($footer) ? '<div class="panel-footer">'.$footer.'</div>' : '';
2553
        $typeList = ['primary', 'success', 'info', 'warning', 'danger'];
2554
        $style = !in_array($type, $typeList) ? 'default' : $type;
2555
2556
        if (!empty($id)) {
2557
            $id = " id='$id'";
2558
        }
2559
2560
        return '
2561
            <div '.$id.' class="panel panel-'.$style.' '.$extraClass.' ">
2562
                '.$title.'
2563
                '.self::contentPanel($content).'
2564
                '.$footer.'
2565
            </div>'
2566
        ;
2567
    }
2568
2569
    /**
2570
     * @param string $content
2571
     *
2572
     * @return string
2573
     */
2574
    public static function contentPanel($content)
2575
    {
2576
        if (empty($content)) {
2577
            return '';
2578
        }
2579
2580
        return '<div class="panel-body">'.$content.'</div>';
2581
    }
2582
2583
    /**
2584
     * Get the button HTML with an Awesome Font icon.
2585
     *
2586
     * @param string $text        The button content
2587
     * @param string $url         The url to button
2588
     * @param string $icon        The Awesome Font class for icon
2589
     * @param string $type        Optional. The button Bootstrap class. Default 'default' class
2590
     * @param array  $attributes  The additional attributes
2591
     * @param bool   $includeText
2592
     *
2593
     * @return string The button HTML
2594
     */
2595
    public static function toolbarButton(
2596
        $text,
2597
        $url,
2598
        $icon = 'check',
2599
        $type = 'default',
2600
        array $attributes = [],
2601
        $includeText = true
2602
    ) {
2603
        $buttonClass = "btn btn-$type";
2604
        $icon = self::tag('i', null, ['class' => "fa fa-$icon fa-fw", 'aria-hidden' => 'true']);
2605
        $attributes['class'] = isset($attributes['class']) ? "$buttonClass {$attributes['class']}" : $buttonClass;
2606
        $attributes['title'] = isset($attributes['title']) ? $attributes['title'] : $text;
2607
2608
        if (!$includeText) {
2609
            $text = '<span class="sr-only">'.$text.'</span>';
2610
        }
2611
2612
        return self::url("$icon $text", $url, $attributes);
2613
    }
2614
2615
    /**
2616
     * @param string $id
2617
     * @param array  $content
2618
     * @param array  $colsWidth Optional. Columns width
2619
     *
2620
     * @return string
2621
     */
2622
    public static function toolbarAction($id, $content, $colsWidth = [])
2623
    {
2624
        $col = count($content);
2625
2626
        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...
2627
            $width = 12 / $col;
2628
            array_walk($content, function () use ($width, &$colsWidth) {
2629
                $colsWidth[] = $width;
2630
            });
2631
        }
2632
2633
        $html = '<div id="'.$id.'" class="actions">';
2634
        $html .= '<div class="row">';
2635
2636
        for ($i = 0; $i < $col; $i++) {
2637
            $class = 'col-sm-'.$colsWidth[$i];
2638
2639
            if ($col > 1) {
2640
                if ($i > 0 && $i < count($content) - 1) {
2641
                    $class .= ' text-center';
2642
                } elseif ($i === count($content) - 1) {
2643
                    $class .= ' text-right';
2644
                }
2645
            }
2646
2647
            $html .= '<div class="'.$class.'">'.$content[$i].'</div>';
2648
        }
2649
2650
        $html .= '</div>';
2651
        $html .= '</div>';
2652
2653
        return $html;
2654
    }
2655
2656
    /**
2657
     * Get a HTML code for a icon by Font Awesome.
2658
     *
2659
     * @param string     $name            The icon name. Example: "mail-reply"
2660
     * @param int|string $size            Optional. The size for the icon. (Example: lg, 2, 3, 4, 5)
2661
     * @param bool       $fixWidth        Optional. Whether add the fw class
2662
     * @param string     $additionalClass
2663
     * @param string     $title
2664
     *
2665
     * @return string
2666
     */
2667
    public static function returnFontAwesomeIcon(
2668
        $name,
2669
        $size = '',
2670
        $fixWidth = false,
2671
        $additionalClass = '',
2672
        $title = ''
2673
    ) {
2674
        $className = "fa fa-$name";
2675
2676
        if ($fixWidth) {
2677
            $className .= ' fa-fw';
2678
        }
2679
2680
        switch ($size) {
2681
            case 'lg':
2682
                $className .= ' fa-lg';
2683
                break;
2684
            case 2:
2685
            case 3:
2686
            case 4:
2687
            case 5:
2688
                $className .= " fa-{$size}x";
2689
                break;
2690
        }
2691
2692
        if (!empty($additionalClass)) {
2693
            $className .= " $additionalClass";
2694
        }
2695
2696
        $icon = self::tag('em', null, ['class' => $className, 'title' => $title]);
2697
2698
        return "$icon ";
2699
    }
2700
2701
    /**
2702
     * @param string     $title
2703
     * @param string     $content
2704
     * @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...
2705
     * @param array      $params
2706
     * @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...
2707
     * @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...
2708
     * @param bool|true  $open
2709
     * @param bool|false $fullClickable
2710
     *
2711
     * @return string|null
2712
     *
2713
     * @todo rework function to easy use
2714
     */
2715
    public static function panelCollapse(
2716
        $title,
2717
        $content,
2718
        $id = null,
2719
        $params = [],
2720
        $idAccordion = null,
2721
        $idCollapse = null,
2722
        $open = true,
2723
        $fullClickable = false
2724
    ) {
2725
        if (!empty($idAccordion)) {
2726
            $headerClass = '';
2727
            $headerClass .= $fullClickable ? 'center-block ' : '';
2728
            $headerClass .= $open ? '' : 'collapsed';
2729
            $contentClass = 'panel-collapse collapse ';
2730
            $contentClass .= $open ? 'in ' : '';
2731
            $contentClass .= $params['class'] ?? '';
2732
            $ariaExpanded = $open ? 'true' : 'false';
2733
2734
            $attributes = [
2735
                'id' => $idCollapse,
2736
                'class' => $contentClass,
2737
                'role' => 'tabpanel',
2738
            ];
2739
2740
            if (!empty($params)) {
2741
                $attributes = array_merge(
2742
                    $params,
2743
                    $attributes
2744
                );
2745
            }
2746
2747
            $collapseDiv = self::div('<div class="panel-body">'.$content.'</div>', $attributes);
2748
2749
            $html = <<<HTML
2750
                <div class="panel-group" id="$idAccordion" role="tablist" aria-multiselectable="true">
2751
                    <div class="panel panel-default" id="$id">
2752
                        <div class="panel-heading" role="tab">
2753
                            <h4 class="panel-title">
2754
                                <a
2755
                                    class="$headerClass"
2756
                                    role="button" data-toggle="collapse"
2757
                                    data-parent="#$idAccordion" href="#$idCollapse"
2758
                                    aria-expanded="$ariaExpanded"
2759
                                    aria-controls="$idCollapse">$title</a>
2760
                            </h4>
2761
                        </div>
2762
                        $collapseDiv
2763
                    </div>
2764
                </div>
2765
HTML;
2766
        } else {
2767
            if (!empty($id)) {
2768
                $params['id'] = $id;
2769
            }
2770
            $params['class'] = 'panel panel-default';
2771
            $html = null;
2772
            if (!empty($title)) {
2773
                $html .= '<div class="panel-heading">'.$title.'</div>'.PHP_EOL;
2774
            }
2775
            $html .= '<div class="panel-body">'.$content.'</div>'.PHP_EOL;
2776
            $html = self::div($html, $params);
2777
        }
2778
2779
        return $html;
2780
    }
2781
2782
    /**
2783
     * Returns the string "1 day ago" with a link showing the exact date time.
2784
     *
2785
     * @param string|DateTime $dateTime in UTC or a DateTime in UTC
2786
     *
2787
     * @return string
2788
     */
2789
    public static function dateToStringAgoAndLongDate($dateTime)
2790
    {
2791
        if (empty($dateTime) || $dateTime === '0000-00-00 00:00:00') {
2792
            return '';
2793
        }
2794
2795
        if ($dateTime instanceof \DateTime) {
2796
            $dateTime = $dateTime->format('Y-m-d H:i:s');
2797
        }
2798
2799
        return self::tip(
2800
            date_to_str_ago($dateTime),
2801
            api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG)
2802
            //api_get_local_time($dateTime)
2803
        );
2804
    }
2805
2806
    /**
2807
     * @param array  $userInfo
2808
     * @param string $status
2809
     * @param string $toolbar
2810
     *
2811
     * @return string
2812
     */
2813
    public static function getUserCard($userInfo, $status = '', $toolbar = '')
2814
    {
2815
        if (empty($userInfo)) {
2816
            return '';
2817
        }
2818
2819
        if (!empty($status)) {
2820
            $status = '<div class="items-user-status">'.$status.'</div>';
2821
        }
2822
2823
        if (!empty($toolbar)) {
2824
            $toolbar = '<div class="btn-group pull-right">'.$toolbar.'</div>';
2825
        }
2826
2827
        return '<div id="user_card_'.$userInfo['id'].'" class="col-md-12">
2828
                    <div class="row">
2829
                        <div class="col-md-2">
2830
                            <img src="'.$userInfo['avatar'].'" class="img-responsive img-circle">
2831
                        </div>
2832
                        <div class="col-md-10">
2833
                           <p>'.$userInfo['complete_name'].'</p>
2834
                           <div class="row">
2835
                           <div class="col-md-2">
2836
                           '.$status.'
2837
                           </div>
2838
                           <div class="col-md-10">
2839
                           '.$toolbar.'
2840
                           </div>
2841
                           </div>
2842
                        </div>
2843
                    </div>
2844
                    <hr />
2845
              </div>';
2846
    }
2847
2848
    /**
2849
     * @param string $fileName
2850
     * @param string $fileUrl
2851
     *
2852
     * @return string
2853
     */
2854
    public static function fileHtmlGuesser($fileName, $fileUrl)
2855
    {
2856
        $data = pathinfo($fileName);
2857
2858
        //$content = self::url($data['basename'], $fileUrl);
2859
        $content = '';
2860
        switch ($data['extension']) {
2861
            case 'webm':
2862
            case 'mp4':
2863
            case 'ogg':
2864
                $content = '<video style="width: 400px; height:100%;" src="'.$fileUrl.'"></video>';
2865
                // Allows video to play when loading during an ajax call
2866
                $content .= "<script>jQuery('video:not(.skip), audio:not(.skip)').mediaelementplayer();</script>";
2867
                break;
2868
            case 'jpg':
2869
            case 'jpeg':
2870
            case 'gif':
2871
            case 'png':
2872
                $content = '<img class="img-responsive" src="'.$fileUrl.'" />';
2873
                break;
2874
            default:
2875
                //$html = self::url($data['basename'], $fileUrl);
2876
                break;
2877
        }
2878
        //$html = self::url($content, $fileUrl, ['ajax']);
2879
2880
        return $content;
2881
    }
2882
2883
    public static function isVrViewEnabled(): bool
2884
    {
2885
        $featuresConf = api_get_configuration_value('video_features');
2886
2887
        if (!isset($featuresConf['features'])) {
2888
            return false;
2889
        }
2890
2891
        if (in_array('vrview', $featuresConf['features'])) {
2892
            return true;
2893
        }
2894
2895
        return false;
2896
    }
2897
2898
    public static function getFrameReadyBlock(
2899
        string $frameName,
2900
        string $itemType = '',
2901
        string $jsConditionalFunction = 'function () { return false; }'
2902
    ): string {
2903
        $webPublicPath = api_get_path(WEB_PUBLIC_PATH);
2904
        $webJsPath = api_get_path(WEB_LIBRARY_JS_PATH);
2905
2906
        $isVrViewEnabled = self::isVrViewEnabled();
2907
2908
        $videoFeatures = [
2909
            'playpause',
2910
            'current',
2911
            'progress',
2912
            'duration',
2913
            'tracks',
2914
            'volume',
2915
            'fullscreen',
2916
            'markersrolls',
2917
        ];
2918
2919
        if ($isVrViewEnabled) {
2920
            $videoFeatures[] = 'vrview';
2921
        }
2922
2923
        $features = api_get_configuration_value('video_features');
2924
        $videoPluginsJS = [];
2925
        $videoPluginCSS = [];
2926
        if (!empty($features) && isset($features['features'])) {
2927
            foreach ($features['features'] as $feature) {
2928
                if ($feature === 'vrview') {
2929
                    continue;
2930
                }
2931
                $defaultFeatures[] = $feature;
2932
                $videoPluginsJS[] = "mediaelement/plugins/$feature/$feature.min.js";
2933
                $videoPluginCSS[] = "mediaelement/plugins/$feature/$feature.min.css";
2934
            }
2935
        }
2936
2937
        $videoPluginFiles = '';
2938
        foreach ($videoPluginsJS as $file) {
2939
            $videoPluginFiles .= '{type: "script", src: "'.$webJsPath.$file.'"},';
2940
        }
2941
2942
        $videoPluginCssFiles = '';
2943
        foreach ($videoPluginCSS as $file) {
2944
            $videoPluginCssFiles .= '{type: "stylesheet", src: "'.$webJsPath.$file.'"},';
2945
        }
2946
2947
        if ($renderers = api_get_configuration_sub_value('video_player_renderers/renderers')) {
2948
            foreach ($renderers as $renderName) {
2949
                if ('youtube' === $renderName) {
2950
                    continue;
2951
                }
2952
2953
                $file = $webPublicPath."assets/mediaelement/build/renderers/$renderName.min.js";
2954
                $videoPluginFiles .= '{type: "script", src: "'.$file.'"},';
2955
            }
2956
        }
2957
2958
        $translateHtml = '';
2959
        $translate = api_get_configuration_value('translate_html');
2960
        if ($translate) {
2961
            $translateHtml = '{type:"script", src:"'.api_get_path(WEB_AJAX_PATH).'lang.ajax.php?a=translate_html&'.api_get_cidreq().'"},';
2962
        }
2963
2964
        $fixLinkSetting = api_get_configuration_value('lp_fix_embed_content');
2965
        $fixLink = '';
2966
        if ($fixLinkSetting) {
2967
            $fixLink = '{type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'fixlinks.js"},';
2968
        }
2969
2970
        $videoContextMenyHiddenNative = '';
2971
        $videoContextMenyHiddenMejs = '';
2972
        if (api_get_configuration_value('video_context_menu_hidden')) {
2973
            $videoContextMenyHiddenNative = '$("video").on("contextmenu", function(e) {
2974
                e.preventDefault();
2975
            });';
2976
            $videoContextMenyHiddenMejs = '$(".mejs__container").on("contextmenu", function(e) {
2977
                e.preventDefault();
2978
            });';
2979
        }
2980
2981
        $strMediaElementAdditionalConf = '';
2982
        $strMediaElementJsDeps = '';
2983
        $strMediaElementCssDeps = '';
2984
2985
        if ($isVrViewEnabled) {
2986
            $strMediaElementAdditionalConf = ', vrPath: "'.$webPublicPath.'assets/vrview/build/vrview.js"';
2987
            $strMediaElementJsDeps = '{type:"script", src: "'.$webJsPath.'mediaelement/plugins/vrview/vrview.js"},';
2988
            $strMediaElementCssDeps = '{type:"stylesheet", src: "'.$webJsPath.'mediaelement/plugins/vrview/vrview.css"},';
2989
        }
2990
2991
        $videoFeatures = implode("','", $videoFeatures);
2992
        $frameReady = '
2993
        $.frameReady(function() {
2994
             $(function () {
2995
                '.$videoContextMenyHiddenNative.'
2996
2997
                $("video:not(.skip), audio:not(.skip)").mediaelementplayer({
2998
                    pluginPath: "'.$webJsPath.'mediaelement/plugins/",
2999
                    features: [\''.$videoFeatures.'\'],
3000
                    success: function(mediaElement, originalNode, instance) {
3001
                        '.$videoContextMenyHiddenMejs.PHP_EOL.ChamiloApi::getQuizMarkersRollsJS().'
3002
                    }
3003
                    '.$strMediaElementAdditionalConf.'
3004
                });
3005
            });
3006
        },
3007
        "'.$frameName.'",
3008
        ';
3009
3010
        if ('quiz' === $itemType) {
3011
            $jquery = '
3012
                '.$fixLink.'
3013
                {type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
3014
                {type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
3015
                {type:"script", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelement-and-player.min.js",
3016
                    deps: [
3017
                    '.$strMediaElementJsDeps.'
3018
                    {type:"script", src: "'.$webJsPath.'mediaelement/plugins/markersrolls/markersrolls.min.js"},
3019
                    '.$videoPluginFiles.'
3020
                ]},
3021
                '.$translateHtml.'
3022
            ';
3023
        } else {
3024
            $jquery = '
3025
                {type:"script", src:"'.api_get_jquery_web_path().'", deps: [
3026
                '.$fixLink.'
3027
                {type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
3028
                {type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
3029
                {type:"script", src:"'.$webPublicPath.'assets/jquery-ui/jquery-ui.min.js"},
3030
                {type:"script", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelement-and-player.min.js",
3031
                    deps: [
3032
                    '.$strMediaElementJsDeps.'
3033
                    {type:"script", src: "'.$webJsPath.'mediaelement/plugins/markersrolls/markersrolls.min.js"},
3034
                    '.$videoPluginFiles.'
3035
                ]},
3036
                '.$translateHtml.'
3037
            ]},';
3038
        }
3039
3040
        $frameReady .= '
3041
        [
3042
            '.$jquery.'
3043
            '.$videoPluginCssFiles.'
3044
            {type:"script", src:"'.$webPublicPath.'assets/MathJax/MathJax.js?config=AM_HTMLorMML"},
3045
            {type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/jquery-ui.min.css"},
3046
            {type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/theme.css"},
3047
            {type:"stylesheet", src:"'.$webPublicPath.'css/dialog.css"},
3048
            {type:"stylesheet", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelementplayer.min.css"},
3049
            '.$strMediaElementCssDeps.'
3050
        ], '.$jsConditionalFunction.');';
3051
3052
        return $frameReady;
3053
    }
3054
3055
    /**
3056
     * @param string $image
3057
     * @param int    $size
3058
     *
3059
     * @return string
3060
     */
3061
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
3062
    {
3063
        return self::return_icon($image, '', [], $size, false, true);
3064
    }
3065
3066
    /**
3067
     * @param string $image
3068
     * @param int    $size
3069
     * @param string $name
3070
     *
3071
     * @return string
3072
     */
3073
    public static function get_image($image, $size = ICON_SIZE_SMALL, $name = '')
3074
    {
3075
        return self::return_icon($image, $name, [], $size);
3076
    }
3077
3078
    public static function returnHeaderWithPercentage($header, $percentage)
3079
    {
3080
        $percentHtml = sprintf(
3081
            get_lang('XPercent'),
3082
            round($percentage, 2)
3083
        );
3084
3085
        return "$header<br><small>$percentHtml</small>";
3086
    }
3087
3088
    /**
3089
     * Get the latest view (later than given date) in any LP in this course/session
3090
     * as datetime format, or null.
3091
     *
3092
     * @param int $latestTimestamp The latest time for the tool in general, as obtained through track_e_access
3093
     *
3094
     * @return string|null The latest view if later than $latestTimestamp, or null otherwise
3095
     */
3096
    public static function getLatestLpView(int $courseId, int $userId, int $sessionId, int $latestTimestamp): ?string
3097
    {
3098
        // Control if the latest view in c_lp_view is more recent than in track_e_access
3099
        // Use case: a user skipped the course home page by following a direct link to a LP in an email
3100
        // $latestDate is in datetime format, while c_lp_item_view.start_time is in EPOCH
3101
        $t_lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3102
        $t_lp_view = Database::get_course_table(TABLE_LP_VIEW);
3103
        $sql = "SELECT cliv.start_time FROM $t_lp_item_view cliv
3104
        INNER JOIN $t_lp_view clv ON cliv.lp_view_id = clv.id
3105
        WHERE
3106
        clv.c_id = $courseId AND
3107
        clv.user_id = $userId AND
3108
        clv.session_id = $sessionId AND
3109
        cliv.start_time > $latestTimestamp
3110
        ORDER BY cliv.start_time DESC
3111
        LIMIT 1";
3112
        $resultItems = Database::query($sql);
3113
        if (Database::num_rows($resultItems)) {
3114
            $rowItems = Database::fetch_assoc($resultItems);
3115
            $controlDate = $rowItems['start_time'];
3116
            // convert to date
3117
            return date('Y-m-d H:i:s', $controlDate);
3118
        }
3119
3120
        return null;
3121
    }
3122
}
3123