Passed
Push — master ( 099693...6fec2b )
by Julito
09:41
created

Display::addFlash()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
8
use Chamilo\CoreBundle\Framework\Container;
9
use ChamiloSession as Session;
10
use Symfony\Component\HttpFoundation\Response;
11
12
/**
13
 * Class Display
14
 * Contains several public functions dealing with the display of
15
 * table data, messages, help topics, ...
16
 *
17
 * Include/require it in your code to use its public functionality.
18
 * There are also several display public functions in the main api library.
19
 *
20
 * All public functions static public functions inside a class called Display,
21
 * so you use them like this: e.g.
22
 * Display::return_message($message)
23
 */
24
class Display
25
{
26
    /** @var Template */
27
    public static $global_template;
28
    public static $preview_style = null;
29
    public static $legacyTemplate;
30
31
    public function __construct()
32
    {
33
    }
34
35
    /**
36
     * @return array
37
     */
38
    public static function toolList()
39
    {
40
        return [
41
            'group',
42
            'work',
43
            'glossary',
44
            'forum',
45
            'course_description',
46
            'gradebook',
47
            'attendance',
48
            'course_progress',
49
            'notebook',
50
        ];
51
    }
52
53
    /**
54
     * Displays the page header.
55
     *
56
     * @param string The name of the page (will be showed in the page title)
57
     * @param string Optional help file name
58
     * @param string $page_header
59
     */
60
    public static function display_header(
61
        $tool_name = '',
62
        $help = null,
63
        $page_header = null
64
    ) {
65
        global $interbreadcrumb;
66
        $interbreadcrumb[] = ['url' => '#', 'name' => $tool_name];
67
68
        ob_start();
69
70
        return true;
71
    }
72
73
    /**
74
     * Displays the reduced page header (without banner).
75
     */
76
    public static function display_reduced_header()
77
    {
78
        ob_start();
79
        self::$legacyTemplate = '@ChamiloCore/Layout/no_layout.html.twig';
80
81
        return true;
82
    }
83
84
    /**
85
     * Display no header.
86
     */
87
    public static function display_no_header()
88
    {
89
        global $tool_name, $show_learnpath;
90
        self::$global_template = new Template(
91
            $tool_name,
92
            false,
93
            false,
94
            $show_learnpath
95
        );
96
    }
97
98
    /**
99
     * Display the page footer.
100
     */
101
    public static function display_footer()
102
    {
103
        $contents = ob_get_contents();
104
        if (ob_get_length()) {
105
            ob_end_clean();
106
        }
107
        $tpl = '@ChamiloCore/Layout/layout_one_col.html.twig';
108
        if (!empty(self::$legacyTemplate)) {
109
            $tpl = self::$legacyTemplate;
110
        }
111
        $response = new Response();
112
        $params['content'] = $contents;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
113
        global $interbreadcrumb, $htmlHeadXtra;
114
115
        $courseInfo = api_get_course_info();
116
        if (!empty($courseInfo)) {
117
            $url = $courseInfo['course_public_url'];
118
            $sessionId = api_get_session_id();
119
            if (!empty($sessionId)) {
120
                $url .= '?sid='.$sessionId;
121
            }
122
123
            if (!empty($interbreadcrumb)) {
124
                array_unshift(
125
                    $interbreadcrumb,
126
                    ['name' => $courseInfo['title'], 'url' => $url]
127
                );
128
            }
129
        }
130
131
        if (empty($interbreadcrumb)) {
132
            $interbreadcrumb = [];
133
        }
134
135
        $params['legacy_javascript'] = $htmlHeadXtra;
136
        $params['legacy_breadcrumb'] = json_encode($interbreadcrumb);
137
138
        Template::setVueParams($params);
139
        $content = Container::getTwig()->render($tpl, $params);
140
        $response->setContent($content);
141
        $response->send();
142
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
143
    }
144
145
    /**
146
     * Display the page footer.
147
     */
148
    public static function display_reduced_footer()
149
    {
150
        return self::display_footer();
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::display_footer() targeting Display::display_footer() 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...
151
    }
152
153
    /**
154
     * Displays the tool introduction of a tool.
155
     *
156
     * @author Patrick Cool <[email protected]>, Ghent University
157
     *
158
     * @param string $tool          these are the constants that are used for indicating the tools
159
     * @param array  $editor_config Optional configuration settings for the online editor.
160
     *                              return: $tool return a string array list with the "define" in main_api.lib
161
     *
162
     * @return string html code for adding an introduction
163
     */
164
    public static function display_introduction_section(
165
        $tool,
166
        $editor_config = null
167
    ) {
168
        // @todo replace introduction section with a vue page.
169
        return;
170
        echo self::return_introduction_section($tool, $editor_config);
0 ignored issues
show
Unused Code introduced by
echo self::return_introd...($tool, $editor_config) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
171
    }
172
173
    /**
174
     * @param string $tool
175
     * @param array  $editor_config
176
     */
177
    public static function return_introduction_section(
178
        $tool,
179
        $editor_config = null
180
    ) {
181
        $moduleId = $tool;
182
        if ('true' === api_get_setting('enable_tool_introduction') || TOOL_COURSE_HOMEPAGE == $tool) {
183
            $introduction_section = null;
184
            require api_get_path(SYS_CODE_PATH).'inc/introductionSection.inc.php';
185
186
            return $introduction_section;
187
        }
188
    }
189
190
    /**
191
     * Displays a table.
192
     *
193
     * @param array  $header          Titles for the table header
194
     *                                each item in this array can contain 3 values
195
     *                                - 1st element: the column title
196
     *                                - 2nd element: true or false (column sortable?)
197
     *                                - 3th element: additional attributes for
198
     *                                th-tag (eg for column-width)
199
     *                                - 4the element: additional attributes for the td-tags
200
     * @param array  $content         2D-array with the tables content
201
     * @param array  $sorting_options Keys are:
202
     *                                'column' = The column to use as sort-key
203
     *                                'direction' = SORT_ASC or SORT_DESC
204
     * @param array  $paging_options  Keys are:
205
     *                                'per_page_default' = items per page when switching from
206
     *                                full-    list to per-page-view
207
     *                                'per_page' = number of items to show per page
208
     *                                'page_nr' = The page to display
209
     * @param array  $query_vars      Additional variables to add in the query-string
210
     * @param array  $form_actions
211
     * @param string $style           The style that the table will show. You can set 'table' or 'grid'
212
     * @param string $tableName
213
     * @param string $tableId
214
     *
215
     * @author [email protected]
216
     */
217
    public static function display_sortable_table(
218
        $header,
219
        $content,
220
        $sorting_options = [],
221
        $paging_options = [],
222
        $query_vars = null,
223
        $form_actions = [],
224
        $style = 'table',
225
        $tableName = 'tablename',
226
        $tableId = ''
227
    ) {
228
        $column = $sorting_options['column'] ?? 0;
229
        $default_items_per_page = $paging_options['per_page'] ?? 20;
230
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $tableName, null, $tableId);
231
        if (is_array($query_vars)) {
232
            $table->set_additional_parameters($query_vars);
233
        }
234
        if ('table' === $style) {
235
            if (is_array($header) && count($header) > 0) {
236
                foreach ($header as $index => $header_item) {
237
                    $table->set_header(
238
                        $index,
239
                        isset($header_item[0]) ? $header_item[0] : null,
240
                        isset($header_item[1]) ? $header_item[1] : null,
241
                        isset($header_item[2]) ? $header_item[2] : null,
242
                        isset($header_item[3]) ? $header_item[3] : null
243
                    );
244
                }
245
            }
246
            $table->set_form_actions($form_actions);
247
            $table->display();
248
        } else {
249
            $table->display_grid();
250
        }
251
    }
252
253
    /**
254
     * Returns an HTML table with sortable column (through complete page refresh).
255
     *
256
     * @param array  $header
257
     * @param array  $content         Array of row arrays
258
     * @param array  $sorting_options
259
     * @param array  $paging_options
260
     * @param array  $query_vars
261
     * @param array  $form_actions
262
     * @param string $style
263
     *
264
     * @return string HTML string for array
265
     */
266
    public static function return_sortable_table(
267
        $header,
268
        $content,
269
        $sorting_options = [],
270
        $paging_options = [],
271
        $query_vars = null,
272
        $form_actions = [],
273
        $style = 'table'
274
    ) {
275
        ob_start();
276
        self::display_sortable_table(
277
            $header,
278
            $content,
279
            $sorting_options,
280
            $paging_options,
281
            $query_vars,
282
            $form_actions,
283
            $style
284
        );
285
        $content = ob_get_contents();
286
        ob_end_clean();
287
288
        return $content;
289
    }
290
291
    /**
292
     * Shows a nice grid.
293
     *
294
     * @param string grid name (important to create css)
295
     * @param array header content
296
     * @param array array with the information to show
297
     * @param array $paging_options Keys are:
298
     *                              'per_page_default' = items per page when switching from
299
     *                              full-    list to per-page-view
300
     *                              'per_page' = number of items to show per page
301
     *                              'page_nr' = The page to display
302
     *                              'hide_navigation' =  true to hide the navigation
303
     * @param array $query_vars     Additional variables to add in the query-string
304
     * @param mixed An array with bool values to know which columns show.
305
     * i.e: $visibility_options= array(true, false) we will only show the first column
306
     *                Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
307
     */
308
    public static function display_sortable_grid(
309
        $name,
310
        $header,
311
        $content,
312
        $paging_options = [],
313
        $query_vars = null,
314
        $form_actions = [],
315
        $visibility_options = true,
316
        $sort_data = true,
317
        $grid_class = []
318
    ) {
319
        echo self::return_sortable_grid(
320
            $name,
321
            $header,
322
            $content,
323
            $paging_options,
324
            $query_vars,
325
            $form_actions,
326
            $visibility_options,
327
            $sort_data,
328
            $grid_class
329
        );
330
    }
331
332
    /**
333
     * Gets a nice grid in html string.
334
     *
335
     * @param string grid name (important to create css)
336
     * @param array header content
337
     * @param array array with the information to show
338
     * @param array $paging_options Keys are:
339
     *                              'per_page_default' = items per page when switching from
340
     *                              full-    list to per-page-view
341
     *                              'per_page' = number of items to show per page
342
     *                              'page_nr' = The page to display
343
     *                              'hide_navigation' =  true to hide the navigation
344
     * @param array $query_vars     Additional variables to add in the query-string
345
     * @param mixed An array with bool values to know which columns show. i.e:
346
     *  $visibility_options= array(true, false) we will only show the first column
347
     *    Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
348
     * @param bool  true for sorting data or false otherwise
349
     * @param array grid classes
350
     *
351
     * @return string html grid
352
     */
