Passed
Push — master ( 8af40c...bb6d6a )
by Julito
11:02
created

Display::dateToStringAgoAndLongDate()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
8
use Chamilo\CoreBundle\Framework\Container;
9
use ChamiloSession as Session;
10
use Symfony\Component\HttpFoundation\Response;
11
12
/**
13
 * Class Display
14
 * Contains several public functions dealing with the display of
15
 * table data, messages, help topics, ...
16
 *
17
 * Include/require it in your code to use its public functionality.
18
 * There are also several display public functions in the main api library.
19
 *
20
 * All public functions static public functions inside a class called Display,
21
 * so you use them like this: e.g.
22
 * Display::return_message($message)
23
 */
24
class Display
25
{
26
    /** @var Template */
27
    public static $global_template;
28
    public static $preview_style = null;
29
    public static $legacyTemplate;
30
31
    /**
32
     * Constructor.
33
     */
34
    public function __construct()
35
    {
36
    }
37
38
    /**
39
     * @return array
40
     */
41
    public static function toolList()
42
    {
43
        return [
44
            'group',
45
            'work',
46
            'glossary',
47
            'forum',
48
            'course_description',
49
            'gradebook',
50
            'attendance',
51
            'course_progress',
52
            'notebook',
53
        ];
54
    }
55
56
    /**
57
     * Displays the page header.
58
     *
59
     * @param string The name of the page (will be showed in the page title)
60
     * @param string Optional help file name
61
     * @param string $page_header
62
     */
63
    public static function display_header(
64
        $tool_name = '',
65
        $help = null,
66
        $page_header = null
67
    ) {
68
        global $interbreadcrumb;
69
        $interbreadcrumb[] = ['url' => '#', 'name' => $tool_name];
70
71
        ob_start();
72
73
        return true;
74
    }
75
76
    /**
77
     * Displays the reduced page header (without banner).
78
     */
79
    public static function display_reduced_header()
80
    {
81
        ob_start();
82
        self::$legacyTemplate = '@ChamiloCore/Layout/no_layout.html.twig';
83
84
        return true;
85
86
        global $show_learnpath, $tool_name;
0 ignored issues
show
Unused Code introduced by
GlobalNode is not reachable.

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

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

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

    return false;
}

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

Loading history...
87
        self::$global_template = new Template(
88
            $tool_name,
89
            false,
90
            false,
91
            $show_learnpath
92
        );
93
    }
94
95
    /**
96
     * Display no header.
97
     */
98
    public static function display_no_header()
99
    {
100
        global $tool_name, $show_learnpath;
101
        self::$global_template = new Template(
102
            $tool_name,
103
            false,
104
            false,
105
            $show_learnpath
106
        );
107
    }
108
109
    /**
110
     * Display the page footer.
111
     */
112
    public static function display_footer()
113
    {
114
        $contents = ob_get_contents();
115
        if (ob_get_length()) {
116
            ob_end_clean();
117
        }
118
        $tpl = '@ChamiloCore/Layout/layout_one_col.html.twig';
119
        if (!empty(self::$legacyTemplate)) {
120
            $tpl = self::$legacyTemplate;
121
        }
122
        $response = new Response();
123
        $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...
124
        global $interbreadcrumb, $htmlHeadXtra;
125
126
        $courseInfo = api_get_course_info();
127
        if (!empty($courseInfo)) {
128
            array_unshift(
129
                $interbreadcrumb,
130
                ['name' => $courseInfo['title'], 'url' => $courseInfo['course_public_url']]
131
            );
132
        }
133
134
        $params['legacy_javascript'] = $htmlHeadXtra;
135
        $params['legacy_breadcrumb'] = $interbreadcrumb;
136
137
        Template::setVueParams($params);
138
        $content = Container::getTemplating()->render($tpl, $params);
139
        $response->setContent($content);
140
        $response->send();
141
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
142
    }
143
144
    /**
145
     * Display the page footer.
146
     */
147
    public static function display_reduced_footer()
148
    {
149
        $contents = ob_get_contents();
150
        if (ob_get_length()) {
151
            ob_end_clean();
152
        }
153
        $tpl = '@ChamiloCore/Layout/no_layout.html.twig';
154
        if (!empty(self::$legacyTemplate)) {
155
            $tpl = self::$legacyTemplate;
156
        }
157
        $response = new Response();
158
        $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...
159
        global $interbreadcrumb, $htmlHeadXtra;
160
        $params['legacy_javascript'] = $htmlHeadXtra;
161
        $params['legacy_breadcrumb'] = $interbreadcrumb;
162
163
        $content = Container::getTemplating()->render($tpl, $params);
164
        $response->setContent($content);
165
        $response->send();
166
        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...
167
    }
168
169
    /**
170
     * Displays the tool introduction of a tool.
171
     *
172
     * @author Patrick Cool <[email protected]>, Ghent University
173
     *
174
     * @param string $tool          these are the constants that are used for indicating the tools
175
     * @param array  $editor_config Optional configuration settings for the online editor.
176
     *                              return: $tool return a string array list with the "define" in main_api.lib
177
     *
178
     * @return string html code for adding an introduction
179
     */
180
    public static function display_introduction_section(
181
        $tool,
182
        $editor_config = null
183
    ) {
184
        echo self::return_introduction_section($tool, $editor_config);
0 ignored issues
show
Bug introduced by
Are you sure the usage of self::return_introductio...($tool, $editor_config) targeting Display::return_introduction_section() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
185
    }
186
187
    /**
188
     * @param string $tool
189
     * @param array  $editor_config
190
     */
191
    public static function return_introduction_section(
192
        $tool,
193
        $editor_config = null
194
    ) {
195
        $moduleId = $tool;
196
        if ('true' == api_get_setting('enable_tool_introduction') || TOOL_COURSE_HOMEPAGE == $tool) {
197
            $introduction_section = null;
198
            require api_get_path(SYS_CODE_PATH).'inc/introductionSection.inc.php';
199
200
            return $introduction_section;
201
        }
202
    }
203
204
    /**
205
     * Displays a table.
206
     *
207
     * @param array  $header          Titles for the table header
208
     *                                each item in this array can contain 3 values
209
     *                                - 1st element: the column title
210
     *                                - 2nd element: true or false (column sortable?)
211
     *                                - 3th element: additional attributes for
212
     *                                th-tag (eg for column-width)
213
     *                                - 4the element: additional attributes for the td-tags
214
     * @param array  $content         2D-array with the tables content
215
     * @param array  $sorting_options Keys are:
216
     *                                'column' = The column to use as sort-key
217
     *                                'direction' = SORT_ASC or SORT_DESC
218
     * @param array  $paging_options  Keys are:
219
     *                                'per_page_default' = items per page when switching from
220
     *                                full-    list to per-page-view
221
     *                                'per_page' = number of items to show per page
222
     *                                'page_nr' = The page to display
223
     * @param array  $query_vars      Additional variables to add in the query-string
224
     * @param array  $form_actions
225
     * @param string $style           The style that the table will show. You can set 'table' or 'grid'
226
     * @param string $tableName
227
     * @param string $tableId
228
     *
229
     * @author [email protected]
230
     */
231
    public static function display_sortable_table(
232
        $header,
233
        $content,
234
        $sorting_options = [],
235
        $paging_options = [],
236
        $query_vars = null,
237
        $form_actions = [],
238
        $style = 'table',
239
        $tableName = 'tablename',
240
        $tableId = ''
241
    ) {
242
        $column = isset($sorting_options['column']) ? $sorting_options['column'] : 0;
243
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
244
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $tableName, null, $tableId);
245
        if (is_array($query_vars)) {
246
            $table->set_additional_parameters($query_vars);
247
        }
248
        if ('table' == $style) {
249
            if (is_array($header) && count($header) > 0) {
250
                foreach ($header as $index => $header_item) {
251
                    $table->set_header(
252
                        $index,
253
                        isset($header_item[0]) ? $header_item[0] : null,
254
                        isset($header_item[1]) ? $header_item[1] : null,
255
                        isset($header_item[2]) ? $header_item[2] : null,
256
                        isset($header_item[3]) ? $header_item[3] : null
257
                    );
258
                }
259
            }
260
            $table->set_form_actions($form_actions);
261
            $table->display();
262
        } else {
263
            $table->display_grid();
264
        }
265
    }
266
267
    /**
268
     * Returns an HTML table with sortable column (through complete page refresh).
269
     *
270
     * @param array  $header
271
     * @param array  $content         Array of row arrays
272
     * @param array  $sorting_options
273
     * @param array  $paging_options
274
     * @param array  $query_vars
275
     * @param array  $form_actions
276
     * @param string $style
277
     *
278
     * @return string HTML string for array
279
     */
280
    public static function return_sortable_table(
281
        $header,
282
        $content,
283
        $sorting_options = [],
284
        $paging_options = [],
285
        $query_vars = null,
286
        $form_actions = [],
287
        $style = 'table'
288
    ) {
289
        ob_start();
290
        self::display_sortable_table(
291
            $header,
292
            $content,
293
            $sorting_options,
294
            $paging_options,
295
            $query_vars,
296
            $form_actions,
297
            $style
298
        );
299
        $content = ob_get_contents();
300
        ob_end_clean();
301
302
        return $content;
303
    }
304
305
    /**
306
     * Shows a nice grid.
307
     *
308
     * @param string grid name (important to create css)
309
     * @param array header content
310
     * @param array array with the information to show
311
     * @param array $paging_options Keys are:
312
     *                              'per_page_default' = items per page when switching from
313
     *                              full-    list to per-page-view
314
     *                              'per_page' = number of items to show per page
315
     *                              'page_nr' = The page to display
316
     *                              'hide_navigation' =  true to hide the navigation
317
     * @param array $query_vars     Additional variables to add in the query-string
318
     * @param array $form           actions Additional variables to add in the query-string
319
     * @param mixed An array with bool values to know which columns show.
320
     * i.e: $visibility_options= array(true, false) we will only show the first column
321
     *                Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
322
     */
323
    public static function display_sortable_grid(
324
        $name,
325
        $header,
326
        $content,
327
        $paging_options = [],
328
        $query_vars = null,
329
        $form_actions = [],
330
        $visibility_options = true,
331
        $sort_data = true,
332
        $grid_class = []
333
    ) {
334
        echo self::return_sortable_grid(
335
            $name,
336
            $header,
337
            $content,
338
            $paging_options,
339
            $query_vars,
340
            $form_actions,
341
            $visibility_options,
342
            $sort_data,
343
            $grid_class
344
        );
345
    }
346
347
    /**
348
     * Gets a nice grid in html string.
349
     *
350
     * @param string grid name (important to create css)
351
     * @param array header content
352
     * @param array array with the information to show
353
     * @param array $paging_options Keys are:
354
     *                              'per_page_default' = items per page when switching from
355
     *                              full-    list to per-page-view
356
     *                              'per_page' = number of items to show per page
357
     *                              'page_nr' = The page to display
358
     *                              'hide_navigation' =  true to hide the navigation
359
     * @param array $query_vars     Additional variables to add in the query-string
360
     * @param array $form           actions Additional variables to add in the query-string
361
     * @param mixed An array with bool values to know which columns show. i.e:
362
     *  $visibility_options= array(true, false) we will only show the first column
363
     *    Can be also only a bool value. TRUE: show all columns, FALSE: show nothing
364
     * @param bool  true for sorting data or false otherwise
365
     * @param array grid classes
366
     *
367
     * @return string html grid
368
     */
369
    public static function return_sortable_grid(
370
        $name,
371
        $header,
372
        $content,
373
        $paging_options = [],
374
        $query_vars = null,
375
        $form_actions = [],
376
        $visibility_options = true,
377
        $sort_data = true,
378
        $grid_class = [],
379
        $elementCount = 0
380
    ) {
381
        $column = 0;
382
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
383
        $table = new SortableTableFromArray($content, $column, $default_items_per_page, $name);
384
        $table->total_number_of_items = intval($elementCount);
385
        if (is_array($query_vars)) {
386
            $table->set_additional_parameters($query_vars);
387
        }
388
389
        return $table->display_simple_grid(
390
            $visibility_options,
391
            $paging_options['hide_navigation'],
392
            $default_items_per_page,
393
            $sort_data,
394
            $grid_class
395
        );
396
    }
