Passed
Pull Request — master (#7151)
by
unknown
09:27
created

Display::fileHtmlGuesser()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
7
use Chamilo\CoreBundle\Enums\ActionIcon;
8
use Chamilo\CoreBundle\Enums\ObjectIcon;
9
use Chamilo\CoreBundle\Enums\StateIcon;
10
use Chamilo\CoreBundle\Enums\ToolIcon;
11
use Chamilo\CoreBundle\Framework\Container;
12
use Chamilo\CoreBundle\Helpers\ThemeHelper;
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'));
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: var/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
        if ('text' === $type) {
883
            $attributes['class'] = isset($attributes['class'])
884
                ? $attributes['class'].' p-inputtext p-component '
885
                : 'p-inputtext p-component ';
886
        }
887
888
        return self::tag('input', '', $attributes);
889
    }
890
891
    /**
892
     * Displays an HTML select tag.
893
     */
894
    public static function select(
895
        string $name,
896
        array $values,
897
        mixed $default = -1,
898
        array $extra_attributes = [],
899
        bool $show_blank_item = true,
900
        string $blank_item_text = ''
901
    ): string {
902
        $html = '';
903
        $extra = '';
904
        $default_id = 'id="'.$name.'" ';
905
        $extra_attributes = array_merge(
906
            ['class' => 'p-select p-component p-inputwrapper p-inputwrapper-filled'],
907
            $extra_attributes
908
        );
909
        foreach ($extra_attributes as $key => $parameter) {
910
            if ('id' == $key) {
911
                $default_id = '';
912
            }
913
            $extra .= $key.'="'.$parameter.'" ';
914
        }
915
        $html .= '<select name="'.$name.'" '.$default_id.' '.$extra.'>';
916
917
        if ($show_blank_item) {
918
            if (empty($blank_item_text)) {
919
                $blank_item_text = get_lang('Select');
920
            } else {
921
                $blank_item_text = Security::remove_XSS($blank_item_text);
922
            }
923
            $html .= self::tag(
924
                'option',
925
                '-- '.$blank_item_text.' --',
926
                ['value' => '-1']
927
            );
928
        }
929
        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...
930
            foreach ($values as $key => $value) {
931
                if (is_array($value) && isset($value['name'])) {
932
                    $value = $value['name'];
933
                }
934
                $html .= '<option value="'.$key.'"';
935
936
                if (is_array($default)) {
937
                    foreach ($default as $item) {
938
                        if ($item == $key) {
939
                            $html .= ' selected="selected"';
940
                            break;
941
                        }
942
                    }
943
                } else {
944
                    if ($default == $key) {
945
                        $html .= ' selected="selected"';
946
                    }
947
                }
948
949
                $html .= '>'.$value.'</option>';
950
            }
951
        }
952
        $html .= '</select>';
953
954
        return $html;
955
    }
956
957
    /**
958
     * @param $name
959
     * @param $value
960
     * @param array $attributes
961
     *
962
     * @return string
963
     */
964
    public static function button($name, $value, $attributes = [])
965
    {
966
        if (!empty($name)) {
967
            $attributes['name'] = $name;
968
        }
969
970
        return self::tag('button', $value, $attributes);
971
    }
972
973
    /**
974
     * Creates a tab menu
975
     * Requirements: declare the jquery, jquery-ui libraries + the jquery-ui.css
976
     * in the $htmlHeadXtra variable before the display_header
977
     * Add this script.
978
     *
979
     * @param array  $headers       list of the tab titles
980
     * @param array  $items
981
     * @param string $id            id of the container of the tab in the example "tabs"
982
     * @param array  $attributes    for the ul
983
     * @param array  $ul_attributes
984
     * @param string $selected
985
     *
986
     * @return string
987
     */
988
    public static function tabs(
989
        $headers,
990
        $items,
991
        $id = 'tabs',
992
        $attributes = [],
993
        $ul_attributes = [],
994
        $selected = ''
995
    ) {
996
        if (empty($headers) || 0 === count($headers)) {
997
            return '';
998
        }
999
1000
        // ---------------------------------------------------------------------
1001
        // Build tab headers
1002
        // ---------------------------------------------------------------------
1003
        $lis = '';
1004
        $i = 1;
1005
        foreach ($headers as $item) {
1006
            $isActive = (empty($selected) && 1 === $i) || (!empty($selected) && (int) $selected === $i);
1007
            $activeClass = $isActive ? ' active' : '';
1008
            $ariaSelected = $isActive ? 'true' : 'false';
1009
1010
            $item = self::tag(
1011
                'a',
1012
                $item,
1013
                [
1014
                    'href' => 'javascript:void(0)',
1015
                    'class' => 'nav-item nav-link text-primary'.$activeClass,
1016
                    '@click' => "openTab = $i",
1017
                    'id' => $id.$i.'-tab',
1018
                    'data-toggle' => 'tab',
1019
                    'role' => 'tab',
1020
                    'aria-controls' => $id.'-'.$i,
1021
                    'aria-selected' => $ariaSelected,
1022
                ]
1023
            );
1024
1025
            $lis .= $item;
1026
            $i++;
1027
        }
1028
1029
        $ul = self::tag(
1030
            'nav',
1031
            $lis,
1032
            [
1033
                'id' => 'nav_'.$id,
1034
                'class' => 'nav nav-tabs bg-white px-3 pt-3 border-bottom-0',
1035
                'role' => 'tablist',
1036
            ]
1037
        );
1038
1039
        // ---------------------------------------------------------------------
1040
        // Build tab contents
1041
        // ---------------------------------------------------------------------
1042
        $i = 1;
1043
        $divs = '';
1044
        foreach ($items as $content) {
1045
            $isActive = (empty($selected) && 1 === $i) || (!empty($selected) && (int) $selected === $i);
1046
            $panelClass = 'tab-panel';
1047
            if ($isActive) {
1048
                $panelClass .= ' is-active';
1049
            }
1050
1051
            $divs .= self::tag(
1052
                'div',
1053
                $content,
1054
                [
1055
                    'id' => $id.'-'.$i,
1056
                    'x-show' => "openTab === $i",
1057
                    'class' => $panelClass,
1058
                ]
1059
            );
1060
            $i++;
1061
        }
1062
1063
        // Wrapper for contents: white background, gray border, padding
1064
        $contentWrapper = self::tag(
1065
            'div',
1066
            $divs,
1067
            [
1068
                'class' => 'tab-content bg-white border border-gray-25 rounded-bottom px-3 py-3 mt-2',
1069
            ]
1070
        );
1071
1072
        // ---------------------------------------------------------------------
1073
        // Outer wrapper
1074
        // ---------------------------------------------------------------------
1075
        $attributes['id'] = (string) $id;
1076
        if (empty($attributes['class'])) {
1077
            $attributes['class'] = '';
1078
        }
1079
        // Shadow, rounded corners, small top margin
1080
        $attributes['class'] .= ' tab_wrapper shadow-sm rounded mt-3';
1081
1082
        $initialTab = !empty($selected) ? (int) $selected : 1;
1083
        $attributes['x-data'] = '{ openTab: '.$initialTab.' }';
1084
1085
        return self::tag(
1086
            'div',
1087
            $ul.$contentWrapper,
1088
            $attributes
1089
        );
1090
    }
1091
1092
    /**
1093
     * @param $headers
1094
     * @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...
1095
     *
1096
     * @return string
1097
     */
1098
    public static function tabsOnlyLink($headers, $selected = null, string $tabList = '')
1099
    {
1100
        $id = uniqid('tabs_');
1101
        $list = '';
1102
1103
        if ('integer' === gettype($selected)) {
1104
            $selected -= 1;
1105
        }
1106
1107
        foreach ($headers as $key => $item) {
1108
            $class = null;
1109
            if ($key == $selected) {
1110
                $class = 'active';
1111
            }
1112
            $item = self::tag(
1113
                'a',
1114
                $item['content'],
1115
                [
1116
                    'id' => $id.'-'.$key,
1117
                    'href' => $item['url'],
1118
                    'class' => 'nav-link '.$class,
1119
                ]
1120
            );
1121
            $list .= '<li class="nav-item">'.$item.'</li>';
1122
        }
1123
1124
        return self::div(
1125
            self::tag(
1126
                'ul',
1127
                $list,
1128
                ['class' => 'nav nav-tabs']
1129
            ),
1130
            ['class' => "ul-tablist $tabList"]
1131
        );
1132
    }
1133
1134
    /**
1135
     * In order to display a grid using jqgrid you have to:.
1136
     *
1137
     * @example
1138
     * After your Display::display_header function you have to add the nex javascript code:
1139
     * <script>
1140
     *   echo Display::grid_js('my_grid_name', $url,$columns, $column_model, $extra_params,[]);
1141
     *   // for more information of this function check the grid_js() function
1142
     * </script>
1143
     * //Then you have to call the grid_html
1144
     * echo Display::grid_html('my_grid_name');
1145
     * As you can see both function use the same "my_grid_name" this is very important otherwise nothing will work
1146
     *
1147
     * @param   string  the div id, this value must be the same with the first parameter of Display::grid_js()
1148
     *
1149
     * @return string html
1150
     */
