Passed
Push — master ( 08a9bc...ffbf23 )
by Yannick
08:49 queued 10s
created

Display::label()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 13
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 19
rs 9.8333
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
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...
6
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
7
use Chamilo\CoreBundle\Framework\Container;
8
use ChamiloSession as Session;
9
use Symfony\Component\HttpFoundation\Response;
10
11
/**
12
 * Class Display
13
 * Contains several public functions dealing with the display of
14
 * table data, messages, help topics, ...
15
 *
16
 * Include/require it in your code to use its public functionality.
17
 * There are also several display public functions in the main api library.
18
 *
19
 * All public functions static public functions inside a class called Display,
20
 * so you use them like this: e.g.
21
 * Display::return_message($message)
22
 */
23
class Display
24
{
25
    /** @var Template */
26
    public static $global_template;
27
    public static $preview_style = null;
28
    public static $legacyTemplate;
29
30
    public function __construct()
31
    {
32
    }
33
34
    /**
35
     * @return array
36
     */
37
    public static function toolList()
38
    {
39
        return [
40
            'group',
41
            'work',
42
            'glossary',
43
            'forum',
44
            'course_description',
45
            'gradebook',
46
            'attendance',
47
            'course_progress',
48
            'notebook',
49
        ];
50
    }
51
52
    /**
53
     * Displays the page header.
54
     *
55
     * @param string The name of the page (will be showed in the page title)
56
     * @param string Optional help file name
57
     * @param string $page_header
58
     */
59
    public static function display_header(
60
        $tool_name = '',
61
        $help = null,
62
        $page_header = null
63
    ) {
64
        global $interbreadcrumb;
65
        $interbreadcrumb[] = ['url' => '#', 'name' => $tool_name];
66
67
        ob_start();
68
69
        return true;
70
    }
71
72
    /**
73
     * Displays the reduced page header (without banner).
74
     */
75
    public static function display_reduced_header()
76
    {
77
        ob_start();
78
        self::$legacyTemplate = '@ChamiloCore/Layout/no_layout.html.twig';
79
80
        return true;
81
    }
82
83
    /**
84
     * Display no header.
85
     */
86
    public static function display_no_header()
87
    {
88
        global $tool_name, $show_learnpath;
89
        self::$global_template = new Template(
90
            $tool_name,
91
            false,
92
            false,
93
            $show_learnpath
94
        );
95
    }
96
97
    /**
98
     * Display the page footer.
99
     */
100
    public static function display_footer()
101
    {
102
        $contents = ob_get_contents();
103
        if (ob_get_length()) {
104
            ob_end_clean();
105
        }
106
        $tpl = '@ChamiloCore/Layout/layout_one_col.html.twig';
107
        if (!empty(self::$legacyTemplate)) {
108
            $tpl = self::$legacyTemplate;
109
        }
110
        $response = new Response();
111
        $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...
112
        global $interbreadcrumb, $htmlHeadXtra;
113
114
        $courseInfo = api_get_course_info();
115
        if (!empty($courseInfo)) {
116
            $url = $courseInfo['course_public_url'];
117
            $sessionId = api_get_session_id();
118
            if (!empty($sessionId)) {
119
                $url .= '?sid='.$sessionId;
120
            }
121
122
            if (!empty($interbreadcrumb)) {
123
                array_unshift(
124
                    $interbreadcrumb,
125
                    ['name' => $courseInfo['title'], 'url' => $url]
126
                );
127
            }
128
        }
129
130
        if (empty($interbreadcrumb)) {
131
            $interbreadcrumb = [];
132
        }
133
134
        $params['legacy_javascript'] = $htmlHeadXtra;
135
        $params['legacy_breadcrumb'] = json_encode($interbreadcrumb);
136
137
        Template::setVueParams($params);
138
        $content = Container::getTwig()->render($tpl, $params);
139
        $response->setContent($content);
140
        $response->send();
141
        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...
142
    }
143
144
    /**
145
     * Display the page footer.
146
     */
147
    public static function display_reduced_footer()
148
    {
149
        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...
150
    }
151
152
    /**
153
     * Displays the tool introduction of a tool.
154
     *
155
     * @author Patrick Cool <[email protected]>, Ghent University
156
     *
157
     * @param string $tool          these are the constants that are used for indicating the tools
158
     * @param array  $editor_config Optional configuration settings for the online editor.
159
     *                              return: $tool return a string array list with the "define" in main_api.lib
160
     *
161
     * @return string html code for adding an introduction
162
     */
163
    public static function display_introduction_section(
164
        $tool,
165
        $editor_config = null
166
    ) {
167
        // @todo replace introduction section with a vue page.
168
        return;
169
        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...
170
    }
171
172
    /**
173
     * @param string $tool
174
     * @param array  $editor_config
175
     */
176
    public static function return_introduction_section(
177
        $tool,
178
        $editor_config = null
179
    ) {
180
        $moduleId = $tool;
181
        if ('true' === api_get_setting('enable_tool_introduction') || TOOL_COURSE_HOMEPAGE == $tool) {
182
            $introduction_section = null;
183
            require api_get_path(SYS_CODE_PATH).'inc/introductionSection.inc.php';
184
185
            return $introduction_section;
186
        }
187
    }
188
189
    /**
190
     * Displays a table.
191
     *
192
     * @param array  $header          Titles for the table header
193
     *                                each item in this array can contain 3 values
194
     *                                - 1st element: the column title
195
     *                                - 2nd element: true or false (column sortable?)
196
     *                                - 3th element: additional attributes for
197
     *                                th-tag (eg for column-width)
198
     *                                - 4the element: additional attributes for the td-tags
199
     * @param array  $content         2D-array with the tables content
200
     * @param array  $sorting_options Keys are:
201
     *                                'column' = The column to use as sort-key
202
     *                                'direction' = SORT_ASC or SORT_DESC
203
     * @param array  $paging_options  Keys are:
204
     *                                'per_page_default' = items per page when switching from
205
     *                                full-    list to per-page-view
206
     *                                'per_page' = number of items to show per page
207
     *                                'page_nr' = The page to display
208
     * @param array  $query_vars      Additional variables to add in the query-string
209
     * @param array  $form_actions
210
     * @param string $style           The style that the table will show. You can set 'table' or 'grid'
211
     * @param string $tableName
212
     * @param string $tableId
213
     *
214
     * @author [email protected]
215
     */
216
    public static function display_sortable_table(
217
        $header,
218
        $content,
219
        $sorting_options = [],
220
        $paging_options = [],
221
        $query_vars = null,
222
        $form_actions = [],
223
        $style = 'table',
224
        $tableName = 'tablename',
225
        $tableId = ''
226
    ) {
227
        $column = $sorting_options['column'] ?? 0;
228
        $default_items_per_page = $paging_options['per_page'] ?? 20;
229
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $tableName, null, $tableId);
230
        if (is_array($query_vars)) {
231
            $table->set_additional_parameters($query_vars);
232
        }
233
        if ('table' === $style) {
234
            if (is_array($header) && count($header) > 0) {
235
                foreach ($header as $index => $header_item) {
236
                    $table->set_header(
237
                        $index,
238
                        isset($header_item[0]) ? $header_item[0] : null,
239
                        isset($header_item[1]) ? $header_item[1] : null,
240
                        isset($header_item[2]) ? $header_item[2] : null,
241
                        isset($header_item[3]) ? $header_item[3] : null
242
                    );
243
                }
244
            }
245
            $table->set_form_actions($form_actions);
246
            $table->display();
247
        } else {
248
            $table->display_grid();
249
        }
250
    }