397
398
    /**
399
     * Displays a table with a special configuration.
400
     *
401
     * @param array $header          Titles for the table header
402
     *                               each item in this array can contain 3 values
403
     *                               - 1st element: the column title
404
     *                               - 2nd element: true or false (column sortable?)
405
     *                               - 3th element: additional attributes for th-tag (eg for column-width)
406
     *                               - 4the element: additional attributes for the td-tags
407
     * @param array $content         2D-array with the tables content
408
     * @param array $sorting_options Keys are:
409
     *                               'column' = The column to use as sort-key
410
     *                               'direction' = SORT_ASC or SORT_DESC
411
     * @param array $paging_options  Keys are:
412
     *                               'per_page_default' = items per page when switching from full list to per-page-view
413
     *                               'per_page' = number of items to show per page
414
     *                               'page_nr' = The page to display
415
     * @param array $query_vars      Additional variables to add in the query-string
416
     * @param array $column_show     Array of binaries 1= show columns 0. hide a column
417
     * @param array $column_order    An array of integers that let us decide how the columns are going to be sort.
418
     *                               i.e:  $column_order=array('1''4','3','4'); The 2nd column will be order like the 4th column
419
     * @param array $form_actions    Set optional forms actions
420
     *
421
     * @author Julio Montoya
422
     */
423
    public static function display_sortable_config_table(
424
        $table_name,
425
        $header,
426
        $content,
427
        $sorting_options = [],
428
        $paging_options = [],
429
        $query_vars = null,
430
        $column_show = [],
431
        $column_order = [],
432
        $form_actions = []
433
    ) {
434
        $column = isset($sorting_options['column']) ? $sorting_options['column'] : 0;
435
        $default_items_per_page = isset($paging_options['per_page']) ? $paging_options['per_page'] : 20;
436
437
        $table = new SortableTableFromArrayConfig(
438
            $content,
439
            $column,
440
            $default_items_per_page,
441
            $table_name,
442
            $column_show,
443
            $column_order
444
        );
445
446
        if (is_array($query_vars)) {
447
            $table->set_additional_parameters($query_vars);
448
        }
449
        // Show or hide the columns header
450
        if (is_array($column_show)) {
451
            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...
452
                if (!empty($column_show[$i])) {
453
                    $val0 = isset($header[$i][0]) ? $header[$i][0] : null;
454
                    $val1 = isset($header[$i][1]) ? $header[$i][1] : null;
455
                    $val2 = isset($header[$i][2]) ? $header[$i][2] : null;
456
                    $val3 = isset($header[$i][3]) ? $header[$i][3] : null;
457
                    $table->set_header($i, $val0, $val1, $val2, $val3);
458
                }
459
            }
460
        }
461
        $table->set_form_actions($form_actions);
462
        $table->display();
463
    }
464
465
    /**
466
     * Returns a div html string with.
467
     *
468
     * @param string $message
469
     * @param string $type    Example: confirm, normal, warning, error
470
     * @param bool   $filter  Whether to XSS-filter or not
471
     *
472
     * @return string Message wrapped into an HTML div
473
     */
474
    public static function return_message(
475
        $message,
476
        $type = 'normal',
477
        $filter = true
478
    ) {
479
        if (empty($message)) {
480
            return '';
481
        }
482
483
        if ($filter) {
484
            $message = api_htmlentities(
485
                $message,
486
                ENT_QUOTES,
487
                api_is_xml_http_request() ? 'UTF-8' : api_get_system_encoding()
488
            );
489
        }
490
491
        $class = '';
492
        switch ($type) {
493
            case 'warning':
494
                $class .= 'alert alert-warning';
495
                break;
496
            case 'error':
497
                $class .= 'alert alert-danger';
498
                break;
499
            case 'confirmation':
500
            case 'confirm':
501
            case 'success':
502
                $class .= 'alert alert-success';
503
                break;
504
            case 'normal':
505
            default:
506
                $class .= 'alert alert-info';
507
        }
508
509
        return self::div($message, ['class' => $class]);
510
    }
511
512
    /**
513
     * Returns an encrypted mailto hyperlink.
514
     *
515
     * @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...
516
     * @param string  clickable text
517
     * @param string  optional, class from stylesheet
518
     * @param bool $addExtraContent
519
     *
520
     * @return string encrypted mailto hyperlink
521
     */
522
    public static function encrypted_mailto_link(
523
        $email,
524
        $clickable_text = null,
525
        $style_class = '',
526
        $addExtraContent = false
527
    ) {
528
        if (is_null($clickable_text)) {
529
            $clickable_text = $email;
530
        }
531
532
        // "mailto:" already present?
533
        if ('mailto:' !== substr($email, 0, 7)) {
534
            $email = 'mailto:'.$email;
535
        }
536
537
        // Class (stylesheet) defined?
538
        if ('' !== $style_class) {
539
            $style_class = ' class="'.$style_class.'"';
540
        }
541
542
        // Encrypt email
543
        $hmail = '';
544
        for ($i = 0; $i < strlen($email); $i++) {
545
            $hmail .= '&#'.ord($email[$i]).';';
546
        }
547
548
        $value = api_get_configuration_value('add_user_course_information_in_mailto');
549
550
        if ($value) {
551
            if ('false' === api_get_setting('allow_email_editor')) {
552
                $hmail .= '?';
553
            }
554
555
            if (!api_is_anonymous()) {
556
                $hmail .= '&subject='.Security::remove_XSS(api_get_setting('siteName'));
557
            }
558
            if ($addExtraContent) {
559
                $content = '';
560
                if (!api_is_anonymous()) {
561
                    $userInfo = api_get_user_info();
562
                    $content .= get_lang('User').': '.$userInfo['complete_name']."\n";
563
564
                    $courseInfo = api_get_course_info();
565
                    if (!empty($courseInfo)) {
566
                        $content .= get_lang('Course').': ';
567
                        $content .= $courseInfo['name'];
568
                        $sessionInfo = api_get_session_info(api_get_session_id());
569
                        if (!empty($sessionInfo)) {
570
                            $content .= ' '.$sessionInfo['name'].' <br />';
571
                        }
572
                    }
573
                }
574
                $hmail .= '&body='.rawurlencode($content);
575
            }
576
        }
577
578
        $hclickable_text = '';
579
        // Encrypt clickable text if @ is present
580
        if (strpos($clickable_text, '@')) {
581
            for ($i = 0; $i < strlen($clickable_text); $i++) {
582
                $hclickable_text .= '&#'.ord($clickable_text[$i]).';';
583
            }
584
        } else {
585
            $hclickable_text = @htmlspecialchars(
586
                $clickable_text,
587
                ENT_QUOTES,
588
                api_get_system_encoding()
589
            );
590
        }
591
        // Return encrypted mailto hyperlink
592
        return '<a href="'.$hmail.'"'.$style_class.' class="clickable_email_link">'.$hclickable_text.'</a>';
593
    }
594
595
    /**
596
     * Returns an mailto icon hyperlink.
597
     *
598
     * @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...
599
     * @param string  icon source file from the icon lib
600
     * @param int  icon size from icon lib
601
     * @param string  optional, class from stylesheet
602
     *
603
     * @return string encrypted mailto hyperlink
604
     */
605
    public static function icon_mailto_link(
606
        $email,
607
        $icon_file = "mail.png",
608
        $icon_size = 22,
609
        $style_class = ''
610
    ) {
611
        // "mailto:" already present?
612
        if ('mailto:' != substr($email, 0, 7)) {
613
            $email = 'mailto:'.$email;
614
        }
615
        // Class (stylesheet) defined?
616
        if ('' != $style_class) {
617
            $style_class = ' class="'.$style_class.'"';
618
        }
619
        // Encrypt email
620
        $hmail = '';
621
        for ($i = 0; $i < strlen($email); $i++) {
622
            $hmail .= '&#'.ord($email[
623
            $i]).';';
624
        }
625
        // icon html code
626
        $icon_html_source = self::return_icon(
627
            $icon_file,
628
            $hmail,
629
            '',
630
            $icon_size
631
        );
632
        // Return encrypted mailto hyperlink
633
634
        return '<a href="'.$hmail.'"'.$style_class.' class="clickable_email_link">'.$icon_html_source.'</a>';
635
    }
636
637
    /**
638
     * Prints an <option>-list with all letters (A-Z).
639
     *
640
     * @param string $selected_letter The letter that should be selected
641
     *
642
     * @todo This is English language specific implementation.
643
     * It should be adapted for the other languages.
644
     *
645
     * @return string
646
     */
647
    public static function get_alphabet_options($selectedLetter = '')
648
    {
649
        $result = '';
650
        for ($i = 65; $i <= 90; $i++) {
651
            $letter = chr($i);
652
            $result .= '<option value="'.$letter.'"';
653
            if ($selectedLetter == $letter) {
654
                $result .= ' selected="selected"';
655
            }
656
            $result .= '>'.$letter.'</option>';
657
        }
658
659
        return $result;
660
    }
661
662
    /**
663
     * Get the options withing a select box within the given values.
664
     *
665
     * @param int   Min value
666
     * @param int   Max value
667
     * @param int   Default value
668
     *
669
     * @return string HTML select options
670
     */
671
    public static function get_numeric_options($min, $max, $selected_num = 0)
672
    {
673
        $result = '';
674
        for ($i = $min; $i <= $max; $i++) {
675
            $result .= '<option value="'.$i.'"';
676
            if (is_int($selected_num)) {
677
                if ($selected_num == $i) {
678
                    $result .= ' selected="selected"';
679
                }
680
            }
681
            $result .= '>'.$i.'</option>';
682
        }
683
684
        return $result;
685
    }
686
687
    /**
688
     * This public function displays an icon.
689
     *
690
     * @param string   The filename of the file (in the main/img/ folder
691
     * @param string   The alt text (probably a language variable)
692
     * @param array    additional attributes (for instance height, width, onclick, ...)
693
     * @param int  The wanted width of the icon (to be looked for in the corresponding img/icons/ folder)
694
     */
695
    public static function display_icon(
696
        $image,
697
        $alt_text = '',
698
        $additional_attributes = [],
699
        $size = null
700
    ) {
701
        echo self::return_icon($image, $alt_text, $additional_attributes, $size);
702
    }
703
704
    /**
705
     * Gets the path of an icon.
706
     *
707
     * @param string $icon
708
     * @param int    $size
709
     *
710
     * @return string
711
     */
712
    public static function returnIconPath($icon, $size = ICON_SIZE_SMALL)
713
    {
714
        return self::return_icon($icon, null, null, $size, null, true, false);
715
    }
716
717
    /**
718
     * This public function returns the htmlcode for an icon.
719
     *
720
     * @param string   The filename of the file (in the main/img/ folder
721
     * @param string   The alt text (probably a language variable)
722
     * @param array    Additional attributes (for instance height, width, onclick, ...)
723
     * @param int  The wanted width of the icon (to be looked for in the corresponding img/icons/ folder)
724
     *
725
     * @return string An HTML string of the right <img> tag
726
     *
727
     * @author Patrick Cool <[email protected]>, Ghent University 2006
728
     * @author Julio Montoya 2010 Function improved, adding image constants
729
     * @author Yannick Warnier 2011 Added size handler
730
     *
731
     * @version Feb 2011
732
     */
733
    public static function return_icon(
734
        $image,
735
        $alt_text = '',
736
        $additional_attributes = [],
737
        $size = ICON_SIZE_SMALL,
738
        $show_text = true,
739
        $return_only_path = false,
740
        $loadThemeIcon = true
741
    ) {
742
        $code_path = api_get_path(SYS_PUBLIC_PATH);
743
        $w_code_path = api_get_path(WEB_PUBLIC_PATH);
744
        // The following path is checked to see if the file exist. It's
745
        // important to use the public path (i.e. web/css/) rather than the
746
        // internal path (/app/Resource/public/css/) because the path used
747
        // in the end must be the public path
748
        $alternateCssPath = api_get_path(SYS_PUBLIC_PATH).'css/';
749
        $alternateWebCssPath = api_get_path(WEB_PUBLIC_PATH).'css/';
750
751
        // Avoid issues with illegal string offset for legacy calls to this
752
        // method with an empty string rather than null or an empty array
753
        if (empty($additional_attributes)) {
754
            $additional_attributes = [];
755
        }
756
757
        $image = trim($image);
758
759
        if (isset($size)) {
760
            $size = (int) $size;
761
        } else {
762
            $size = ICON_SIZE_SMALL;
763
        }
764
765
        $size_extra = $size.'/';
766
        $icon = $w_code_path.'img/'.$image;
767
        $theme = 'themes/chamilo/icons/';
768
769
        if ($loadThemeIcon) {
770
            // @todo with chamilo 2 code
771
            $theme = 'themes/'.api_get_visual_theme().'/icons/';
772
            if (is_file($alternateCssPath.$theme.$image)) {
773
                $icon = $alternateWebCssPath.$theme.$image;
774
            }
775
            // Checking the theme icons folder example: app/Resources/public/css/themes/chamilo/icons/XXX
776
            if (is_file($alternateCssPath.$theme.$size_extra.$image)) {
777
                $icon = $alternateWebCssPath.$theme.$size_extra.$image;
778
            } elseif (is_file($code_path.'img/icons/'.$size_extra.$image)) {
779
                //Checking the main/img/icons/XXX/ folder
780
                $icon = $w_code_path.'img/icons/'.$size_extra.$image;
781
            }
782
        } else {
783
            if (is_file($code_path.'img/icons/'.$size_extra.$image)) {
784
                // Checking the main/img/icons/XXX/ folder
785
                $icon = $w_code_path.'img/icons/'.$size_extra.$image;
786
            }
787
        }
788
789
        // Special code to enable SVG - refs #7359 - Needs more work
790
        // The code below does something else to "test out" SVG: for each icon,
791
        // it checks if there is an SVG version. If so, it uses it.
792
        // When moving this to production, the return_icon() calls should
793
        // ask for the SVG version directly
794
        $svgIcons = api_get_setting('icons_mode_svg');
795
        if ('true' == $svgIcons && false == $return_only_path) {
796
            $svgImage = substr($image, 0, -3).'svg';
797
            if (is_file($code_path.$theme.'svg/'.$svgImage)) {
798
                $icon = $w_code_path.$theme.'svg/'.$svgImage;
799
            } elseif (is_file($code_path.'img/icons/svg/'.$svgImage)) {
800
                $icon = $w_code_path.'img/icons/svg/'.$svgImage;
801
            }
802
803
            if (empty($additional_attributes['height'])) {
804
                $additional_attributes['height'] = $size;
805
            }
806
            if (empty($additional_attributes['width'])) {
807
                $additional_attributes['width'] = $size;
808
            }
809
        }
810
811
        if ($return_only_path) {
812
            return $icon;
813
        }
814
815
        $img = self::img($icon, $alt_text, $additional_attributes);
816
        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...
817
            if ($show_text) {
818
                $img = "$img $alt_text";
819
            }
820
        }