1151
    public static function grid_html($div_id)
1152
    {
1153
        $table = self::tag('table', '', ['id' => $div_id]);
1154
        $table .= self::tag('div', '', ['id' => $div_id.'_pager']);
1155
1156
        return $table;
1157
    }
1158
1159
    /**
1160
     * This is a wrapper to use the jqgrid in Chamilo.
1161
     * For the other jqgrid options visit http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options
1162
     * This function need to be in the ready jquery function
1163
     * example --> $(function() { <?php echo Display::grid_js('grid' ...); ?> }
1164
     * In order to work this function needs the Display::grid_html function with the same div id.
1165
     *
1166
     * @param string $div_id       div id
1167
     * @param string $url          url where the jqgrid will ask for data (if datatype = json)
1168
     * @param array  $column_names Visible columns (you should use get_lang).
1169
     *                             An array in which we place the names of the columns.
1170
     *                             This is the text that appears in the head of the grid (Header layer).
1171
     *                             Example: colname   {name:'date',     index:'date',   width:120, align:'right'},
1172
     * @param array  $column_model the column model :  Array which describes the parameters of the columns.
1173
     *                             This is the most important part of the grid.
1174
     *                             For a full description of all valid values see colModel API. See the url above.
1175
     * @param array  $extra_params extra parameters
1176
     * @param array  $data         data that will be loaded
1177
     * @param string $formatter    A string that will be appended to the JSON returned
1178
     * @param bool   $fixed_width  not implemented yet
1179
     *
1180
     * @return string the js code
1181
     */
1182
    public static function grid_js(
1183
        $div_id,
1184
        $url,
1185
        $column_names,
1186
        $column_model,
1187
        $extra_params,
1188
        $data = [],
1189
        $formatter = '',
1190
        $fixed_width = false
1191
    ) {
1192
        $obj = new stdClass();
1193
        $obj->first = 'first';
1194
1195
        if (!empty($url)) {
1196
            $obj->url = $url;
1197
        }
1198
1199
        // Needed it in order to render the links/html in the grid
1200
        foreach ($column_model as &$columnModel) {
1201
            if (!isset($columnModel['formatter'])) {
1202
                $columnModel['formatter'] = '';
1203
            }
1204
        }
1205
1206
        //This line should only be used/modified in case of having characters
1207
        // encoding problems - see #6159
1208
        //$column_names = array_map("utf8_encode", $column_names);
1209
        $obj->colNames = $column_names;
1210
        $obj->colModel = $column_model;
1211
        $obj->pager = '#'.$div_id.'_pager';
1212
        $obj->datatype = 'json';
1213
        $obj->viewrecords = 'true';
1214
        $obj->guiStyle = 'bootstrap4';
1215
        $obj->iconSet = 'materialDesignIcons';
1216
        $all_value = 10000000;
1217
1218
        // Sets how many records we want to view in the grid
1219
        $obj->rowNum = 20;
1220
1221
        // Default row quantity
1222
        if (!isset($extra_params['rowList'])) {
1223
            $defaultRowList = [20, 50, 100, 500, 1000, $all_value];
1224
            $rowList = api_get_setting('display.table_row_list', true);
1225
            if (is_array($rowList) && isset($rowList['options']) && is_array($rowList['options'])) {
1226
                $rowList = $rowList['options'];
1227
                $rowList[] = $all_value;
1228
            } else {
1229
                $rowList = $defaultRowList;
1230
            }
1231
            $extra_params['rowList'] = $rowList;
1232
        }
1233
1234
        $defaultRow = (int) api_get_setting('display.table_default_row');
1235
        if ($defaultRow > 0) {
1236
            $obj->rowNum = $defaultRow;
1237
        }
1238
1239
        $json = '';
1240
        if (!empty($extra_params['datatype'])) {
1241
            $obj->datatype = $extra_params['datatype'];
1242
        }
1243
1244
        // Row even odd style.
1245
        $obj->altRows = true;
1246
        if (!empty($extra_params['altRows'])) {
1247
            $obj->altRows = $extra_params['altRows'];
1248
        }
1249
1250
        if (!empty($extra_params['sortname'])) {
1251
            $obj->sortname = $extra_params['sortname'];
1252
        }
1253
1254
        if (!empty($extra_params['sortorder'])) {
1255
            $obj->sortorder = $extra_params['sortorder'];
1256
        }
1257
1258
        if (!empty($extra_params['rowList'])) {
1259
            $obj->rowList = $extra_params['rowList'];
1260
        }
1261
1262
        if (!empty($extra_params['rowNum'])) {
1263
            $obj->rowNum = $extra_params['rowNum'];
1264
        } else {
1265
            // Try to load max rows from Session
1266
            $urlInfo = parse_url($url);
1267
            if (isset($urlInfo['query'])) {
1268
                parse_str($urlInfo['query'], $query);
1269
                if (isset($query['a'])) {
1270
                    $action = $query['a'];
1271
                    // This value is set in model.ajax.php
1272
                    $savedRows = Session::read('max_rows_'.$action);
1273
                    if (!empty($savedRows)) {
1274
                        $obj->rowNum = $savedRows;
1275
                    }
1276
                }
1277
            }
1278
        }
1279
1280
        if (!empty($extra_params['viewrecords'])) {
1281
            $obj->viewrecords = $extra_params['viewrecords'];
1282
        }
1283
1284
        $beforeSelectRow = null;
1285
        if (isset($extra_params['beforeSelectRow'])) {
1286
            $beforeSelectRow = 'beforeSelectRow: '.$extra_params['beforeSelectRow'].', ';
1287
            unset($extra_params['beforeSelectRow']);
1288
        }
1289
1290
        $beforeProcessing = '';
1291
        if (isset($extra_params['beforeProcessing'])) {
1292
            $beforeProcessing = 'beforeProcessing : function() { '.$extra_params['beforeProcessing'].' },';
1293
            unset($extra_params['beforeProcessing']);
1294
        }
1295
1296
        $beforeRequest = '';
1297
        if (isset($extra_params['beforeRequest'])) {
1298
            $beforeRequest = 'beforeRequest : function() { '.$extra_params['beforeRequest'].' },';
1299
            unset($extra_params['beforeRequest']);
1300
        }
1301
1302
        $gridComplete = '';
1303
        if (isset($extra_params['gridComplete'])) {
1304
            $gridComplete = 'gridComplete : function() { '.$extra_params['gridComplete'].' },';
1305
            unset($extra_params['gridComplete']);
1306
        }
1307
1308
        // Adding extra params
1309
        if (!empty($extra_params)) {
1310
            foreach ($extra_params as $key => $element) {
1311
                // the groupHeaders key gets a special treatment
1312
                if ('groupHeaders' != $key) {
1313
                    $obj->$key = $element;
1314
                }
1315
            }
1316
        }
1317
1318
        // Adding static data.
1319
        if (!empty($data)) {
1320
            $data_var = $div_id.'_data';
1321
            $json .= ' var '.$data_var.' = '.json_encode($data).';';
1322
            $obj->data = $data_var;
1323
            $obj->datatype = 'local';
1324
            $json .= "\n";
1325
        }
1326
1327
        $obj->end = 'end';
1328
1329
        $json_encode = json_encode($obj);
1330
1331
        if (!empty($data)) {
1332
            //Converts the "data":"js_variable" to "data":js_variable,
1333
            // otherwise it will not work
1334
            $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...
1335
        }
1336
1337
        // Fixing true/false js values that doesn't need the ""
1338
        $json_encode = str_replace(':"true"', ':true', $json_encode);
1339
        // wrap_cell is not a valid jqgrid attributes is a hack to wrap a text
1340
        $json_encode = str_replace('"wrap_cell":true', 'cellattr : function(rowId, value, rowObject, colModel, arrData) { return \'class = "jqgrid_whitespace"\'; }', $json_encode);
1341
        $json_encode = str_replace(':"false"', ':false', $json_encode);
1342
        $json_encode = str_replace('"formatter":"action_formatter"', 'formatter:action_formatter', $json_encode);
1343
        $json_encode = str_replace('"formatter":"extra_formatter"', 'formatter:extra_formatter', $json_encode);
1344
        $json_encode = str_replace(['{"first":"first",', '"end":"end"}'], '', $json_encode);
1345
1346
        if (('true' === api_get_setting('work.allow_compilatio_tool')) &&
1347
            (false !== strpos($_SERVER['REQUEST_URI'], 'work/work.php') ||
1348
             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...
1349
            )
1350
        ) {
1351
            $json_encode = str_replace('"function () { compilatioInit() }"',
1352
                'function () { compilatioInit() }',
1353
                $json_encode
1354
            );
1355
        }
1356
        // Creating the jqgrid element.
1357
        $json .= '$("#'.$div_id.'").jqGrid({';
1358
        $json .= "autowidth: true,";
1359
        //$json .= $beforeSelectRow;
1360
        $json .= $gridComplete;
1361
        $json .= $beforeProcessing;
1362
        $json .= $beforeRequest;
1363
        $json .= $json_encode;
1364
        $json .= '});';
1365
1366
        // Grouping headers option
1367
        if (isset($extra_params['groupHeaders'])) {
1368
            $groups = '';
1369
            foreach ($extra_params['groupHeaders'] as $group) {
1370
                //{ "startColumnName" : "courses", "numberOfColumns" : 1, "titleText" : "Order Info" },
1371
                $groups .= '{ "startColumnName" : "'.$group['startColumnName'].'", "numberOfColumns" : '.$group['numberOfColumns'].', "titleText" : "'.$group['titleText'].'" },';
1372
            }
1373
            $json .= '$("#'.$div_id.'").jqGrid("setGroupHeaders", {
1374
                "useColSpanStyle" : false,
1375
                "groupHeaders"    : [
1376
                    '.$groups.'
1377
                ]
1378
            });';