353
    public static function return_sortable_grid(
354
        $name,
355
        $header,
356
        $content,
357
        $paging_options = [],
358
        $query_vars = null,
359
        $form_actions = [],
360
        $visibility_options = true,
361
        $sort_data = true,
362
        $grid_class = [],
363
        $elementCount = 0
364
    ) {
365
        $column = 0;
366
        $default_items_per_page = $paging_options['per_page'] ?? 20;
367
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $name);
368
        $table->total_number_of_items = (int) $elementCount;
369
        if (is_array($query_vars)) {
370
            $table->set_additional_parameters($query_vars);
371
        }
372
373
        return $table->display_simple_grid(
374
            $visibility_options,
375
            $paging_options['hide_navigation'],
376
            $default_items_per_page,
377
            $sort_data,
378
            $grid_class
379
        );
380
    }
381
382
    /**
383
     * Displays a table with a special configuration.
384
     *
385
     * @param array $header          Titles for the table header
386
     *                               each item in this array can contain 3 values
387
     *                               - 1st element: the column title
388
     *                               - 2nd element: true or false (column sortable?)
389
     *                               - 3th element: additional attributes for th-tag (eg for column-width)
390
     *                               - 4the element: additional attributes for the td-tags
391
     * @param array $content         2D-array with the tables content
392
     * @param array $sorting_options Keys are:
393
     *                               'column' = The column to use as sort-key
394
     *                               'direction' = SORT_ASC or SORT_DESC
395
     * @param array $paging_options  Keys are:
396
     *                               'per_page_default' = items per page when switching from full list to per-page-view
397
     *                               'per_page' = number of items to show per page
398
     *                               'page_nr' = The page to display
399
     * @param array $query_vars      Additional variables to add in the query-string
400
     * @param array $column_show     Array of binaries 1= show columns 0. hide a column
401
     * @param array $column_order    An array of integers that let us decide how the columns are going to be sort.
402
     *                               i.e:  $column_order=array('1''4','3','4'); The 2nd column will be order like the 4th column
403
     * @param array $form_actions    Set optional forms actions
404
     *
405
     * @author Julio Montoya
406
     */
407
    public static function display_sortable_config_table(
408
        $table_name,
409
        $header,
410
        $content,
411
        $sorting_options = [],
412
        $paging_options = [],
413
        $query_vars = null,
414
        $column_show = [],
415
        $column_order = [],
416
        $form_actions = []
417
    ) {
418
        $column = isset($sorting_options['column']) ? $sorting_options['column'] : 0;
419
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
420
421
        $table = new SortableTableFromArrayConfig(
422
            $content,
423
            $column,
424
            $default_items_per_page,
425
            $table_name,
426
            $column_show,
427
            $column_order
428
        );
429
430
        if (is_array($query_vars)) {
431
            $table->set_additional_parameters($query_vars);
432
        }
433
        // Show or hide the columns header
434
        if (is_array($column_show)) {
435
            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...
436
                if (!empty($column_show[$i])) {
437
                    $val0 = isset($header[$i][0]) ? $header[$i][0] : null;
438
                    $val1 = isset($header[$i][1]) ? $header[$i][1] : null;
439
                    $val2 = isset($header[$i][2]) ? $header[$i][2] : null;
440
                    $val3 = isset($header[$i][3]) ? $header[$i][3] : null;
441
                    $table->set_header($i, $val0, $val1, $val2, $val3);
442
                }
443
            }
444
        }
445
        $table->set_form_actions($form_actions);
446
        $table->display();
447
    }
448
449
    /**
450
     * Returns a div html string with.
451
     *
452
     * @param string $message
453
     * @param string $type    Example: confirm, normal, warning, error
454
     * @param bool   $filter  Whether to XSS-filter or not
455
     *
456
     * @return string Message wrapped into an HTML div
457
     */
458
    public static function return_message($message, $type = 'normal', $filter = true)
459
    {
460
        if (empty($message)) {
461
            return '';
462
        }
463
464
        if ($filter) {
465
            $message = api_htmlentities(
466
                $message,
467
                ENT_QUOTES,
468
                api_is_xml_http_request() ? 'UTF-8' : api_get_system_encoding()
469
            );
470
        }
471
472
        $class = '';
473
        switch ($type) {
474
            case 'warning':
475
                $class .= 'alert alert-warning';
476
                break;
477
            case 'error':
478
                $class .= 'alert alert-danger';
479
                break;
480
            case 'confirmation':
481
            case 'confirm':
482
            case 'success':
483
                $class .= 'alert alert-success';
484
                break;
485
            case 'normal':
486
            case 'info':
487
            default:
488
                $class .= 'alert alert-info';
489
        }
490
491
        return self::div($message, ['class' => $class]);
492
    }
493
494
    /**
495
     * Returns an encrypted mailto hyperlink.
496
     *
497
     * @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...
498
     * @param string  clickable text
499
     * @param string  optional, class from stylesheet
500
     * @param bool $addExtraContent
501
     *
502
     * @return string encrypted mailto hyperlink
503
     */
504
    public static function encrypted_mailto_link(
505
        $email,
506
        $clickable_text = null,
507
        $style_class = '',
508
        $addExtraContent = false
509
    ) {
510
        if (is_null($clickable_text)) {
511
            $clickable_text = $email;
512
        }
513
514
        // "mailto:" already present?
515
        if ('mailto:' !== substr($email, 0, 7)) {
516
            $email = 'mailto:'.$email;
517
        }
518
519
        // Class (stylesheet) defined?
520
        if ('' !== $style_class) {
521
            $style_class = ' class="'.$style_class.'"';
522
        }
523
524
        // Encrypt email
525
        $hmail = '';
526
        for ($i = 0; $i < strlen($email); $i++) {
527
            $hmail .= '&#'.ord($email[$i]).';';
528
        }
529
530
        $value = api_get_configuration_value('add_user_course_information_in_mailto');
531
532
        if ($value) {
533
            if ('false' === api_get_setting('allow_email_editor')) {
534
                $hmail .= '?';
535
            }
536
537
            if (!api_is_anonymous()) {
538
                $hmail .= '&subject='.Security::remove_XSS(api_get_setting('siteName'));
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS(api_get_setting('siteName')) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

538
                $hmail .= '&subject='./** @scrutinizer ignore-type */ Security::remove_XSS(api_get_setting('siteName'));
Loading history...
539
            }
540
            if ($addExtraContent) {
541
                $content = '';
542
                if (!api_is_anonymous()) {
543
                    $userInfo = api_get_user_info();
544
                    $content .= get_lang('User').': '.$userInfo['complete_name']."\n";
545
546
                    $courseInfo = api_get_course_info();
547
                    if (!empty($courseInfo)) {
548
                        $content .= get_lang('Course').': ';
549
                        $content .= $courseInfo['name'];
550
                        $sessionInfo = api_get_session_info(api_get_session_id());
551
                        if (!empty($sessionInfo)) {
552
                            $content .= ' '.$sessionInfo['name'].' <br />';
553
                        }
554
                    }
555
                }
556
                $hmail .= '&body='.rawurlencode($content);
557
            }
558
        }
559
560
        $hclickable_text = '';
561
        // Encrypt clickable text if @ is present
562
        if (strpos($clickable_text, '@')) {
563
            for ($i = 0; $i < strlen($clickable_text); $i++) {
564
                $hclickable_text .= '&#'.ord($clickable_text[$i]).';';
565
            }
566
        } else {
567
            $hclickable_text = @htmlspecialchars(
568
                $clickable_text,
569
                ENT_QUOTES,
570
                api_get_system_encoding()
571
            );
572
        }
573
        // Return encrypted mailto hyperlink
574
        return '<a href="'.$hmail.'"'.$style_class.' class="clickable_email_link">'.$hclickable_text.'</a>';
575
    }
576
577
    /**
578
     * Prints an <option>-list with all letters (A-Z).
579
     *
580
     * @todo This is English language specific implementation.
581
     * It should be adapted for the other languages.
582
     *
583
     * @return string
584
     */
585
    public static function get_alphabet_options($selectedLetter = '')
586
    {
587
        $result = '';
588
        for ($i = 65; $i <= 90; $i++) {
589
            $letter = chr($i);
590
            $result .= '<option value="'.$letter.'"';
591
            if ($selectedLetter == $letter) {
592
                $result .= ' selected="selected"';
593
            }
594
            $result .= '>'.$letter.'</option>';
595
        }
596
597
        return $result;
598
    }
599
600
    /**
601
     * Get the options withing a select box within the given values.
602
     *
603
     * @param int   Min value
604
     * @param int   Max value
605
     * @param int   Default value
606
     *
607
     * @return string HTML select options
608
     */
609
    public static function get_numeric_options($min, $max, $selected_num = 0)
610
    {
611
        $result = '';
612
        for ($i = $min; $i <= $max; $i++) {
613
            $result .= '<option value="'.$i.'"';
614
            if (is_int($selected_num)) {
615
                if ($selected_num == $i) {
616
                    $result .= ' selected="selected"';
617
                }
618
            }
619
            $result .= '>'.$i.'</option>';
620
        }
621
622
        return $result;
623
    }
624
625
    /**
626
     * Gets the path of an icon.
627
     *
628
     * @param string $icon
629
     * @param int    $size
630
     *
631
     * @return string
632
     */
633
    public static function returnIconPath($icon, $size = ICON_SIZE_SMALL)
634
    {
635
        return self::return_icon($icon, null, null, $size, null, true, false);
636
    }
637
638
    /**
639
     * This public function returns the htmlcode for an icon.
640
     *
641
     * @param string   The filename of the file (in the main/img/ folder
642
     * @param string   The alt text (probably a language variable)
643
     * @param array    Additional attributes (for instance height, width, onclick, ...)
644
     * @param int  The wanted width of the icon (to be looked for in the corresponding img/icons/ folder)
645
     *
646
     * @return string An HTML string of the right <img> tag
647
     *
648
     * @author Patrick Cool <[email protected]>, Ghent University 2006
649
     * @author Julio Montoya 2010 Function improved, adding image constants
650
     * @author Yannick Warnier 2011 Added size handler
651
     *
652
     * @version Feb 2011
653
     */