821
822
        return $img;
823
    }
824
825
    /**
826
     * Returns the htmlcode for an image.
827
     *
828
     * @param string $image_path            the filename of the file (in the main/img/ folder
829
     * @param string $alt_text              the alt text (probably a language variable)
830
     * @param array  $additional_attributes (for instance height, width, onclick, ...)
831
     * @param bool   $filterPath            Optional. Whether filter the image path. Default is true
832
     *
833
     * @return string
834
     *
835
     * @author Julio Montoya 2010
836
     */
837
    public static function img(
838
        $image_path,
839
        $alt_text = '',
840
        $additional_attributes = null,
841
        $filterPath = true
842
    ) {
843
        if (empty($image_path)) {
844
            // For some reason, the call to img() happened without a proper
845
            // image. Log the error and return an empty string to avoid
846
            // breaking the HTML
847
            $trace = debug_backtrace();
848
            $caller = $trace[1];
849
            //error_log('No image provided in Display::img(). Caller info: '.print_r($caller, 1));
850
            return '';
851
        }
852
        // Sanitizing the parameter $image_path
853
        if ($filterPath) {
854
            $image_path = Security::filter_img_path($image_path);
855
        }
856
857
        // alt text = the image name if there is none provided (for XHTML compliance)
858
        if ('' == $alt_text) {
859
            $alt_text = basename($image_path);
860
        }
861
862
        if (empty($additional_attributes)) {
863
            $additional_attributes = [];
864
        }
865
866
        $additional_attributes['src'] = $image_path;
867
868
        if (empty($additional_attributes['alt'])) {
869
            $additional_attributes['alt'] = $alt_text;
870
        }
871
        if (empty($additional_attributes['title'])) {
872
            $additional_attributes['title'] = $alt_text;
873
        }
874
875
        return self::tag('img', '', $additional_attributes);
876
    }
877
878
    /**
879
     * Returns the htmlcode for a tag (h3, h1, div, a, button), etc.
880
     *
881
     * @param string $tag                   the tag name
882
     * @param string $content               the tag's content
883
     * @param array  $additional_attributes (for instance height, width, onclick, ...)
884
     *
885
     * @return string
886
     *
887
     * @author Julio Montoya 2010
888
     */
889
    public static function tag($tag, $content, $additional_attributes = [])
890
    {
891
        $attribute_list = '';
892
        // Managing the additional attributes
893
        if (!empty($additional_attributes) && is_array($additional_attributes)) {
894
            $attribute_list = '';
895
            foreach ($additional_attributes as $key => &$value) {
896
                $attribute_list .= $key.'="'.$value.'" ';
897
            }
898
        }
899
        //some tags don't have this </XXX>
900
        if (in_array($tag, ['img', 'input', 'br'])) {
901
            $return_value = '<'.$tag.' '.$attribute_list.' />';
902
        } else {
903
            $return_value = '<'.$tag.' '.$attribute_list.' >'.$content.'</'.$tag.'>';
904
        }
905
906
        return $return_value;
907
    }
908
909
    /**
910
     * Creates a URL anchor.
911
     *
912
     * @param string $name
913
     * @param string $url
914
     * @param array  $attributes
915
     *
916
     * @return string
917
     */
918
    public static function url($name, $url, $attributes = [])
919
    {
920
        if (!empty($url)) {
921
            $url = preg_replace('#&amp;#', '&', $url);
922
            $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
923
            $attributes['href'] = $url;
924
        }
925
926
        return self::tag('a', $name, $attributes);
927
    }
928
929
    /**
930
     * Creates a div tag.
931
     *
932
     * @param string $content
933
     * @param array  $attributes
934
     *
935
     * @return string
936
     */
937
    public static function div($content, $attributes = [])
938
    {
939
        return self::tag('div', $content, $attributes);
940
    }
941
942
    /**
943
     * Creates a span tag.
944
     */
945
    public static function span($content, $attributes = [])
946
    {
947
        return self::tag('span', $content, $attributes);
948
    }
949
950
    /**
951
     * Displays an HTML input tag.
952
     */
953
    public static function input($type, $name, $value, $attributes = [])
954
    {
955
        if (isset($type)) {
956
            $attributes['type'] = $type;
957
        }
958
        if (isset($name)) {
959
            $attributes['name'] = $name;
960
        }
961
        if (isset($value)) {
962
            $attributes['value'] = $value;
963
        }
964
965
        return self::tag('input', '', $attributes);
966
    }
967
968
    /**
969
     * @param $name
970
     * @param $value
971
     * @param array $attributes
972
     *
973
     * @return string
974
     */
975
    public static function button($name, $value, $attributes = [])
976
    {
977
        if (!empty($name)) {
978
            $attributes['name'] = $name;
979
        }
980
981
        return self::tag('button', $value, $attributes);
982
    }
983
984
    /**
985
     * Displays an HTML select tag.
986
     *
987
     * @param string $name
988
     * @param array  $values
989
     * @param int    $default
990
     * @param array  $extra_attributes
991
     * @param bool   $show_blank_item
992
     * @param string $blank_item_text
993
     *
994
     * @return string
995
     */
996
    public static function select(
997
        $name,
998
        $values,
999
        $default = -1,
1000
        $extra_attributes = [],
1001
        $show_blank_item = true,
1002
        $blank_item_text = ''
1003
    ) {
1004
        $html = '';
1005
        $extra = '';
1006
        $default_id = 'id="'.$name.'" ';
1007
        $extra_attributes = array_merge(['class' => 'form-control'], $extra_attributes);
1008
        foreach ($extra_attributes as $key => $parameter) {
1009
            if ('id' == $key) {
1010
                $default_id = '';
1011
            }
1012
            $extra .= $key.'="'.$parameter.'" ';
1013
        }
1014
        $html .= '<select name="'.$name.'" '.$default_id.' '.$extra.'>';
1015
1016
        if ($show_blank_item) {
1017
            if (empty($blank_item_text)) {
1018
                $blank_item_text = get_lang('Select');
1019
            } else {
1020
                $blank_item_text = Security::remove_XSS($blank_item_text);
1021
            }
1022
            $html .= self::tag(
1023
                'option',
1024
                '-- '.$blank_item_text.' --',
1025
                ['value' => '-1']
1026
            );
1027
        }
1028
        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...
1029
            foreach ($values as $key => $value) {
1030
                if (is_array($value) && isset($value['name'])) {
1031
                    $value = $value['name'];
1032
                }
1033
                $html .= '<option value="'.$key.'"';
1034
1035
                if (is_array($default)) {
1036
                    foreach ($default as $item) {
1037
                        if ($item == $key) {
1038
                            $html .= ' selected="selected"';
1039
                            break;
1040
                        }
1041
                    }
1042
                } else {
1043
                    if ($default == $key) {
1044
                        $html .= ' selected="selected"';
1045
                    }
1046
                }
1047
1048
                $html .= '>'.$value.'</option>';
1049
            }
1050
        }
1051
        $html .= '</select>';
1052
1053
        return $html;
1054
    }
1055
1056
    /**
1057
     * Creates a tab menu
1058
     * Requirements: declare the jquery, jquery-ui libraries + the jquery-ui.css
1059
     * in the $htmlHeadXtra variable before the display_header
1060
     * Add this script.
1061
     *
1062
     * @example
1063
     * <script>
1064
                </script>
1065
     * @param array  $headers       list of the tab titles
1066
     * @param array  $items
1067
     * @param string $id            id of the container of the tab in the example "tabs"
1068
     * @param array  $attributes    for the ul
1069
     * @param array  $ul_attributes
1070
     * @param string $selected
1071
     *
1072
     * @return string
1073
     */
1074
    public static function tabs(
1075
        $headers,
1076
        $items,
1077
        $id = 'tabs',
1078
        $attributes = [],
1079
        $ul_attributes = [],
1080
        $selected = ''
1081
    ) {
1082
        if (empty($headers) || 0 == count($headers)) {
1083
            return '';
1084
        }
1085
1086
        $lis = '';
1087
        $i = 1;
1088
        foreach ($headers as $item) {
1089
            $active = '';
1090
            if (1 == $i) {
1091
                $active = ' active';
1092
            }
1093
1094
            if (!empty($selected)) {
1095
                $active = '';
1096
                if ($selected == $i) {
1097
                    $active = ' active';
1098
                }
1099
            }
1100
1101
            $item = self::tag(
1102
                'a',
1103
                $item,
1104
                [
1105
                    'href' => '#'.$id.'-'.$i,
1106
                    'class' => 'nav-item nav-link '.$active,
1107
                    'id' => $id.$i.'-tab',
1108
                    'data-toggle' => 'tab',
1109
                    'role' => 'tab',
1110
                    'aria-controls' => $id.'-'.$i,
1111
                    'aria-selected' => $selected,
1112
                ]
1113
            );
1114
            $lis .= $item;
1115
            $i++;
1116
        }
1117
1118
        $ul = self::tag(
1119
            'nav',
1120
            $lis,
1121
            [
1122
                'id' => 'ul_'.$id,
1123
                'class' => 'nav nav-tabs',
1124
                'role' => 'tablist',
1125
            ]
1126
        );
1127
1128
        $i = 1;
1129
        $divs = '';
1130
        foreach ($items as $content) {
1131
            $active = '';
1132
            if (1 == $i) {
1133
                $active = ' show active';
1134
            }
1135
1136
            if (!empty($selected)) {
1137
                $active = '';
1138
                if ($selected == $i) {
1139
                    $active = ' show active';
1140
                }
1141
            }
1142
1143
            $divs .= self::tag(
1144
                'div',
1145
                $content,
1146
                [
1147
                    'id' => $id.'-'.$i,
1148
                    'class' => 'tab-pane fade '.$active,
1149
                    'role' => 'tabpanel',
1150
                    'aria-labelledby' => $id.$i.'-tab',
1151
                ]
1152
            );
1153
            $i++;
1154
        }
1155
1156
        $attributes['id'] = $id;
1157
        $attributes['class'] = 'tab_wrapper';
1158
1159
        $html = self::tag(
1160
            'div',
1161
            $ul.
1162
            self::tag('div', $divs, ['class' => 'tab-content']),
1163
            $attributes
1164
        );
1165
1166
        return $html;
1167
    }
1168
1169
    /**
1170
     * @param $headers
1171
     * @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...
1172
     *
1173
     * @return string
1174
     */
1175
    public static function tabsOnlyLink($headers, $selected = null)
1176
    {
1177
        $id = uniqid();
1178
        $i = 1;
1179
        $lis = null;
1180
        foreach ($headers as $item) {
1181
            $class = null;
1182
            if ($i == $selected) {
1183
                $class = 'active';
1184
            }
1185
            $item = self::tag(
1186
                'a',
1187
                $item['content'],
1188
                [
1189
                    'id' => $id.'-'.$i,
1190
                    'href' => $item['url'],
1191
                    'class' => 'nav-link '.$class,
1192
                ]
1193
            );
1194
            $lis .= self::tag('li', $item, ['class' => 'nav-item']);
1195
            $i++;
1196
        }
1197
1198
        return self::tag(
1199
            'ul',
1200
            $lis,
1201
            ['class' => 'nav nav-tabs tabs-margin']
1202
        );
1203
    }