1379
        }
1380
1381
        $all_text = addslashes(get_lang('All'));
1382
        $json .= '$("'.$obj->pager.' option[value='.$all_value.']").text("'.$all_text.'");';
1383
        $json .= "\n";
1384
        // Adding edit/delete icons.
1385
        $json .= $formatter;
1386
1387
        return $json;
1388
    }
1389
1390
    /**
1391
     * @param array $headers
1392
     * @param array $rows
1393
     * @param array $attributes
1394
     *
1395
     * @return string
1396
     */
1397
    public static function table($headers, $rows, $attributes = [])
1398
    {
1399
        if (empty($attributes)) {
1400
            $attributes['class'] = 'data_table';
1401
        }
1402
        $table = new HTML_Table($attributes);
1403
        $row = 0;
1404
        $column = 0;
1405
1406
        // Course headers
1407
        if (!empty($headers)) {
1408
            foreach ($headers as $item) {
1409
                $table->setHeaderContents($row, $column, $item);
1410
                $column++;
1411
            }
1412
            $row = 1;
1413
            $column = 0;
1414
        }
1415
1416
        if (!empty($rows)) {
1417
            foreach ($rows as $content) {
1418
                $table->setCellContents($row, $column, $content);
1419
                $row++;
1420
            }
1421
        }
1422
1423
        return $table->toHtml();
1424
    }
1425
1426
    /**
1427
     * Get the session box details as an array.
1428
     *
1429
     * @todo check session visibility.
1430
     *
1431
     * @param int $session_id
1432
     *
1433
     * @return array Empty array or session array
1434
     *               ['title'=>'...','category'=>'','dates'=>'...','coach'=>'...','active'=>true/false,'session_category_id'=>int]
1435
     */
1436
    public static function getSessionTitleBox($session_id)
1437
    {
1438
        $session_info = api_get_session_info($session_id);
1439
        $generalCoachesNames = implode(
1440
            ' - ',
1441
            SessionManager::getGeneralCoachesNamesForSession($session_id)
1442
        );
1443
1444
        $session = [];
1445
        $session['category_id'] = $session_info['session_category_id'];
1446
        $session['title'] = $session_info['name'];
1447
        $session['dates'] = '';
1448
        $session['coach'] = '';
1449
        if ('true' === api_get_setting('show_session_coach') && $generalCoachesNames) {
1450
            $session['coach'] = get_lang('General coach').': '.$generalCoachesNames;
1451
        }
1452
        $active = false;
1453
        if (('0000-00-00 00:00:00' === $session_info['access_end_date'] &&
1454
            '0000-00-00 00:00:00' === $session_info['access_start_date']) ||
1455
            (empty($session_info['access_end_date']) && empty($session_info['access_start_date']))
1456
        ) {
1457
            if (isset($session_info['duration']) && !empty($session_info['duration'])) {
1458
                $daysLeft = SessionManager::getDayLeftInSession($session_info, api_get_user_id());
1459
                $session['duration'] = $daysLeft >= 0
1460
                    ? sprintf(get_lang('This session has a maximum duration. Only %s days to go.'), $daysLeft)
1461
                    : get_lang('You are already registered but your allowed access time has expired.');
1462
            }
1463
            $active = true;
1464
        } else {
1465
            $dates = SessionManager::parseSessionDates($session_info, true);
1466
            $session['dates'] = $dates['access'];
1467
            //$active = $date_start <= $now && $date_end >= $now;
1468
        }
1469
        $session['active'] = $active;
1470
        $session['session_category_id'] = $session_info['session_category_id'];
1471
        $session['visibility'] = $session_info['visibility'];
1472
        $session['num_users'] = $session_info['nbr_users'];
1473
        $session['num_courses'] = $session_info['nbr_courses'];
1474
        $session['description'] = $session_info['description'];
1475
        $session['show_description'] = $session_info['show_description'];
1476
        //$session['image'] = SessionManager::getSessionImage($session_info['id']);
1477
        $session['url'] = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$session_info['id'];
1478
1479
        $entityManager = Database::getManager();
1480
        $fieldValuesRepo = $entityManager->getRepository(ExtraFieldValues::class);
1481
        $extraFieldValues = $fieldValuesRepo->getVisibleValues(
1482
            ExtraField::SESSION_FIELD_TYPE,
1483
            $session_id
1484
        );
1485
1486
        $session['extra_fields'] = [];
1487
        /** @var ExtraFieldValues $value */
1488
        foreach ($extraFieldValues as $value) {
1489
            if (empty($value)) {
1490
                continue;
1491
            }
1492
            $session['extra_fields'][] = [
1493
                'field' => [
1494
                    'variable' => $value->getField()->getVariable(),
1495
                    'display_text' => $value->getField()->getDisplayText(),
1496
                ],
1497
                'value' => $value->getFieldValue(),
1498
            ];
1499
        }
1500
1501
        return $session;
1502
    }
1503
1504
    /**
1505
     * Return the five star HTML.
1506
     *
1507
     * @param string $id              of the rating ul element
1508
     * @param string $url             that will be added (for jquery see hot_courses.tpl)
1509
     * @param array  $point_info      point info array see function CourseManager::get_course_ranking()
1510
     * @param bool   $add_div_wrapper add a div wrapper
1511
     *
1512
     * @return string
1513
     */
1514
    public static function return_rating_system(
1515
        $id,
1516
        $url,
1517
        $point_info = [],
1518
        $add_div_wrapper = true
1519
    ) {
1520
        $number_of_users_who_voted = isset($point_info['users_who_voted']) ? $point_info['users_who_voted'] : null;
1521
        $percentage = isset($point_info['point_average']) ? $point_info['point_average'] : 0;
1522
1523
        if (!empty($percentage)) {
1524
            $percentage = $percentage * 125 / 100;
1525
        }
1526
        $accesses = isset($point_info['accesses']) ? $point_info['accesses'] : 0;
1527
        $star_label = sprintf(get_lang('%s stars out of 5'), $point_info['point_average_star']);
1528
1529
        $html = '<section class="rating-widget">';
1530
        $html .= '<div class="rating-stars"><ul id="stars">';
1531
        $html .= '<li class="star" data-link="'.$url.'&amp;star=1" title="Poor" data-value="1"><i class="fa fa-star fa-fw"></i></li>
1532
                 <li class="star" data-link="'.$url.'&amp;star=2" title="Fair" data-value="2"><i class="fa fa-star fa-fw"></i></li>
1533
                 <li class="star" data-link="'.$url.'&amp;star=3" title="Good" data-value="3"><i class="fa fa-star fa-fw"></i></li>
1534
                 <li class="star" data-link="'.$url.'&amp;star=4" title="Excellent" data-value="4"><i class="fa fa-star fa-fw"></i></li>
1535
                 <li class="star" data-link="'.$url.'&amp;star=5" title="WOW!!!" data-value="5"><i class="fa fa-star fa-fw"></i></li>
1536
        ';
1537
        $html .= '</ul></div>';
1538
        $html .= '</section>';
1539
        $labels = [];
1540
1541
        $labels[] = 1 == $number_of_users_who_voted ? $number_of_users_who_voted.' '.get_lang('Vote') : $number_of_users_who_voted.' '.get_lang('Votes');
1542
        $labels[] = 1 == $accesses ? $accesses.' '.get_lang('Visit') : $accesses.' '.get_lang('Visits');
1543
        $labels[] = $point_info['user_vote'] ? get_lang('Your vote').' ['.$point_info['user_vote'].']' : get_lang('Your vote').' [?] ';
1544
1545
        if (!$add_div_wrapper && api_is_anonymous()) {
1546
            $labels[] = self::tag('span', get_lang('Login to vote'), ['class' => 'error']);
1547
        }
1548
1549
        $html .= self::div(implode(' | ', $labels), ['id' => 'vote_label_'.$id, 'class' => 'vote_label_info']);
1550
        $html .= ' '.self::span(' ', ['id' => 'vote_label2_'.$id]);
1551
1552
        if ($add_div_wrapper) {
1553
            $html = self::div($html, ['id' => 'rating_wrapper_'.$id]);
1554
        }
1555
1556
        return $html;
1557
    }