251
252
    /**
253
     * Returns an HTML table with sortable column (through complete page refresh).
254
     *
255
     * @param array  $header
256
     * @param array  $content         Array of row arrays
257
     * @param array  $sorting_options
258
     * @param array  $paging_options
259
     * @param array  $query_vars
260
     * @param array  $form_actions
261
     * @param string $style
262
     *
263
     * @return string HTML string for array
264
     */
265
    public static function return_sortable_table(
266
        $header,
267
        $content,
268
        $sorting_options = [],
269
        $paging_options = [],
270
        $query_vars = null,
271
        $form_actions = [],
272
        $style = 'table'
273
    ) {
274
        ob_start();
275
        self::display_sortable_table(
276
            $header,
277
            $content,
278
            $sorting_options,
279
            $paging_options,
280
            $query_vars,
281
            $form_actions,
282
            $style
283
        );
284
        $content = ob_get_contents();
285
        ob_end_clean();
286
287
        return $content;
288
    }
289
290
    /**
291
     * Shows a nice grid.
292
     *
293
     * @param string grid name (important to create css)
294
     * @param array header content
295
     * @param array array with the information to show
296
     * @param array $paging_options Keys are:
297
     *                              'per_page_default' = items per page when switching from
298
     *                              full-    list to per-page-view
299
     *                              'per_page' = number of items to show per page
300
     *                              'page_nr' = The page to display
301
     *                              'hide_navigation' =  true to hide the navigation
302
     * @param array $query_vars     Additional variables to add in the query-string
303
     * @param mixed An array with bool values to know which columns show.
304
     * i.e: $visibility_options= array(true, false) we will only show the first column
305
     *                Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
306
     */
307
    public static function display_sortable_grid(
308
        $name,
309
        $header,
310
        $content,
311
        $paging_options = [],
312
        $query_vars = null,
313
        $form_actions = [],
314
        $visibility_options = true,
315
        $sort_data = true,
316
        $grid_class = []
317
    ) {
318
        echo self::return_sortable_grid(
319
            $name,
320
            $header,
321
            $content,
322
            $paging_options,
323
            $query_vars,
324
            $form_actions,
325
            $visibility_options,
326
            $sort_data,
327
            $grid_class
328
        );
329
    }
330
331
    /**
332
     * Gets a nice grid in html string.
333
     *
334
     * @param string grid name (important to create css)
335
     * @param array header content
336
     * @param array array with the information to show
337
     * @param array $paging_options Keys are:
338
     *                              'per_page_default' = items per page when switching from
339
     *                              full-    list to per-page-view
340
     *                              'per_page' = number of items to show per page
341
     *                              'page_nr' = The page to display
342
     *                              'hide_navigation' =  true to hide the navigation
343
     * @param array $query_vars     Additional variables to add in the query-string
344
     * @param mixed An array with bool values to know which columns show. i.e:
345
     *  $visibility_options= array(true, false) we will only show the first column
346
     *    Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
347
     * @param bool  true for sorting data or false otherwise
348
     * @param array grid classes
349
     *
350
     * @return string html grid
351
     */