1204
1205
    /**
1206
     * In order to display a grid using jqgrid you have to:.
1207
     *
1208
     * @example
1209
     * After your Display::display_header function you have to add the nex javascript code:
1210
     * <script>
1211
     *   echo Display::grid_js('my_grid_name', $url,$columns, $column_model, $extra_params,[]);
1212
     *   // for more information of this function check the grid_js() function
1213
     * </script>
1214
     * //Then you have to call the grid_html
1215
     * echo Display::grid_html('my_grid_name');
1216
     * As you can see both function use the same "my_grid_name" this is very important otherwise nothing will work
1217
     *
1218
     * @param   string  the div id, this value must be the same with the first parameter of Display::grid_js()
1219
     *
1220
     * @return string html
1221
     */
1222
    public static function grid_html($div_id)
1223
    {
1224
        $table = self::tag('table', '', ['id' => $div_id]);
1225
        $table .= self::tag('div', '', ['id' => $div_id.'_pager']);
1226
1227
        return $table;
1228
    }
1229
1230
    /**
1231
     * @param string $label
1232
     * @param string $form_item
1233
     *
1234
     * @return string
1235
     */
1236
    public static function form_row($label, $form_item)
1237
    {
1238
        $label = self::tag('label', $label, ['class' => 'col-sm-2 control-label']);
1239
        $form_item = self::div($form_item, ['class' => 'col-sm-10']);
1240
1241
        return self::div($label.$form_item, ['class' => 'form-group']);
1242
    }
1243
1244
    /**
1245
     * This is a wrapper to use the jqgrid in Chamilo.
1246
     * For the other jqgrid options visit http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options
1247
     * This function need to be in the ready jquery function
1248
     * example --> $(function() { <?php echo Display::grid_js('grid' ...); ?> }
1249
     * In order to work this function needs the Display::grid_html function with the same div id.
1250
     *
1251
     * @param string $div_id       div id
1252
     * @param string $url          url where the jqgrid will ask for data (if datatype = json)
1253
     * @param array  $column_names Visible columns (you should use get_lang).
1254
     *                             An array in which we place the names of the columns.
1255
     *                             This is the text that appears in the head of the grid (Header layer).
1256
     *                             Example: colname   {name:'date',     index:'date',   width:120, align:'right'},
1257
     * @param array  $column_model the column model :  Array which describes the parameters of the columns.
1258
     *                             This is the most important part of the grid.
1259
     *                             For a full description of all valid values see colModel API. See the url above.
1260
     * @param array  $extra_params extra parameters
1261
     * @param array  $data         data that will be loaded
1262
     * @param string $formatter    A string that will be appended to the JSON returned
1263
     * @param bool   $fixed_width  not implemented yet
1264
     *
1265
     * @return string the js code
1266
     */
1267
    public static function grid_js(
1268
        $div_id,
1269
        $url,
1270
        $column_names,
1271
        $column_model,
1272
        $extra_params,
1273
        $data = [],
1274
        $formatter = '',
1275
        $fixed_width = false
1276
    ) {
1277
        $obj = new stdClass();
1278
        $obj->first = 'first';
1279
1280
        if (!empty($url)) {
1281
            $obj->url = $url;
1282
        }
1283
1284
        // Needed it in order to render the links/html in the grid
1285
        foreach ($column_model as &$columnModel) {
1286
            if (!isset($columnModel['formatter'])) {
1287
                $columnModel['formatter'] = '';
1288
            }
1289
        }
1290
1291
        //This line should only be used/modified in case of having characters
1292
        // encoding problems - see #6159
1293
        //$column_names = array_map("utf8_encode", $column_names);
1294
        $obj->colNames = $column_names;
1295
        $obj->colModel = $column_model;
1296
        $obj->pager = '#'.$div_id.'_pager';
1297
        $obj->datatype = 'json';
1298
        $obj->viewrecords = 'true';
1299
        $obj->guiStyle = 'bootstrap4';
1300
        $obj->iconSet = 'fontAwesomeSolid';
1301
        $all_value = 10000000;
1302
1303
        // Sets how many records we want to view in the grid
1304
        $obj->rowNum = 20;
1305
1306
        // Default row quantity
1307
        if (!isset($extra_params['rowList'])) {
1308
            $extra_params['rowList'] = [20, 50, 100, 500, 1000, $all_value];
1309
            $rowList = api_get_configuration_value('table_row_list');
1310
            if (!empty($rowList) && isset($rowList['options'])) {
1311
                $rowList = $rowList['options'];
1312
                $rowList[] = $all_value;
1313
            }
1314
            $extra_params['rowList'] = $rowList;
1315
        }
1316
1317
        $defaultRow = api_get_configuration_value('table_default_row');
1318
        if (!empty($defaultRow)) {
1319
            $obj->rowNum = (int) $defaultRow;
1320
        }
1321
1322
        $json = '';
1323
        if (!empty($extra_params['datatype'])) {
1324
            $obj->datatype = $extra_params['datatype'];
1325
        }
1326
1327
        // Row even odd style.
1328
        $obj->altRows = true;
1329
        if (!empty($extra_params['altRows'])) {
1330
            $obj->altRows = $extra_params['altRows'];
1331
        }
1332
1333
        if (!empty($extra_params['sortname'])) {
1334
            $obj->sortname = $extra_params['sortname'];
1335
        }
1336
1337
        if (!empty($extra_params['sortorder'])) {
1338
            $obj->sortorder = $extra_params['sortorder'];
1339
        }
1340
1341
        if (!empty($extra_params['rowList'])) {
1342
            $obj->rowList = $extra_params['rowList'];
1343
        }
1344
1345
        if (!empty($extra_params['rowNum'])) {
1346
            $obj->rowNum = $extra_params['rowNum'];
1347
        } else {
1348
            // Try to load max rows from Session
1349
            $urlInfo = parse_url($url);
1350
            if (isset($urlInfo['query'])) {
1351
                parse_str($urlInfo['query'], $query);
1352
                if (isset($query['a'])) {
1353
                    $action = $query['a'];
1354
                    // This value is set in model.ajax.php
1355
                    $savedRows = Session::read('max_rows_'.$action);
1356
                    if (!empty($savedRows)) {
1357
                        $obj->rowNum = $savedRows;
1358
                    }
1359
                }
1360
            }
1361
        }
1362
1363
        if (!empty($extra_params['viewrecords'])) {
1364
            $obj->viewrecords = $extra_params['viewrecords'];
1365
        }
1366
1367
        $beforeSelectRow = null;
1368
        if (isset($extra_params['beforeSelectRow'])) {
1369
            $beforeSelectRow = 'beforeSelectRow: '.$extra_params['beforeSelectRow'].', ';
1370
            unset($extra_params['beforeSelectRow']);
1371
        }
1372
1373
        $beforeProcessing = '';
1374
        if (isset($extra_params['beforeProcessing'])) {
1375
            $beforeProcessing = 'beforeProcessing : function() { '.$extra_params['beforeProcessing'].' },';
1376
            unset($extra_params['beforeProcessing']);
1377
        }
1378
1379
        $beforeRequest = '';
1380
        if (isset($extra_params['beforeRequest'])) {
1381
            $beforeRequest = 'beforeRequest : function() { '.$extra_params['beforeRequest'].' },';
1382
            unset($extra_params['beforeRequest']);
1383
        }
1384
1385
        $gridComplete = '';
1386
        if (isset($extra_params['gridComplete'])) {
1387
            $gridComplete = 'gridComplete : function() { '.$extra_params['gridComplete'].' },';
1388
            unset($extra_params['gridComplete']);
1389
        }
1390
1391
        // Adding extra params
1392
        if (!empty($extra_params)) {
1393
            foreach ($extra_params as $key => $element) {
1394
                // the groupHeaders key gets a special treatment
1395
                if ('groupHeaders' != $key) {
1396
                    $obj->$key = $element;
1397
                }
1398
            }
1399
        }
1400
1401
        // Adding static data.
1402
        if (!empty($data)) {
1403
            $data_var = $div_id.'_data';
1404
            $json .= ' var '.$data_var.' = '.json_encode($data).';';
1405
            $obj->data = $data_var;
1406
            $obj->datatype = 'local';
1407
            $json .= "\n";
1408
        }
1409
1410
        $obj->end = 'end';
1411
1412
        $json_encode = json_encode($obj);
1413
1414
        if (!empty($data)) {
1415
            //Converts the "data":"js_variable" to "data":js_variable,
1416
            // otherwise it will not work
1417
            $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...
1418
        }
1419
1420
        // Fixing true/false js values that doesn't need the ""
1421
        $json_encode = str_replace(':"true"', ':true', $json_encode);
1422
        // wrap_cell is not a valid jqgrid attributes is a hack to wrap a text
1423
        $json_encode = str_replace('"wrap_cell":true', 'cellattr : function(rowId, value, rowObject, colModel, arrData) { return \'class = "jqgrid_whitespace"\'; }', $json_encode);
1424
        $json_encode = str_replace(':"false"', ':false', $json_encode);
1425
        $json_encode = str_replace('"formatter":"action_formatter"', 'formatter:action_formatter', $json_encode);
1426
        $json_encode = str_replace('"formatter":"extra_formatter"', 'formatter:extra_formatter', $json_encode);
1427
        $json_encode = str_replace(['{"first":"first",', '"end":"end"}'], '', $json_encode);
1428
1429
        if (api_get_configuration_value('allow_compilatio_tool') &&
1430
            (false !== strpos($_SERVER['REQUEST_URI'], 'work/work.php') ||
1431
             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...
1432
            )
1433
        ) {
1434
            $json_encode = str_replace('"function () { compilatioInit() }"',
1435
                'function () { compilatioInit() }',
1436
                $json_encode
1437
            );
1438
        }
1439
        // Creating the jqgrid element.
1440
        $json .= '$("#'.$div_id.'").jqGrid({';
1441
        //$json .= $beforeSelectRow;
1442
        $json .= $gridComplete;
1443
        $json .= $beforeProcessing;
1444
        $json .= $beforeRequest;
1445
        $json .= $json_encode;
1446
        $json .= '});';
1447
1448
        // Grouping headers option
1449
        if (isset($extra_params['groupHeaders'])) {
1450
            $groups = '';
1451
            foreach ($extra_params['groupHeaders'] as $group) {
1452
                //{ "startColumnName" : "courses", "numberOfColumns" : 1, "titleText" : "Order Info" },
1453
                $groups .= '{ "startColumnName" : "'.$group['startColumnName'].'", "numberOfColumns" : '.$group['numberOfColumns'].', "titleText" : "'.$group['titleText'].'" },';
1454
            }
1455
            $json .= '$("#'.$div_id.'").jqGrid("setGroupHeaders", {
1456
                "useColSpanStyle" : false,
1457
                "groupHeaders"    : [
1458
                    '.$groups.'
1459
                ]
1460
            });';
1461
        }
1462
1463
        $all_text = addslashes(get_lang('All'));
1464
        $json .= '$("'.$obj->pager.' option[value='.$all_value.']").text("'.$all_text.'");';
1465
        $json .= "\n";
1466
        // Adding edit/delete icons.
1467
        $json .= $formatter;
1468
1469
        return $json;
1470
    }
1471
1472
    /**
1473
     * @param array $headers
1474
     * @param array $rows
1475
     * @param array $attributes
1476
     *
1477
     * @return string
1478
     */
1479
    public static function table($headers, $rows, $attributes = [])
1480
    {
1481
        if (empty($attributes)) {
1482
            $attributes['class'] = 'data_table';
1483
        }
1484
        $table = new HTML_Table($attributes);
1485
        $row = 0;
1486
        $column = 0;
1487
1488
        // Course headers
1489
        if (!empty($headers)) {
1490
            foreach ($headers as $item) {
1491
                $table->setHeaderContents($row, $column, $item);
1492
                $column++;
1493
            }
1494
            $row = 1;
1495
            $column = 0;
1496
        }
1497
1498
        if (!empty($rows)) {
1499
            foreach ($rows as $content) {
1500
                $table->setCellContents($row, $column, $content);
1501
                $row++;
1502
            }
1503
        }
1504
1505
        return $table->toHtml();
1506
    }
1507
1508
    /**
1509
     * Returns the "what's new" icon notifications.
1510
     *
1511
     * The general logic of this function is to track the last time the user
1512
     * entered the course and compare to what has changed inside this course
1513
     * since then, based on the item_property table inside this course. Note that,
1514
     * if the user never entered the course before, he will not see notification
1515
     * icons. This function takes session ID into account (if any) and only shows
1516
     * the corresponding notifications.
1517
     *
1518
     * @param array $courseInfo Course information array, containing at least elements 'db' and 'k'
1519
     * @param bool  $loadAjax
1520
     *
1521
     * @return string The HTML link to be shown next to the course
1522
     */