654
    public static function return_icon(
655
        $image,
656
        $alt_text = '',
657
        $additional_attributes = [],
658
        $size = ICON_SIZE_SMALL,
659
        $show_text = true,
660
        $return_only_path = false,
661
        $loadThemeIcon = true
662
    ) {
663
        $code_path = api_get_path(SYS_PUBLIC_PATH);
664
        $w_code_path = api_get_path(WEB_PUBLIC_PATH);
665
        // The following path is checked to see if the file exist. It's
666
        // important to use the public path (i.e. web/css/) rather than the
667
        // internal path (/app/Resource/public/css/) because the path used
668
        // in the end must be the public path
669
        $alternateCssPath = api_get_path(SYS_PUBLIC_PATH).'css/';
670
        $alternateWebCssPath = api_get_path(WEB_PUBLIC_PATH).'css/';
671
672
        // Avoid issues with illegal string offset for legacy calls to this
673
        // method with an empty string rather than null or an empty array
674
        if (empty($additional_attributes)) {
675
            $additional_attributes = [];
676
        }
677
678
        $image = trim($image);
679
680
        if (isset($size)) {
681
            $size = (int) $size;
682
        } else {
683
            $size = ICON_SIZE_SMALL;
684
        }
685
686
        $size_extra = $size.'/';
687
        $icon = $w_code_path.'img/'.$image;
688
        $theme = 'themes/chamilo/icons/';
689
690
        if ($loadThemeIcon) {
691
            // @todo with chamilo 2 code
692
            $theme = 'themes/'.api_get_visual_theme().'/icons/';
693
            if (is_file($alternateCssPath.$theme.$image)) {
694
                $icon = $alternateWebCssPath.$theme.$image;
695
            }
696
            // Checking the theme icons folder example: app/Resources/public/css/themes/chamilo/icons/XXX
697
            if (is_file($alternateCssPath.$theme.$size_extra.$image)) {
698
                $icon = $alternateWebCssPath.$theme.$size_extra.$image;
699
            } elseif (is_file($code_path.'img/icons/'.$size_extra.$image)) {
700
                //Checking the main/img/icons/XXX/ folder
701
                $icon = $w_code_path.'img/icons/'.$size_extra.$image;
702
            }
703
        } else {
704
            if (is_file($code_path.'img/icons/'.$size_extra.$image)) {
705
                // Checking the main/img/icons/XXX/ folder
706
                $icon = $w_code_path.'img/icons/'.$size_extra.$image;
707
            }
708
        }
709
710
        // Special code to enable SVG - refs #7359 - Needs more work
711
        // The code below does something else to "test out" SVG: for each icon,
712
        // it checks if there is an SVG version. If so, it uses it.
713
        // When moving this to production, the return_icon() calls should
714
        // ask for the SVG version directly
715
        $svgIcons = api_get_setting('icons_mode_svg');
716
        if ('true' == $svgIcons && false == $return_only_path) {
717
            $svgImage = substr($image, 0, -3).'svg';
718
            if (is_file($code_path.$theme.'svg/'.$svgImage)) {
719
                $icon = $w_code_path.$theme.'svg/'.$svgImage;
720
            } elseif (is_file($code_path.'img/icons/svg/'.$svgImage)) {
721
                $icon = $w_code_path.'img/icons/svg/'.$svgImage;
722
            }
723
724
            if (empty($additional_attributes['height'])) {
725
                $additional_attributes['height'] = $size;
726
            }
727
            if (empty($additional_attributes['width'])) {
728
                $additional_attributes['width'] = $size;
729
            }
730
        }
731
732
        if ($return_only_path) {
733
            return $icon;
734
        }
735
736
        $img = self::img($icon, $alt_text, $additional_attributes);
737
        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...
738
            if ($show_text) {
739
                $img = "$img $alt_text";
740
            }
741
        }
742
743
        return $img;
744
    }
745
746
    /**
747
     * Returns the htmlcode for an image.
748
     *
749
     * @param string $image_path            the filename of the file (in the main/img/ folder
750
     * @param string $alt_text              the alt text (probably a language variable)
751
     * @param array  $additional_attributes (for instance height, width, onclick, ...)
752
     * @param bool   $filterPath            Optional. Whether filter the image path. Default is true
753
     *
754
     * @return string
755
     *
756
     * @author Julio Montoya 2010
757
     */
758
    public static function img(
759
        $image_path,
760
        $alt_text = '',
761
        $additional_attributes = null,
762
        $filterPath = true
763
    ) {
764
        if (empty($image_path)) {
765
            return '';
766
        }
767
        // Sanitizing the parameter $image_path
768
        if ($filterPath) {
769
            $image_path = Security::filter_img_path($image_path);
770
        }
771
772
        // alt text = the image name if there is none provided (for XHTML compliance)
773
        if ('' == $alt_text) {
774
            $alt_text = basename($image_path);
775
        }
776
777
        if (empty($additional_attributes)) {
778
            $additional_attributes = [];
779
        }
780
781
        $additional_attributes['src'] = $image_path;
782
783
        if (empty($additional_attributes['alt'])) {
784
            $additional_attributes['alt'] = $alt_text;
785
        }
786
        if (empty($additional_attributes['title'])) {
787
            $additional_attributes['title'] = $alt_text;
788
        }
789
790
        return self::tag('img', '', $additional_attributes);
791
    }
792
793
    /**
794
     * Returns the htmlcode for a tag (h3, h1, div, a, button), etc.
795
     *
796
     * @param string $tag                   the tag name
797
     * @param string $content               the tag's content
798
     * @param array  $additional_attributes (for instance height, width, onclick, ...)
799
     *
800
     * @return string
801
     *
802
     * @author Julio Montoya 2010
803
     */
804
    public static function tag($tag, $content, $additional_attributes = [])
805
    {
806
        $attribute_list = '';
807
        // Managing the additional attributes
808
        if (!empty($additional_attributes) && is_array($additional_attributes)) {
809
            $attribute_list = '';
810
            foreach ($additional_attributes as $key => &$value) {
811
                $attribute_list .= $key.'="'.$value.'" ';
812
            }
813
        }
814
        //some tags don't have this </XXX>
815
        if (in_array($tag, ['img', 'input', 'br'])) {
816
            $return_value = '<'.$tag.' '.$attribute_list.' />';
817
        } else {
818
            $return_value = '<'.$tag.' '.$attribute_list.' >'.$content.'</'.$tag.'>';
819
        }
820
821
        return $return_value;
822
    }
823
824
    /**
825
     * Creates a URL anchor.
826
     *
827
     * @param string $name
828
     * @param string $url
829
     * @param array  $attributes
830
     *
831
     * @return string
832
     */
833
    public static function url($name, $url, $attributes = [])
834
    {
835
        if (!empty($url)) {
836
            $url = preg_replace('#&amp;#', '&', $url);
837
            $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
838
            $attributes['href'] = $url;
839
        }
840
841
        return self::tag('a', $name, $attributes);
842
    }
843
844
    /**
845
     * Creates a div tag.
846
     *
847
     * @param string $content
848
     * @param array  $attributes
849
     *
850
     * @return string
851
     */
852
    public static function div($content, $attributes = [])
853
    {
854
        return self::tag('div', $content, $attributes);
855
    }
856
857
    /**
858
     * Creates a span tag.
859
     */
860
    public static function span($content, $attributes = [])
861
    {
862
        return self::tag('span', $content, $attributes);
863
    }
864
865
    /**
866
     * Displays an HTML input tag.
867
     */
868
    public static function input($type, $name, $value, $attributes = [])
869
    {
870
        if (isset($type)) {
871
            $attributes['type'] = $type;
872
        }
873
        if (isset($name)) {
874
            $attributes['name'] = $name;
875
        }
876
        if (isset($value)) {
877
            $attributes['value'] = $value;
878
        }
879
880
        return self::tag('input', '', $attributes);
881
    }
882
883
    /**
884
     * @param $name
885
     * @param $value
886
     * @param array $attributes
887
     *
888
     * @return string
889
     */
890
    public static function button($name, $value, $attributes = [])
891
    {
892
        if (!empty($name)) {
893
            $attributes['name'] = $name;
894
        }
895
896
        return self::tag('button', $value, $attributes);
897
    }
898
899
    /**
900
     * Displays an HTML select tag.
901
     *
902
     * @param string $name
903
     * @param array  $values
904
     * @param int    $default
905
     * @param array  $extra_attributes
906
     * @param bool   $show_blank_item
907
     * @param string $blank_item_text
908
     *
909
     * @return string
910
     */
911
    public static function select(
912
        $name,
913
        $values,
914
        $default = -1,
915
        $extra_attributes = [],
916
        $show_blank_item = true,
917
        $blank_item_text = ''
918
    ) {
919
        $html = '';
920
        $extra = '';
921
        $default_id = 'id="'.$name.'" ';
922
        $extra_attributes = array_merge(['class' => 'form-control'], $extra_attributes);
923
        foreach ($extra_attributes as $key => $parameter) {
924
            if ('id' == $key) {
925
                $default_id = '';
926
            }
927
            $extra .= $key.'="'.$parameter.'" ';
928
        }
929
        $html .= '<select name="'.$name.'" '.$default_id.' '.$extra.'>';
930
931
        if ($show_blank_item) {
932
            if (empty($blank_item_text)) {
933
                $blank_item_text = get_lang('Select');
934
            } else {
935
                $blank_item_text = Security::remove_XSS($blank_item_text);
936
            }
937
            $html .= self::tag(
938
                'option',
939
                '-- '.$blank_item_text.' --',
0 ignored issues
show
Bug introduced by
Are you sure $blank_item_text of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

939
                '-- './** @scrutinizer ignore-type */ $blank_item_text.' --',
Loading history...
940
                ['value' => '-1']
941
            );
942
        }
943
        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...
944
            foreach ($values as $key => $value) {
945
                if (is_array($value) && isset($value['name'])) {
946
                    $value = $value['name'];
947
                }
948
                $html .= '<option value="'.$key.'"';
949
950
                if (is_array($default)) {
951
                    foreach ($default as $item) {
952
                        if ($item == $key) {
953
                            $html .= ' selected="selected"';
954
                            break;
955
                        }
956
                    }
957
                } else {
958
                    if ($default == $key) {
959
                        $html .= ' selected="selected"';
960
                    }
961
                }
962
963
                $html .= '>'.$value.'</option>';
964
            }
965
        }
966
        $html .= '</select>';
967
968
        return $html;
969
    }
970
971
    /**
972
     * Creates a tab menu
973
     * Requirements: declare the jquery, jquery-ui libraries + the jquery-ui.css
974
     * in the $htmlHeadXtra variable before the display_header
975
     * Add this script.
976
     *
977
     * @param array  $headers       list of the tab titles
978
     * @param array  $items
979
     * @param string $id            id of the container of the tab in the example "tabs"
980
     * @param array  $attributes    for the ul
981
     * @param array  $ul_attributes
982
     * @param string $selected
983
     *
984
     * @return string
985
     */