1558
1559
    /**
1560
     * @param string $title
1561
     * @param string $second_title
1562
     * @param string $size
1563
     * @param bool   $filter
1564
     *
1565
     * @return string
1566
     */
1567
    public static function page_header($title, $second_title = null, $size = 'h2', $filter = true)
1568
    {
1569
        if ($filter) {
1570
            $title = Security::remove_XSS($title);
1571
        }
1572
1573
        if (!empty($second_title)) {
1574
            if ($filter) {
1575
                $second_title = Security::remove_XSS($second_title);
1576
            }
1577
            $title .= "<small> $second_title</small>";
1578
        }
1579
1580
        return '<div class="page-header section-header mb-6"><'.$size.' class="section-header__title">'.$title.'</'.$size.'></div>';
1581
    }
1582
1583
    public static function page_header_and_translate($title, $second_title = null)
1584
    {
1585
        $title = get_lang($title);
1586
1587
        return self::page_header($title, $second_title);
1588
    }
1589
1590
    public static function page_subheader($title, $second_title = null, $size = 'h3', $attributes = [])
1591
    {
1592
        if (!empty($second_title)) {
1593
            $second_title = Security::remove_XSS($second_title);
1594
            $title .= "<small> $second_title<small>";
1595
        }
1596
        $subTitle = self::tag($size, Security::remove_XSS($title), $attributes);
1597
1598
        return $subTitle;
1599
    }
1600
1601
    public static function page_subheader2($title, $second_title = null)
1602
    {
1603
        return self::page_header($title, $second_title, 'h4');
1604
    }
1605
1606
    public static function page_subheader3($title, $second_title = null)
1607
    {
1608
        return self::page_header($title, $second_title, 'h5');
1609
    }
1610
1611
    public static function description(array $list): string
1612
    {
1613
        $html = '';
1614
        if (!empty($list)) {
1615
            $html = '<dl class="dl-horizontal">';
1616
            foreach ($list as $item) {
1617
                $html .= '<dt>'.$item['title'].'</dt>';
1618
                $html .= '<dd>'.$item['content'].'</dd>';
1619
            }
1620
            $html .= '</dl>';
1621
        }
1622
1623
        return $html;
1624
    }
1625
1626
    /**
1627
     * @param int    $percentage      int value between 0 and 100
1628
     * @param bool   $show_percentage
1629
     * @param string $extra_info
1630
     * @param string $class           danger/success/infowarning
1631
     *
1632
     * @return string
1633
     */
1634
    public static function bar_progress($percentage, $show_percentage = true, $extra_info = '', $class = '')
1635
    {
1636
        $percentage = (int) $percentage;
1637
        $class = empty($class) ? '' : "progress-bar-$class";
1638
1639
        $div = '<div class="progress">
1640
                <div
1641
                    class="progress-bar progress-bar-striped '.$class.'"
1642
                    role="progressbar"
1643
                    aria-valuenow="'.$percentage.'"
1644
                    aria-valuemin="0"
1645
                    aria-valuemax="100"
1646
                    style="width: '.$percentage.'%;"
1647
                >';
1648
        if ($show_percentage) {
1649
            $div .= $percentage.'%';
1650
        } else {
1651
            if (!empty($extra_info)) {
1652
                $div .= $extra_info;
1653
            }
1654
        }
1655
        $div .= '</div></div>';
1656
1657
        return $div;
1658
    }
1659
1660
    /**
1661
     * @param array $badge_list
1662
     *
1663
     * @return string
1664
     */
1665
    public static function badgeGroup($list)
1666
    {
1667
        $html = '<div class="badge-group">';
1668
        foreach ($list as $badge) {
1669
            $html .= $badge;
1670
        }
1671
        $html .= '</div>';
1672
1673
        return $html;
1674
    }
1675
1676
    /**
1677
     * Return an HTML span element with the badge class and an additional bg-$type class
1678
     */
1679
    public static function label(string $content, string $type = 'default'): string
1680
    {
1681
        $html = '';
1682
        if (!empty($content)) {
1683
            $class = match ($type) {
1684
                'success' => 'success',
1685
                'warning' => 'warning',
1686
                'important', 'danger', 'error' => 'error',
1687
                'info' => 'info',
1688
                'primary' => 'primary',
1689
                default => 'secondary',
1690
            };
1691
1692
            $html = '<span class="badge badge--'.$class.'">';
1693
            $html .= $content;
1694
            $html .= '</span>';
1695
        }
1696
1697
        return $html;
1698
    }
1699
1700
    public static function actions(array $items): string
1701
    {
1702
        if (empty($items)) {
1703
            return '';
1704
        }
1705
1706
        $links = '';
1707
        foreach ($items as $value) {
1708
            $attributes = $value['url_attributes'] ?? [];
1709
            $links .= self::url($value['content'], $value['url'], $attributes);
1710
        }
1711
1712
        return self::toolbarAction(uniqid('toolbar', false), [$links]);
1713
    }
1714
1715
    /**
1716
     * Prints a tooltip.
1717
     *
1718
     * @param string $text
1719
     * @param string $tip
1720
     *
1721
     * @return string
1722
     */
1723
    public static function tip($text, $tip)
1724
    {
1725
        if (empty($tip)) {
1726
            return $text;
1727
        }
1728
1729
        return self::span(
1730
            $text,
1731
            ['class' => 'boot-tooltip', 'title' => strip_tags($tip)]
1732
        );
1733
    }
1734
1735
    /**
1736
     * @param array $buttons
1737
     *
1738
     * @return string
1739
     */
1740
    public static function groupButton($buttons)
1741
    {
1742
        $html = '<div class="btn-group" role="group">';
1743
        foreach ($buttons as $button) {
1744
            $html .= $button;
1745
        }
1746
        $html .= '</div>';
1747
1748
        return $html;
1749
    }
1750
1751
    /**
1752
     * @todo use twig
1753
     *
1754
     * @param string $title
1755
     * @param array  $elements
1756
     * @param bool   $alignToRight
1757
     *
1758
     * @return string
1759
     */
1760
    public static function groupButtonWithDropDown($title, $elements, $alignToRight = false)
1761
    {
1762
        $id = uniqid('dropdown', false);
1763
        $html = '
1764
        <div class="dropdown inline-block relative">
1765
            <button
1766
                id="'.$id.'"
1767
                type="button"
1768
                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"
1769
                aria-expanded="false"
1770
                aria-haspopup="true"
1771
                onclick="document.querySelector(\'#'.$id.'_menu\').classList.toggle(\'hidden\')"
1772
            >
1773
              '.$title.'
1774
              <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">
1775
                <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" />
1776
              </svg>
1777
            </button>
1778
            <div
1779
                id="'.$id.'_menu"
1780
                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"
1781
                role="menu"
1782
                aria-orientation="vertical"
1783
                aria-labelledby="menu-button"
1784
                tabindex="-1"
1785
            >
1786
            <div class="py-1" role="none">';
1787
        foreach ($elements as $item) {
1788
            $html .= self::url(
1789
                    $item['title'],
1790
                    $item['href'],
1791
                    [
1792
                        'class' => 'text-gray-700 block px-4 py-2 text-sm',
1793
                        'role' => 'menuitem',
1794
                        'onclick' => $item['onclick'] ?? '',
1795
                        'data-action' => $item['data-action'] ?? '',
1796
                        'data-confirm' => $item['data-confirm'] ?? '',
1797
                    ]
1798
                );
1799
        }
1800
        $html .= '
1801
            </div>
1802
            </div>
1803
            </div>
1804
        ';
1805
1806
        return $html;
1807
    }
1808
1809
    /**
1810
     * @param string $file
1811
     * @param array  $params
1812
     *
1813
     * @return string|null
1814
     */
1815
    public static function getMediaPlayer($file, $params = [])
1816
    {
1817
        $fileInfo = pathinfo($file);
1818
1819
        $autoplay = isset($params['autoplay']) && 'true' === $params['autoplay'] ? 'autoplay' : '';
1820
        $id = isset($params['id']) ? $params['id'] : $fileInfo['basename'];
1821
        $width = isset($params['width']) ? 'width="'.$params['width'].'"' : null;
1822
        $class = isset($params['class']) ? ' class="'.$params['class'].'"' : null;
1823
1824
        switch ($fileInfo['extension']) {
1825
            case 'mp3':
1826
            case 'webm':
1827
                $html = '<audio id="'.$id.'" '.$class.' controls '.$autoplay.' '.$width.' src="'.$params['url'].'" >';
1828
                $html .= '<object width="'.$width.'" height="50" type="application/x-shockwave-flash" data="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf">
1829
                            <param name="movie" value="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf" />
1830
                            <param name="flashvars" value="controls=true&file='.$params['url'].'" />
1831
                          </object>';
1832
                $html .= '</audio>';
1833
1834
                return $html;
1835
                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...
1836
            case 'wav':
1837
            case 'ogg':
1838
                $html = '<audio width="300px" controls id="'.$id.'" '.$autoplay.' src="'.$params['url'].'" >';
1839
1840
                return $html;
1841
                break;
1842
        }
1843
1844
        return null;
1845
    }