1523
    public static function show_notification($courseInfo, $loadAjax = true)
1524
    {
1525
        if (empty($courseInfo)) {
1526
            return '';
1527
        }
1528
1529
        return '';
1530
1531
        $t_track_e_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
0 ignored issues
show
Unused Code introduced by
$t_track_e_access = Data...TIC_TRACK_E_LASTACCESS) is not reachable.

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

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

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

    return false;
}

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

Loading history...
1532
        $course_tool_table = Database::get_course_table(TABLE_TOOL_LIST);
1533
        $tool_edit_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
1534
        $course_code = Database::escape_string($courseInfo['code']);
1535
1536
        $user_id = api_get_user_id();
1537
        $course_id = (int) $courseInfo['real_id'];
1538
        $sessionId = (int) $courseInfo['id_session'];
1539
        $status = (int) $courseInfo['status'];
1540
1541
        $loadNotificationsByAjax = api_get_configuration_value('user_portal_load_notification_by_ajax');
1542
1543
        if ($loadNotificationsByAjax) {
1544
            if ($loadAjax) {
1545
                $id = 'notification_'.$course_id.'_'.$sessionId.'_'.$status;
1546
                Session::write($id, true);
1547
1548
                return '<span id ="'.$id.'" class="course_notification"></span>';
1549
            }
1550
        }
1551
1552
        // Get the user's last access dates to all tools of this course
1553
        $sql = "SELECT *
1554
                FROM $t_track_e_access
1555
                WHERE
1556
                    c_id = $course_id AND
1557
                    access_user_id = '$user_id' AND
1558
                    access_session_id ='".$sessionId."'
1559
                ORDER BY access_date DESC
1560
                LIMIT 1
1561
                ";
1562
        $result = Database::query($sql);
1563
1564
        // latest date by default is the creation date
1565
        $latestDate = $courseInfo['creation_date'];
1566
        if (Database::num_rows($result)) {
1567
            $row = Database::fetch_array($result, 'ASSOC');
1568
            $latestDate = $row['access_date'];
1569
        }
1570
1571
        $sessionCondition = api_get_session_condition(
1572
            $sessionId,
1573
            true,
1574
            false,
1575
            'session_id'
1576
        );
1577
1578
        $hideTools = [TOOL_NOTEBOOK, TOOL_CHAT];
1579
        // Get current tools in course
1580
        $sql = "SELECT name
1581
                FROM $course_tool_table
1582
                WHERE
1583
                    c_id = $course_id AND
1584
                    visibility = '1' AND
1585
                    name NOT IN ('".implode("','", $hideTools)." ')
1586
                ";
1587
        $result = Database::query($sql);
1588
        $tools = Database::store_result($result);
1589
1590
        $group_ids = GroupManager::get_group_ids($courseInfo['real_id'], $user_id);
1591
        $group_ids[] = 0; //add group 'everyone'
1592
        $notifications = [];
1593
        if ($tools) {
1594
            foreach ($tools as $tool) {
1595
                $toolName = $tool['name'];
1596
                $toolName = Database::escape_string($toolName);
1597
                // Fix to get student publications
1598
                $toolCondition = " tool = '$toolName' AND ";
1599
                if ('student_publication' == $toolName || 'work' == $toolName) {
1600
                    $toolCondition = " (tool = 'work' OR tool = 'student_publication') AND ";
1601
                }
1602
1603
                $toolName = addslashes($toolName);
1604
1605
                $sql = "SELECT * FROM $tool_edit_table
1606
                        WHERE
1607
                            c_id = $course_id AND
1608
                            $toolCondition
1609
                            lastedit_type NOT LIKE '%Deleted%' AND
1610
                            lastedit_type NOT LIKE '%deleted%' AND
1611
                            lastedit_type NOT LIKE '%DocumentInvisible%' AND
1612
                            lastedit_date > '$latestDate' AND
1613
                            lastedit_user_id != $user_id $sessionCondition AND
1614
                            visibility != 2 AND
1615
                            (to_user_id IN ('$user_id', '0') OR to_user_id IS NULL) AND
1616
                            (to_group_id IN ('".implode("','", $group_ids)."') OR to_group_id IS NULL)
1617
                        ORDER BY lastedit_date DESC
1618
                        LIMIT 1";
1619
                $result = Database::query($sql);
1620
1621
                $latestChange = Database::fetch_array($result, 'ASSOC');
1622
1623
                if ($latestChange) {
1624
                    $latestChange['link'] = $tool['link'];
1625
                    $latestChange['image'] = $tool['image'];
1626
                    $latestChange['tool'] = $tool['name'];
1627
                    $notifications[$toolName] = $latestChange;
1628
                }
1629
            }
1630
        }
1631
1632
        // Show all tool icons where there is something new.
1633
        $return = '';
1634
        foreach ($notifications as $notification) {
1635
            $toolName = $notification['tool'];
1636
            if (!(
1637
                    '1' == $notification['visibility'] ||
1638
                    ('1' == $status && '0' == $notification['visibility']) ||
1639
                    !isset($notification['visibility'])
1640
                )
1641
            ) {
1642
                continue;
1643
            }
1644
1645
            if (TOOL_SURVEY == $toolName) {
1646
                $survey_info = SurveyManager::get_survey($notification['ref'], 0, $course_code);
1647
                if (!empty($survey_info)) {
1648
                    $invited_users = SurveyUtil::get_invited_users(
1649
                        $survey_info['code'],
1650
                        $course_code
1651
                    );
1652
                    if (!in_array($user_id, $invited_users['course_users'])) {
1653
                        continue;
1654
                    }
1655
                }
1656
            }
1657
1658
            if (TOOL_LEARNPATH == $notification['tool']) {
1659
                if (!learnpath::is_lp_visible_for_student($notification['ref'], $user_id, $courseInfo)) {
1660
                    continue;
1661
                }
1662
            }
1663
1664
            if (TOOL_DROPBOX == $notification['tool']) {
1665
                $notification['link'] = 'dropbox/dropbox_download.php?id='.$notification['ref'];
1666
            }
1667
1668
            if ('work' == $notification['tool'] &&
1669
                'DirectoryCreated' == $notification['lastedit_type']
1670
            ) {
1671
                $notification['lastedit_type'] = 'WorkAdded';
1672
            }
1673
1674
            $lastDate = api_get_local_time($notification['lastedit_date']);
1675
            $type = $notification['lastedit_type'];
1676
            if ('CalendareventVisible' == $type) {
1677
                $type = 'Visible';
1678
            }
1679
            $label = get_lang('Since your latest visit').": ".get_lang($type)." ($lastDate)";
1680
1681
            if (false === strpos($notification['link'], '?')) {
1682
                $notification['link'] = $notification['link'].'?notification=1';
1683
            } else {
1684
                $notification['link'] = $notification['link'].'&notification=1';
1685
            }
1686
1687
            $image = substr($notification['image'], 0, -4).'.png';
1688
1689
            $return .= self::url(
1690
                self::return_icon($image, $label),
1691
                api_get_path(WEB_CODE_PATH).
1692
                $notification['link'].'&cidReq='.$course_code.
1693
                '&ref='.$notification['ref'].
1694
                '&gid='.$notification['to_group_id'].
1695
                '&sid='.$sessionId
1696
            ).PHP_EOL;
1697
        }
1698
1699
        return $return;
1700
    }
1701
1702
    /**
1703
     * Get the session box details as an array.
1704
     *
1705
     * @todo check session visibility.
1706
     *
1707
     * @param int $session_id
1708
     *
1709
     * @return array Empty array or session array
1710
     *               ['title'=>'...','category'=>'','dates'=>'...','coach'=>'...','active'=>true/false,'session_category_id'=>int]
1711
     */
1712
    public static function getSessionTitleBox($session_id)
1713
    {
1714
        $session_info = api_get_session_info($session_id);
1715
        $coachInfo = [];
1716
        if (!empty($session_info['id_coach'])) {
1717
            $coachInfo = api_get_user_info($session_info['id_coach']);
1718
        }
1719
1720
        $session = [];
1721
        $session['category_id'] = $session_info['session_category_id'];
1722
        $session['title'] = $session_info['name'];
1723
        $session['coach_id'] = $session['id_coach'] = $session_info['id_coach'];
1724
        $session['dates'] = '';
1725
        $session['coach'] = '';
1726
        if ('true' === api_get_setting('show_session_coach') && isset($coachInfo['complete_name'])) {
1727
            $session['coach'] = get_lang('General coach').': '.$coachInfo['complete_name'];
1728
        }
1729
        $active = false;
1730
        if (('0000-00-00 00:00:00' == $session_info['access_end_date'] &&
1731
            '0000-00-00 00:00:00' == $session_info['access_start_date']) ||
1732
            (empty($session_info['access_end_date']) && empty($session_info['access_start_date']))
1733
        ) {
1734
            if (isset($session_info['duration']) && !empty($session_info['duration'])) {
1735
                $daysLeft = SessionManager::getDayLeftInSession($session_info, api_get_user_id());
1736
                $session['duration'] = $daysLeft >= 0
1737
                    ? sprintf(get_lang('This session has a maximum duration. Only %s days to go.'), $daysLeft)
1738
                    : get_lang('You are already registered but your allowed access time has expired.');
1739
            }
1740
            $active = true;
1741
        } else {
1742
            $dates = SessionManager::parseSessionDates($session_info, true);
1743
            $session['dates'] = $dates['access'];
1744
            if ('true' === api_get_setting('show_session_coach') && isset($coachInfo['complete_name'])) {
1745
                $session['coach'] = $coachInfo['complete_name'];
1746
            }
1747
            //$active = $date_start <= $now && $date_end >= $now;
1748
        }
1749
        $session['active'] = $active;
1750
        $session['session_category_id'] = $session_info['session_category_id'];
1751
        $session['visibility'] = $session_info['visibility'];
1752
        $session['num_users'] = $session_info['nbr_users'];
1753
        $session['num_courses'] = $session_info['nbr_courses'];
1754
        $session['description'] = $session_info['description'];
1755
        $session['show_description'] = $session_info['show_description'];
1756
        //$session['image'] = SessionManager::getSessionImage($session_info['id']);
1757
        $session['url'] = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$session_info['id'];
1758
1759
        $entityManager = Database::getManager();
1760
        $fieldValuesRepo = $entityManager->getRepository(ExtraFieldValues::class);
1761
        $extraFieldValues = $fieldValuesRepo->getVisibleValues(
1762
            ExtraField::SESSION_FIELD_TYPE,
1763
            $session_id
1764
        );
1765
1766
        $session['extra_fields'] = [];
1767
        /** @var ExtraFieldValues $value */
1768
        foreach ($extraFieldValues as $value) {
1769
            if (empty($value)) {
1770
                continue;
1771
            }
1772
            $session['extra_fields'][] = [
1773
                'field' => [
1774
                    'variable' => $value->getField()->getVariable(),
1775
                    'display_text' => $value->getField()->getDisplayText(),
1776
                ],
1777
                'value' => $value->getValue(),
1778
            ];
1779
        }
1780
1781
1782
        return $session;
1783
    }
1784
1785
    /**
1786
     * Return the five star HTML.
1787
     *
1788
     * @param string $id              of the rating ul element
1789
     * @param string $url             that will be added (for jquery see hot_courses.tpl)
1790
     * @param array  $point_info      point info array see function CourseManager::get_course_ranking()
1791
     * @param bool   $add_div_wrapper add a div wrapper
1792
     *
1793
     * @return string
1794
     */