986
    public static function tabs(
987
        $headers,
988
        $items,
989
        $id = 'tabs',
990
        $attributes = [],
991
        $ul_attributes = [],
992
        $selected = ''
993
    ) {
994
        if (empty($headers) || 0 === count($headers)) {
995
            return '';
996
        }
997
998
        $lis = '';
999
        $i = 1;
1000
        foreach ($headers as $item) {
1001
            $active = '';
1002
            if (1 == $i) {
1003
                $active = ' active';
1004
            }
1005
1006
            if (!empty($selected)) {
1007
                $active = '';
1008
                if ($selected == $i) {
1009
                    $active = ' active';
1010
                }
1011
            }
1012
1013
            $item = self::tag(
1014
                'a',
1015
                $item,
1016
                [
1017
                    //'href' => '#'.$id.'-'.$i,
1018
                    'href' => 'javascript:void(0)',
1019
                    'class' => 'nav-item nav-link '.$active,
1020
                    '@click' => "openTab =  $i",
1021
                    'id' => $id.$i.'-tab',
1022
                    'data-toggle' => 'tab',
1023
                    'role' => 'tab',
1024
                    'aria-controls' => $id.'-'.$i,
1025
                    'aria-selected' => $selected,
1026
                ]
1027
            );
1028
            $lis .= $item;
1029
            $i++;
1030
        }
1031
1032
        $ul = self::tag(
1033
            'nav',
1034
            $lis,
1035
            [
1036
                'id' => 'nav_'.$id,
1037
                'class' => 'nav nav-tabs',
1038
                'role' => 'tablist',
1039
            ]
1040
        );
1041
1042
        $i = 1;
1043
        $divs = '';
1044
        foreach ($items as $content) {
1045
            $active = '';
1046
            if (1 == $i) {
1047
                $active = ' show active';
1048
            }
1049
1050
            if (!empty($selected)) {
1051
                $active = '';
1052
                if ($selected == $i) {
1053
                    $active = ' show active';
1054
                }
1055
            }
1056
1057
            $divs .= self::tag(
1058
                'div',
1059
                $content,
1060
                [
1061
                    'id' => $id.'-'.$i,
1062
                    'x-show' => "openTab === $i",
1063
                    //'class' => 'tab-pane fade '.$active,
1064
                    //'role' => 'tabpanel',
1065
                    //'aria-labelledby' => $id.$i.'-tab',
1066
                ]
1067
            );
1068
            $i++;
1069
        }
1070
1071
        $attributes['id'] = ''.$id;
1072
        $attributes['class'] = 'tab_wrapper ';
1073
        $attributes['x-data'] = ' { openTab: 1 } ';
1074
1075
        return self::tag(
1076
            'div',
1077
            $ul.
1078
            $divs,
1079
            $attributes
1080
        );
1081
    }
1082
1083
    /**
1084
     * @param $headers
1085
     * @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...
1086
     *
1087
     * @return string
1088
     */
1089
    public static function tabsOnlyLink($headers, $selected = null)
1090
    {
1091
        $id = uniqid('tabs_');
1092
        $i = 1;
1093
        $list = '';
1094
        foreach ($headers as $item) {
1095
            $class = null;
1096
            if ($i == $selected) {
1097
                $class = 'active';
1098
            }
1099
            $item = self::tag(
1100
                'a',
1101
                $item['content'],
1102
                [
1103
                    'id' => $id.'-'.$i,
1104
                    'href' => $item['url'],
1105
                    'class' => 'btn '.$class,
1106
                ]
1107
            );
1108
            $list .= $item;
1109
            $i++;
1110
        }
1111
1112
        return self::toolbarAction($id, [$list]);
1113
    }
1114
1115
    /**
1116
     * In order to display a grid using jqgrid you have to:.
1117
     *
1118
     * @example
1119
     * After your Display::display_header function you have to add the nex javascript code:
1120
     * <script>
1121
     *   echo Display::grid_js('my_grid_name', $url,$columns, $column_model, $extra_params,[]);
1122
     *   // for more information of this function check the grid_js() function
1123
     * </script>
1124
     * //Then you have to call the grid_html
1125
     * echo Display::grid_html('my_grid_name');
1126
     * As you can see both function use the same "my_grid_name" this is very important otherwise nothing will work
1127
     *
1128
     * @param   string  the div id, this value must be the same with the first parameter of Display::grid_js()
1129
     *
1130
     * @return string html
1131
     */
1132
    public static function grid_html($div_id)
1133
    {
1134
        $table = self::tag('table', '', ['id' => $div_id]);
1135
        $table .= self::tag('div', '', ['id' => $div_id.'_pager']);
1136
1137
        return $table;
1138
    }
1139
1140
    /**
1141
     * This is a wrapper to use the jqgrid in Chamilo.
1142
     * For the other jqgrid options visit http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options
1143
     * This function need to be in the ready jquery function
1144
     * example --> $(function() { <?php echo Display::grid_js('grid' ...); ?> }
1145
     * In order to work this function needs the Display::grid_html function with the same div id.
1146
     *
1147
     * @param string $div_id       div id
1148
     * @param string $url          url where the jqgrid will ask for data (if datatype = json)
1149
     * @param array  $column_names Visible columns (you should use get_lang).
1150
     *                             An array in which we place the names of the columns.
1151
     *                             This is the text that appears in the head of the grid (Header layer).
1152
     *                             Example: colname   {name:'date',     index:'date',   width:120, align:'right'},
1153
     * @param array  $column_model the column model :  Array which describes the parameters of the columns.
1154
     *                             This is the most important part of the grid.
1155
     *                             For a full description of all valid values see colModel API. See the url above.
1156
     * @param array  $extra_params extra parameters
1157
     * @param array  $data         data that will be loaded
1158
     * @param string $formatter    A string that will be appended to the JSON returned
1159
     * @param bool   $fixed_width  not implemented yet
1160
     *
1161
     * @return string the js code
1162
     */
1163
    public static function grid_js(
1164
        $div_id,
1165
        $url,
1166
        $column_names,
1167
        $column_model,
1168
        $extra_params,
1169
        $data = [],
1170
        $formatter = '',
1171
        $fixed_width = false
1172
    ) {
1173
        $obj = new stdClass();
1174
        $obj->first = 'first';
1175
1176
        if (!empty($url)) {
1177
            $obj->url = $url;
1178
        }
1179
1180
        // Needed it in order to render the links/html in the grid
1181
        foreach ($column_model as &$columnModel) {
1182
            if (!isset($columnModel['formatter'])) {
1183
                $columnModel['formatter'] = '';
1184
            }
1185
        }
1186
1187
        //This line should only be used/modified in case of having characters
1188
        // encoding problems - see #6159
1189
        //$column_names = array_map("utf8_encode", $column_names);
1190
        $obj->colNames = $column_names;
1191
        $obj->colModel = $column_model;
1192
        $obj->pager = '#'.$div_id.'_pager';
1193
        $obj->datatype = 'json';
1194
        $obj->viewrecords = 'true';
1195
        $obj->guiStyle = 'bootstrap4';
1196
        $obj->iconSet = 'fontAwesomeSolid';
1197
        $all_value = 10000000;
1198
1199
        // Sets how many records we want to view in the grid
1200
        $obj->rowNum = 20;
1201
1202
        // Default row quantity
1203
        if (!isset($extra_params['rowList'])) {
1204
            $extra_params['rowList'] = [20, 50, 100, 500, 1000, $all_value];
1205
            $rowList = api_get_configuration_value('table_row_list');
1206
            if (!empty($rowList) && isset($rowList['options'])) {
1207
                $rowList = $rowList['options'];
1208
                $rowList[] = $all_value;
1209
            }
1210
            $extra_params['rowList'] = $rowList;
1211
        }
1212
1213
        $defaultRow = api_get_configuration_value('table_default_row');
1214
        if (!empty($defaultRow)) {
1215
            $obj->rowNum = (int) $defaultRow;
1216
        }
1217
1218
        $json = '';
1219
        if (!empty($extra_params['datatype'])) {
1220
            $obj->datatype = $extra_params['datatype'];
1221
        }
1222
1223
        // Row even odd style.
1224
        $obj->altRows = true;
1225
        if (!empty($extra_params['altRows'])) {
1226
            $obj->altRows = $extra_params['altRows'];
1227
        }
1228
1229
        if (!empty($extra_params['sortname'])) {
1230
            $obj->sortname = $extra_params['sortname'];
1231
        }
1232
1233
        if (!empty($extra_params['sortorder'])) {
1234
            $obj->sortorder = $extra_params['sortorder'];
1235
        }
1236
1237
        if (!empty($extra_params['rowList'])) {
1238
            $obj->rowList = $extra_params['rowList'];
1239
        }
1240
1241
        if (!empty($extra_params['rowNum'])) {
1242
            $obj->rowNum = $extra_params['rowNum'];
1243
        } else {
1244
            // Try to load max rows from Session
1245
            $urlInfo = parse_url($url);
1246
            if (isset($urlInfo['query'])) {
1247
                parse_str($urlInfo['query'], $query);
1248
                if (isset($query['a'])) {
1249
                    $action = $query['a'];
1250
                    // This value is set in model.ajax.php
1251
                    $savedRows = Session::read('max_rows_'.$action);
1252
                    if (!empty($savedRows)) {
1253
                        $obj->rowNum = $savedRows;
1254
                    }
1255
                }
1256
            }
1257
        }
1258
1259
        if (!empty($extra_params['viewrecords'])) {
1260
            $obj->viewrecords = $extra_params['viewrecords'];
1261
        }
1262
1263
        $beforeSelectRow = null;
1264
        if (isset($extra_params['beforeSelectRow'])) {
1265
            $beforeSelectRow = 'beforeSelectRow: '.$extra_params['beforeSelectRow'].', ';
1266
            unset($extra_params['beforeSelectRow']);
1267
        }
1268
1269
        $beforeProcessing = '';
1270
        if (isset($extra_params['beforeProcessing'])) {
1271
            $beforeProcessing = 'beforeProcessing : function() { '.$extra_params['beforeProcessing'].' },';
1272
            unset($extra_params['beforeProcessing']);
1273
        }
1274
1275
        $beforeRequest = '';
1276
        if (isset($extra_params['beforeRequest'])) {
1277
            $beforeRequest = 'beforeRequest : function() { '.$extra_params['beforeRequest'].' },';
1278
            unset($extra_params['beforeRequest']);
1279
        }
1280
1281
        $gridComplete = '';
1282
        if (isset($extra_params['gridComplete'])) {
1283
            $gridComplete = 'gridComplete : function() { '.$extra_params['gridComplete'].' },';
1284
            unset($extra_params['gridComplete']);
1285
        }
1286
1287
        // Adding extra params
1288
        if (!empty($extra_params)) {
1289
            foreach ($extra_params as $key => $element) {
1290
                // the groupHeaders key gets a special treatment
1291
                if ('groupHeaders' != $key) {
1292
                    $obj->$key = $element;
1293
                }
1294
            }
1295
        }
1296
1297
        // Adding static data.
1298
        if (!empty($data)) {
1299
            $data_var = $div_id.'_data';
1300
            $json .= ' var '.$data_var.' = '.json_encode($data).';';
1301
            $obj->data = $data_var;
1302
            $obj->datatype = 'local';
1303
            $json .= "\n";
1304
        }
1305
1306
        $obj->end = 'end';
1307
1308
        $json_encode = json_encode($obj);
1309
1310
        if (!empty($data)) {
1311
            //Converts the "data":"js_variable" to "data":js_variable,
1312
            // otherwise it will not work
1313
            $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...
1314
        }
1315
1316
        // Fixing true/false js values that doesn't need the ""
1317
        $json_encode = str_replace(':"true"', ':true', $json_encode);
1318
        // wrap_cell is not a valid jqgrid attributes is a hack to wrap a text
1319
        $json_encode = str_replace('"wrap_cell":true', 'cellattr : function(rowId, value, rowObject, colModel, arrData) { return \'class = "jqgrid_whitespace"\'; }', $json_encode);
1320
        $json_encode = str_replace(':"false"', ':false', $json_encode);
1321
        $json_encode = str_replace('"formatter":"action_formatter"', 'formatter:action_formatter', $json_encode);
1322
        $json_encode = str_replace('"formatter":"extra_formatter"', 'formatter:extra_formatter', $json_encode);
1323
        $json_encode = str_replace(['{"first":"first",', '"end":"end"}'], '', $json_encode);
1324
1325
        if (api_get_configuration_value('allow_compilatio_tool') &&
1326
            (false !== strpos($_SERVER['REQUEST_URI'], 'work/work.php') ||
1327
             false != strpos($_SERVER['REQUEST_URI'], 'work/work_list_all.php')
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...
1328
            )
1329
        ) {
1330
            $json_encode = str_replace('"function () { compilatioInit() }"',
1331
                'function () { compilatioInit() }',
1332
                $json_encode
1333
            );
1334
        }
1335
        // Creating the jqgrid element.
1336
        $json .= '$("#'.$div_id.'").jqGrid({';
1337
        //$json .= $beforeSelectRow;
1338
        $json .= $gridComplete;
1339
        $json .= $beforeProcessing;
1340
        $json .= $beforeRequest;
1341
        $json .= $json_encode;
1342
        $json .= '});';
1343
1344
        // Grouping headers option
1345
        if (isset($extra_params['groupHeaders'])) {
1346
            $groups = '';
1347
            foreach ($extra_params['groupHeaders'] as $group) {
1348
                //{ "startColumnName" : "courses", "numberOfColumns" : 1, "titleText" : "Order Info" },
1349
                $groups .= '{ "startColumnName" : "'.$group['startColumnName'].'", "numberOfColumns" : '.$group['numberOfColumns'].', "titleText" : "'.$group['titleText'].'" },';
1350
            }
1351
            $json .= '$("#'.$div_id.'").jqGrid("setGroupHeaders", {
1352
                "useColSpanStyle" : false,
1353
                "groupHeaders"    : [
1354
                    '.$groups.'
1355
                ]
1356
            });';