1846
1847
    /**
1848
     * @param int    $nextValue
1849
     * @param array  $list
1850
     * @param int    $current
1851
     * @param int    $fixedValue
1852
     * @param array  $conditions
1853
     * @param string $link
1854
     * @param bool   $isMedia
1855
     * @param bool   $addHeaders
1856
     * @param array  $linkAttributes
1857
     *
1858
     * @return string
1859
     */
1860
    public static function progressPaginationBar(
1861
        $nextValue,
1862
        $list,
1863
        $current,
1864
        $fixedValue = null,
1865
        $conditions = [],
1866
        $link = null,
1867
        $isMedia = false,
1868
        $addHeaders = true,
1869
        $linkAttributes = []
1870
    ) {
1871
        if ($addHeaders) {
1872
            $pagination_size = 'pagination-mini';
1873
            $html = '<div class="exercise_pagination pagination '.$pagination_size.'"><ul>';
1874
        } else {
1875
            $html = null;
1876
        }
1877
        $affectAllItems = false;
1878
        if ($isMedia && isset($fixedValue) && ($nextValue + 1 == $current)) {
1879
            $affectAllItems = true;
1880
        }
1881
        $localCounter = 0;
1882
        foreach ($list as $itemId) {
1883
            $isCurrent = false;
1884
            if ($affectAllItems) {
1885
                $isCurrent = true;
1886
            } else {
1887
                if (!$isMedia) {
1888
                    $isCurrent = $current == ($localCounter + $nextValue + 1) ? true : false;
1889
                }
1890
            }
1891
            $html .= self::parsePaginationItem(
1892
                $itemId,
1893
                $isCurrent,
1894
                $conditions,
1895
                $link,
1896
                $nextValue,
1897
                $isMedia,
1898
                $localCounter,
1899
                $fixedValue,
1900
                $linkAttributes
1901
            );
1902
            $localCounter++;
1903
        }
1904
        if ($addHeaders) {
1905
            $html .= '</ul></div>';
1906
        }
1907
1908
        return $html;
1909
    }
1910
1911
    /**
1912
     * @param int    $itemId
1913
     * @param bool   $isCurrent
1914
     * @param array  $conditions
1915
     * @param string $link
1916
     * @param int    $nextValue
1917
     * @param bool   $isMedia
1918
     * @param int    $localCounter
1919
     * @param int    $fixedValue
1920
     * @param array  $linkAttributes
1921
     *
1922
     * @return string
1923
     */
1924
    public static function parsePaginationItem(
1925
        $itemId,
1926
        $isCurrent,
1927
        $conditions,
1928
        $link,
1929
        $nextValue = 0,
1930
        $isMedia = false,
1931
        $localCounter = null,
1932
        $fixedValue = null,
1933
        $linkAttributes = []
1934
    ) {
1935
        $defaultClass = 'before';
1936
        $class = $defaultClass;
1937
        foreach ($conditions as $condition) {
1938
            $array = isset($condition['items']) ? $condition['items'] : [];
1939
            $class_to_applied = $condition['class'];
1940
            $type = isset($condition['type']) ? $condition['type'] : 'positive';
1941
            $mode = isset($condition['mode']) ? $condition['mode'] : 'add';
1942
            switch ($type) {
1943
                case 'positive':
1944
                    if (in_array($itemId, $array)) {
1945
                        if ('overwrite' == $mode) {
1946
                            $class = " $defaultClass $class_to_applied";
1947
                        } else {
1948
                            $class .= " $class_to_applied";
1949
                        }
1950
                    }
1951
                    break;
1952
                case 'negative':
1953
                    if (!in_array($itemId, $array)) {
1954
                        if ('overwrite' == $mode) {
1955
                            $class = " $defaultClass $class_to_applied";
1956
                        } else {
1957
                            $class .= " $class_to_applied";
1958
                        }
1959
                    }
1960
                    break;
1961
            }
1962
        }
1963
        if ($isCurrent) {
1964
            $class = 'before current';
1965
        }
1966
        if ($isMedia && $isCurrent) {
1967
            $class = 'before current';
1968
        }
1969
        if (empty($link)) {
1970
            $link_to_show = '#';
1971
        } else {
1972
            $link_to_show = $link.($nextValue + $localCounter);
1973
        }
1974
        $label = $nextValue + $localCounter + 1;
1975
        if ($isMedia) {
1976
            $label = ($fixedValue + 1).' '.chr(97 + $localCounter);
1977
            $link_to_show = $link.$fixedValue.'#questionanchor'.$itemId;
1978
        }
1979
        $link = self::url($label.' ', $link_to_show, $linkAttributes);
1980
1981
        return '<li class = "'.$class.'">'.$link.'</li>';
1982
    }
1983
1984
    /**
1985
     * @param int $current
1986
     * @param int $total
1987
     *
1988
     * @return string
1989
     */
1990
    public static function paginationIndicator($current, $total)
1991
    {
1992
        $html = null;
1993
        if (!empty($current) && !empty($total)) {
1994
            $label = sprintf(get_lang('%s of %s'), $current, $total);
1995
            $html = self::url($label, '#', ['class' => 'btn disabled']);
1996
        }
1997
1998
        return $html;
1999
    }
2000
2001
    /**
2002
     * @param $url
2003
     * @param $currentPage
2004
     * @param $pagesCount
2005
     * @param $totalItems
2006
     *
2007
     * @return string
2008
     */
2009
    public static function getPagination($url, $currentPage, $pagesCount, $totalItems)
2010
    {
2011
        $pagination = '';
2012
        if ($totalItems > 1 && $pagesCount > 1) {
2013
            $pagination .= '<ul class="pagination">';
2014
            for ($i = 0; $i < $pagesCount; $i++) {
2015
                $newPage = $i + 1;
2016
                if ($currentPage == $newPage) {
2017
                    $pagination .= '<li class="active"><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2018
                } else {
2019
                    $pagination .= '<li><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2020
                }
2021
            }
2022
            $pagination .= '</ul>';
2023
        }
2024
2025
        return $pagination;
2026
    }
2027
2028
    /**
2029
     * Adds a legacy message in the queue.
2030
     *
2031
     * @param string $message
2032
     */
2033
    public static function addFlash($message)
2034
    {
2035
        // Detect type of message.
2036
        $parts = preg_match('/alert-([a-z]*)/', $message, $matches);
2037
        $type = 'primary';
2038
        if ($parts && isset($matches[1]) && $matches[1]) {
2039
            $type = $matches[1];
2040
        }
2041
        // Detect legacy content of message.
2042
        $result = preg_match('/<div(.*?)\>(.*?)\<\/div>/s', $message, $matches);
2043
        if ($result && isset($matches[2])) {
2044
            Container::getSession()->getFlashBag()->add($type, $matches[2]);
2045
        }
2046
    }
2047
2048
    /**
2049
     * Get the profile edition link for a user.
2050
     *
2051
     * @param int  $userId  The user id
2052
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
2053
     *
2054
     * @return string The link
2055
     */
2056
    public static function getProfileEditionLink($userId, $asAdmin = false)
2057
    {
2058
        $editProfileUrl = api_get_path(WEB_CODE_PATH).'auth/profile.php';
2059
        if ($asAdmin) {
2060
            $editProfileUrl = api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=".intval($userId);
2061
        }
2062
2063
        return $editProfileUrl;
2064
    }
2065
2066
    /**
2067
     * Get the vCard for a user.
2068
     *
2069
     * @param int $userId The user id
2070
     *
2071
     * @return string *.*vcf file
2072
     */
2073
    public static function getVCardUserLink($userId)
2074
    {
2075
        return api_get_path(WEB_PATH).'main/social/vcard_export.php?userId='.intval($userId);
2076
    }
2077
2078
    /**
2079
     * @param string $content
2080
     * @param string $title
2081
     * @param string $footer
2082
     * @param string $type        primary|success|info|warning|danger
2083
     * @param string $extra
2084
     * @param string $id
2085
     * @param string $customColor
2086
     * @param string $rightAction
2087
     *
2088
     * @return string
2089
     */
2090
    public static function panel(
2091
        $content,
2092
        $title = '',
2093
        $footer = '',
2094
        $type = 'default',
2095
        $extra = '',
2096
        $id = '',
2097
        $customColor = '',
2098
        $rightAction = ''
2099
    ) {
2100
        $headerStyle = '';
2101
        if (!empty($customColor)) {
2102
            $headerStyle = 'style = "color: white; background-color: '.$customColor.'" ';
2103
        }
2104
2105
        $footer = !empty($footer) ? '<p class="card-text"><small class="text-muted">'.$footer.'</small></p>' : '';
2106
        $typeList = ['primary', 'success', 'info', 'warning', 'danger'];
2107
        $style = !in_array($type, $typeList) ? 'default' : $type;
2108
2109
        if (!empty($id)) {
2110
            $id = " id='$id'";
2111
        }
2112
        $cardBody = $title.' '.self::contentPanel($content).' '.$footer;
2113
2114
        return "
2115
            <div $id class=card>
2116
                <div class='flex justify-between items-center py-2'>
2117
                    <div class='relative mt-1 flex'>
2118
                        $title
2119
                    </div>
2120
                    <div>
2121
                        $rightAction
2122
                    </div>
2123
                </div>
2124
2125
                $content
2126
                $footer
2127
            </div>"
2128
        ;
2129
    }