1795
    public static function return_rating_system(
1796
        $id,
1797
        $url,
1798
        $point_info = [],
1799
        $add_div_wrapper = true
1800
    ) {
1801
        $number_of_users_who_voted = isset($point_info['users_who_voted']) ? $point_info['users_who_voted'] : null;
1802
        $percentage = isset($point_info['point_average']) ? $point_info['point_average'] : 0;
1803
1804
        if (!empty($percentage)) {
1805
            $percentage = $percentage * 125 / 100;
1806
        }
1807
        $accesses = isset($point_info['accesses']) ? $point_info['accesses'] : 0;
1808
        $star_label = sprintf(get_lang('%s stars out of 5'), $point_info['point_average_star']);
1809
1810
        $html = '<section class="rating-widget">';
1811
        $html .= '<div class="rating-stars"><ul id="stars">';
1812
        $html .= '<li class="star" data-link="'.$url.'&amp;star=1" title="Poor" data-value="1"><i class="fa fa-star fa-fw"></i></li>
1813
                 <li class="star" data-link="'.$url.'&amp;star=2" title="Fair" data-value="2"><i class="fa fa-star fa-fw"></i></li>
1814
                 <li class="star" data-link="'.$url.'&amp;star=3" title="Good" data-value="3"><i class="fa fa-star fa-fw"></i></li>
1815
                 <li class="star" data-link="'.$url.'&amp;star=4" title="Excellent" data-value="4"><i class="fa fa-star fa-fw"></i></li>
1816
                 <li class="star" data-link="'.$url.'&amp;star=5" title="WOW!!!" data-value="5"><i class="fa fa-star fa-fw"></i></li>
1817
        ';
1818
        $html .= '</ul></div>';
1819
        $html .= '</section>';
1820
        $labels = [];
1821
1822
        $labels[] = 1 == $number_of_users_who_voted ? $number_of_users_who_voted.' '.get_lang('Vote') : $number_of_users_who_voted.' '.get_lang('Votes');
1823
        $labels[] = 1 == $accesses ? $accesses.' '.get_lang('Visit') : $accesses.' '.get_lang('Visits');
1824
        $labels[] = $point_info['user_vote'] ? get_lang('Your vote').' ['.$point_info['user_vote'].']' : get_lang('Your vote').' [?] ';
1825
1826
        if (!$add_div_wrapper && api_is_anonymous()) {
1827
            $labels[] = self::tag('span', get_lang('Login to vote'), ['class' => 'error']);
1828
        }
1829
1830
        $html .= self::div(implode(' | ', $labels), ['id' => 'vote_label_'.$id, 'class' => 'vote_label_info']);
1831
        $html .= ' '.self::span(' ', ['id' => 'vote_label2_'.$id]);
1832
1833
        if ($add_div_wrapper) {
1834
            $html = self::div($html, ['id' => 'rating_wrapper_'.$id]);
1835
        }
1836
1837
        return $html;
1838
    }
1839
1840
    /**
1841
     * @param string $title
1842
     * @param string $second_title
1843
     * @param string $size
1844
     * @param bool   $filter
1845
     *
1846
     * @return string
1847
     */
1848
    public static function page_header($title, $second_title = null, $size = 'h2', $filter = true)
1849
    {
1850
        if ($filter) {
1851
            $title = Security::remove_XSS($title);
1852
        }
1853
1854
        if (!empty($second_title)) {
1855
            if ($filter) {
1856
                $second_title = Security::remove_XSS($second_title);
1857
            }
1858
            $title .= "<small> $second_title</small>";
1859
        }
1860
1861
        return '<'.$size.' class="page-header">'.$title.'</'.$size.'>';
1862
    }
1863
1864
    public static function page_header_and_translate($title, $second_title = null)
1865
    {
1866
        $title = get_lang($title);
1867
1868
        return self::page_header($title, $second_title);
1869
    }
1870
1871
    public static function page_subheader_and_translate($title, $second_title = null)
1872
    {
1873
        $title = get_lang($title);
1874
1875
        return self::page_subheader($title, $second_title);
1876
    }
1877
1878
    public static function page_subheader($title, $second_title = null, $size = 'h3', $attributes = [])
1879
    {
1880
        if (!empty($second_title)) {
1881
            $second_title = Security::remove_XSS($second_title);
1882
            $title .= "<small> $second_title<small>";
1883
        }
1884
        $subTitle = self::tag($size, Security::remove_XSS($title), $attributes);
1885
1886
        return $subTitle;
1887
    }
1888
1889
    public static function page_subheader2($title, $second_title = null)
1890
    {
1891
        return self::page_header($title, $second_title, 'h4');
1892
    }
1893
1894
    public static function page_subheader3($title, $second_title = null)
1895
    {
1896
        return self::page_header($title, $second_title, 'h5');
1897
    }
1898
1899
    /**
1900
     * @param array $list
1901
     *
1902
     * @return string|null
1903
     */
1904
    public static function description($list)
1905
    {
1906
        $html = null;
1907
        if (!empty($list)) {
1908
            $html = '<dl class="dl-horizontal">';
1909
            foreach ($list as $item) {
1910
                $html .= '<dt>'.$item['title'].'</dt>';
1911
                $html .= '<dd>'.$item['content'].'</dd>';
1912
            }
1913
            $html .= '</dl>';
1914
        }
1915
1916
        return $html;
1917
    }
1918
1919
    /**
1920
     * @param int    $percentage      int value between 0 and 100
1921
     * @param bool   $show_percentage
1922
     * @param string $extra_info
1923
     * @param string $class           danger/success/infowarning
1924
     *
1925
     * @return string
1926
     */
1927
    public static function bar_progress($percentage, $show_percentage = true, $extra_info = '', $class = '')
1928
    {
1929
        $percentage = (int) $percentage;
1930
        $class = empty($class) ? '' : "progress-bar-$class";
1931
1932
        $div = '<div class="progress">
1933
                <div
1934
                    class="progress-bar progress-bar-striped '.$class.'"
1935
                    role="progressbar"
1936
                    aria-valuenow="'.$percentage.'"
1937
                    aria-valuemin="0"
1938
                    aria-valuemax="100"
1939
                    style="width: '.$percentage.'%;"
1940
                >';
1941
        if ($show_percentage) {
1942
            $div .= $percentage.'%';
1943
        } else {
1944
            if (!empty($extra_info)) {
1945
                $div .= $extra_info;
1946
            }
1947
        }
1948
        $div .= '</div></div>';
1949
1950
        return $div;
1951
    }
1952
1953
    /**
1954
     * @param string $count
1955
     * @param string $type
1956
     *
1957
     * @return string|null
1958
     */
1959
    public static function badge($count, $type = 'warning')
1960
    {
1961
        $class = '';
1962
1963
        switch ($type) {
1964
            case 'success':
1965
                $class = 'bg-success';
1966
                break;
1967
            case 'warning':
1968
                $class = 'bg-warning text-dark';
1969
                break;
1970
            case 'important':
1971
                $class = 'bg-important';
1972
                break;
1973
            case 'info':
1974
                $class = 'bg-info';
1975
                break;
1976
            case 'inverse':
1977
                $class = 'bg-inverse';
1978
                break;
1979
        }
1980
1981
        if (!empty($count)) {
1982
            return ' <span class="badge '.$class.'">'.$count.'</span>';
1983
        }
1984
1985
        return null;
1986
    }
1987
1988
    /**
1989
     * @param array $badge_list
1990
     *
1991
     * @return string
1992
     */
1993
    public static function badge_group($badge_list)
1994
    {
1995
        $html = '<div class="badge-group">';
1996
        foreach ($badge_list as $badge) {
1997
            $html .= $badge;
1998
        }
1999
        $html .= '</div>';
2000
2001
        return $html;
2002
    }
2003
2004
    /**
2005
     * @param string $content
2006
     * @param string $type
2007
     *
2008
     * @return string
2009
     */
2010
    public static function label($content, $type = 'default')
2011
    {
2012
        switch ($type) {
2013
            case 'success':
2014
                $class = 'success';
2015
                break;
2016
            case 'warning':
2017
                $class = 'warning text-dark';
2018
                break;
2019
            case 'important':
2020
            case 'danger':
2021
                $class = 'danger';
2022
                break;
2023
            case 'info':
2024
                $class = 'info';
2025
                break;
2026
            case 'primary':
2027
                $class = 'primary';
2028
                break;
2029
            default:
2030
                $class = 'secondary';
2031
                break;
2032
        }
2033
2034
        $html = '';
2035
        if (!empty($content)) {
2036
            $html = '<span class="badge bg-'.$class.'">';
2037
            $html .= $content;
2038
            $html .= '</span>';
2039
        }
2040
2041
        return $html;
2042
    }
2043
2044
    /**
2045
     * @param array  $items
2046
     * @param string $class
2047
     *
2048
     * @return string|null
2049
     */
2050
    public static function actions($items, $class = 'new_actions')
2051
    {
2052
        $html = null;
2053
        if (!empty($items)) {
2054
            $html = '<div class="'.$class.'"><ul class="nav nav-pills">';
2055
            foreach ($items as $value) {
2056
                $class = null;
2057
                if (isset($value['active']) && $value['active']) {
2058
                    $class = 'class ="active"';
2059
                }
2060
2061
                if (basename($_SERVER['REQUEST_URI']) == basename($value['url'])) {
2062
                    $class = 'class ="active"';
2063
                }
2064
                $html .= "<li $class >";
2065
                $attributes = isset($value['url_attributes']) ? $value['url_attributes'] : [];
2066
                $html .= self::url($value['content'], $value['url'], $attributes);
2067
                $html .= '</li>';
2068
            }
2069
            $html .= '</ul></div>';
2070
            $html .= '<br />';
2071
        }
2072
2073
        return $html;
2074
    }
2075
2076
    /**
2077
     * Prints a tooltip.
2078
     *
2079
     * @param string $text
2080
     * @param string $tip
2081
     *
2082
     * @return string
2083
     */
2084
    public static function tip($text, $tip)
2085
    {
2086
        if (empty($tip)) {
2087
            return $text;
2088
        }
2089
2090
        return self::span(
2091
            $text,
2092
            ['class' => 'boot-tooltip', 'title' => strip_tags($tip)]
2093
        );
2094
    }
2095
2096
    /**
2097
     * @param array  $items
2098
     * @param string $type
2099
     * @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...
2100
     *
2101
     * @return string|null
2102
     */
2103
    public static function generate_accordion($items, $type = 'jquery', $id = null)
2104
    {
2105
        $html = null;
2106
        if (!empty($items)) {
2107
            if (empty($id)) {
2108
                $id = api_get_unique_id();
2109
            }
2110
            if ('jquery' == $type) {
2111
                $html = '<div class="accordion_jquery" id="'.$id.'">'; //using jquery
2112
            } else {
2113
                $html = '<div class="accordion" id="'.$id.'">'; //using bootstrap
2114
            }
2115
2116
            $count = 1;
2117
            foreach ($items as $item) {
2118
                $html .= '<div class="accordion-my-group">';
2119
                $html .= '<div class="accordion-heading">
2120
                            <a class="accordion-toggle" data-toggle="collapse" data-parent="#'.$id.'" href="#collapse'.$count.'">
2121
                            '.$item['title'].'
2122
                            </a>
2123
                          </div>';
2124
2125
                $html .= '<div id="collapse'.$count.'" class="accordion-body">';
2126
                $html .= '<div class="accordion-my-inner">
2127
                            '.$item['content'].'
2128
                            </div>
2129
                          </div>';
2130
            }
2131
            $html .= '</div>';
2132
        }
2133
2134
        return $html;
2135
    }
2136
2137
    /**
2138
     * @param array $buttons
2139
     *
2140
     * @return string
2141
     */
2142
    public static function groupButton($buttons)
2143
    {
2144
        $html = '<div class="btn-group" role="group">';
2145
        foreach ($buttons as $button) {
2146
            $html .= $button;
2147
        }
2148
        $html .= '</div>';
2149
2150
        return $html;
2151
    }
2152
2153
    /**
2154
     * @todo use twig
2155
     *
2156
     * @param string $title
2157
     * @param array  $elements
2158
     * @param bool   $alignToRight
2159
     *
2160
     * @return string
2161
     */
2162
    public static function groupButtonWithDropDown($title, $elements, $alignToRight = false)
2163
    {
2164
        $id = uniqid('dropdown', true);
2165
        $html = '<div class="btn-group" role="group">
2166
                <button id = "'.$id.'" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
2167
                '.$title.'
2168
                </button>
2169
                <div class="dropdown-menu aria-labelledby="'.$id.'" '.($alignToRight ? 'dropdown-menu-right' : '').'">';
2170
        foreach ($elements as $item) {
2171
            $html .= self::tag('li', self::url($item['title'], $item['href'], ['class' => 'dropdown-item']));
2172
        }
2173
        $html .= '</div>
2174
            </div>';
2175
2176
        return $html;
2177
    }
2178
2179
    /**
2180
     * @param string $file
2181
     * @param array  $params
2182
     *
2183
     * @return string|null
2184
     */
2185
    public static function getMediaPlayer($file, $params = [])
2186
    {
2187
        $fileInfo = pathinfo($file);
2188
2189
        $autoplay = isset($params['autoplay']) && 'true' === $params['autoplay'] ? 'autoplay' : '';
2190
        $id = isset($params['id']) ? $params['id'] : $fileInfo['basename'];
2191
        $width = isset($params['width']) ? 'width="'.$params['width'].'"' : null;
2192
        $class = isset($params['class']) ? ' class="'.$params['class'].'"' : null;
2193
2194
        switch ($fileInfo['extension']) {
2195
            case 'mp3':
2196
            case 'webm':
2197
                $html = '<audio id="'.$id.'" '.$class.' controls '.$autoplay.' '.$width.' src="'.$params['url'].'" >';
2198
                $html .= '<object width="'.$width.'" height="50" type="application/x-shockwave-flash" data="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf">
2199
                            <param name="movie" value="'.api_get_path(WEB_LIBRARY_PATH).'javascript/mediaelement/flashmediaelement.swf" />
2200
                            <param name="flashvars" value="controls=true&file='.$params['url'].'" />
2201
                          </object>';
2202
                $html .= '</audio>';
2203
2204
                return $html;
2205
                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...
2206
            case 'wav':
2207
            case 'ogg':
2208
                $html = '<audio width="300px" controls id="'.$id.'" '.$autoplay.' src="'.$params['url'].'" >';
2209
2210
                return $html;
2211
                break;
2212
        }
2213
2214
        return null;
2215
    }