1357
        }
1358
1359
        $all_text = addslashes(get_lang('All'));
1360
        $json .= '$("'.$obj->pager.' option[value='.$all_value.']").text("'.$all_text.'");';
1361
        $json .= "\n";
1362
        // Adding edit/delete icons.
1363
        $json .= $formatter;
1364
1365
        return $json;
1366
    }
1367
1368
    /**
1369
     * @param array $headers
1370
     * @param array $rows
1371
     * @param array $attributes
1372
     *
1373
     * @return string
1374
     */
1375
    public static function table($headers, $rows, $attributes = [])
1376
    {
1377
        if (empty($attributes)) {
1378
            $attributes['class'] = 'data_table';
1379
        }
1380
        $table = new HTML_Table($attributes);
1381
        $row = 0;
1382
        $column = 0;
1383
1384
        // Course headers
1385
        if (!empty($headers)) {
1386
            foreach ($headers as $item) {
1387
                $table->setHeaderContents($row, $column, $item);
1388
                $column++;
1389
            }
1390
            $row = 1;
1391
            $column = 0;
1392
        }
1393
1394
        if (!empty($rows)) {
1395
            foreach ($rows as $content) {
1396
                $table->setCellContents($row, $column, $content);
1397
                $row++;
1398
            }
1399
        }
1400
1401
        return $table->toHtml();
1402
    }
1403
1404
    /**
1405
     * Get the session box details as an array.
1406
     *
1407
     * @todo check session visibility.
1408
     *
1409
     * @param int $session_id
1410
     *
1411
     * @return array Empty array or session array
1412
     *               ['title'=>'...','category'=>'','dates'=>'...','coach'=>'...','active'=>true/false,'session_category_id'=>int]
1413
     */
1414
    public static function getSessionTitleBox($session_id)
1415
    {
1416
        $session_info = api_get_session_info($session_id);
1417
        $coachInfo = [];
1418
        if (!empty($session_info['id_coach'])) {
1419
            $coachInfo = api_get_user_info($session_info['id_coach']);
1420
        }
1421
1422
        $session = [];
1423
        $session['category_id'] = $session_info['session_category_id'];
1424
        $session['title'] = $session_info['name'];
1425
        $session['coach_id'] = $session['id_coach'] = $session_info['id_coach'];
1426
        $session['dates'] = '';
1427
        $session['coach'] = '';
1428
        if ('true' === api_get_setting('show_session_coach') && isset($coachInfo['complete_name'])) {
1429
            $session['coach'] = get_lang('General coach').': '.$coachInfo['complete_name'];
1430
        }
1431
        $active = false;
1432
        if (('0000-00-00 00:00:00' === $session_info['access_end_date'] &&
1433
            '0000-00-00 00:00:00' === $session_info['access_start_date']) ||
1434
            (empty($session_info['access_end_date']) && empty($session_info['access_start_date']))
1435
        ) {
1436
            if (isset($session_info['duration']) && !empty($session_info['duration'])) {
1437
                $daysLeft = SessionManager::getDayLeftInSession($session_info, api_get_user_id());
1438
                $session['duration'] = $daysLeft >= 0
1439
                    ? sprintf(get_lang('This session has a maximum duration. Only %s days to go.'), $daysLeft)
1440
                    : get_lang('You are already registered but your allowed access time has expired.');
1441
            }
1442
            $active = true;
1443
        } else {
1444
            $dates = SessionManager::parseSessionDates($session_info, true);
1445
            $session['dates'] = $dates['access'];
1446
            if ('true' === api_get_setting('show_session_coach') && isset($coachInfo['complete_name'])) {
1447
                $session['coach'] = $coachInfo['complete_name'];
1448
            }
1449
            //$active = $date_start <= $now && $date_end >= $now;
1450
        }
1451
        $session['active'] = $active;
1452
        $session['session_category_id'] = $session_info['session_category_id'];
1453
        $session['visibility'] = $session_info['visibility'];
1454
        $session['num_users'] = $session_info['nbr_users'];
1455
        $session['num_courses'] = $session_info['nbr_courses'];
1456
        $session['description'] = $session_info['description'];
1457
        $session['show_description'] = $session_info['show_description'];
1458
        //$session['image'] = SessionManager::getSessionImage($session_info['id']);
1459
        $session['url'] = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$session_info['id'];
1460
1461
        $entityManager = Database::getManager();
1462
        $fieldValuesRepo = $entityManager->getRepository(ExtraFieldValues::class);
1463
        $extraFieldValues = $fieldValuesRepo->getVisibleValues(
1464
            ExtraField::SESSION_FIELD_TYPE,
1465
            $session_id
1466
        );
1467
1468
        $session['extra_fields'] = [];
1469
        /** @var ExtraFieldValues $value */
1470
        foreach ($extraFieldValues as $value) {
1471
            if (empty($value)) {
1472
                continue;
1473
            }
1474
            $session['extra_fields'][] = [
1475
                'field' => [
1476
                    'variable' => $value->getField()->getVariable(),
1477
                    'display_text' => $value->getField()->getDisplayText(),
1478
                ],
1479
                'value' => $value->getValue(),
1480
            ];
1481
        }
1482
1483
        return $session;
1484
    }
1485
1486
    /**
1487
     * Return the five star HTML.
1488
     *
1489
     * @param string $id              of the rating ul element
1490
     * @param string $url             that will be added (for jquery see hot_courses.tpl)
1491
     * @param array  $point_info      point info array see function CourseManager::get_course_ranking()
1492
     * @param bool   $add_div_wrapper add a div wrapper
1493
     *
1494
     * @return string
1495
     */
1496
    public static function return_rating_system(
1497
        $id,
1498
        $url,
1499
        $point_info = [],
1500
        $add_div_wrapper = true
1501
    ) {
1502
        $number_of_users_who_voted = isset($point_info['users_who_voted']) ? $point_info['users_who_voted'] : null;
1503
        $percentage = isset($point_info['point_average']) ? $point_info['point_average'] : 0;
1504
1505
        if (!empty($percentage)) {
1506
            $percentage = $percentage * 125 / 100;
1507
        }
1508
        $accesses = isset($point_info['accesses']) ? $point_info['accesses'] : 0;
1509
        $star_label = sprintf(get_lang('%s stars out of 5'), $point_info['point_average_star']);
1510
1511
        $html = '<section class="rating-widget">';
1512
        $html .= '<div class="rating-stars"><ul id="stars">';
1513
        $html .= '<li class="star" data-link="'.$url.'&amp;star=1" title="Poor" data-value="1"><i class="fa fa-star fa-fw"></i></li>
1514
                 <li class="star" data-link="'.$url.'&amp;star=2" title="Fair" data-value="2"><i class="fa fa-star fa-fw"></i></li>
1515
                 <li class="star" data-link="'.$url.'&amp;star=3" title="Good" data-value="3"><i class="fa fa-star fa-fw"></i></li>
1516
                 <li class="star" data-link="'.$url.'&amp;star=4" title="Excellent" data-value="4"><i class="fa fa-star fa-fw"></i></li>
1517
                 <li class="star" data-link="'.$url.'&amp;star=5" title="WOW!!!" data-value="5"><i class="fa fa-star fa-fw"></i></li>
1518
        ';
1519
        $html .= '</ul></div>';
1520
        $html .= '</section>';
1521
        $labels = [];
1522
1523
        $labels[] = 1 == $number_of_users_who_voted ? $number_of_users_who_voted.' '.get_lang('Vote') : $number_of_users_who_voted.' '.get_lang('Votes');
1524
        $labels[] = 1 == $accesses ? $accesses.' '.get_lang('Visit') : $accesses.' '.get_lang('Visits');
1525
        $labels[] = $point_info['user_vote'] ? get_lang('Your vote').' ['.$point_info['user_vote'].']' : get_lang('Your vote').' [?] ';
1526
1527
        if (!$add_div_wrapper && api_is_anonymous()) {
1528
            $labels[] = self::tag('span', get_lang('Login to vote'), ['class' => 'error']);
1529
        }
1530
1531
        $html .= self::div(implode(' | ', $labels), ['id' => 'vote_label_'.$id, 'class' => 'vote_label_info']);
1532
        $html .= ' '.self::span(' ', ['id' => 'vote_label2_'.$id]);
1533
1534
        if ($add_div_wrapper) {
1535
            $html = self::div($html, ['id' => 'rating_wrapper_'.$id]);
1536
        }
1537
1538
        return $html;
1539
    }