352
    public static function return_sortable_grid(
353
        $name,
354
        $header,
355
        $content,
356
        $paging_options = [],
357
        $query_vars = null,
358
        $form_actions = [],
359
        $visibility_options = true,
360
        $sort_data = true,
361
        $grid_class = [],
362
        $elementCount = 0
363
    ) {
364
        $column = 0;
365
        $default_items_per_page = $paging_options['per_page'] ?? 20;
366
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $name);
367
        $table->total_number_of_items = (int) $elementCount;
368
        if (is_array($query_vars)) {
369
            $table->set_additional_parameters($query_vars);
370
        }
371
372
        return $table->display_simple_grid(
373
            $visibility_options,
374
            $paging_options['hide_navigation'],
375
            $default_items_per_page,
376
            $sort_data,
377
            $grid_class
378
        );
379
    }
380
381
    /**
382
     * Displays a table with a special configuration.
383
     *
384
     * @param array $header          Titles for the table header
385
     *                               each item in this array can contain 3 values
386
     *                               - 1st element: the column title
387
     *                               - 2nd element: true or false (column sortable?)
388
     *                               - 3th element: additional attributes for th-tag (eg for column-width)
389
     *                               - 4the element: additional attributes for the td-tags
390
     * @param array $content         2D-array with the tables content
391
     * @param array $sorting_options Keys are:
392
     *                               'column' = The column to use as sort-key
393
     *                               'direction' = SORT_ASC or SORT_DESC
394
     * @param array $paging_options  Keys are:
395
     *                               'per_page_default' = items per page when switching from full list to per-page-view
396
     *                               'per_page' = number of items to show per page
397
     *                               'page_nr' = The page to display
398
     * @param array $query_vars      Additional variables to add in the query-string
399
     * @param array $column_show     Array of binaries 1= show columns 0. hide a column
400
     * @param array $column_order    An array of integers that let us decide how the columns are going to be sort.
401
     *                               i.e:  $column_order=array('1''4','3','4'); The 2nd column will be order like the 4th column
402
     * @param array $form_actions    Set optional forms actions
403
     *
404
     * @author Julio Montoya
405
     */
406
    public static function display_sortable_config_table(
407
        $table_name,
408
        $header,
409
        $content,
410
        $sorting_options = [],
411
        $paging_options = [],
412
        $query_vars = null,
413
        $column_show = [],
414
        $column_order = [],
415
        $form_actions = []
416
    ) {
417
        $column = isset($sorting_options['column']) ? $sorting_options['column'] : 0;
418
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
419
420
        $table = new SortableTableFromArrayConfig(
421
            $content,
422
            $column,
423
            $default_items_per_page,
424
            $table_name,
425
            $column_show,
426
            $column_order
427
        );
428
429
        if (is_array($query_vars)) {
430
            $table->set_additional_parameters($query_vars);
431
        }
432
        // Show or hide the columns header
433
        if (is_array($column_show)) {
434
            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...
435
                if (!empty($column_show[$i])) {
436
                    $val0 = isset($header[$i][0]) ? $header[$i][0] : null;
437
                    $val1 = isset($header[$i][1]) ? $header[$i][1] : null;
438
                    $val2 = isset($header[$i][2]) ? $header[$i][2] : null;
439
                    $val3 = isset($header[$i][3]) ? $header[$i][3] : null;
440
                    $table->set_header($i, $val0, $val1, $val2, $val3);
441
                }
442
            }
443
        }
444
        $table->set_form_actions($form_actions);
445
        $table->display();
446
    }
447
448
    /**
449
     * Returns a div html string with.
450
     *
451
     * @param string $message
452
     * @param string $type    Example: confirm, normal, warning, error
453
     * @param bool   $filter  Whether to XSS-filter or not
454
     *
455
     * @return string Message wrapped into an HTML div
456
     */
457
    public static function return_message($message, $type = 'normal', $filter = true)
458
    {
459
        if (empty($message)) {
460
            return '';
461
        }
462
463
        if ($filter) {
464
            $message = api_htmlentities(
465
                $message,
466
                ENT_QUOTES,
467
                api_is_xml_http_request() ? 'UTF-8' : api_get_system_encoding()
468
            );
469
        }
470
471
        $class = '';
472
        switch ($type) {
473
            case 'warning':
474
                $class .= 'alert alert-warning';
475
                break;
476
            case 'error':
477
                $class .= 'alert alert-danger';
478
                break;
479
            case 'confirmation':
480
            case 'confirm':
481
            case 'success':
482
                $class .= 'alert alert-success';
483
                break;
484
            case 'normal':
485
            case 'info':
486
            default:
487
                $class .= 'alert alert-info';
488
        }
489
490
        return self::div($message, ['class' => $class]);
491
    }
492
493
    /**
494
     * Returns an encrypted mailto hyperlink.
495
     *
496
     * @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...
497
     * @param string  clickable text
498
     * @param string  optional, class from stylesheet
499
     * @param bool $addExtraContent
500
     *
501
     * @return string encrypted mailto hyperlink
502
     */