2130
2131
    /**
2132
     * @param string $content
2133
     */
2134
    public static function contentPanel($content): string
2135
    {
2136
        if (empty($content)) {
2137
            return '';
2138
        }
2139
2140
        return '<div class="card-text">'.$content.'</div>';
2141
    }
2142
2143
    /**
2144
     * Get the button HTML with an Awesome Font icon.
2145
     *
2146
     * @param string $text        The button content
2147
     * @param string $url         The url to button
2148
     * @param string $icon        The Awesome Font class for icon
2149
     * @param string $type        Optional. The button Bootstrap class. Default 'default' class
2150
     * @param array  $attributes  The additional attributes
2151
     * @param bool   $includeText
2152
     *
2153
     * @return string The button HTML
2154
     */
2155
    public static function toolbarButton(
2156
        $text,
2157
        $url,
2158
        $icon = 'check',
2159
        $type = null,
2160
        array $attributes = [],
2161
        $includeText = true
2162
    ) {
2163
        $buttonClass = "btn btn--secondary-outline";
2164
        if (!empty($type)) {
2165
            $buttonClass = "btn btn--$type";
2166
        }
2167
        //$icon = self::tag('i', null, ['class' => "fa fa-$icon fa-fw", 'aria-hidden' => 'true']);
2168
        $icon = self::getMdiIcon($icon);
2169
        $attributes['class'] = isset($attributes['class']) ? "$buttonClass {$attributes['class']}" : $buttonClass;
2170
        $attributes['title'] = $attributes['title'] ?? $text;
2171
2172
        if (!$includeText) {
2173
            $text = '<span class="sr-only">'.$text.'</span>';
2174
        }
2175
2176
        return self::url("$icon $text", $url, $attributes);
2177
    }
2178
2179
    /**
2180
     * Generate an HTML "p-toolbar" div element with the given id attribute.
2181
     * @param string $id The HTML div's "id" attribute to set
2182
     * @param array  $contentList Array of left-center-right elements for the toolbar. If only 2 elements are defined, this becomes left-right (no center)
2183
     * @return string HTML div for the toolbar
2184
     */
2185
    public static function toolbarAction(string $id, array $contentList): string
2186
    {
2187
        $contentListPurged = array_filter($contentList);
2188
2189
        if (empty($contentListPurged)) {
2190
            return '';
2191
        }
2192
2193
        $count = count($contentList);
2194
2195
        $start = $contentList[0];
2196
        $center = '';
2197
        $end = '';
2198
2199
        if (2 === $count) {
2200
            $end = $contentList[1];
2201
        } elseif (3 === $count) {
2202
            $center = $contentList[1];
2203
            $end = $contentList[2];
2204
        }
2205
2206
        return '<div id="'.$id.'" class="toolbar-action p-toolbar p-component flex items-center justify-between flex-wrap w-full" role="toolbar">
2207
            <div class="p-toolbar-group-start p-toolbar-group-left">'.$start.'</div>
2208
            <div class="p-toolbar-group-center">'.$center.'</div>
2209
            <div class="p-toolbar-group-end p-toolbar-group-right">'.$end.'</div>
2210
        </div>';
2211
    }
2212
2213
    /**
2214
     * @param array  $content
2215
     * @param array  $colsWidth Optional. Columns width
2216
     *
2217
     * @return string
2218
     */
2219
    public static function toolbarGradeAction($content, $colsWidth = [])
2220
    {
2221
        $col = count($content);
2222
2223
        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...
2224
            $width = 8 / $col;
2225
            array_walk($content, function () use ($width, &$colsWidth) {
2226
                $colsWidth[] = $width;
2227
            });
2228
        }
2229
2230
        $html = '<div id="grade" class="p-toolbar p-component flex items-center justify-between flex-wrap" role="toolbar">';
2231
        for ($i = 0; $i < $col; $i++) {
2232
            $class = 'col-sm-'.$colsWidth[$i];
2233
            if ($col > 1) {
2234
                if ($i > 0 && $i < count($content) - 1) {
2235
                    $class .= ' text-center';
2236
                } elseif ($i === count($content) - 1) {
2237
                    $class .= ' text-right';
2238
                }
2239
            }
2240
            $html .= '<div class="'.$class.'">'.$content[$i].'</div>';
2241
        }
2242
        $html .= '</div>';
2243
2244
        return $html;
2245
    }
2246
2247
    /**
2248
     * The auto-translated title version of getMdiIconSimple()
2249
     * Shortcut method to getMdiIcon, to be used from Twig (see ChamiloExtension.php)
2250
     * using acceptable default values
2251
     * @param string $name The icon name or a string representing the icon in our *Icon Enums
2252
     * @param int|null $size The icon size
2253
     * @param string|null $additionalClass Additional CSS class to add to the icon
2254
     * @param string|null $title A title for the icon
2255
     * @return string
2256
     * @throws InvalidArgumentException
2257
     * @throws ReflectionException
2258
     */
2259
    public static function getMdiIconTranslate(
2260
        string $name,
2261
        ?int $size = ICON_SIZE_SMALL,
2262
        ?string $additionalClass = 'ch-tool-icon',
2263
        ?string $title = null
2264
    ): string
2265
    {
2266
        if (!empty($title)) {
2267
            $title = get_lang($title);
2268
        }
2269
2270
        return self::getMdiIconSimple($name, $size, $additionalClass, $title);
2271
    }
2272
    /**
2273
     * Shortcut method to getMdiIcon, to be used from Twig (see ChamiloExtension.php)
2274
     * using acceptable default values
2275
     * @param string $name The icon name or a string representing the icon in our *Icon Enums
2276
     * @param int|null $size The icon size
2277
     * @param string|null $additionalClass Additional CSS class to add to the icon
2278
     * @param string|null $title A title for the icon
2279
     * @return string
2280
     * @throws InvalidArgumentException
2281
     * @throws ReflectionException
2282
     */
2283
    public static function getMdiIconSimple(
2284
        string $name,
2285
        ?int $size = ICON_SIZE_SMALL,
2286
        ?string $additionalClass = 'ch-tool-icon',
2287
        ?string $title = null
2288
    ): string
2289
    {
2290
        // If the string contains '::', we assume it is a reference to one of the icon Enum classes in src/CoreBundle/Enums/
2291
        $matches = [];
2292
        if (preg_match('/(\w*)::(\w*)/', $name, $matches)) {
2293
            if (count($matches) != 3) {
2294
                throw new InvalidArgumentException('Invalid enum case string format. Expected format is "EnumClass::CASE".');
2295
            }
2296
            $enum = $matches[1];
2297
            $case = $matches[2];
2298
            if (!class_exists('Chamilo\CoreBundle\Enums\\'.$enum)) {
2299
                throw new InvalidArgumentException("Class {$enum} does not exist.");
2300
            }
2301
            $reflection = new ReflectionEnum('Chamilo\CoreBundle\Enums\\'.$enum);
2302
            // Check if the case exists in the Enum class
2303
            if (!$reflection->hasCase($case)) {
2304
                throw new InvalidArgumentException("Case {$case} does not exist in enum class {$enum}.");
2305
            }
2306
            // Get the Enum case
2307
            /* @var ReflectionEnumUnitCase $enumUnitCaseObject */
2308
            $enumUnitCaseObject = $reflection->getCase($case);
2309
            $enumValue = $enumUnitCaseObject->getValue();
2310
            $name = $enumValue->value;
2311
2312
        }
2313
2314
        return self::getMdiIcon($name, $additionalClass, null, $size, $title);
2315
    }
2316
2317
    /**
2318
     * Get a full HTML <i> tag for an icon from the Material Design Icons set
2319
     * @param string|ActionIcon|ToolIcon|ObjectIcon|StateIcon $name
2320
     * @param string|null                                     $additionalClass
2321
     * @param string|null                                     $style
2322
     * @param int|null                                        $pixelSize
2323
     * @param string|null                                     $title
2324
     * @param array|null                                      $additionalAttributes
2325
     * @return string
2326
     */
2327
    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
2328
    {
2329
        $sizeString = '';
2330
        if (!empty($pixelSize)) {
2331
            $sizeString = 'font-size: '.$pixelSize.'px; width: '.$pixelSize.'px; height: '.$pixelSize.'px; ';
2332
        }
2333
        if (empty($style)) {
2334
            $style = '';
2335
        }
2336
2337
        $additionalAttributes['class'] = 'mdi mdi-';
2338
2339
        if ($name instanceof ActionIcon
2340
            || $name instanceof ToolIcon
2341
            || $name instanceof ObjectIcon
2342
            || $name instanceof StateIcon
2343
        ) {
2344
            $additionalAttributes['class'] .= $name->value;
2345
        } else {
2346
            $additionalAttributes['class'] .= $name;
2347
        }
2348
2349
        $additionalAttributes['class'] .= " $additionalClass";
2350
        $additionalAttributes['style'] = $sizeString.$style;
2351
        $additionalAttributes['aria-hidden'] = 'true';
2352
2353
        if (!empty($title)) {
2354
            $additionalAttributes['title'] = htmlentities($title);
2355
        }
2356
2357
        return self::tag(
2358
            'i',
2359
            '',
2360
            $additionalAttributes
2361
        );
2362
    }