1540
1541
    /**
1542
     * @param string $title
1543
     * @param string $second_title
1544
     * @param string $size
1545
     * @param bool   $filter
1546
     *
1547
     * @return string
1548
     */
1549
    public static function page_header($title, $second_title = null, $size = 'h2', $filter = true)
1550
    {
1551
        if ($filter) {
1552
            $title = Security::remove_XSS($title);
1553
        }
1554
1555
        if (!empty($second_title)) {
1556
            if ($filter) {
1557
                $second_title = Security::remove_XSS($second_title);
1558
            }
1559
            $title .= "<small> $second_title</small>";
1560
        }
1561
1562
        return '<'.$size.' class="page-header">'.$title.'</'.$size.'>';
0 ignored issues
show
Bug introduced by
Are you sure $title of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

1562
        return '<'.$size.' class="page-header">'./** @scrutinizer ignore-type */ $title.'</'.$size.'>';
Loading history...
1563
    }
1564
1565
    public static function page_header_and_translate($title, $second_title = null)
1566
    {
1567
        $title = get_lang($title);
1568
1569
        return self::page_header($title, $second_title);
1570
    }
1571
1572
    public static function page_subheader($title, $second_title = null, $size = 'h3', $attributes = [])
1573
    {
1574
        if (!empty($second_title)) {
1575
            $second_title = Security::remove_XSS($second_title);
1576
            $title .= "<small> $second_title<small>";
1577
        }
1578
        $subTitle = self::tag($size, Security::remove_XSS($title), $attributes);
1579
1580
        return $subTitle;
1581
    }
1582
1583
    public static function page_subheader2($title, $second_title = null)
1584
    {
1585
        return self::page_header($title, $second_title, 'h4');
1586
    }
1587
1588
    public static function page_subheader3($title, $second_title = null)
1589
    {
1590
        return self::page_header($title, $second_title, 'h5');
1591
    }
1592
1593
    public static function description(array $list): string
1594
    {
1595
        $html = '';
1596
        if (!empty($list)) {
1597
            $html = '<dl class="dl-horizontal">';
1598
            foreach ($list as $item) {
1599
                $html .= '<dt>'.$item['title'].'</dt>';
1600
                $html .= '<dd>'.$item['content'].'</dd>';
1601
            }
1602
            $html .= '</dl>';
1603
        }
1604
1605
        return $html;
1606
    }
1607
1608
    /**
1609
     * @param int    $percentage      int value between 0 and 100
1610
     * @param bool   $show_percentage
1611
     * @param string $extra_info
1612
     * @param string $class           danger/success/infowarning
1613
     *
1614
     * @return string
1615
     */
1616
    public static function bar_progress($percentage, $show_percentage = true, $extra_info = '', $class = '')
1617
    {
1618
        $percentage = (int) $percentage;
1619
        $class = empty($class) ? '' : "progress-bar-$class";
1620
1621
        $div = '<div class="progress">
1622
                <div
1623
                    class="progress-bar progress-bar-striped '.$class.'"
1624
                    role="progressbar"
1625
                    aria-valuenow="'.$percentage.'"
1626
                    aria-valuemin="0"
1627
                    aria-valuemax="100"
1628
                    style="width: '.$percentage.'%;"
1629
                >';
1630
        if ($show_percentage) {
1631
            $div .= $percentage.'%';
1632
        } else {
1633
            if (!empty($extra_info)) {
1634
                $div .= $extra_info;
1635
            }
1636
        }
1637
        $div .= '</div></div>';
1638
1639
        return $div;
1640
    }
1641
1642
    /**
1643
     * @param string $count
1644
     * @param string $type
1645
     *
1646
     * @return string|null
1647
     */
1648
    public static function badge($count, $type = 'warning')
1649
    {
1650
        $class = '';
1651
1652
        switch ($type) {
1653
            case 'success':
1654
                $class = 'bg-success';
1655
                break;
1656
            case 'warning':
1657
                $class = 'bg-warning text-dark';
1658
                break;
1659
            case 'important':
1660
                $class = 'bg-important';
1661
                break;
1662
            case 'info':
1663
                $class = 'bg-info';
1664
                break;
1665
            case 'inverse':
1666
                $class = 'bg-inverse';
1667
                break;
1668
        }
1669
1670
        if (!empty($count)) {
1671
            return ' <span class="badge '.$class.'">'.$count.'</span>';
1672
        }
1673
1674
        return null;
1675
    }
1676
1677
    /**
1678
     * @param array $badge_list
1679
     *
1680
     * @return string
1681
     */
1682
    public static function badgeGroup($list)
1683
    {
1684
        $html = '<div class="badge-group">';
1685
        foreach ($list as $badge) {
1686
            $html .= $badge;
1687
        }
1688
        $html .= '</div>';
1689
1690
        return $html;
1691
    }
1692
1693
    /**
1694
     * @param string $content
1695
     * @param string $type
1696
     *
1697
     * @return string
1698
     */
1699
    public static function label($content, $type = 'default')
1700
    {
1701
        switch ($type) {
1702
            case 'success':
1703
                $class = 'success';
1704
                break;
1705
            case 'warning':
1706
                $class = 'warning text-dark';
1707
                break;
1708
            case 'important':
1709
            case 'danger':
1710
                $class = 'danger';
1711
                break;
1712
            case 'info':
1713
                $class = 'info';
1714
                break;
1715
            case 'primary':
1716
                $class = 'primary';
1717
                break;
1718
            default:
1719
                $class = 'secondary';
1720
                break;
1721
        }
1722
1723
        $html = '';
1724
        if (!empty($content)) {
1725
            $html = '<span class="badge bg-'.$class.'">';
1726
            $html .= $content;
1727
            $html .= '</span>';
1728
        }
1729
1730
        return $html;
1731
    }
1732
1733
    /**
1734
     * @param array  $items
1735
     * @param string $class
1736
     */
1737
    public static function actions($items, $class = 'new_actions'): string
1738
    {
1739
        if (empty($items)) {
1740
            return '';
1741
        }
1742
        $links = '';
1743
        foreach ($items as $value) {
1744
            $attributes = $value['url_attributes'] ?? [];
1745
            $links .= self::url($value['content'], $value['url'], $attributes);
1746
        }
1747
1748
        return self::toolbarAction(uniqid('toolbar', false), [$links]);
1749
    }
1750
1751
    /**
1752
     * Prints a tooltip.
1753
     *
1754
     * @param string $text
1755
     * @param string $tip
1756
     *
1757
     * @return string
1758
     */
1759
    public static function tip($text, $tip)
1760
    {
1761
        if (empty($tip)) {
1762
            return $text;
1763
        }
1764
1765
        return self::span(
1766
            $text,
1767
            ['class' => 'boot-tooltip', 'title' => strip_tags($tip)]
1768
        );
1769
    }
1770
1771
    /**
1772
     * @param array $buttons
1773
     *
1774
     * @return string
1775
     */
1776
    public static function groupButton($buttons)
1777
    {
1778
        $html = '<div class="btn-group" role="group">';
1779
        foreach ($buttons as $button) {
1780
            $html .= $button;
1781
        }
1782
        $html .= '</div>';
1783
1784
        return $html;
1785
    }
1786
1787
    /**
1788
     * @todo use twig
1789
     *
1790
     * @param string $title
1791
     * @param array  $elements
1792
     * @param bool   $alignToRight
1793
     *
1794
     * @return string
1795
     */
1796
    public static function groupButtonWithDropDown($title, $elements, $alignToRight = false)
1797
    {
1798
        $id = uniqid('dropdown', false);
1799
        $html = '
1800
        <div class="dropdown inline-block relative">
1801
            <button
1802
                id="'.$id.'"
1803
                type="button"
1804
                class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500"
1805
                aria-expanded="false"
1806
                aria-haspopup="true"
1807
            >
1808
              '.$title.'
1809
              <svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
1810
                <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
1811
              </svg>
1812
            </button>
1813
            <div
1814
                id="'.$id.'_menu"
1815
                class=" dropdown-menu hidden origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
1816
                role="menu"
1817
                aria-orientation="vertical"
1818
                aria-labelledby="menu-button"
1819
                tabindex="-1"
1820
            >
1821
            <div class="py-1" role="none">';
1822
        foreach ($elements as $item) {
1823
            $html .= self::url(
1824
                    $item['title'],
1825
                    $item['href'],
1826
                    [
1827
                        'class' => 'text-gray-700 block px-4 py-2 text-sm',
1828
                        'role' => 'menuitem',
1829
                        'onclick' => $item['onclick'] ?? '',
1830
                        'data-action' => $item['data-action'] ?? '',
1831
                    ]
1832
                );
1833
        }
1834
        $html .= '
1835
            </div>
1836
            </div>
1837
            </div>
1838
            <script>
1839
             document.addEventListener("DOMContentLoaded", function() {
1840
                const button = document.querySelector("#'.$id.'");
1841
                    button.addEventListener("click", (e) => {
1842
                    let menu = document.querySelector("#'.$id.'_menu");
1843
                    if (menu.classList.contains("hidden")) {
1844
                        menu.classList.remove("hidden");
1845
                    } else {
1846
                        menu.classList.add("hidden");
1847
                    }
1848
                });
1849
            });
1850
            </script>';
1851
1852
        return $html;
1853
    }
1854
1855
    /**
1856
     * @param string $file
1857
     * @param array  $params
1858
     *
1859
     * @return string|null
1860
     */
1861
    public static function getMediaPlayer($file, $params = [])
1862
    {
1863
        $fileInfo = pathinfo($file);
1864
1865
        $autoplay = isset($params['autoplay']) && 'true' === $params['autoplay'] ? 'autoplay' : '';
1866
        $id = isset($params['id']) ? $params['id'] : $fileInfo['basename'];
1867
        $width = isset($params['width']) ? 'width="'.$params['width'].'"' : null;
1868
        $class = isset($params['class']) ? ' class="'.$params['class'].'"' : null;
1869
1870
        switch ($fileInfo['extension']) {
1871
            case 'mp3':
1872
            case 'webm':
1873
                $html = '<audio id="'.$id.'" '.$class.' controls '.$autoplay.' '.$width.' src="'.$params['url'].'" >';
1874
                $html .= '<object width="'.$width.'" height="50" type="application/x-shockwave-flash" data="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf">
1875
                            <param name="movie" value="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf" />
1876
                            <param name="flashvars" value="controls=true&file='.$params['url'].'" />
1877
                          </object>';
1878
                $html .= '</audio>';
1879
1880
                return $html;
1881
                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...
1882
            case 'wav':
1883
            case 'ogg':
1884
                $html = '<audio width="300px" controls id="'.$id.'" '.$autoplay.' src="'.$params['url'].'" >';
1885
1886
                return $html;
1887
                break;
1888
        }
1889
1890
        return null;
1891
    }
