Passed
Push — master ( f38048...3387a6 )
by Angel Fernando Quiroz
12:35 queued 03:42
created

Display::getFrameReadyBlock()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

Loading history...
2203
            $width = 8 / $col;
2204
            array_walk($content, function () use ($width, &$colsWidth) {
2205
                $colsWidth[] = $width;
2206
            });
2207
        }
2208
2209
        $html = '<div id="grade" class="p-toolbar p-component flex items-center justify-between flex-wrap" role="toolbar">';
2210
        for ($i = 0; $i < $col; $i++) {
2211
            $class = 'col-sm-'.$colsWidth[$i];
2212
            if ($col > 1) {
2213
                if ($i > 0 && $i < count($content) - 1) {
2214
                    $class .= ' text-center';
2215
                } elseif ($i === count($content) - 1) {
2216
                    $class .= ' text-right';
2217
                }
2218
            }
2219
            $html .= '<div class="'.$class.'">'.$content[$i].'</div>';
2220
        }
2221
        $html .= '</div>';
2222
2223
        return $html;
2224
    }
2225
2226
    /**
2227
     * The auto-translated title version of getMdiIconSimple()
2228
     * Shortcut method to getMdiIcon, to be used from Twig (see ChamiloExtension.php)
2229
     * using acceptable default values
2230
     * @param string $name The icon name or a string representing the icon in our *Icon Enums
2231
     * @param int|null $size The icon size
2232
     * @param string|null $additionalClass Additional CSS class to add to the icon
2233
     * @param string|null $title A title for the icon
2234
     * @return string
2235
     * @throws InvalidArgumentException
2236
     * @throws ReflectionException
2237
     */
2238
    public static function getMdiIconTranslate(
2239
        string $name,
2240
        ?int $size = ICON_SIZE_SMALL,
2241
        ?string $additionalClass = 'ch-tool-icon',
2242
        ?string $title = null
2243
    ): string
2244
    {
2245
        if (!empty($title)) {
2246
            $title = get_lang($title);
2247
        }
2248
2249
        return self::getMdiIconSimple($name, $size, $additionalClass, $title);
2250
    }
2251
    /**
2252
     * Shortcut method to getMdiIcon, to be used from Twig (see ChamiloExtension.php)
2253
     * using acceptable default values
2254
     * @param string $name The icon name or a string representing the icon in our *Icon Enums
2255
     * @param int|null $size The icon size
2256
     * @param string|null $additionalClass Additional CSS class to add to the icon
2257
     * @param string|null $title A title for the icon
2258
     * @return string
2259
     * @throws InvalidArgumentException
2260
     * @throws ReflectionException
2261
     */
2262
    public static function getMdiIconSimple(
2263
        string $name,
2264
        ?int $size = ICON_SIZE_SMALL,
2265
        ?string $additionalClass = 'ch-tool-icon',
2266
        ?string $title = null
2267
    ): string
2268
    {
2269
        // If the string contains '::', we assume it is a reference to one of the icon Enum classes in src/CoreBundle/Component/Utils/
2270
        $matches = [];
2271
        if (preg_match('/(\w*)::(\w*)/', $name, $matches)) {
2272
            if (count($matches) != 3) {
2273
                throw new InvalidArgumentException('Invalid enum case string format. Expected format is "EnumClass::CASE".');
2274
            }
2275
            $enum = $matches[1];
2276
            $case = $matches[2];
2277
            if (!class_exists('Chamilo\CoreBundle\Component\Utils\\'.$enum)) {
2278
                throw new InvalidArgumentException("Class {$enum} does not exist.");
2279
            }
2280
            $reflection = new ReflectionEnum('Chamilo\CoreBundle\Component\Utils\\'.$enum);
2281
            // Check if the case exists in the Enum class
2282
            if (!$reflection->hasCase($case)) {
2283
                throw new InvalidArgumentException("Case {$case} does not exist in enum class {$enum}.");
2284
            }
2285
            // Get the Enum case
2286
            /* @var ReflectionEnumUnitCase $enumUnitCaseObject */
2287
            $enumUnitCaseObject = $reflection->getCase($case);
2288
            $enumValue = $enumUnitCaseObject->getValue();
2289
            $name = $enumValue->value;
2290
2291
        }
2292
2293
        return self::getMdiIcon($name, $additionalClass, null, $size, $title);
2294
    }