2363
2364
    /**
2365
     * Get a HTML code for a icon by Font Awesome.
2366
     *
2367
     * @param string     $name            The icon name. Example: "mail-reply"
2368
     * @param int|string $size            Optional. The size for the icon. (Example: lg, 2, 3, 4, 5)
2369
     * @param bool       $fixWidth        Optional. Whether add the fw class
2370
     * @param string     $additionalClass Optional. Additional class
2371
     *
2372
     * @return string
2373
     * @deprecated Use getMdiIcon() instead
2374
     */
2375
    public static function returnFontAwesomeIcon(
2376
        $name,
2377
        $size = '',
2378
        $fixWidth = false,
2379
        $additionalClass = ''
2380
    ) {
2381
        $className = "mdi mdi-$name";
2382
2383
        if ($fixWidth) {
2384
            $className .= ' fa-fw';
2385
        }
2386
2387
        switch ($size) {
2388
            case 'xs':
2389
            case 'sm':
2390
            case 'lg':
2391
                $className .= " fa-{$size}";
2392
                break;
2393
            case 2:
2394
            case 3:
2395
            case 4:
2396
            case 5:
2397
                $className .= " fa-{$size}x";
2398
                break;
2399
        }
2400
2401
        if (!empty($additionalClass)) {
2402
            $className .= " $additionalClass";
2403
        }
2404
2405
        $icon = self::tag('em', null, ['class' => $className]);
2406
2407
        return "$icon ";
2408
    }
2409
2410
    public static function returnPrimeIcon(
2411
        $name,
2412
        $size = '',
2413
        $fixWidth = false,
2414
        $additionalClass = ''
2415
    ) {
2416
        $className = "pi pi-$name";
2417
2418
        if ($fixWidth) {
2419
            $className .= ' pi-fw';
2420
        }
2421
2422
        if ($size) {
2423
            $className .= " pi-$size";
2424
        }
2425
2426
        if (!empty($additionalClass)) {
2427
            $className .= " $additionalClass";
2428
        }
2429
2430
        $icon = self::tag('i', null, ['class' => $className]);
2431
2432
        return "$icon ";
2433
    }
2434
2435
    /**
2436
     * @param string     $title
2437
     * @param string     $content
2438
     * @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...
2439
     * @param array      $params
2440
     * @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...
2441
     * @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...
2442
     * @param bool|true  $open
2443
     * @param bool|false $fullClickable
2444
     *
2445
     * @return string
2446
     *
2447
     * @todo rework function to easy use
2448
     */
2449
    public static function panelCollapse(
2450
        $title,
2451
        $content,
2452
        $id = null,
2453
        $params = [],
2454
        $idAccordion = null,
2455
        $idCollapse = null,
2456
        $open = true,
2457
        $fullClickable = false
2458
    ) {
2459
        $javascript = '';
2460
        if (!empty($idAccordion)) {
2461
            $javascript = '
2462
        <script>
2463
            document.addEventListener("DOMContentLoaded", function() {
2464
                const buttons = document.querySelectorAll("#card_'.$idAccordion.' a");
2465
                const menus = document.querySelectorAll("#collapse_'.$idAccordion.'");
2466
                buttons.forEach((button, index) => {
2467
                    button.addEventListener("click", function() {
2468
                        menus.forEach((menu, menuIndex) => {
2469
                            if (index === menuIndex) {
2470
                                button.setAttribute("aria-expanded", "true" === button.getAttribute("aria-expanded") ? "false" : "true")
2471
                                button.classList.toggle("mdi-chevron-down")
2472
                                button.classList.toggle("mdi-chevron-up")
2473
                                menu.classList.toggle("active");
2474
                            } else {
2475
                                menu.classList.remove("active");
2476
                            }
2477
                        });
2478
                    });
2479
                });
2480
            });
2481
        </script>';
2482
            $html = '
2483
        <div class="display-panel-collapse mb-2">
2484
            <div class="display-panel-collapse__header" id="card_'.$idAccordion.'">
2485
                <a role="button"
2486
                    class="mdi mdi-chevron-down"
2487
                    data-toggle="collapse"
2488
                    data-target="#collapse_'.$idAccordion.'"
2489
                    aria-expanded="'.(($open) ? 'true' : 'false').'"
2490
                    aria-controls="collapse_'.$idAccordion.'"
2491
                >
2492
                    '.$title.'
2493
                </a>
2494
            </div>
2495
            <div
2496
                id="collapse_'.$idAccordion.'"
2497
                class="display-panel-collapse__collapsible '.(($open) ? 'active' : '').'"
2498
            >
2499
                <div id="collapse_contant_'.$idAccordion.'">';
2500
2501
            $html .= $content;
2502
            $html .= '</div></div></div>';
2503
2504
        } else {
2505
            if (!empty($id)) {
2506
                $params['id'] = $id;
2507
            }
2508
            $params['class'] = 'v-card bg-white mx-2';
2509
            $html = '';
2510
            if (!empty($title)) {
2511
                $html .= '<div class="v-card-header text-h5 my-2">'.$title.'</div>'.PHP_EOL;
2512
            }
2513
            $html .= '<div class="v-card-text">'.$content.'</div>'.PHP_EOL;
2514
            $html = self::div($html, $params);
2515
        }
2516
2517
        return $javascript.$html;
2518
    }
2519
2520
    /**
2521
     * Returns the string "1 day ago" with a link showing the exact date time.
2522
     *
2523
     * @param string|DateTime $dateTime in UTC or a DateTime in UTC
2524
     *
2525
     * @throws Exception
2526
     *
2527
     * @return string
2528
     */
2529
    public static function dateToStringAgoAndLongDate(string|DateTime $dateTime): string
2530
    {
2531
        if (empty($dateTime) || '0000-00-00 00:00:00' === $dateTime) {
2532
            return '';
2533
        }
2534
2535
        if (is_string($dateTime)) {
2536
            $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC'));
2537
        }
2538
2539
        return self::tip(
2540
            date_to_str_ago($dateTime),
2541
            api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG)
2542
        );
2543
    }
2544
2545
    /**
2546
     * @param array  $userInfo
2547
     * @param string $status
2548
     * @param string $toolbar
2549
     *
2550
     * @return string
2551
     */
2552
    public static function getUserCard($userInfo, $status = '', $toolbar = '')
2553
    {
2554
        if (empty($userInfo)) {
2555
            return '';
2556
        }
2557
2558
        if (!empty($status)) {
2559
            $status = '<div class="items-user-status">'.$status.'</div>';
2560
        }
2561
2562
        if (!empty($toolbar)) {
2563
            $toolbar = '<div class="btn-group pull-right">'.$toolbar.'</div>';
2564
        }
2565
2566
        return '<div id="user_card_'.$userInfo['id'].'" class="card d-flex flex-row">
2567
                    <img src="'.$userInfo['avatar'].'" class="rounded" />
2568
                    <h3 class="card-title">'.$userInfo['complete_name'].'</h3>
2569
                    <div class="card-body">
2570
                       <div class="card-title">
2571
                       '.$status.'
2572
                       '.$toolbar.'
2573
                       </div>
2574
                    </div>
2575
                    <hr />
2576
              </div>';
2577
    }
2578
2579
    /**
2580
     * @param string $fileName
2581
     * @param string $fileUrl
2582
     *
2583
     * @return string
2584
     */
2585
    public static function fileHtmlGuesser($fileName, $fileUrl)
2586
    {
2587
        $data = pathinfo($fileName);
2588
2589
        //$content = self::url($data['basename'], $fileUrl);
2590
        $content = '';
2591
        switch ($data['extension']) {
2592
            case 'webm':
2593
            case 'mp4':
2594
            case 'ogg':
2595
                $content = '<video style="width: 400px; height:100%;" src="'.$fileUrl.'"></video>';
2596
                // Allows video to play when loading during an ajax call
2597
                $content .= "<script>jQuery('video:not(.skip), audio:not(.skip)').mediaelementplayer();</script>";
2598
                break;
2599
            case 'jpg':
2600
            case 'jpeg':
2601
            case 'gif':
2602
            case 'png':
2603
                $content = '<img class="img-responsive" src="'.$fileUrl.'" />';
2604
                break;
2605
            default:
2606
                //$html = self::url($data['basename'], $fileUrl);
2607
                break;
2608
        }
2609
        //$html = self::url($content, $fileUrl, ['ajax']);
2610
2611
        return $content;
2612
    }
2613
2614
    /**
2615
     * @param string $image
2616
     * @param int    $size
2617
     *
2618
     * @return string
2619
     */
2620
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
2621
    {
2622
        return self::return_icon($image, '', [], $size, false, true);
2623
    }