1892
1893
    /**
1894
     * @param int    $nextValue
1895
     * @param array  $list
1896
     * @param int    $current
1897
     * @param int    $fixedValue
1898
     * @param array  $conditions
1899
     * @param string $link
1900
     * @param bool   $isMedia
1901
     * @param bool   $addHeaders
1902
     * @param array  $linkAttributes
1903
     *
1904
     * @return string
1905
     */
1906
    public static function progressPaginationBar(
1907
        $nextValue,
1908
        $list,
1909
        $current,
1910
        $fixedValue = null,
1911
        $conditions = [],
1912
        $link = null,
1913
        $isMedia = false,
1914
        $addHeaders = true,
1915
        $linkAttributes = []
1916
    ) {
1917
        if ($addHeaders) {
1918
            $pagination_size = 'pagination-mini';
1919
            $html = '<div class="exercise_pagination pagination '.$pagination_size.'"><ul>';
1920
        } else {
1921
            $html = null;
1922
        }
1923
        $affectAllItems = false;
1924
        if ($isMedia && isset($fixedValue) && ($nextValue + 1 == $current)) {
1925
            $affectAllItems = true;
1926
        }
1927
        $localCounter = 0;
1928
        foreach ($list as $itemId) {
1929
            $isCurrent = false;
1930
            if ($affectAllItems) {
1931
                $isCurrent = true;
1932
            } else {
1933
                if (!$isMedia) {
1934
                    $isCurrent = $current == ($localCounter + $nextValue + 1) ? true : false;
1935
                }
1936
            }
1937
            $html .= self::parsePaginationItem(
1938
                $itemId,
1939
                $isCurrent,
1940
                $conditions,
1941
                $link,
1942
                $nextValue,
1943
                $isMedia,
1944
                $localCounter,
1945
                $fixedValue,
1946
                $linkAttributes
1947
            );
1948
            $localCounter++;
1949
        }
1950
        if ($addHeaders) {
1951
            $html .= '</ul></div>';
1952
        }
1953
1954
        return $html;
1955
    }
1956
1957
    /**
1958
     * @param int    $itemId
1959
     * @param bool   $isCurrent
1960
     * @param array  $conditions
1961
     * @param string $link
1962
     * @param int    $nextValue
1963
     * @param bool   $isMedia
1964
     * @param int    $localCounter
1965
     * @param int    $fixedValue
1966
     * @param array  $linkAttributes
1967
     *
1968
     * @return string
1969
     */
1970
    public static function parsePaginationItem(
1971
        $itemId,
1972
        $isCurrent,
1973
        $conditions,
1974
        $link,
1975
        $nextValue = 0,
1976
        $isMedia = false,
1977
        $localCounter = null,
1978
        $fixedValue = null,
1979
        $linkAttributes = []
1980
    ) {
1981
        $defaultClass = 'before';
1982
        $class = $defaultClass;
1983
        foreach ($conditions as $condition) {
1984
            $array = isset($condition['items']) ? $condition['items'] : [];
1985
            $class_to_applied = $condition['class'];
1986
            $type = isset($condition['type']) ? $condition['type'] : 'positive';
1987
            $mode = isset($condition['mode']) ? $condition['mode'] : 'add';
1988
            switch ($type) {
1989
                case 'positive':
1990
                    if (in_array($itemId, $array)) {
1991
                        if ('overwrite' == $mode) {
1992
                            $class = " $defaultClass $class_to_applied";
1993
                        } else {
1994
                            $class .= " $class_to_applied";
1995
                        }
1996
                    }
1997
                    break;
1998
                case 'negative':
1999
                    if (!in_array($itemId, $array)) {
2000
                        if ('overwrite' == $mode) {
2001
                            $class = " $defaultClass $class_to_applied";
2002
                        } else {
2003
                            $class .= " $class_to_applied";
2004
                        }
2005
                    }
2006
                    break;
2007
            }
2008
        }
2009
        if ($isCurrent) {
2010
            $class = 'before current';
2011
        }
2012
        if ($isMedia && $isCurrent) {
2013
            $class = 'before current';
2014
        }
2015
        if (empty($link)) {
2016
            $link_to_show = '#';
2017
        } else {
2018
            $link_to_show = $link.($nextValue + $localCounter);
2019
        }
2020
        $label = $nextValue + $localCounter + 1;
2021
        if ($isMedia) {
2022
            $label = ($fixedValue + 1).' '.chr(97 + $localCounter);
2023
            $link_to_show = $link.$fixedValue.'#questionanchor'.$itemId;
2024
        }
2025
        $link = self::url($label.' ', $link_to_show, $linkAttributes);
2026
2027
        return '<li class = "'.$class.'">'.$link.'</li>';
2028
    }
2029
2030
    /**
2031
     * @param int $current
2032
     * @param int $total
2033
     *
2034
     * @return string
2035
     */
2036
    public static function paginationIndicator($current, $total)
2037
    {
2038
        $html = null;
2039
        if (!empty($current) && !empty($total)) {
2040
            $label = sprintf(get_lang('%s of %s'), $current, $total);
2041
            $html = self::url($label, '#', ['class' => 'btn disabled']);
2042
        }
2043
2044
        return $html;
2045
    }
2046
2047
    /**
2048
     * @param $url
2049
     * @param $currentPage
2050
     * @param $pagesCount
2051
     * @param $totalItems
2052
     *
2053
     * @return string
2054
     */
2055
    public static function getPagination($url, $currentPage, $pagesCount, $totalItems)
2056
    {
2057
        $pagination = '';
2058
        if ($totalItems > 1 && $pagesCount > 1) {
2059
            $pagination .= '<ul class="pagination">';
2060
            for ($i = 0; $i < $pagesCount; $i++) {
2061
                $newPage = $i + 1;
2062
                if ($currentPage == $newPage) {
2063
                    $pagination .= '<li class="active"><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2064
                } else {
2065
                    $pagination .= '<li><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2066
                }
2067
            }
2068
            $pagination .= '</ul>';
2069
        }
2070
2071
        return $pagination;
2072
    }
2073
2074
    /**
2075
     * Adds a legacy message in the queue.
2076
     *
2077
     * @param string $message
2078
     */
2079
    public static function addFlash($message)
2080
    {
2081
        // Detect type of message.
2082
        $parts = preg_match('/alert-([a-z]*)/', $message, $matches);
2083
        $type = 'primary';
2084
        if ($parts && isset($matches[1]) && $matches[1]) {
2085
            $type = $matches[1];
2086
        }
2087
        // Detect legacy content of message.
2088
        $result = preg_match('/<div(.*?)\>(.*?)\<\/div>/s', $message, $matches);
2089
        if ($result && isset($matches[2])) {
2090
            Container::getSession()->getFlashBag()->add($type, $matches[2]);
2091
        }
2092
    }
2093
2094
    /**
2095
     * Get the profile edition link for a user.
2096
     *
2097
     * @param int  $userId  The user id
2098
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
2099
     *
2100
     * @return string The link
2101
     */
2102
    public static function getProfileEditionLink($userId, $asAdmin = false)
2103
    {
2104
        $editProfileUrl = api_get_path(WEB_CODE_PATH).'auth/profile.php';
2105
        if ($asAdmin) {
2106
            $editProfileUrl = api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=".intval($userId);
2107
        }
2108
2109
        return $editProfileUrl;
2110
    }
2111
2112
    /**
2113
     * Get the vCard for a user.
2114
     *
2115
     * @param int $userId The user id
2116
     *
2117
     * @return string *.*vcf file
2118
     */
2119
    public static function getVCardUserLink($userId)
2120
    {
2121
        return api_get_path(WEB_PATH).'main/social/vcard_export.php?userId='.intval($userId);
2122
    }
2123
2124
    /**
2125
     * @param string $content
2126
     * @param string $title
2127
     * @param string $footer
2128
     * @param string $type        primary|success|info|warning|danger
2129
     * @param string $extra
2130
     * @param string $id
2131
     * @param string $customColor
2132
     * @param string $rightAction
2133
     *
2134
     * @return string
2135
     */
2136
    public static function panel(
2137
        $content,
2138
        $title = '',
2139
        $footer = '',
2140
        $type = 'default',
2141
        $extra = '',
2142
        $id = '',
2143
        $customColor = '',
2144
        $rightAction = ''
2145
    ) {
2146
        $headerStyle = '';
2147
        if (!empty($customColor)) {
2148
            $headerStyle = 'style = "color: white; background-color: '.$customColor.'" ';
2149
        }
2150
2151
        $footer = !empty($footer) ? '<p class="card-text"><small class="text-muted">'.$footer.'</small></p>' : '';
2152
        $typeList = ['primary', 'success', 'info', 'warning', 'danger'];
2153
        $style = !in_array($type, $typeList) ? 'default' : $type;
2154
2155
        if (!empty($id)) {
2156
            $id = " id='$id'";
2157
        }
2158
        $cardBody = $title.' '.self::contentPanel($content).' '.$footer;
2159
2160
        return "
2161
            <div $id class=card>
2162
                <div class='flex justify-between items-center py-2'>
2163
                    <div class='relative mt-1 flex'>
2164
                        $title
2165
                    </div>
2166
                    <div>
2167
                        $rightAction
2168
                    </div>
2169
                </div>
2170
                
2171
                $content
2172
                $footer
2173
            </div>"
2174
        ;
2175
    }
2176
2177
    /**
2178
     * @param string $content
2179
     */
2180
    public static function contentPanel($content): string
2181
    {
2182
        if (empty($content)) {
2183
            return '';
2184
        }
2185
2186
        return '<div class="card-text">'.$content.'</div>';
2187
    }
2188
2189
    /**
2190
     * Get the button HTML with an Awesome Font icon.
2191
     *
2192
     * @param string $text        The button content
2193
     * @param string $url         The url to button
2194
     * @param string $icon        The Awesome Font class for icon
2195
     * @param string $type        Optional. The button Bootstrap class. Default 'default' class
2196
     * @param array  $attributes  The additional attributes
2197
     * @param bool   $includeText
2198
     *
2199
     * @return string The button HTML
2200
     */