2295
2296
    /**
2297
     * Get a full HTML <i> tag for an icon from the Material Design Icons set
2298
     * @param string|ActionIcon|ToolIcon|ObjectIcon|StateIcon $name
2299
     * @param string|null                                     $additionalClass
2300
     * @param string|null                                     $style
2301
     * @param int|null                                        $pixelSize
2302
     * @param string|null                                     $title
2303
     * @param array|null                                      $additionalAttributes
2304
     * @return string
2305
     */
2306
    public static function getMdiIcon(string|ActionIcon|ToolIcon|ObjectIcon|StateIcon $name, string $additionalClass = null, string $style = null, int $pixelSize = null, string $title = null, array $additionalAttributes = null): string
2307
    {
2308
        $sizeString = '';
2309
        if (!empty($pixelSize)) {
2310
            $sizeString = 'font-size: '.$pixelSize.'px; width: '.$pixelSize.'px; height: '.$pixelSize.'px; ';
2311
        }
2312
        if (empty($style)) {
2313
            $style = '';
2314
        }
2315
2316
        $additionalAttributes['class'] = 'mdi mdi-';
2317
2318
        if ($name instanceof ActionIcon
2319
            || $name instanceof ToolIcon
2320
            || $name instanceof ObjectIcon
2321
            || $name instanceof StateIcon
2322
        ) {
2323
            $additionalAttributes['class'] .= $name->value;
2324
        } else {
2325
            $additionalAttributes['class'] .= $name;
2326
        }
2327
2328
        $additionalAttributes['class'] .= " $additionalClass";
2329
        $additionalAttributes['style'] = $sizeString.$style;
2330
        $additionalAttributes['aria-hidden'] = 'true';
2331
2332
        if (!empty($title)) {
2333
            $additionalAttributes['title'] = htmlentities($title);
2334
        }
2335
2336
        return self::tag(
2337
            'i',
2338
            '',
2339
            $additionalAttributes
2340
        );
2341
    }
2342
2343
    /**
2344
     * Get a HTML code for a icon by Font Awesome.
2345
     *
2346
     * @param string     $name            The icon name. Example: "mail-reply"
2347
     * @param int|string $size            Optional. The size for the icon. (Example: lg, 2, 3, 4, 5)
2348
     * @param bool       $fixWidth        Optional. Whether add the fw class
2349
     * @param string     $additionalClass Optional. Additional class
2350
     *
2351
     * @return string
2352
     * @deprecated Use getMdiIcon() instead
2353
     */
2354
    public static function returnFontAwesomeIcon(
2355
        $name,
2356
        $size = '',
2357
        $fixWidth = false,
2358
        $additionalClass = ''
2359
    ) {
2360
        $className = "mdi mdi-$name";
2361
2362
        if ($fixWidth) {
2363
            $className .= ' fa-fw';
2364
        }
2365
2366
        switch ($size) {
2367
            case 'xs':
2368
            case 'sm':
2369
            case 'lg':
2370
                $className .= " fa-{$size}";
2371
                break;
2372
            case 2:
2373
            case 3:
2374
            case 4:
2375
            case 5:
2376
                $className .= " fa-{$size}x";
2377
                break;
2378
        }
2379
2380
        if (!empty($additionalClass)) {
2381
            $className .= " $additionalClass";
2382
        }
2383
2384
        $icon = self::tag('em', null, ['class' => $className]);
2385
2386
        return "$icon ";
2387
    }
2388
2389
    public static function returnPrimeIcon(
2390
        $name,
2391
        $size = '',
2392
        $fixWidth = false,
2393
        $additionalClass = ''
2394
    ) {
2395
        $className = "pi pi-$name";
2396
2397
        if ($fixWidth) {
2398
            $className .= ' pi-fw';
2399
        }
2400
2401
        if ($size) {
2402
            $className .= " pi-$size";
2403
        }
2404
2405
        if (!empty($additionalClass)) {
2406
            $className .= " $additionalClass";
2407
        }
2408
2409
        $icon = self::tag('i', null, ['class' => $className]);
2410
2411
        return "$icon ";
2412
    }
2413
2414
    /**
2415
     * @param string     $title
2416
     * @param string     $content
2417
     * @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...
2418
     * @param array      $params
2419
     * @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...
2420
     * @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...
2421
     * @param bool|true  $open
2422
     * @param bool|false $fullClickable
2423
     *
2424
     * @return string
2425
     *
2426
     * @todo rework function to easy use
2427
     */