2216
2217
    /**
2218
     * @param int    $nextValue
2219
     * @param array  $list
2220
     * @param int    $current
2221
     * @param int    $fixedValue
2222
     * @param array  $conditions
2223
     * @param string $link
2224
     * @param bool   $isMedia
2225
     * @param bool   $addHeaders
2226
     * @param array  $linkAttributes
2227
     *
2228
     * @return string
2229
     */
2230
    public static function progressPaginationBar(
2231
        $nextValue,
2232
        $list,
2233
        $current,
2234
        $fixedValue = null,
2235
        $conditions = [],
2236
        $link = null,
2237
        $isMedia = false,
2238
        $addHeaders = true,
2239
        $linkAttributes = []
2240
    ) {
2241
        if ($addHeaders) {
2242
            $pagination_size = 'pagination-mini';
2243
            $html = '<div class="exercise_pagination pagination '.$pagination_size.'"><ul>';
2244
        } else {
2245
            $html = null;
2246
        }
2247
        $affectAllItems = false;
2248
        if ($isMedia && isset($fixedValue) && ($nextValue + 1 == $current)) {
2249
            $affectAllItems = true;
2250
        }
2251
        $localCounter = 0;
2252
        foreach ($list as $itemId) {
2253
            $isCurrent = false;
2254
            if ($affectAllItems) {
2255
                $isCurrent = true;
2256
            } else {
2257
                if (!$isMedia) {
2258
                    $isCurrent = $current == ($localCounter + $nextValue + 1) ? true : false;
2259
                }
2260
            }
2261
            $html .= self::parsePaginationItem(
2262
                $itemId,
2263
                $isCurrent,
2264
                $conditions,
2265
                $link,
2266
                $nextValue,
2267
                $isMedia,
2268
                $localCounter,
2269
                $fixedValue,
2270
                $linkAttributes
2271
            );
2272
            $localCounter++;
2273
        }
2274
        if ($addHeaders) {
2275
            $html .= '</ul></div>';
2276
        }
2277
2278
        return $html;
2279
    }
2280
2281
    /**
2282
     * @param int    $itemId
2283
     * @param bool   $isCurrent
2284
     * @param array  $conditions
2285
     * @param string $link
2286
     * @param int    $nextValue
2287
     * @param bool   $isMedia
2288
     * @param int    $localCounter
2289
     * @param int    $fixedValue
2290
     * @param array  $linkAttributes
2291
     *
2292
     * @return string
2293
     */
2294
    public static function parsePaginationItem(
2295
        $itemId,
2296
        $isCurrent,
2297
        $conditions,
2298
        $link,
2299
        $nextValue = 0,
2300
        $isMedia = false,
2301
        $localCounter = null,
2302
        $fixedValue = null,
2303
        $linkAttributes = []
2304
    ) {
2305
        $defaultClass = 'before';
2306
        $class = $defaultClass;
2307
        foreach ($conditions as $condition) {
2308
            $array = isset($condition['items']) ? $condition['items'] : [];
2309
            $class_to_applied = $condition['class'];
2310
            $type = isset($condition['type']) ? $condition['type'] : 'positive';
2311
            $mode = isset($condition['mode']) ? $condition['mode'] : 'add';
2312
            switch ($type) {
2313
                case 'positive':
2314
                    if (in_array($itemId, $array)) {
2315
                        if ('overwrite' == $mode) {
2316
                            $class = " $defaultClass $class_to_applied";
2317
                        } else {
2318
                            $class .= " $class_to_applied";
2319
                        }
2320
                    }
2321
                    break;
2322
                case 'negative':
2323
                    if (!in_array($itemId, $array)) {
2324
                        if ('overwrite' == $mode) {
2325
                            $class = " $defaultClass $class_to_applied";
2326
                        } else {
2327
                            $class .= " $class_to_applied";
2328
                        }
2329
                    }
2330
                    break;
2331
            }
2332
        }
2333
        if ($isCurrent) {
2334
            $class = 'before current';
2335
        }
2336
        if ($isMedia && $isCurrent) {
2337
            $class = 'before current';
2338
        }
2339
        if (empty($link)) {
2340
            $link_to_show = '#';
2341
        } else {
2342
            $link_to_show = $link.($nextValue + $localCounter);
2343
        }
2344
        $label = $nextValue + $localCounter + 1;
2345
        if ($isMedia) {
2346
            $label = ($fixedValue + 1).' '.chr(97 + $localCounter);
2347
            $link_to_show = $link.$fixedValue.'#questionanchor'.$itemId;
2348
        }
2349
        $link = self::url($label.' ', $link_to_show, $linkAttributes);
2350
2351
        return  '<li class = "'.$class.'">'.$link.'</li>';
2352
    }
2353
2354
    /**
2355
     * @param int $current
2356
     * @param int $total
2357
     *
2358
     * @return string
2359
     */
2360
    public static function paginationIndicator($current, $total)
2361
    {
2362
        $html = null;
2363
        if (!empty($current) && !empty($total)) {
2364
            $label = sprintf(get_lang('%s of %s'), $current, $total);
2365
            $html = self::url($label, '#', ['class' => 'btn disabled']);
2366
        }
2367
2368
        return $html;
2369
    }
2370
2371
    /**
2372
     * @param $url
2373
     * @param $currentPage
2374
     * @param $pagesCount
2375
     * @param $totalItems
2376
     *
2377
     * @return string
2378
     */
2379
    public static function getPagination($url, $currentPage, $pagesCount, $totalItems)
2380
    {
2381
        $pagination = '';
2382
        if ($totalItems > 1 && $pagesCount > 1) {
2383
            $pagination .= '<ul class="pagination">';
2384
            for ($i = 0; $i < $pagesCount; $i++) {
2385
                $newPage = $i + 1;
2386
                if ($currentPage == $newPage) {
2387
                    $pagination .= '<li class="active"><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2388
                } else {
2389
                    $pagination .= '<li><a href="'.$url.'&page='.$newPage.'">'.$newPage.'</a></li>';
2390
                }
2391
            }
2392
            $pagination .= '</ul>';
2393
        }
2394
2395
        return $pagination;
2396
    }
2397
2398
    /**
2399
     * Adds a legacy message in the queue.
2400
     *
2401
     * @param string $message
2402
     */
2403
    public static function addFlash($message)
2404
    {
2405
        // Detect type of message.
2406
        $parts = preg_match('/alert-([a-z]*)/', $message, $matches);
2407
        $type = 'primary';
2408
        if ($parts && isset($matches[1]) && $matches[1]) {
2409
            $type = $matches[1];
2410
        }
2411
        // Detect legacy content of message.
2412
        $result = preg_match('/<div(.*?)\>(.*?)\<\/div>/s', $message, $matches);
2413
        if ($result && isset($matches[2])) {
2414
            Container::getSession()->getFlashBag()->add($type, $matches[2]);
2415
        }
2416
    }
2417
2418
    /**
2419
     * Get the profile edition link for a user.
2420
     *
2421
     * @param int  $userId  The user id
2422
     * @param bool $asAdmin Optional. Whether get the URL for the platform admin
2423
     *
2424
     * @return string The link
2425
     */
2426
    public static function getProfileEditionLink($userId, $asAdmin = false)
2427
    {
2428
        $editProfileUrl = api_get_path(WEB_CODE_PATH).'auth/profile.php';
2429
        if ($asAdmin) {
2430
            $editProfileUrl = api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=".intval($userId);
2431
        }
2432
2433
        return $editProfileUrl;
2434
    }
2435
2436
    /**
2437
     * Get the vCard for a user.
2438
     *
2439
     * @param int $userId The user id
2440
     *
2441
     * @return string *.*vcf file
2442
     */
2443
    public static function getVCardUserLink($userId)
2444
    {
2445
        $vCardUrl = api_get_path(WEB_PATH).'main/social/vcard_export.php?userId='.intval($userId);
2446
2447
        return $vCardUrl;
2448
    }
2449
2450
    /**
2451
     * @param string $content
2452
     * @param string $title
2453
     * @param string $footer
2454
     * @param string $type        primary|success|info|warning|danger
2455
     * @param string $extra
2456
     * @param string $id
2457
     * @param string $customColor
2458
     * @param string $rightAction
2459
     *
2460
     * @return string
2461
     */
2462
    public static function panel(
2463
        $content,
2464
        $title = '',
2465
        $footer = '',
2466
        $type = 'default',
2467
        $extra = '',
2468
        $id = '',
2469
        $customColor = '',
2470
        $rightAction = ''
2471
    ) {
2472
        $headerStyle = '';
2473
        if (!empty($customColor)) {
2474
            $headerStyle = 'style = "color: white; background-color: '.$customColor.'" ';
2475
        }
2476
2477
        if (!empty($rightAction)) {
2478
            $rightAction = '<span class="float-right">'.$rightAction.'</span>';
2479
        }
2480
2481
        $title = !empty($title) ? '<h5 class="card-title">'.$title.' '.$rightAction.'</h5>'.$extra : '';
2482
        $footer = !empty($footer) ? '<p class="card-text"><small class="text-muted">'.$footer.'</small></p>' : '';
2483
        $typeList = ['primary', 'success', 'info', 'warning', 'danger'];
2484
        $style = !in_array($type, $typeList) ? 'default' : $type;
2485
2486
        if (!empty($id)) {
2487
            $id = " id='$id'";
2488
        }
2489
        $cardBody = $title.' '.self::contentPanel($content).' '.$footer;
2490
2491
        $panel = Display::tag('div', $cardBody, ['id' => 'card-'.$id, 'class' => 'card-body']);
2492
2493
        return '
2494
            <div '.$id.' class="card">
2495
                '.$panel.'
2496
            </div>'
2497
        ;
2498
    }
2499
2500
    /**
2501
     * @param string $content
2502
     */
2503
    public static function contentPanel($content): string
2504
    {
2505
        if (empty($content)) {
2506
            return '';
2507
        }
2508
2509
        return '<div class="card-text">'.$content.'</div>';
2510
    }
2511
2512
    /**
2513
     * Get the button HTML with an Awesome Font icon.
2514
     *
2515
     * @param string $text        The button content
2516
     * @param string $url         The url to button
2517
     * @param string $icon        The Awesome Font class for icon
2518
     * @param string $type        Optional. The button Bootstrap class. Default 'default' class
2519
     * @param array  $attributes  The additional attributes
2520
     * @param bool   $includeText
2521
     *
2522
     * @return string The button HTML
2523
     */
2524
    public static function toolbarButton(
2525
        $text,
2526
        $url,
2527
        $icon = 'check',
2528
        $type = null,
2529
        array $attributes = [],
2530
        $includeText = true
2531
    ) {
2532
        $buttonClass = "btn btn-outline-secondary";
2533
        if (!empty($type)) {
2534
            $buttonClass = "btn btn-$type";
2535
        }
2536
        $icon = self::tag('i', null, ['class' => "fa fa-$icon fa-fw", 'aria-hidden' => 'true']);
2537
        $attributes['class'] = isset($attributes['class']) ? "$buttonClass {$attributes['class']}" : $buttonClass;
2538
        $attributes['title'] = isset($attributes['title']) ? $attributes['title'] : $text;
2539
2540
        if (!$includeText) {
2541
            $text = '<span class="sr-only">'.$text.'</span>';
2542
        }
2543
2544
        return self::url("$icon $text", $url, $attributes);
2545
    }
2546
2547
    /**
2548
     * @param string $id
2549
     * @param array  $content
2550
     * @param array  $colsWidth Optional. Columns width
2551
     *
2552
     * @return string
2553
     */
2554
    public static function toolbarAction($id, $content, $colsWidth = [])
2555
    {
2556
        $col = count($content);
2557
2558
        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...
2559
            $width = 12 / $col;
2560
            array_walk($content, function () use ($width, &$colsWidth) {
2561
                $colsWidth[] = $width;
2562
            });
2563
        }
2564
2565
        $html = '<div id="'.$id.'" class="actions">';
2566
        $html .= '<div class="row">';
2567
2568
        for ($i = 0; $i < $col; $i++) {
2569
            $class = 'col-sm-'.$colsWidth[$i];
2570
2571
            if ($col > 1) {
2572
                if ($i > 0 && $i < count($content) - 1) {
2573
                    $class .= ' text-center';
2574
                } elseif ($i === count($content) - 1) {
2575
                    $class .= ' text-right';
2576
                }
2577
            }
2578
2579
            $html .= '<div class="'.$class.'">'.$content[$i].'</div>';
2580
        }