2201
    public static function toolbarButton(
2202
        $text,
2203
        $url,
2204
        $icon = 'check',
2205
        $type = null,
2206
        array $attributes = [],
2207
        $includeText = true
2208
    ) {
2209
        $buttonClass = "btn btn-outline-secondary";
2210
        if (!empty($type)) {
2211
            $buttonClass = "btn btn-$type";
2212
        }
2213
        //$icon = self::tag('i', null, ['class' => "fa fa-$icon fa-fw", 'aria-hidden' => 'true']);
2214
        $icon = self::getMdiIcon($icon);
2215
        $attributes['class'] = isset($attributes['class']) ? "$buttonClass {$attributes['class']}" : $buttonClass;
2216
        $attributes['title'] = $attributes['title'] ?? $text;
2217
2218
        if (!$includeText) {
2219
            $text = '<span class="sr-only">'.$text.'</span>';
2220
        }
2221
2222
        return self::url("$icon $text", $url, $attributes);
2223
    }
2224
2225
    public static function toolbarAction(string $id, array $contentList): string
2226
    {
2227
        $contentList = array_filter($contentList);
2228
2229
        if (empty($contentList)) {
2230
            return '';
2231
        }
2232
2233
        $col = count($contentList);
2234
        $html = ' <div id="'.$id.'" class="q-card p-2 mb-4">';
2235
        $html .= ' <div class="flex justify-between '.$col.'">';
2236
        foreach ($contentList as $item) {
2237
            $html .= '<div class="flex p-2 gap-2 ">'.$item.'</div>';
2238
        }
2239
        $html .= '</div>';
2240
        $html .= '</div>';
2241
2242
        return $html;
2243
    }
2244
2245
    public static function getMdiIcon(string $name): string
2246
    {
2247
        return '<i class="mdi-'.$name.' mdi v-icon notranslate v-icon--size-default" aria-hidden="true" medium=""></i>';
2248
    }
2249
2250
    /**
2251
     * Get a HTML code for a icon by Font Awesome.
2252
     *
2253
     * @param string     $name            The icon name. Example: "mail-reply"
2254
     * @param int|string $size            Optional. The size for the icon. (Example: lg, 2, 3, 4, 5)
2255
     * @param bool       $fixWidth        Optional. Whether add the fw class
2256
     * @param string     $additionalClass Optional. Additional class
2257
     *
2258
     * @return string
2259
     */
2260
    public static function returnFontAwesomeIcon(
2261
        $name,
2262
        $size = '',
2263
        $fixWidth = false,
2264
        $additionalClass = ''
2265
    ) {
2266
        $className = "mdi mdi-$name";
2267
2268
        if ($fixWidth) {
2269
            $className .= ' fa-fw';
2270
        }
2271
2272
        switch ($size) {
2273
            case 'xs':
2274
            case 'sm':
2275
            case 'lg':
2276
                $className .= " fa-{$size}";
2277
                break;
2278
            case 2:
2279
            case 3:
2280
            case 4:
2281
            case 5:
2282
                $className .= " fa-{$size}x";
2283
                break;
2284
        }
2285
2286
        if (!empty($additionalClass)) {
2287
            $className .= " $additionalClass";
2288
        }
2289
2290
        $icon = self::tag('em', null, ['class' => $className]);
2291
2292
        return "$icon ";
2293
    }
2294
2295
    /**
2296
     * @param string     $title
2297
     * @param string     $content
2298
     * @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...
2299
     * @param array      $params
2300
     * @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...
2301
     * @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...
2302
     * @param bool|true  $open
2303
     * @param bool|false $fullClickable
2304
     *
2305
     * @return string
2306
     *
2307
     * @todo rework function to easy use
2308
     */
2309
    public static function panelCollapse(
2310
        $title,
2311
        $content,
2312
        $id = null,
2313
        $params = [],
2314
        $idAccordion = null,
2315
        $idCollapse = null,
2316
        $open = true,
2317
        $fullClickable = false
2318
    ) {
2319
        if (!empty($idAccordion)) {
2320
            $headerClass = $fullClickable ? 'center-block ' : '';
2321
            $headerClass .= $open ? '' : 'collapsed';
2322
            $contentClass = 'panel-collapse collapse ';
2323
            $contentClass .= $open ? 'in' : '';
2324
            $ariaExpanded = $open ? 'true' : 'false';
2325
2326
            $html = <<<HTML
2327
                <div class="card" id="$id">
2328
                    <div class="card-header">
2329
                        $title
2330
                    </div>
2331
                    <div class="card-body">$content</div>
2332
                </div>
2333
HTML;
2334
        } else {
2335
            if (!empty($id)) {
2336
                $params['id'] = $id;
2337
            }
2338
            $params['class'] = 'card';
2339
            $html = '';
2340
            if (!empty($title)) {
2341
                $html .= '<div class="card-header">'.$title.'</div>'.PHP_EOL;
2342
            }
2343
            $html .= '<div class="card-body">'.$content.'</div>'.PHP_EOL;
2344
            $html = self::div($html, $params);
2345
        }
2346
2347
        return $html;
2348
    }
2349
2350
    /**
2351
     * Returns the string "1 day ago" with a link showing the exact date time.
2352
     *
2353
     * @param string $dateTime in UTC or a DateTime in UTC
2354
     *
2355
     * @return string
2356
     */
2357
    public static function dateToStringAgoAndLongDate($dateTime)
2358
    {
2359
        if (empty($dateTime) || '0000-00-00 00:00:00' === $dateTime) {
2360
            return '';
2361
        }
2362
2363
        if ($dateTime instanceof \DateTime) {
0 ignored issues
show
introduced by
$dateTime is never a sub-type of DateTime.
Loading history...
2364
            $dateTime = $dateTime->format('Y-m-d H:i:s');
2365
        }
2366
2367
        return self::tip(
2368
            date_to_str_ago($dateTime),
2369
            api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG)
2370
            //api_get_local_time($dateTime)
2371
        );
2372
    }
2373
2374
    /**
2375
     * @param array  $userInfo
2376
     * @param string $status
2377
     * @param string $toolbar
2378
     *
2379
     * @return string
2380
     */
2381
    public static function getUserCard($userInfo, $status = '', $toolbar = '')
2382
    {
2383
        if (empty($userInfo)) {
2384
            return '';
2385
        }
2386
2387
        if (!empty($status)) {
2388
            $status = '<div class="items-user-status">'.$status.'</div>';
2389
        }
2390
2391
        if (!empty($toolbar)) {
2392
            $toolbar = '<div class="btn-group pull-right">'.$toolbar.'</div>';
2393
        }
2394
2395
        return '<div id="user_card_'.$userInfo['id'].'" class="card d-flex flex-row">
2396
                    <img src="'.$userInfo['avatar'].'" class="rounded" />
2397
                    <h3 class="card-title">'.$userInfo['complete_name'].'</h3>
2398
                    <div class="card-body">
2399
                       <div class="card-title">
2400
                       '.$status.'
2401
                       '.$toolbar.'
2402
                       </div>
2403
                    </div>
2404
                    <hr />
2405
              </div>';
2406
    }
2407
2408
    /**
2409
     * @param string $fileName
2410
     * @param string $fileUrl
2411
     *
2412
     * @return string
2413
     */
2414
    public static function fileHtmlGuesser($fileName, $fileUrl)
2415
    {
2416
        $data = pathinfo($fileName);
2417
2418
        //$content = self::url($data['basename'], $fileUrl);
2419
        $content = '';
2420
        switch ($data['extension']) {
2421
            case 'webm':
2422
            case 'mp4':
2423
            case 'ogg':
2424
                $content = '<video style="width: 400px; height:100%;" src="'.$fileUrl.'"></video>';
2425
                // Allows video to play when loading during an ajax call
2426
                $content .= "<script>jQuery('video:not(.skip), audio:not(.skip)').mediaelementplayer();</script>";
2427
                break;
2428
            case 'jpg':
2429
            case 'jpeg':
2430
            case 'gif':
2431
            case 'png':
2432
                $content = '<img class="img-responsive" src="'.$fileUrl.'" />';
2433
                break;
2434
            default:
2435
                //$html = self::url($data['basename'], $fileUrl);
2436
                break;
2437
        }
2438
        //$html = self::url($content, $fileUrl, ['ajax']);
2439
2440
        return $content;
2441
    }
2442
2443
    /**
2444
     * @param string $image
2445
     * @param int    $size
2446
     *
2447
     * @return string
2448
     */
2449
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
2450
    {
2451
        return self::return_icon($image, '', [], $size, false, true);
2452
    }
2453
2454
    /**
2455
     * @param string $image
2456
     * @param int    $size
2457
     * @param string $name
2458
     *
2459
     * @return string
2460
     */
2461
    public static function get_image($image, $size = ICON_SIZE_SMALL, $name = '')
2462
    {
2463
        return self::return_icon($image, $name, [], $size);
2464
    }
2465
2466
    /**
2467
     * @param $id
2468
     *
2469
     * @return array|mixed
2470
     */
2471
    public static function randomColor($id)
2472
    {
2473
        static $colors = [];
2474
2475
        if (!empty($colors[$id])) {
2476
            return $colors[$id];
2477
        } else {
2478
            $color = substr(md5(time() * $id), 0, 6);
2479
            $c1 = hexdec(substr($color, 0, 2));
2480
            $c2 = hexdec(substr($color, 2, 2));
2481
            $c3 = hexdec(substr($color, 4, 2));
2482
            $luminosity = $c1 + $c2 + $c3;
2483
2484
            $type = '#000000';
2485
            if ($luminosity < (255 + 255 + 255) / 2) {
2486
                $type = '#FFFFFF';
2487
            }
2488
2489
            $result = [
2490
                'color' => '#'.$color,
2491
                'luminosity' => $type,
2492
            ];
2493
            $colors[$id] = $result;
2494
2495
            return $result; // example: #fc443a
2496
        }
2497
    }
2498
2499
    public static function noDataView(string $title, string $icon, string $buttonTitle, string $url): string
2500
    {
2501
        $content = '<div id="no-data-view">';
2502
        $content .= '<h3>'.$title.'</h3>';
2503
        $content .= $icon;
2504
        $content .= '<div class="controls">';
2505
        $content .= self::url(
2506
            '<em class="fa fa-plus"></em> '.$buttonTitle,
2507
            $url,
2508
            ['class' => 'btn btn-primary']
2509
        );
2510
        $content .= '</div>';
2511
        $content .= '</div>';
2512
2513
        return $content;
2514
    }
2515
2516
    public static function prose(string $contents): string
2517
    {
2518
        return "
2519
            <div class=''>
2520
                <div class='prose prose-blue'>
2521
                $contents
2522
                </div>
2523
            </div>
2524
            ";
2525
    }
2526
}
2527