2428
    public static function panelCollapse(
2429
        $title,
2430
        $content,
2431
        $id = null,
2432
        $params = [],
2433
        $idAccordion = null,
2434
        $idCollapse = null,
2435
        $open = true,
2436
        $fullClickable = false
2437
    ) {
2438
        $javascript = '';
2439
        if (!empty($idAccordion)) {
2440
            $javascript = '
2441
        <script>
2442
            document.addEventListener("DOMContentLoaded", function() {
2443
                const buttons = document.querySelectorAll("#card_'.$idAccordion.' a");
2444
                const menus = document.querySelectorAll("#collapse_'.$idAccordion.'");
2445
                buttons.forEach((button, index) => {
2446
                    button.addEventListener("click", function() {
2447
                        menus.forEach((menu, menuIndex) => {
2448
                            if (index === menuIndex) {
2449
                                button.setAttribute("aria-expanded", "true" === button.getAttribute("aria-expanded") ? "false" : "true")
2450
                                button.classList.toggle("mdi-chevron-down")
2451
                                button.classList.toggle("mdi-chevron-up")
2452
                                menu.classList.toggle("active");
2453
                            } else {
2454
                                menu.classList.remove("active");
2455
                            }
2456
                        });
2457
                    });
2458
                });
2459
            });
2460
        </script>';
2461
            $html = '
2462
        <div class="display-panel-collapse">
2463
            <div class="display-panel-collapse__header" id="card_'.$idAccordion.'">
2464
                <a role="button"
2465
                    class="mdi mdi-chevron-down"
2466
                    data-toggle="collapse"
2467
                    data-target="#collapse_'.$idAccordion.'"
2468
                    aria-expanded="'.(($open) ? 'true' : 'false').'"
2469
                    aria-controls="collapse_'.$idAccordion.'"
2470
                >
2471
                    '.$title.'
2472
                </a>
2473
            </div>
2474
            <div
2475
                id="collapse_'.$idAccordion.'"
2476
                class="display-panel-collapse__collapsible '.(($open) ? 'active' : '').'"
2477
            >
2478
                <div id="collapse_contant_'.$idAccordion.'">';
2479
2480
            $html .= $content;
2481
            $html .= '</div></div></div>';
2482
2483
        } else {
2484
            if (!empty($id)) {
2485
                $params['id'] = $id;
2486
            }
2487
            $params['class'] = 'v-card bg-white mx-2';
2488
            $html = '';
2489
            if (!empty($title)) {
2490
                $html .= '<div class="v-card-header text-h5 my-2">'.$title.'</div>'.PHP_EOL;
2491
            }
2492
            $html .= '<div class="v-card-text">'.$content.'</div>'.PHP_EOL;
2493
            $html = self::div($html, $params);
2494
        }
2495
2496
        return $javascript.$html;
2497
    }
2498
2499
    /**
2500
     * Returns the string "1 day ago" with a link showing the exact date time.
2501
     *
2502
     * @param string|DateTime $dateTime in UTC or a DateTime in UTC
2503
     *
2504
     * @throws Exception
2505
     *
2506
     * @return string
2507
     */
2508
    public static function dateToStringAgoAndLongDate(string|DateTime $dateTime): string
2509
    {
2510
        if (empty($dateTime) || '0000-00-00 00:00:00' === $dateTime) {
2511
            return '';
2512
        }
2513
2514
        if (is_string($dateTime)) {
2515
            $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
2516
        }
2517
2518
        return self::tip(
2519
            date_to_str_ago($dateTime),
2520
            api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG)
2521
        );
2522
    }
2523
2524
    /**
2525
     * @param array  $userInfo
2526
     * @param string $status
2527
     * @param string $toolbar
2528
     *
2529
     * @return string
2530
     */
2531
    public static function getUserCard($userInfo, $status = '', $toolbar = '')
2532
    {
2533
        if (empty($userInfo)) {
2534
            return '';
2535
        }
2536
2537
        if (!empty($status)) {
2538
            $status = '<div class="items-user-status">'.$status.'</div>';
2539
        }
2540
2541
        if (!empty($toolbar)) {
2542
            $toolbar = '<div class="btn-group pull-right">'.$toolbar.'</div>';
2543
        }
2544
2545
        return '<div id="user_card_'.$userInfo['id'].'" class="card d-flex flex-row">
2546
                    <img src="'.$userInfo['avatar'].'" class="rounded" />
2547
                    <h3 class="card-title">'.$userInfo['complete_name'].'</h3>
2548
                    <div class="card-body">
2549
                       <div class="card-title">
2550
                       '.$status.'
2551
                       '.$toolbar.'
2552
                       </div>
2553
                    </div>
2554
                    <hr />
2555
              </div>';
2556
    }
2557
2558
    /**
2559
     * @param string $fileName
2560
     * @param string $fileUrl
2561
     *
2562
     * @return string
2563
     */