2624
2625
    /**
2626
     * @param $id
2627
     *
2628
     * @return array|mixed
2629
     */
2630
    public static function randomColor($id)
2631
    {
2632
        static $colors = [];
2633
2634
        if (!empty($colors[$id])) {
2635
            return $colors[$id];
2636
        } else {
2637
            $color = substr(md5(time() * $id), 0, 6);
2638
            $c1 = hexdec(substr($color, 0, 2));
2639
            $c2 = hexdec(substr($color, 2, 2));
2640
            $c3 = hexdec(substr($color, 4, 2));
2641
            $luminosity = $c1 + $c2 + $c3;
2642
2643
            $type = '#000000';
2644
            if ($luminosity < (255 + 255 + 255) / 2) {
2645
                $type = '#FFFFFF';
2646
            }
2647
2648
            $result = [
2649
                'color' => '#'.$color,
2650
                'luminosity' => $type,
2651
            ];
2652
            $colors[$id] = $result;
2653
2654
            return $result; // example: #fc443a
2655
        }
2656
    }
2657
2658
    public static function noDataView(string $title, string $icon, string $buttonTitle, string $url): string
2659
    {
2660
        $content = '<div id="no-data-view">';
2661
        $content .= '<h3>'.$title.'</h3>';
2662
        $content .= $icon;
2663
        $content .= '<div class="controls">';
2664
        $content .= self::url(
2665
            '<em class="fa fa-plus"></em> '.$buttonTitle,
2666
            $url,
2667
            ['class' => 'btn btn--primary']
2668
        );
2669
        $content .= '</div>';
2670
        $content .= '</div>';
2671
2672
        return $content;
2673
    }
2674
2675
    public static function prose(string $contents): string
2676
    {
2677
        return "
2678
    <div class='w-full my-8'>
2679
      <div class='prose prose-blue max-w-none px-6 py-4 bg-white rounded-lg shadow'>
2680
        $contents
2681
      </div>
2682
    </div>
2683
    ";
2684
    }
2685
2686
    public static function getFrameReadyBlock(
2687
        string $frameName,
2688
        string $itemType = '',
2689
        string $jsConditionalFunction = 'function () { return false; }'
2690
    ): string {
2691
2692
        if (in_array($itemType, ['link', 'sco', 'xapi', 'quiz', 'h5p', 'forum'])) {
2693
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the type-hinted return string.
Loading history...
2694
        }
2695
2696
        $themeHelper = Container::$container->get(ThemeHelper::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

2696
        /** @scrutinizer ignore-call */ 
2697
        $themeHelper = Container::$container->get(ThemeHelper::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...
2697
2698
        $themeColorsUrl = $themeHelper->getThemeAssetUrl('colors.css');
2699
2700
        $colorThemeItem = $themeColorsUrl
2701
            ? '{ type: "stylesheet", src: "'.$themeColorsUrl.'" },'
2702
            : '';
2703
2704
        return '$.frameReady(function() {},
2705
            "'.$frameName.'",
2706
            [
2707
                { type: "script", src: "/build/runtime.js" },
2708
                { type: "script", src: "/build/legacy_framereadyloader.js" },
2709
                { type: "stylesheet", src: "/build/legacy_framereadyloader.css" },
2710
                '.$colorThemeItem.'
2711
            ],
2712
            '.$jsConditionalFunction
2713
            .');';
2714
    }
2715
2716
    /**
2717
     * Renders and consumes messages stored with Display::addFlash().
2718
     * Returns HTML ready to inject into the view (alert-*).
2719
     *
2720
     * @return html string with all Flash messages (or '' if none)
2721
     */
2722
    public static function getFlash(): string
2723
    {
2724
        $html = '';
2725
2726
        try {
2727
            $flashBag = Container::getSession()->getFlashBag();
2728
            $all = $flashBag->all();
2729
2730
            foreach ($all as $type => $messages) {
2731
                switch ($type) {
2732
                    case 'success':
2733
                        $displayType = 'success';
2734
                        break;
2735
                    case 'warning':
2736
                        $displayType = 'warning';
2737
                        break;
2738
                    case 'danger':
2739
                    case 'error':
2740
                        $displayType = 'error';
2741
                        break;
2742
                    case 'info':
2743
                    case 'primary':
2744
                    default:
2745
                        $displayType = 'info';
2746
                        break;
2747
                }
2748
2749
                foreach ((array) $messages as $msg) {
2750
                    if (is_string($msg) && $msg !== '') {
2751
                        $html .= self::return_message($msg, $displayType, false);
2752
                    }
2753
                }
2754
            }
2755
        } catch (\Throwable $e) {
2756
            error_log('Display::getFlash error: '.$e->getMessage());
2757
        }
2758
2759
        return $html;
2760
    }
2761
2762
    /**
2763
     * Build the common MySpace / tracking menu (left part of the toolbar).
2764
     *
2765
     * $current can be:
2766
     *  - overview
2767
     *  - students
2768
     *  - teachers
2769
     *  - admin_view
2770
     *  - exams
2771
     *  - current_courses
2772
     *  - courses
2773
     *  - sessions
2774
     *  - company_reports
2775
     *  - company_reports_resumed
2776
     */
2777
    public static function mySpaceMenu(string $current): string
2778
    {
2779
        $base = api_get_path(WEB_CODE_PATH).'my_space/';
2780
        $items = [];
2781
2782
        $isPlatformAdmin = api_is_platform_admin();
2783
        $isDrh = api_is_drh();
2784
2785
        if ($isDrh) {
2786
            // DRH menu.
2787
            $items = [
2788
                'students' => [
2789
                    'icon' => 'account',
2790
                    'title' => get_lang('Learners'),
2791
                    'url' => $base.'student.php',
2792
                ],
2793
                'teachers' => [
2794
                    'icon' => 'human-male-board',
2795
                    'title' => get_lang('Teachers'),
2796
                    'url' => $base.'teachers.php',
2797
                ],
2798
                'courses' => [
2799
                    'icon' => 'book-open-page-variant',
2800
                    'title' => get_lang('Courses'),
2801
                    'url' => $base.'course.php',
2802
                ],
2803
                'sessions' => [
2804
                    'icon' => 'book-open-page-variant',
2805
                    'title' => get_lang('Course sessions'),
2806
                    'url' => $base.'session.php',
2807
                ],
2808
                'company_reports' => [
2809
                    'icon' => 'chart-box',
2810
                    'title' => get_lang('Corporate report'),
2811
                    'url' => $base.'company_reports.php',
2812
                ],
2813
                'company_reports_resumed' => [
2814
                    'icon' => 'chart-box-outline',
2815
                    'title' => get_lang('Corporate report, short version'),
2816
                    'url' => $base.'company_reports_resumed.php',
2817
                ],
2818
            ];
2819
        } else {
2820
            // Teacher / platform admin menu.
2821
            $items = [
2822
                'overview' => [
2823
                    'icon' => 'chart-bar',
2824
                    'title' => get_lang('Global view'),
2825
                    'url' => $base.'index.php',
2826
                ],
2827
                'students' => [
2828
                    'icon' => 'account-star',
2829
                    'title' => get_lang('Learners'),
2830
                    'url' => $base.'student.php',
2831
                ],
2832
                'teachers' => [
2833
                    'icon' => 'human-male-board',
2834
                    'title' => get_lang('Teachers'),
2835
                    'url' => $base.'teachers.php',
2836
                ],
2837
            ];
2838
2839
            if ($isPlatformAdmin) {
2840
                $items['admin_view'] = [
2841
                    'icon' => 'star-outline',
2842
                    'title' => get_lang('Admin view'),
2843
                    'url' => $base.'admin_view.php',
2844
                ];
2845
                $items['exams'] = [
2846
                    'icon' => 'order-bool-ascending-variant',
2847
                    'title' => get_lang('Exam tracking'),
2848
                    'url' => api_get_path(WEB_CODE_PATH).'tracking/exams.php',
2849
                ];
2850
                $items['current_courses'] = [
2851
                    'icon' => 'book-open-page-variant',
2852
                    'title' => get_lang('Current courses report'),
2853
                    'url' => $base.'current_courses.php',
2854
                ];
2855
                $items['certificate_report'] = [
2856
                    'icon' => 'certificate',
2857
                    'title' => get_lang('See list of learner certificates'),
2858
                    'url' => api_get_path(WEB_CODE_PATH).'gradebook/certificate_report.php',
2859
                ];
2860
            }
2861
        }
2862
2863
        $html = '';
2864
2865
        foreach ($items as $name => $item) {
2866
            $iconClass = $name === $current ? 'ch-tool-icon-disabled' : 'ch-tool-icon';
2867
            $url = $name === $current ? '' : $item['url'];
2868
2869
            $html .= self::url(
2870
                self::getMdiIcon(
2871
                    $item['icon'],
2872
                    $iconClass,
2873
                    null,
2874
                    32,
2875
                    $item['title']
2876
                ),
2877
                $url
2878
            );
2879
        }
2880
2881
        return $html;
2882
    }
2883
}
2884