2581
2582
        $html .= '</div>';
2583
        $html .= '</div>';
2584
2585
        return $html;
2586
    }
2587
2588
    /**
2589
     * Get a HTML code for a icon by Font Awesome.
2590
     *
2591
     * @param string     $name            The icon name. Example: "mail-reply"
2592
     * @param int|string $size            Optional. The size for the icon. (Example: lg, 2, 3, 4, 5)
2593
     * @param bool       $fixWidth        Optional. Whether add the fw class
2594
     * @param string     $additionalClass Optional. Additional class
2595
     *
2596
     * @return string
2597
     */
2598
    public static function returnFontAwesomeIcon(
2599
        $name,
2600
        $size = '',
2601
        $fixWidth = false,
2602
        $additionalClass = ''
2603
    ) {
2604
        $className = "fa fa-$name";
2605
2606
        if ($fixWidth) {
2607
            $className .= ' fa-fw';
2608
        }
2609
2610
        switch ($size) {
2611
            case 'xs':
2612
            case 'sm':
2613
            case 'lg':
2614
                $className .= " fa-{$size}";
2615
                break;
2616
            case 2:
2617
            case 3:
2618
            case 4:
2619
            case 5:
2620
                $className .= " fa-{$size}x";
2621
                break;
2622
        }
2623
2624
        if (!empty($additionalClass)) {
2625
            $className .= " $additionalClass";
2626
        }
2627
2628
        $icon = self::tag('em', null, ['class' => $className]);
2629
2630
        return "$icon ";
2631
    }
2632
2633
    /**
2634
     * @param string     $title
2635
     * @param string     $content
2636
     * @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...
2637
     * @param array      $params
2638
     * @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...
2639
     * @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...
2640
     * @param bool|true  $open
2641
     * @param bool|false $fullClickable
2642
     *
2643
     * @return string|null
2644
     *
2645
     * @todo rework function to easy use
2646
     */
2647
    public static function panelCollapse(
2648
        $title,
2649
        $content,
2650
        $id = null,
2651
        $params = [],
2652
        $idAccordion = null,
2653
        $idCollapse = null,
2654
        $open = true,
2655
        $fullClickable = false
2656
    ) {
2657
        if (!empty($idAccordion)) {
2658
            $headerClass = '';
2659
            $headerClass .= $fullClickable ? 'center-block ' : '';
2660
            $headerClass .= $open ? '' : 'collapsed';
2661
            $contentClass = 'panel-collapse collapse ';
2662
            $contentClass .= $open ? 'in' : '';
2663
            $ariaExpanded = $open ? 'true' : 'false';
2664
2665
            $html = <<<HTML
2666
2667
                <div class="card" id="$id">
2668
                    <div class="card-header">
2669
                        $title
2670
                    </div>
2671
                    <div class="card-body">$content</div>
2672
                </div>
2673
HTML;
2674
        } else {
2675
            if (!empty($id)) {
2676
                $params['id'] = $id;
2677
            }
2678
            $params['class'] = 'card';
2679
            $html = null;
2680
            if (!empty($title)) {
2681
                $html .= '<div class="card-header">'.$title.'</div>'.PHP_EOL;
2682
            }
2683
            $html .= '<div class="card-body">'.$content.'</div>'.PHP_EOL;
2684
            $html = self::div($html, $params);
2685
        }
2686
2687
        return $html;
2688
    }
2689
2690
    /**
2691
     * Returns the string "1 day ago" with a link showing the exact date time.
2692
     *
2693
     * @param string $dateTime in UTC or a DateTime in UTC
2694
     *
2695
     * @return string
2696
     */
2697
    public static function dateToStringAgoAndLongDate($dateTime)
2698
    {
2699
        if (empty($dateTime) || '0000-00-00 00:00:00' === $dateTime) {
2700
            return '';
2701
        }
2702
2703
        if ($dateTime instanceof \DateTime) {
0 ignored issues
show
introduced by
$dateTime is never a sub-type of DateTime.
Loading history...
2704
            $dateTime = $dateTime->format('Y-m-d H:i:s');
2705
        }
2706
2707
        return self::tip(
2708
            date_to_str_ago($dateTime),
2709
            api_convert_and_format_date($dateTime, DATE_TIME_FORMAT_LONG)
2710
            //api_get_local_time($dateTime)
2711
        );
2712
    }
2713
2714
    /**
2715
     * @param array  $userInfo
2716
     * @param string $status
2717
     * @param string $toolbar
2718
     *
2719
     * @return string
2720
     */
2721
    public static function getUserCard($userInfo, $status = '', $toolbar = '')
2722
    {
2723
        if (empty($userInfo)) {
2724
            return '';
2725
        }
2726
2727
        if (!empty($status)) {
2728
            $status = '<div class="items-user-status">'.$status.'</div>';
2729
        }
2730
2731
        if (!empty($toolbar)) {
2732
            $toolbar = '<div class="btn-group pull-right">'.$toolbar.'</div>';
2733
        }
2734
2735
        return '<div id="user_card_'.$userInfo['id'].'" class="card d-flex flex-row">
2736
                    <img src="'.$userInfo['avatar'].'" class="rounded">
2737
                    <h3 class="card-title">'.$userInfo['complete_name'].'</h3>
2738
                    <div class="card-body">
2739
                       <div class="card-title">
2740
                       '.$status.'
2741
                       '.$toolbar.'
2742
                       </div>
2743
                    </div>
2744
                    <hr />
2745
              </div>';
2746
    }
2747
2748
    /**
2749
     * @param string $fileName
2750
     * @param string $fileUrl
2751
     *
2752
     * @return string
2753
     */
2754
    public static function fileHtmlGuesser($fileName, $fileUrl)
2755
    {
2756
        $data = pathinfo($fileName);
2757
2758
        //$content = self::url($data['basename'], $fileUrl);
2759
        $content = '';
2760
        switch ($data['extension']) {
2761
            case 'webm':
2762
            case 'mp4':
2763
            case 'ogg':
2764
                $content = '<video style="width: 400px; height:100%;" src="'.$fileUrl.'"></video>';
2765
                // Allows video to play when loading during an ajax call
2766
                $content .= "<script>jQuery('video:not(.skip), audio:not(.skip)').mediaelementplayer();</script>";
2767
                break;
2768
            case 'jpg':
2769
            case 'jpeg':
2770
            case 'gif':
2771
            case 'png':
2772
                $content = '<img class="img-responsive" src="'.$fileUrl.'" />';
2773
                break;
2774
            default:
2775
                //$html = self::url($data['basename'], $fileUrl);
2776
                break;
2777
        }
2778
        //$html = self::url($content, $fileUrl, ['ajax']);
2779
2780
        return $content;
2781
    }
2782
2783
    /**
2784
     * @param string $frameName
2785
     *
2786
     * @return string
2787
     */
2788
    public static function getFrameReadyBlock($frameName)
2789
    {
2790
        $webPublicPath = api_get_path(WEB_PUBLIC_PATH);
2791
2792
        $videoFeatures = [
2793
            'playpause',
2794
            'current',
2795
            'progress',
2796
            'duration',
2797
            'tracks',
2798
            'volume',
2799
            'fullscreen',
2800
            'vrview',
2801
            'markersrolls',
2802
        ];
2803
        $features = api_get_configuration_value('video_features');
2804
        $videoPluginsJS = [];
2805
        $videoPluginCSS = [];
2806
        if (!empty($features) && isset($features['features'])) {
2807
            foreach ($features['features'] as $feature) {
2808
                if ('vrview' === $feature) {
2809
                    continue;
2810
                }
2811
                $defaultFeatures[] = $feature;
2812
                $videoPluginsJS[] = "mediaelement/plugins/$feature/$feature.js";
2813
                $videoPluginCSS[] = "mediaelement/plugins/$feature/$feature.css";
2814
            }
2815
        }
2816
2817
        $videoPluginFiles = '';
2818
        foreach ($videoPluginsJS as $file) {
2819
            $videoPluginFiles .= '{type: "script", src: "'.$webPublicPath.'assets/'.$file.'"},';
2820
        }
2821
2822
        $videoPluginCssFiles = '';
2823
        foreach ($videoPluginCSS as $file) {
2824
            $videoPluginCssFiles .= '{type: "stylesheet", src: "'.$webPublicPath.'assets/'.$file.'"},';
2825
        }
2826
2827
        $translateHtml = '';
2828
        $translate = api_get_configuration_value('translate_html');
2829
        if ($translate) {
2830
            $translateHtml = '{type:"script", src:"'.api_get_path(WEB_AJAX_PATH).'lang.ajax.php?a=translate_html&'.api_get_cidreq().'"},';
2831
        }
2832
2833
        $videoFeatures = implode("','", $videoFeatures);
2834
        $frameReady = '
2835
        $.frameReady(function() {
2836
             $(function () {
2837
                $("video:not(.skip), audio:not(.skip)").mediaelementplayer({
2838
                    pluginPath: "'.$webPublicPath.'assets/mediaelement/plugins/",
2839
                    features: [\''.$videoFeatures.'\'],
2840
                    success: function(mediaElement, originalNode, instance) {
2841
                        '.ChamiloApi::getQuizMarkersRollsJS().'
2842
                    },
2843
                    vrPath: "'.$webPublicPath.'assets/vrview/build/vrview.js"
2844
                });
2845
            });
2846
        },
2847
        "'.$frameName.'",
2848
        [
2849
            {type:"script", src:"'.api_get_jquery_web_path().'", deps: [
2850
            {type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
2851
            {type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
2852
            {type:"script", src:"'.api_get_jquery_ui_js_web_path().'"},
2853
            {type:"script", src: "'.$webPublicPath.'build/libs/mediaelement/mediaelement-and-player.min.js",
2854
                deps: [
2855
                {type:"script", src: "'.$webPublicPath.'build/libs/mediaelement/plugins/vrview/vrview.js"},
2856
                {type:"script", src: "'.$webPublicPath.'build/libs/mediaelement/plugins/markersrolls/markersrolls.js"},
2857
                '.$videoPluginFiles.'
2858
            ]},
2859
            '.$translateHtml.'
2860
            ]},
2861
            '.$videoPluginCssFiles.'
2862
            {type:"script", src:"'.$webPublicPath.'build/libs/mathjax/MathJax.js?config=AM_HTMLorMML"},
2863
            {type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/jquery-ui.min.css"},
2864
            {type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/theme.css"},
2865
        ]);';
2866
2867
        return $frameReady;
2868
    }
2869
2870
    /**
2871
     * @param string $image
2872
     * @param int    $size
2873
     *
2874
     * @return string
2875
     */
2876
    public static function get_icon_path($image, $size = ICON_SIZE_SMALL)
2877
    {
2878
        return self::return_icon($image, '', [], $size, false, true);
2879
    }
2880
2881
    /**
2882
     * @param string $image
2883
     * @param int    $size
2884
     * @param string $name
2885
     *
2886
     * @return string
2887
     */
2888
    public static function get_image($image, $size = ICON_SIZE_SMALL, $name = '')
2889
    {
2890
        return self::return_icon($image, $name, [], $size);
2891
    }
2892
2893
    public static function dropdownMenu($items = [], array $attr = [])
2894
    {
2895
        $links = null;
2896
        $url = null;
2897
        foreach ($items as $row) {
2898
            $url = self::url($row['icon'].$row['item'], $row['url'], ['class' => 'dropdown-item']);
2899
            $links .= self::tag('li', $url);
2900
        }
2901
        $html = self::tag('ul', $links, $attr);
2902
2903
        return  $html;
2904
    }
2905
2906
    /**
2907
     * @param $id
2908
     *
2909
     * @return array|mixed
2910
     */
2911
    public static function randomColor($id)
2912
    {
2913
        static $colors = [];
2914
2915
        if (!empty($colors[$id])) {
2916
            return $colors[$id];
2917
        } else {
2918
            $color = substr(md5(time() * $id), 0, 6);
2919
            $c1 = hexdec(substr($color, 0, 2));
2920
            $c2 = hexdec(substr($color, 2, 2));
2921
            $c3 = hexdec(substr($color, 4, 2));
2922
            $luminosity = $c1 + $c2 + $c3;
2923
2924
            $type = '#000000';
2925
            if ($luminosity < (255 + 255 + 255) / 2) {
2926
                $type = '#FFFFFF';
2927
            }
2928
2929
            $result = [
2930
                'color' => '#'.$color,
2931
                'luminosity' => $type,
2932
            ];
2933
            $colors[$id] = $result;
2934
2935
            return $result; // example: #fc443a
2936
        }
2937
    }
2938
}
2939