503
    public static function encrypted_mailto_link(
504
        $email,
505
        $clickable_text = null,
506
        $style_class = '',
507
        $addExtraContent = false
508
    ) {
509
        if (is_null($clickable_text)) {
510
            $clickable_text = $email;
511
        }
512
513
        // "mailto:" already present?
514
        if ('mailto:' !== substr($email, 0, 7)) {
515
            $email = 'mailto:'.$email;
516
        }
517
518
        // Class (stylesheet) defined?
519
        if ('' !== $style_class) {
520
            $style_class = ' class="'.$style_class.'"';
521
        }
522
523
        // Encrypt email
524
        $hmail = '';
525
        for ($i = 0; $i < strlen($email); $i++) {
526
            $hmail .= '&#'.ord($email[$i]).';';
527
        }
528
529
        $value = api_get_configuration_value('add_user_course_information_in_mailto');
530
531
        if ($value) {
532
            if ('false' === api_get_setting('allow_email_editor')) {
533
                $hmail .= '?';
534
            }
535
536
            if (!api_is_anonymous()) {
537
                $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

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

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

1560
        return '<'.$size.' class="page-header">'./** @scrutinizer ignore-type */ $title.'</'.$size.'>';
Loading history...
1561
    }
1562
1563
    public static function page_header_and_translate($title, $second_title = null)
1564
    {
1565
        $title = get_lang($title);
1566
1567
        return self::page_header($title, $second_title);
1568
    }
1569
1570
    public static function page_subheader($title, $second_title = null, $size = 'h3', $attributes = [])
1571
    {
1572
        if (!empty($second_title)) {
1573
            $second_title = Security::remove_XSS($second_title);
1574
            $title .= "<small> $second_title<small>";
1575
        }
1576
        $subTitle = self::tag($size, Security::remove_XSS($title), $attributes);
1577
1578
        return $subTitle;
1579
    }
1580
1581
    public static function page_subheader2($title, $second_title = null)
1582
    {
1583
        return self::page_header($title, $second_title, 'h4');
1584
    }
1585
1586
    public static function page_subheader3($title, $second_title = null)
1587
    {
1588
        return self::page_header($title, $second_title, 'h5');
1589
    }
1590
1591
    public static function description(array $list): string
1592
    {
1593
        $html = '';
1594
        if (!empty($list)) {
1595
            $html = '<dl class="dl-horizontal">';
1596
            foreach ($list as $item) {
1597
                $html .= '<dt>'.$item['title'].'</dt>';
1598
                $html .= '<dd>'.$item['content'].'</dd>';
1599
            }
1600
            $html .= '</dl>';
1601
        }
1602
1603
        return $html;
1604
    }
1605
1606
    /**
1607
     * @param int    $percentage      int value between 0 and 100
1608
     * @param bool   $show_percentage
1609
     * @param string $extra_info
1610
     * @param string $class           danger/success/infowarning
1611
     *
1612
     * @return string
1613
     */
1614
    public static function bar_progress($percentage, $show_percentage = true, $extra_info = '', $class = '')
1615
    {
1616
        $percentage = (int) $percentage;
1617
        $class = empty($class) ? '' : "progress-bar-$class";
1618
1619
        $div = '<div class="progress">
1620
                <div
1621
                    class="progress-bar progress-bar-striped '.$class.'"
1622
                    role="progressbar"
1623
                    aria-valuenow="'.$percentage.'"
1624
                    aria-valuemin="0"
1625
                    aria-valuemax="100"
1626
                    style="width: '.$percentage.'%;"
1627
                >';
1628
        if ($show_percentage) {
1629
            $div .= $percentage.'%';
1630
        } else {
1631
            if (!empty($extra_info)) {
1632
                $div .= $extra_info;
1633
            }
1634
        }
1635
        $div .= '</div></div>';
1636
1637
        return $div;
1638
    }
1639
1640
    /**
1641
     * @param string $count
1642
     * @param string $type
1643
     *
1644
     * @return string|null
1645
     */
1646
    public static function badge($count, $type = 'warning')
1647
    {
1648
        $class = '';
1649
1650
        switch ($type) {
1651
            case 'success':
1652
                $class = 'bg-success';
1653
                break;
1654
            case 'warning':
1655
                $class = 'bg-warning text-dark';
1656
                break;
1657
            case 'important':
1658
                $class = 'bg-important';
1659
                break;
1660
            case 'info':
1661
                $class = 'bg-info';
1662
                break;
1663
            case 'inverse':
1664
                $class = 'bg-inverse';
1665
                break;
1666
        }
1667
1668
        if (!empty($count)) {
1669
            return ' <span class="badge '.$class.'">'.$count.'</span>';
1670
        }
1671
1672
        return null;
1673
    }
1674
1675
    /**
1676
     * @param array $badge_list
1677
     *
1678
     * @return string
1679
     */
1680
    public static function badgeGroup($list)
1681
    {
1682
        $html = '<div class="badge-group">';
1683
        foreach ($list as $badge) {
1684
            $html .= $badge;
1685
        }
1686
        $html .= '</div>';
1687
1688
        return $html;
1689
    }
1690
1691
    /**
1692
     * Return an HTML span element with the badge class and an additional bg-$type class
1693
     */
1694
    public static function label(string $content, string $type = 'default'): string
1695
    {
1696
        $html = '';
1697
        if (!empty($content)) {
1698
            $class = match ($type) {
1699
                'success' => 'success',
1700
                'warning' => 'warning',
1701
                'important', 'danger' => 'danger',
1702
                'info' => 'info',
1703
                'primary' => 'primary',
1704
                default => 'secondary',
1705
            };
1706
1707
            $html = '<span class="badge bg-ch-'.$class.'">';
1708
            $html .= $content;
1709
            $html .= '</span>';
1710
        }
1711
1712
        return $html;
1713
    }
1714
1715
    public static function actions(array $items): string
1716
    {
1717
        if (empty($items)) {
1718
            return '';
1719
        }
1720
1721
        $links = '';
1722
        foreach ($items as $value) {
1723
            $attributes = $value['url_attributes'] ?? [];
1724
            $links .= self::url($value['content'], $value['url'], $attributes);
1725
        }
1726
1727
        return self::toolbarAction(uniqid('toolbar', false), [$links]);
1728
    }
1729
1730
    /**
1731
     * Prints a tooltip.
1732
     *
1733
     * @param string $text
1734
     * @param string $tip
1735
     *
1736
     * @return string
1737
     */
1738
    public static function tip($text, $tip)
1739
    {
1740
        if (empty($tip)) {
1741
            return $text;
1742
        }
1743
1744
        return self::span(
1745
            $text,
1746
            ['class' => 'boot-tooltip', 'title' => strip_tags($tip)]
1747
        );
1748
    }
1749
1750
    /**
1751
     * @param array $buttons
1752
     *
1753
     * @return string
1754
     */
1755
    public static function groupButton($buttons)
1756
    {
1757
        $html = '<div class="btn-group" role="group">';
1758
        foreach ($buttons as $button) {
1759
            $html .= $button;
1760
        }
1761
        $html .= '</div>';
1762
1763
        return $html;
1764
    }
1765
1766
    /**
1767
     * @todo use twig
1768
     *
1769
     * @param string $title
1770
     * @param array  $elements
1771
     * @param bool   $alignToRight
1772
     *
1773
     * @return string
1774
     */
1775
    public static function groupButtonWithDropDown($title, $elements, $alignToRight = false)
1776
    {
1777
        $id = uniqid('dropdown', false);
1778
        $html = '
1779
        <div class="dropdown inline-block relative">
1780
            <button
1781
                id="'.$id.'"
1782
                type="button"
1783
                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"
1784
                aria-expanded="false"
1785
                aria-haspopup="true"
1786
            >
1787
              '.$title.'
1788
              <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">
1789
                <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" />
1790
              </svg>
1791
            </button>
1792
            <div
1793
                id="'.$id.'_menu"
1794
                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"
1795
                role="menu"
1796
                aria-orientation="vertical"
1797
                aria-labelledby="menu-button"
1798
                tabindex="-1"
1799
            >
1800
            <div class="py-1" role="none">';
1801
        foreach ($elements as $item) {
1802
            $html .= self::url(
1803
                    $item['title'],
1804
                    $item['href'],
1805
                    [
1806
                        'class' => 'text-gray-700 block px-4 py-2 text-sm',
1807
                        'role' => 'menuitem',
1808
                        'onclick' => $item['onclick'] ?? '',
1809
                        'data-action' => $item['data-action'] ?? '',
1810
                    ]
1811
                );
1812
        }
1813
        $html .= '
1814
            </div>
1815
            </div>
1816
            </div>
1817
            <script>
1818
             document.addEventListener("DOMContentLoaded", function() {
1819
                const button = document.querySelector("#'.$id.'");
1820
                    button.addEventListener("click", (e) => {
1821
                    let menu = document.querySelector("#'.$id.'_menu");
1822
                    if (menu.classList.contains("hidden")) {
1823
                        menu.classList.remove("hidden");
1824
                    } else {
1825
                        menu.classList.add("hidden");
1826
                    }
1827
                });
1828
            });
1829
            </script>';
1830
1831
        return $html;
1832
    }
1833
1834
    /**
1835
     * @param string $file
1836
     * @param array  $params
1837
     *
1838
     * @return string|null
1839
     */
1840
    public static function getMediaPlayer($file, $params = [])
1841
    {
1842
        $fileInfo = pathinfo($file);
1843
1844
        $autoplay = isset($params['autoplay']) && 'true' === $params['autoplay'] ? 'autoplay' : '';
1845
        $id = isset($params['id']) ? $params['id'] : $fileInfo['basename'];
1846
        $width = isset($params['width']) ? 'width="'.$params['width'].'"' : null;
1847
        $class = isset($params['class']) ? ' class="'.$params['class'].'"' : null;
1848
1849
        switch ($fileInfo['extension']) {
1850
            case 'mp3':
1851
            case 'webm':
1852
                $html = '<audio id="'.$id.'" '.$class.' controls '.$autoplay.' '.$width.' src="'.$params['url'].'" >';
1853
                $html .= '<object width="'.$width.'" height="50" type="application/x-shockwave-flash" data="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf">
1854
                            <param name="movie" value="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf" />
1855
                            <param name="flashvars" value="controls=true&file='.$params['url'].'" />
1856
                          </object>';
1857
                $html .= '</audio>';
1858
1859
                return $html;
1860
                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...
1861
            case 'wav':
1862
            case 'ogg':
1863
                $html = '<audio width="300px" controls id="'.$id.'" '.$autoplay.' src="'.$params['url'].'" >';
1864
1865
                return $html;
1866
                break;
1867
        }
1868
1869
        return null;
1870
    }
1871
1872
    /**
1873
     * @param int    $nextValue
1874
     * @param array  $list
1875
     * @param int    $current
1876
     * @param int    $fixedValue
1877
     * @param array  $conditions
1878
     * @param string $link
1879
     * @param bool   $isMedia
1880
     * @param bool   $addHeaders
1881
     * @param array  $linkAttributes
1882
     *
1883
     * @return string
1884
     */
1885
    public static function progressPaginationBar(
1886
        $nextValue,
1887
        $list,
1888
        $current,
1889
        $fixedValue = null,
1890
        $conditions = [],
1891
        $link = null,
1892
        $isMedia = false,
1893
        $addHeaders = true,
1894
        $linkAttributes = []
1895
    ) {
1896
        if ($addHeaders) {
1897
            $pagination_size = 'pagination-mini';
1898
            $html = '<div class="exercise_pagination pagination '.$pagination_size.'"><ul>';
1899
        } else {
1900
            $html = null;
1901
        }
1902
        $affectAllItems = false;
1903
        if ($isMedia && isset($fixedValue) && ($nextValue + 1 == $current)) {
1904
            $affectAllItems = true;
1905
        }
1906
        $localCounter = 0;
1907
        foreach ($list as $itemId) {
1908
            $isCurrent = false;
1909
            if ($affectAllItems) {
1910
                $isCurrent = true;
1911
            } else {
1912
                if (!$isMedia) {
1913
                    $isCurrent = $current == ($localCounter + $nextValue + 1) ? true : false;
1914
                }
1915
            }
1916
            $html .= self::parsePaginationItem(
1917
                $itemId,
1918
                $isCurrent,
1919
                $conditions,
1920
                $link,
1921
                $nextValue,
1922
                $isMedia,
1923
                $localCounter,
1924
                $fixedValue,
1925
                $linkAttributes
1926
            );
1927
            $localCounter++;
1928
        }
1929
        if ($addHeaders) {
1930
            $html .= '</ul></div>';
1931
        }
1932
1933
        return $html;
1934
    }
1935
1936
    /**
1937
     * @param int    $itemId
1938
     * @param bool   $isCurrent
1939
     * @param array  $conditions
1940
     * @param string $link
1941
     * @param int    $nextValue
1942
     * @param bool   $isMedia
1943
     * @param int    $localCounter
1944
     * @param int    $fixedValue
1945
     * @param array  $linkAttributes
1946
     *
1947
     * @return string
1948
     */
1949
    public static function parsePaginationItem(
1950
        $itemId,
1951
        $isCurrent,
1952
        $conditions,
1953
        $link,
1954
        $nextValue = 0,
1955
        $isMedia = false,
1956
        $localCounter = null,
1957
        $fixedValue = null,
1958
        $linkAttributes = []
1959
    ) {
1960
        $defaultClass = 'before';
1961
        $class = $defaultClass;
1962
        foreach ($conditions as $condition) {
1963
            $array = isset($condition['items']) ? $condition['items'] : [];
1964
            $class_to_applied = $condition['class'];
1965
            $type = isset($condition['type']) ? $condition['type'] : 'positive';
1966
            $mode = isset($condition['mode']) ? $condition['mode'] : 'add';
1967
            switch ($type) {
1968
                case 'positive':
1969
                    if (in_array($itemId, $array)) {
1970
                        if ('overwrite' == $mode) {
1971
                            $class = " $defaultClass $class_to_applied";
1972
                        } else {
1973
                            $class .= " $class_to_applied";
1974
                        }
1975
                    }
1976
                    break;
1977
                case 'negative':
1978
                    if (!in_array($itemId, $array)) {
1979
                        if ('overwrite' == $mode) {
1980
                            $class = " $defaultClass $class_to_applied";
1981
                        } else {
1982
                            $class .= " $class_to_applied";
1983
                        }
1984
                    }
1985
                    break;
1986
            }
1987
        }
1988
        if ($isCurrent) {
1989
            $class = 'before current';
1990
        }
1991
        if ($isMedia && $isCurrent) {
1992
            $class = 'before current';
1993
        }
1994
        if (empty($link)) {
1995
            $link_to_show = '#';
1996
        } else {
1997
            $link_to_show = $link.($nextValue + $localCounter);
1998
        }
1999
        $label = $nextValue + $localCounter + 1;
2000
        if ($isMedia) {
2001
            $label = ($fixedValue + 1).' '.chr(97 + $localCounter);
2002
            $link_to_show = $link.$fixedValue.'#questionanchor'.$itemId;
2003
        }
2004
        $link = self::url($label.' ', $link_to_show, $linkAttributes);
2005
2006
        return '<li class = "'.$class.'">'.$link.'</li>';
2007
    }
2008
2009
    /**
2010
     * @param int $current
2011
     * @param int $total
2012
     *
2013
     * @return string
2014
     */
2015
    public static function paginationIndicator($current, $total)
2016
    {
2017
        $html = null;
2018
        if (!empty($current) && !empty($total)) {
2019
            $label = sprintf(get_lang('%s of %s'), $current, $total);
2020
            $html = self::url($label, '#', ['class' => 'btn disabled']);
2021
        }
2022
2023
        return $html;
2024
    }
2025
2026
    /**
2027
     * @param $url
2028
     * @param $currentPage
2029
     * @param $pagesCount
2030
     * @param $totalItems
2031
     *
2032
     * @return string
2033
     */
2034
    public static function getPagination($url, $currentPage, $pagesCount, $totalItems)
2035
    {
2036
        $pagination = '';
2037
        if ($totalItems > 1 && $pagesCount > 1) {
2038
            $pagination .= '<ul class="pagination">';
2039
            for ($i = 0; $i < $pagesCount; $i++) {
2040
                $newPage = $i + 1;
2041
                if ($currentPage == $newPage) {
2042
                    $pagination .= '<li class="active"><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2043
                } else {
2044
                    $pagination .= '<li><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2045
                }
2046
            }
2047
            $pagination .= '</ul>';
2048
        }
2049
2050
        return $pagination;
2051
    }
2052
2053
    /**
2054
     * Adds a legacy message in the queue.
2055
     *
2056
     * @param string $message
2057
     */
2058
    public static function addFlash($message)
2059
    {
2060
        // Detect type of message.
2061
        $parts = preg_match('/alert-([a-z]*)/', $message, $matches);
2062
        $type = 'primary';
2063
        if ($parts && isset($matches[1]) && $matches[1]) {
2064
            $type = $matches[1];
2065
        }
2066
        // Detect legacy content of message.
2067
        $result = preg_match('/<div(.*?)\>(.*?)\<\/div>/s', $message, $matches);
2068
        if ($result && isset($matches[2])) {
2069
            Container::getSession()->getFlashBag()->add($type, $matches[2]);
2070
        }
2071
    }
2072
2073
    /**
2074
     * Get the profile edition link for a user.
2075
     *
2076
     * @param int  $userId  The user id
2077
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
2078
     *
2079
     * @return string The link
2080
     */
2081
    public static function getProfileEditionLink($userId, $asAdmin = false)
2082
    {
2083
        $editProfileUrl = api_get_path(WEB_CODE_PATH).'auth/profile.php';
2084
        if ($asAdmin) {
2085
            $editProfileUrl = api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=".intval($userId);
2086
        }
2087
2088
        return $editProfileUrl;
2089
    }
2090
2091
    /**
2092
     * Get the vCard for a user.
2093
     *
2094
     * @param int $userId The user id
2095
     *
2096
     * @return string *.*vcf file
2097
     */
2098
    public static function getVCardUserLink($userId)
2099
    {
2100
        return api_get_path(WEB_PATH).'main/social/vcard_export.php?userId='.intval($userId);
2101
    }
2102
2103
    /**
2104
     * @param string $content
2105
     * @param string $title
2106
     * @param string $footer
2107
     * @param string $type        primary|success|info|warning|danger
2108
     * @param string $extra
2109
     * @param string $id
2110
     * @param string $customColor
2111
     * @param string $rightAction
2112
     *
2113
     * @return string
2114
     */
2115
    public static function panel(
2116
        $content,
2117
        $title = '',
2118
        $footer = '',
2119
        $type = 'default',
2120
        $extra = '',
2121
        $id = '',
2122
        $customColor = '',
2123
        $rightAction = ''
2124
    ) {
2125
        $headerStyle = '';
2126
        if (!empty($customColor)) {
2127
            $headerStyle = 'style = "color: white; background-color: '.$customColor.'" ';
2128
        }
2129
2130
        $footer = !empty($footer) ? '<p class="card-text"><small class="text-muted">'.$footer.'</small></p>' : '';
2131
        $typeList = ['primary', 'success', 'info', 'warning', 'danger'];
2132
        $style = !in_array($type, $typeList) ? 'default' : $type;
2133
2134
        if (!empty($id)) {
2135
            $id = " id='$id'";
2136
        }
2137
        $cardBody = $title.' '.self::contentPanel($content).' '.$footer;
2138
2139
        return "
2140
            <div $id class=card>
2141
                <div class='flex justify-between items-center py-2'>
2142
                    <div class='relative mt-1 flex'>
2143
                        $title
2144
                    </div>
2145
                    <div>
2146
                        $rightAction
2147
                    </div>
2148
                </div>
2149
2150
                $content
2151
                $footer
2152
            </div>"
2153
        ;
2154
    }
2155
2156
    /**
2157
     * @param string $content
2158
     */
2159
    public static function contentPanel($content): string
2160
    {
2161
        if (empty($content)) {
2162
            return '';
2163
        }
2164
2165
        return '<div class="card-text">'.$content.'</div>';
2166
    }
2167
2168
    /**
2169
     * Get the button HTML with an Awesome Font icon.
2170
     *
2171
     * @param string $text        The button content
2172
     * @param string $url         The url to button
2173
     * @param string $icon        The Awesome Font class for icon
2174
     * @param string $type        Optional. The button Bootstrap class. Default 'default' class
2175
     * @param array  $attributes  The additional attributes
2176
     * @param bool   $includeText
2177
     *
2178
     * @return string The button HTML
2179
     */
2180
    public static function toolbarButton(
2181
        $text,
2182
        $url,
2183
        $icon = 'check',
2184
        $type = null,
2185
        array $attributes = [],
2186
        $includeText = true
2187
    ) {
2188
        $buttonClass = "btn btn-outline-secondary";
2189
        if (!empty($type)) {
2190
            $buttonClass = "btn btn-$type";
2191
        }
2192
        //$icon = self::tag('i', null, ['class' => "fa fa-$icon fa-fw", 'aria-hidden' => 'true']);
2193
        $icon = self::getMdiIcon($icon);
2194
        $attributes['class'] = isset($attributes['class']) ? "$buttonClass {$attributes['class']}" : $buttonClass;
2195
        $attributes['title'] = $attributes['title'] ?? $text;
2196
2197
        if (!$includeText) {
2198
            $text = '<span class="sr-only">'.$text.'</span>';
2199
        }
2200
2201
        return self::url("$icon $text", $url, $attributes);
2202
    }
2203
2204
    public static function toolbarAction(string $id, array $contentList): string
2205
    {
2206
        $contentList = array_filter($contentList);
2207
2208
        if (empty($contentList)) {
2209
            return '';
2210
        }
2211
2212
        $col = count($contentList);
2213
        $html = ' <div id="'.$id.'" class="q-card p-2 mb-4">';
2214
        $html .= ' <div class="flex justify-between '.$col.'">';
2215
        foreach ($contentList as $item) {
2216
            $html .= '<div class="flex p-2 gap-2 ">'.$item.'</div>';
2217
        }
2218
        $html .= '</div>';
2219
        $html .= '</div>';
2220
2221
        return $html;
2222
    }
2223
2224
    public static function getMdiIcon(string $name, string $additionalClass = null, string $style = null, int $pixelSize = null, string $title = null): string
2225
    {
2226
        $sizeString = '';
2227
        if (!empty($pixelSize)) {
2228
            $sizeString = 'font-size: '.$pixelSize.'px; width: '.$pixelSize.'px; height: '.$pixelSize.'px; ';
2229
        }
2230
        if (empty($style)) {
2231
            $style = '';
2232
        }
2233
        $additionalAttributes = [
2234
            'class' => "mdi-$name mdi v-icon notranslate v-icon--size-default $additionalClass",
2235
            'style' => $sizeString.$style,
2236
            'medium' => '',
2237
            'aria-hidden' => 'true',
2238
        ];
2239
        if (!empty($title)) {
2240
            $additionalAttributes['title'] = htmlentities($title);
2241
        }
2242
2243
        return self::tag(
2244
            'i',
2245
            '',
2246
            $additionalAttributes
2247
        );
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="v-card bg-white mx-2" id="$id">
2328
                    <div class="v-card-header text-xl my-2">
2329
                        $title
2330
                    </div>
2331
                    <div class="v-card-text">$content</div>
2332
                </div>
2333
HTML;
2334
        } else {
2335
            if (!empty($id)) {
2336
                $params['id'] = $id;
2337
            }
2338
            $params['class'] = 'v-card bg-white mx-2';
2339
            $html = '';
2340
            if (!empty($title)) {
2341
                $html .= '<div class="v-card-header text-xl my-2">'.$title.'</div>'.PHP_EOL;
2342
            }
2343
            $html .= '<div class="v-card-text">'.$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 $id
2456
     *
2457
     * @return array|mixed
2458
     */
2459
    public static function randomColor($id)
2460
    {
2461
        static $colors = [];
2462
2463
        if (!empty($colors[$id])) {
2464
            return $colors[$id];
2465
        } else {
2466
            $color = substr(md5(time() * $id), 0, 6);
2467
            $c1 = hexdec(substr($color, 0, 2));
2468
            $c2 = hexdec(substr($color, 2, 2));
2469
            $c3 = hexdec(substr($color, 4, 2));
2470
            $luminosity = $c1 + $c2 + $c3;
2471
2472
            $type = '#000000';
2473
            if ($luminosity < (255 + 255 + 255) / 2) {
2474
                $type = '#FFFFFF';
2475
            }
2476
2477
            $result = [
2478
                'color' => '#'.$color,
2479
                'luminosity' => $type,
2480
            ];
2481
            $colors[$id] = $result;
2482
2483
            return $result; // example: #fc443a
2484
        }
2485
    }
2486
2487
    public static function noDataView(string $title, string $icon, string $buttonTitle, string $url): string
2488
    {
2489
        $content = '<div id="no-data-view">';
2490
        $content .= '<h3>'.$title.'</h3>';
2491
        $content .= $icon;
2492
        $content .= '<div class="controls">';
2493
        $content .= self::url(
2494
            '<em class="fa fa-plus"></em> '.$buttonTitle,
2495
            $url,
2496
            ['class' => 'btn btn-primary']
2497
        );
2498
        $content .= '</div>';
2499
        $content .= '</div>';
2500
2501
        return $content;
2502
    }
2503
2504
    public static function prose(string $contents): string
2505
    {
2506
        return "
2507
            <div class=''>
2508
                <div class='prose prose-blue'>
2509
                $contents
2510
                </div>
2511
            </div>
2512
            ";
2513
    }
2514
}
2515