2564
    public static function fileHtmlGuesser($fileName, $fileUrl)
2565
    {
2566
        $data = pathinfo($fileName);
2567
2568
        //$content = self::url($data['basename'], $fileUrl);
2569
        $content = '';
2570
        switch ($data['extension']) {
2571
            case 'webm':
2572
            case 'mp4':
2573
            case 'ogg':
2574
                $content = '<video style="width: 400px; height:100%;" src="'.$fileUrl.'"></video>';
2575
                // Allows video to play when loading during an ajax call
2576
                $content .= "<script>jQuery('video:not(.skip), audio:not(.skip)').mediaelementplayer();</script>";
2577
                break;
2578
            case 'jpg':
2579
            case 'jpeg':
2580
            case 'gif':
2581
            case 'png':
2582
                $content = '<img class="img-responsive" src="'.$fileUrl.'" />';
2583
                break;
2584
            default:
2585
                //$html = self::url($data['basename'], $fileUrl);
2586
                break;
2587
        }
2588
        //$html = self::url($content, $fileUrl, ['ajax']);
2589
2590
        return $content;
2591
    }
2592
2593
    /**
2594
     * @param string $image
2595
     * @param int    $size
2596
     *
2597
     * @return string
2598
     */
2599
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
2600
    {
2601
        return self::return_icon($image, '', [], $size, false, true);
2602
    }
2603
2604
    /**
2605
     * @param $id
2606
     *
2607
     * @return array|mixed
2608
     */
2609
    public static function randomColor($id)
2610
    {
2611
        static $colors = [];
2612
2613
        if (!empty($colors[$id])) {
2614
            return $colors[$id];
2615
        } else {
2616
            $color = substr(md5(time() * $id), 0, 6);
2617
            $c1 = hexdec(substr($color, 0, 2));
2618
            $c2 = hexdec(substr($color, 2, 2));
2619
            $c3 = hexdec(substr($color, 4, 2));
2620
            $luminosity = $c1 + $c2 + $c3;
2621
2622
            $type = '#000000';
2623
            if ($luminosity < (255 + 255 + 255) / 2) {
2624
                $type = '#FFFFFF';
2625
            }
2626
2627
            $result = [
2628
                'color' => '#'.$color,
2629
                'luminosity' => $type,
2630
            ];
2631
            $colors[$id] = $result;
2632
2633
            return $result; // example: #fc443a
2634
        }
2635
    }
2636
2637
    public static function noDataView(string $title, string $icon, string $buttonTitle, string $url): string
2638
    {
2639
        $content = '<div id="no-data-view">';
2640
        $content .= '<h3>'.$title.'</h3>';
2641
        $content .= $icon;
2642
        $content .= '<div class="controls">';
2643
        $content .= self::url(
2644
            '<em class="fa fa-plus"></em> '.$buttonTitle,
2645
            $url,
2646
            ['class' => 'btn btn--primary']
2647
        );
2648
        $content .= '</div>';
2649
        $content .= '</div>';
2650
2651
        return $content;
2652
    }
2653
2654
    public static function prose(string $contents): string
2655
    {
2656
        return "
2657
            <div class=''>
2658
                <div class='prose prose-blue'>
2659
                $contents
2660
                </div>
2661
            </div>
2662
            ";
2663
    }
2664
2665
    public static function getFrameReadyBlock(
2666
        string $frameName,
2667
        string $itemType = '',
2668
        string $jsConditionalFunction = 'function () { return false; }'
2669
    ): string {
2670
        $colorThemeRepo = Container::$container->get(ColorThemeRepository::class);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

2670
        /** @scrutinizer ignore-call */ 
2671
        $colorThemeRepo = Container::$container->get(ColorThemeRepository::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2671
        $router = Container::getRouter();
2672
2673
        $colorTheme = $colorThemeRepo->getActiveOne();
2674
        $colorThemeItem = '';
2675
2676
        if ($colorTheme) {
2677
            $colorThemeItem = '{ type: "stylesheet", src: "'.$router->generate('chamilo_color_theme').'" },';
2678
        }
2679
2680
        return '$.frameReady(function() {},
2681
            "'.$frameName.'",
2682
            [
2683
                { type: "script", src: "/build/runtime.js" },
2684
                { type: "script", src: "/build/legacy_framereadyloader.js" },
2685
                { type: "stylesheet", src: "/build/legacy_framereadyloader.css" },
2686
                '.$colorThemeItem.'
2687
            ],
2688
            '.$jsConditionalFunction
2689
            .');';
2690
    }
2691
}
2692