Passed
Pull Request — master (#5614)
by Angel Fernando Quiroz
08:27
created

isStyleChangeable()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 3
nop 0
dl 0
loc 15
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
use Chamilo\CoreBundle\Entity\SystemTemplate;
6
use ChamiloSession as Session;
7
use Symfony\Component\Filesystem\Filesystem;
8
use Chamilo\CoreBundle\Framework\Container;
9
use Chamilo\CoreBundle\Entity\Asset;
10
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
11
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
12
use Chamilo\CoreBundle\Component\Utils\StateIcon;
13
14
/**
15
 * Library of the settings.php file.
16
 *
17
 * @author Julio Montoya <[email protected]>
18
 * @author Guillaume Viguier <[email protected]>
19
 *
20
 * @since Chamilo 1.8.7
21
 */
22
define('CSS_UPLOAD_PATH', api_get_path(SYMFONY_SYS_PATH).'var/themes/');
23
24
/**
25
 * This function allows easy activating and inactivating of regions.
26
 *
27
 * @author Julio Montoya <[email protected]> Beeznest 2012
28
 */
29
function handleRegions()
30
{
31
    if (isset($_POST['submit_plugins'])) {
32
        storeRegions();
33
        // Add event to the system log.
34
        $user_id = api_get_user_id();
35
        $category = $_GET['category'];
36
        Event::addEvent(
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

36
        Event::/** @scrutinizer ignore-call */ 
37
               addEvent(

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

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

Loading history...
37
            LOG_CONFIGURATION_SETTINGS_CHANGE,
38
            LOG_CONFIGURATION_SETTINGS_CATEGORY,
39
            $category,
40
            api_get_utc_datetime(),
41
            $user_id
42
        );
43
        echo Display::return_message(get_lang('The settings have been stored'), 'confirmation');
44
    }
45
46
    $plugin_obj = new AppPlugin();
47
    $installed_plugins = $plugin_obj->getInstalledPlugins();
48
49
    echo '<form name="plugins" method="post" action="'.api_get_self().'?category='.Security::remove_XSS($_GET['category']).'">';
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['category']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

49
    echo '<form name="plugins" method="post" action="'.api_get_self().'?category='./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['category']).'">';
Loading history...
50
    echo '<table class="data_table">';
51
    echo '<tr>';
52
    echo '<th width="400px">';
53
    echo get_lang('Plugin');
54
    echo '</th><th>';
55
    echo get_lang('Regions');
56
    echo '</th>';
57
    echo '</th>';
58
    echo '</tr>';
59
60
    /* We display all the possible plugins and the checkboxes */
61
    $plugin_region_list = [];
62
    $my_plugin_list = $plugin_obj->getPluginRegions();
63
    foreach ($my_plugin_list as $plugin_item) {
64
        $plugin_region_list[$plugin_item] = $plugin_item;
65
    }
66
67
    // Removing course tool
68
    unset($plugin_region_list['course_tool_plugin']);
69
70
    foreach ($installed_plugins as $pluginName) {
71
        $plugin_info_file = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/plugin.php';
72
73
        if (file_exists($plugin_info_file)) {
74
            $plugin_info = [];
75
            require $plugin_info_file;
76
            if (isset($_GET['name']) && $_GET['name'] === $pluginName) {
77
                echo '<tr class="row_selected">';
78
            } else {
79
                echo '<tr>';
80
            }
81
            echo '<td>';
82
            echo '<h4>'.$plugin_info['title'].' <small>v'.$plugin_info['version'].'</small></h4>';
83
            echo '<p>'.$plugin_info['comment'].'</p>';
84
            echo '</td><td>';
85
            $selected_plugins = $plugin_obj->get_areas_by_plugin($pluginName);
86
            $region_list = [];
87
            $isAdminPlugin = isset($plugin_info['is_admin_plugin']) && $plugin_info['is_admin_plugin'];
88
            $isCoursePlugin = isset($plugin_info['is_course_plugin']) && $plugin_info['is_course_plugin'];
89
90
            if (!$isAdminPlugin && !$isCoursePlugin) {
91
                $region_list = $plugin_region_list;
92
            } else {
93
                if ($isAdminPlugin) {
94
                    $region_list['menu_administrator'] = 'menu_administrator';
95
                }
96
                if ($isCoursePlugin) {
97
                    $region_list['course_tool_plugin'] = 'course_tool_plugin';
98
                }
99
            }
100
101
            echo Display::select(
102
                'plugin_'.$pluginName.'[]',
103
                $region_list,
104
                $selected_plugins,
105
                ['multiple' => 'multiple', 'style' => 'width:500px'],
106
                true,
107
                get_lang('none')
108
            );
109
            echo '</td></tr>';
110
        }
111
    }
112
    echo '</table>';
113
    echo '<br />';
114
    echo '<button class="btn btn--success" type="submit" name="submit_plugins">'.get_lang('Enable the selected plugins').'</button></form>';
115
}
116
117
function handleExtensions()
118
{
119
    echo Display::page_subheader(get_lang('Configure extensions'));
120
    echo '<a class="btn btn--success" href="configure_extensions.php?display=ppt2lp" role="button">'.get_lang('Chamilo RAPID').'</a>';
121
}
122
123
/**
124
 * This function allows easy activating and inactivating of plugins.
125
 *
126
 * @todo: a similar function needs to be written to activate or inactivate additional tools.
127
 *
128
 * @author Patrick Cool <[email protected]>, Ghent University
129
 * @author Julio Montoya <[email protected]> Beeznest 2012
130
 */
131
function handlePlugins()
132
{
133
    Session::erase('plugin_data');
134
    $plugin_obj = new AppPlugin();
135
    $token = Security::get_token();
136
    if (isset($_POST['submit_plugins'])) {
137
        storePlugins();
138
        // Add event to the system log.
139
        $user_id = api_get_user_id();
140
        $category = $_GET['category'];
141
        Event::addEvent(
142
            LOG_CONFIGURATION_SETTINGS_CHANGE,
143
            LOG_CONFIGURATION_SETTINGS_CATEGORY,
144
            $category,
145
            api_get_utc_datetime(),
146
            $user_id
147
        );
148
        echo Display::return_message(get_lang('The settings have been stored'), 'confirmation');
149
    }
150
151
    $all_plugins = $plugin_obj->read_plugins_from_path();
152
    $installed_plugins = $plugin_obj->getInstalledPlugins();
153
154
    // Plugins NOT installed
155
    echo Display::page_subheader(get_lang('Plugins'));
156
    echo '<form class="form-horizontal" name="plugins" method="post" action="'.api_get_self().'?category='.Security::remove_XSS($_GET['category']).'&sec_token='.$token.'">';
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['category']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

156
    echo '<form class="form-horizontal" name="plugins" method="post" action="'.api_get_self().'?category='./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['category']).'&sec_token='.$token.'">';
Loading history...
157
    echo '<table class="table table-hover table-striped table-bordered table-fixed">';
158
    echo '<thead class="bg-gray-50">';
159
    echo '<tr>';
160
    echo '<th width="20px" class="px-6 py-2 uppercase font-normal leading-normal mt-0 mb-2 text-gray-500">';
161
    echo get_lang('Action');
162
    echo '</th><th class="px-6 py-2 uppercase font-normal leading-normal mt-0 mb-2 text-gray-500">';
163
    echo get_lang('Description');
164
    echo '</th>';
165
    echo '</tr>';
166
    echo '</thead>';
167
168
    /*$plugin_list = array();
169
    $my_plugin_list = $plugin_obj->get_plugin_regions();
170
    foreach($my_plugin_list as $plugin_item) {
171
        $plugin_list[$plugin_item] = $plugin_item;
172
    }*/
173
    $installed = '';
174
    $notInstalled = '';
175
    foreach ($all_plugins as $pluginName) {
176
        $plugin_info_file = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/plugin.php';
177
        if (file_exists($plugin_info_file)) {
178
            $plugin_info = [];
179
            require $plugin_info_file;
180
181
            $pluginRow = '';
182
183
            if (in_array($pluginName, $installed_plugins)) {
184
                $pluginRow .= '<tr class="row_selected whitespace-nowrap">';
185
            } else {
186
                $pluginRow .= '<tr class="whitespace-nowrap">';
187
            }
188
            $pluginRow .= '<td class="px-6 py-4 text-sm text-gray-500">';
189
            // Checkbox
190
            if (in_array($pluginName, $installed_plugins)) {
191
                $pluginRow .= '<input type="checkbox" name="plugin_'.$pluginName.'[]" checked="checked" class="border rounded">';
192
            } else {
193
                $pluginRow .= '<input type="checkbox" name="plugin_'.$pluginName.'[]" class="border rounded">';
194
            }
195
            $pluginRow .= '</td><td class="px-6 py-4 text-sm text-gray-500">';
196
            $pluginRow .= '<h3 class="text-3xl font-normal leading-normal mt-0 mb-2">'.$plugin_info['title'].' <small>v '.$plugin_info['version'].'</small></h3>';
197
            $pluginRow .= '<p>'.$plugin_info['comment'].'</p>';
198
            $pluginRow .= '<p>'.get_lang('Author').': '.$plugin_info['author'].'</p>';
199
200
            $pluginRow .= '<div class="btn-group btn-group-sm mt-4">';
201
            if (in_array($pluginName, $installed_plugins)) {
202
                $pluginRow .= Display::url(
203
                    '<em class="fa fa-cogs"></em> '.get_lang('Configure'),
204
                    'configure_plugin.php?name='.$pluginName,
205
                    ['class' => 'btn btn--primary']
206
                );
207
                $pluginRow .= Display::url(
208
                    '<em class="fa fa-th-large"></em> '.get_lang('Regions'),
209
                    'settings.php?category=Regions&name='.$pluginName,
210
                    ['class' => 'btn btn--primary']
211
                );
212
            }
213
214
            if (file_exists(api_get_path(SYS_PLUGIN_PATH).$pluginName.'/readme.txt')) {
215
                $pluginRow .= Display::url(
216
                    "<em class='fa fa-file-text-o'></em> readme.txt",
217
                    api_get_path(WEB_PLUGIN_PATH).$pluginName."/readme.txt",
218
                    [
219
                        'class' => 'btn btn-blue ajax',
220
                        'data-title' => $plugin_info['title'],
221
                        'data-size' => 'lg',
222
                        '_target' => '_blank',
223
                    ]
224
                );
225
            }
226
227
            $readmeFile = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/README.md';
228
            if (file_exists($readmeFile)) {
229
                $pluginRow .= Display::url(
230
                    "<em class='fa fa-file-text-o'></em> README.md",
231
                    api_get_path(WEB_AJAX_PATH).'plugin.ajax.php?a=md_to_html&plugin='.$pluginName,
232
                    [
233
                        'class' => 'btn btn-blue ajax',
234
                        'data-title' => $plugin_info['title'],
235
                        'data-size' => 'lg',
236
                        '_target' => '_blank',
237
                    ]
238
                );
239
            }
240
241
            $pluginRow .= '</div>';
242
            $pluginRow .= '</td></tr>';
243
244
            if (in_array($pluginName, $installed_plugins)) {
245
                $installed .= $pluginRow;
246
            } else {
247
                $notInstalled .= $pluginRow;
248
            }
249
        }
250
    }
251
    echo '<tbody class="bg-white">';
252
    echo $installed;
253
    echo $notInstalled;
254
    echo '</tbody>';
255
    echo '</table>';
256
257
    echo '<div class="form-actions bottom_actions">';
258
    echo '<button class="btn btn--primary" type="submit" name="submit_plugins">';
259
    echo '<i class="fa fa-check" aria-hidden="true"></i> ';
260
    echo get_lang('Enable the selected plugins').'</button>';
261
    echo '</div>';
262
    echo '</form>';
263
}
264
265
/**
266
 * Creates the folder (if needed) and uploads the stylesheet in it.
267
 *
268
 * @param array $values  the values of the form
269
 * @param array $picture the values of the uploaded file
270
 *
271
 * @return bool
272
 *
273
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
274
 *
275
 * @version May 2008
276
 *
277
 * @since v1.8.5
278
 */
279
function uploadStylesheet($values, $picture)
280
{
281
    $result = false;
282
    // Valid name for the stylesheet folder.
283
    $style_name = api_preg_replace('/[^A-Za-z0-9]/', '', $values['name_stylesheet']);
284
    if (empty($style_name) || is_array($style_name)) {
285
        // The name of the uploaded stylesheet doesn't have the expected format
286
        return $result;
287
    }
288
    $cssToUpload = CSS_UPLOAD_PATH;
289
290
    // Check if a virtual instance vchamilo is used
291
    $virtualInstanceTheme = api_get_configuration_value('virtual_css_theme_folder');
292
    if (!empty($virtualInstanceTheme)) {
293
        $cssToUpload = $cssToUpload.$virtualInstanceTheme.'/';
294
    }
295
296
    // Create the folder if needed.
297
    if (!is_dir($cssToUpload.$style_name.'/')) {
298
        mkdir($cssToUpload.$style_name.'/', api_get_permissions_for_new_directories());
299
    }
300
301
    $info = pathinfo($picture['name']);
302
303
    if ('zip' == $info['extension']) {
304
        // Try to open the file and extract it in the theme.
305
        $zip = new ZipArchive();
306
        if ($zip->open($picture['tmp_name'])) {
307
            // Make sure all files inside the zip are images or css.
308
            $num_files = $zip->numFiles;
309
            $valid = true;
310
            $single_directory = true;
311
            $invalid_files = [];
312
313
            $allowedFiles = getAllowedFileTypes();
314
315
            for ($i = 0; $i < $num_files; $i++) {
316
                $file = $zip->statIndex($i);
317
                if ('/' != substr($file['name'], -1)) {
318
                    $path_parts = pathinfo($file['name']);
319
                    if (!in_array($path_parts['extension'], $allowedFiles)) {
320
                        $valid = false;
321
                        $invalid_files[] = $file['name'];
322
                    }
323
                }
324
325
                if (false === strpos($file['name'], '/')) {
326
                    $single_directory = false;
327
                }
328
            }
329
            if (!$valid) {
330
                $error_string = '<ul>';
331
                foreach ($invalid_files as $invalid_file) {
332
                    $error_string .= '<li>'.$invalid_file.'</li>';
333
                }
334
                $error_string .= '</ul>';
335
                echo Display::return_message(
336
                    get_lang('The only accepted extensions in the ZIP file are .jp(e)g, .png, .gif and .css.').$error_string,
337
                    'error',
338
                    false
339
                );
340
            } else {
341
                // If the zip does not contain a single directory, extract it.
342
                if (!$single_directory) {
343
                    // Extract zip file.
344
                    $zip->extractTo($cssToUpload.$style_name.'/');
345
                    $result = true;
346
                } else {
347
                    $extraction_path = $cssToUpload.$style_name.'/';
348
                    $mode = api_get_permissions_for_new_directories();
349
                    for ($i = 0; $i < $num_files; $i++) {
350
                        $entry = $zip->getNameIndex($i);
351
                        if ('/' == substr($entry, -1)) {
352
                            continue;
353
                        }
354
355
                        $pos_slash = strpos($entry, '/');
356
                        $entry_without_first_dir = substr($entry, $pos_slash + 1);
357
                        // If there is still a slash, we need to make sure the directories are created.
358
                        if (false !== strpos($entry_without_first_dir, '/')) {
359
                            if (!is_dir($extraction_path.dirname($entry_without_first_dir))) {
360
                                // Create it.
361
                                @mkdir($extraction_path.dirname($entry_without_first_dir), $mode, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

361
                                /** @scrutinizer ignore-unhandled */ @mkdir($extraction_path.dirname($entry_without_first_dir), $mode, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
362
                            }
363
                        }
364
365
                        $fp = $zip->getStream($entry);
366
                        $ofp = fopen($extraction_path.dirname($entry_without_first_dir).'/'.basename($entry), 'w');
367
368
                        while (!feof($fp)) {
369
                            fwrite($ofp, fread($fp, 8192));
370
                        }
371
372
                        fclose($fp);
373
                        fclose($ofp);
374
                    }
375
                    $result = true;
376
                }
377
            }
378
            $zip->close();
379
        } else {
380
            echo Display::return_message(get_lang('Error reading ZIP file').$info['extension'], 'error', false);
381
        }
382
    } else {
383
        // Simply move the file.
384
        move_uploaded_file($picture['tmp_name'], $cssToUpload.$style_name.'/'.$picture['name']);
385
        $result = true;
386
    }
387
388
    if ($result) {
389
        $fs = new Filesystem();
390
        $fs->mirror(
391
            CSS_UPLOAD_PATH,
392
            api_get_path(SYMFONY_SYS_PATH).'var/themes/',
393
            null,
394
            ['override' => true]
395
        );
396
    }
397
398
    return $result;
399
}
400
401
/**
402
 * Store plugin regions.
403
 */
404
function storeRegions()
405
{
406
    $plugin_obj = new AppPlugin();
407
408
    // Get a list of all current 'Plugins' settings
409
    $installed_plugins = $plugin_obj->getInstalledPlugins();
410
    $shortlist_installed = [];
411
    if (!empty($installed_plugins)) {
412
        foreach ($installed_plugins as $plugin) {
413
            if (isset($plugin['subkey'])) {
414
                $shortlist_installed[] = $plugin['subkey'];
415
            }
416
        }
417
    }
418
419
    $plugin_list = $plugin_obj->read_plugins_from_path();
420
421
    foreach ($plugin_list as $plugin) {
422
        if (isset($_POST['plugin_'.$plugin])) {
423
            $areas_to_installed = $_POST['plugin_'.$plugin];
424
            if (!empty($areas_to_installed)) {
425
                $plugin_obj->removeAllRegions($plugin);
426
                foreach ($areas_to_installed as $region) {
427
                    if (!empty($region) && '-1' != $region) {
428
                        $plugin_obj->add_to_region($plugin, $region);
429
                    }
430
                }
431
            }
432
        }
433
    }
434
}
435
436
/**
437
 * This function allows easy activating and inactivating of plugins.
438
 *
439
 * @author Patrick Cool <[email protected]>, Ghent University
440
 */
441
function storePlugins()
442
{
443
    $appPlugin = new AppPlugin();
444
    // Get a list of all current 'Plugins' settings
445
    $plugin_list = $appPlugin->read_plugins_from_path();
446
    $installed_plugins = [];
447
448
    foreach ($plugin_list as $plugin) {
449
        if (isset($_POST['plugin_'.$plugin])) {
450
            $appPlugin->install($plugin);
451
            $installed_plugins[] = $plugin;
452
        }
453
    }
454
455
    if (!empty($installed_plugins)) {
456
        $remove_plugins = array_diff($plugin_list, $installed_plugins);
457
    } else {
458
        $remove_plugins = $plugin_list;
459
    }
460
461
    foreach ($remove_plugins as $plugin) {
462
        $appPlugin->uninstall($plugin);
463
    }
464
}
465
466
/**
467
 * This function checks if the given style is a recognize style that exists in the css directory as
468
 * a standalone directory.
469
 *
470
 * @param string $style
471
 *
472
 * @return bool True if this style is recognized, false otherwise
473
 */
474
function isStyle($style)
475
{
476
    $themeList = api_get_themes();
477
478
    return in_array($style, array_keys($themeList));
479
}
480
481
/**
482
 * Search options
483
 * TODO: support for multiple site. aka $_configuration['access_url'] == 1.
484
 *
485
 * @author Marco Villegas <[email protected]>
486
 */
487
function handleSearch()
488
{
489
    global $SettingsStored, $_configuration;
490
491
    $search_enabled = api_get_setting('search_enabled');
492
493
    $form = new FormValidator(
494
        'search-options',
495
        'post',
496
        api_get_self().'?category=Search'
497
    );
498
    $values = api_get_settings_options('search_enabled');
499
    $form->addElement('header', null, get_lang('Fulltext search'));
500
501
    $group = formGenerateElementsGroup($form, $values, 'search_enabled');
502
503
    // SearchEnabledComment
504
    $form->addGroup(
505
        $group,
506
        'search_enabled',
507
        [get_lang('Fulltext search'), get_lang('This feature allows you to index most of the documents uploaded to your portal, then provide a search feature for users.<br />This feature will not index documents that have already been uploaded, so it is important to enable (if wanted) at the beginning of your implementation.<br />Once enabled, a search box will appear in the courses list of every user. Searching for a specific term will bring a list of corresponding documents, exercises or forum topics, filtered depending on the availability of these contents to the user.')],
508
        null,
509
        false
510
    );
511
512
    $search_enabled = api_get_setting('search_enabled');
513
514
    if ($form->validate()) {
515
        $formValues = $form->exportValues();
516
        setConfigurationSettingsInDatabase($formValues, $_configuration['access_url']);
517
        $search_enabled = $formValues['search_enabled'];
518
        echo Display::return_message($SettingsStored, 'confirm');
519
    }
520
    $specific_fields = get_specific_field_list();
521
522
    if ('true' == $search_enabled) {
523
        $values = api_get_settings_options('search_show_unlinked_results');
524
        $group = formGenerateElementsGroup(
525
            $form,
526
            $values,
527
            'search_show_unlinked_results'
528
        );
529
        $form->addGroup(
530
            $group,
531
            'search_show_unlinked_results',
532
            [
533
                get_lang('Full-text search: show unlinked results'),
534
                get_lang('When showing the results of a full-text search, what should be done with the results that are not accessible to the current user?'),
535
            ],
536
            null,
537
            false
538
        );
539
        $default_values['search_show_unlinked_results'] = api_get_setting('search_show_unlinked_results');
0 ignored issues
show
Comprehensibility Best Practice introduced by
$default_values was never initialized. Although not strictly required by PHP, it is generally a good practice to add $default_values = array(); before regardless.
Loading history...
540
541
        $sf_values = [];
542
        foreach ($specific_fields as $sf) {
543
            $sf_values[$sf['code']] = $sf['name'];
544
        }
545
        $url = Display::div(
546
            Display::url(
547
                get_lang('Add a specific search field'),
548
                'specific_fields.php'
549
            ),
550
            ['class' => 'sectioncomment']
551
        );
552
        if (empty($sf_values)) {
553
            $form->addElement('label', [get_lang('Specific Field for prefilter'), $url]);
554
        } else {
555
            $form->addElement(
556
                'select',
557
                'search_prefilter_prefix',
558
                [get_lang('Specific Field for prefilter'), $url],
559
                $sf_values,
560
                ''
561
            );
562
            $default_values['search_prefilter_prefix'] = api_get_setting('search_prefilter_prefix');
563
        }
564
    }
565
566
    $default_values['search_enabled'] = $search_enabled;
567
568
    $form->addButtonSave(get_lang('Save'));
569
    $form->setDefaults($default_values);
570
571
    echo '<div id="search-options-form">';
572
    $form->display();
573
    echo '</div>';
574
575
    if ('true' == $search_enabled) {
576
        //$xapianPath = api_get_path(SYS_UPLOAD_PATH).'plugins/xapian/searchdb';
577
578
        /*
579
        @todo Test the Xapian connection
580
        if (extension_loaded('xapian')) {
581
            require_once 'xapian.php';
582
            try {
583
                $db = new XapianDatabase($xapianPath.'/');
584
            } catch (Exception $e) {
585
                var_dump($e->getMessage());
586
            }
587
588
            require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php';
589
            require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php';
590
            require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
591
592
            $indexable = new IndexableChunk();
593
            $indexable->addValue("content", 'Test');
594
595
            $di = new ChamiloIndexer();
596
            $di->connectDb(NULL, NULL, 'english');
597
            $di->addChunk($indexable);
598
            $did = $di->index();
599
        }
600
        */
601
602
        $xapianLoaded = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
603
        $dir_exists = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
604
        $dir_is_writable = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
605
        $specific_fields_exists = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
606
607
        //Testing specific fields
608
        if (empty($specific_fields)) {
609
            $specific_fields_exists = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Add a specific search field')
610
            );
611
        }
612
        //Testing xapian extension
613
        if (!extension_loaded('xapian')) {
614
            $xapianLoaded = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
615
        }
616
        //Testing xapian searchdb path
617
        if (!is_dir($xapianPath)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $xapianPath seems to be never defined.
Loading history...
618
            $dir_exists = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
619
        }
620
        //Testing xapian searchdb path is writable
621
        if (!is_writable($xapianPath)) {
622
            $dir_is_writable = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
623
        }
624
625
        $data = [];
626
        $data[] = [get_lang('Xapian module installed'), $xapianLoaded];
627
        $data[] = [get_lang('The directory exists').' - '.$xapianPath, $dir_exists];
628
        $data[] = [get_lang('Is writable').' - '.$xapianPath, $dir_is_writable];
629
        $data[] = [get_lang('Available custom search fields'), $specific_fields_exists];
630
631
        showSearchSettingsTable($data);
632
        showSearchToolsStatusTable();
633
    }
634
}
635
636
/**
637
 * Wrapper for the templates.
638
 *
639
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
640
 * @author Julio Montoya.
641
 *
642
 * @version August 2008
643
 *
644
 * @since v1.8.6
645
 */
646
function handleTemplates()
647
{
648
    /* Drive-by fix to avoid undefined var warnings, without repeating
649
     * isset() combos all over the place. */
650
    $action = isset($_GET['action']) ? $_GET['action'] : "invalid";
651
652
    if ('add' != $action) {
653
        echo '<div class="actions" style="margin-left: 1px;">';
654
        echo '<a href="settings.php?category=Templates&action=add">'.
655
                Display::getMdiIcon(ObjectIcon::TEMPLATE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Add a template')).'</a>';
656
        echo '</div>';
657
    }
658
659
    if ('add' == $action || ('edit' == $action && is_numeric($_GET['id']))) {
660
        addEditTemplate();
661
662
        // Add event to the system log.
663
        $user_id = api_get_user_id();
664
        $category = $_GET['category'];
665
        Event::addEvent(
666
            LOG_CONFIGURATION_SETTINGS_CHANGE,
667
            LOG_CONFIGURATION_SETTINGS_CATEGORY,
668
            $category,
669
            api_get_utc_datetime(),
670
            $user_id
671
        );
672
    } else {
673
        if ('delete' == $action && is_numeric($_GET['id'])) {
674
            deleteTemplate($_GET['id']);
675
676
            // Add event to the system log
677
            $user_id = api_get_user_id();
678
            $category = $_GET['category'];
679
            Event::addEvent(
680
                LOG_CONFIGURATION_SETTINGS_CHANGE,
681
                LOG_CONFIGURATION_SETTINGS_CATEGORY,
682
                $category,
683
                api_get_utc_datetime(),
684
                $user_id
685
            );
686
        }
687
        displayTemplates();
688
    }
689
}
690
691
/**
692
 * Display a sortable table with all the templates that the platform administrator has defined.
693
 *
694
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
695
 *
696
 * @version August 2008
697
 *
698
 * @since v1.8.6
699
 */
700
function displayTemplates()
701
{
702
    $table = new SortableTable(
703
        'templates',
704
        'getNumberOfTemplates',
705
        'getTemplateData',
706
        1
707
    );
708
    $table->set_additional_parameters(
709
        ['category' => Security::remove_XSS($_GET['category'])]
710
    );
711
    $table->set_header(0, get_lang('Image'), true, ['style' => 'width: 101px;']);
712
    $table->set_header(1, get_lang('Title'));
713
    $table->set_header(2, get_lang('Detail'), false, ['style' => 'width:50px;']);
714
    $table->set_column_filter(2, 'actionsFilter');
715
    $table->set_column_filter(0, 'searchImageFilter');
716
    $table->display();
717
}
718
719
/**
720
 * Gets the number of templates that are defined by the platform admin.
721
 *
722
 * @return int
723
 *
724
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
725
 *
726
 * @version August 2008
727
 *
728
 * @since v1.8.6
729
 */
730
function getNumberOfTemplates()
731
{
732
    // Database table definition.
733
    $table = Database::get_main_table('system_template');
734
735
    // The sql statement.
736
    $sql = "SELECT COUNT(id) AS total FROM $table";
737
    $result = Database::query($sql);
738
    $row = Database::fetch_array($result);
739
740
    // Returning the number of templates.
741
    return $row['total'];
742
}
743
744
/**
745
 * Gets all the template data for the sortable table.
746
 *
747
 * @param int    $from            the start of the limit statement
748
 * @param int    $number_of_items the number of elements that have to be retrieved from the database
749
 * @param int    $column          the column that is
750
 * @param string $direction       the sorting direction (ASC or DESC)
751
 *
752
 * @return array
753
 *
754
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
755
 *
756
 * @version August 2008
757
 *
758
 * @since v1.8.6
759
 */
760
function getTemplateData($from, $number_of_items, $column, $direction)
761
{
762
    // Database table definition.
763
    $table_system_template = Database::get_main_table('system_template');
764
765
    $from = (int) $from;
766
    $number_of_items = (int) $number_of_items;
767
    $column = (int) $column;
768
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
769
    // The sql statement.
770
    $sql = "SELECT id as col0, title as col1, id as col2 FROM $table_system_template";
771
    $sql .= " ORDER BY col$column $direction ";
772
    $sql .= " LIMIT $from,$number_of_items";
773
    $result = Database::query($sql);
774
    $return = [];
775
    while ($row = Database::fetch_array($result)) {
776
        $row['1'] = get_lang($row['1']);
777
        $return[] = $row;
778
    }
779
    // Returning all the information for the sortable table.
780
    return $return;
781
}
782
783
/**
784
 * display the edit and delete icons in the sortable table.
785
 *
786
 * @param int $id the id of the template
787
 *
788
 * @return string code for the link to edit and delete the template
789
 *
790
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
791
 *
792
 * @version August 2008
793
 *
794
 * @since v1.8.6
795
 */
796
function actionsFilter($id)
797
{
798
    $return = '<a href="settings.php?category=Templates&action=edit&id='.Security::remove_XSS($id).'">'.Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')).'</a>';
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($id) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

798
    $return = '<a href="settings.php?category=Templates&action=edit&id='./** @scrutinizer ignore-type */ Security::remove_XSS($id).'">'.Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')).'</a>';
Loading history...
799
    $return .= '<a href="settings.php?category=Templates&action=delete&id='.Security::remove_XSS($id).'" onClick="javascript:if(!confirm('."'".get_lang('Please confirm your choice')."'".')) return false;">'.Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')).'</a>';
800
801
    return $return;
802
}
803
804
function searchImageFilter(int $id): string
805
{
806
    $em = Database::getManager();
807
808
    /** @var SystemTemplate $template */
809
    $template = $em->find(SystemTemplate::class, $id);
810
811
    if (null !== $template->getImage()) {
812
        $assetRepo = Container::getAssetRepository();
813
        $imageUrl = $assetRepo->getAssetUrl($template->getImage());
814
815
        return '<img src="'.$imageUrl.'" alt="'.get_lang('Template preview').'"/>';
816
    } else {
817
        return '<img src="'.api_get_path(WEB_PUBLIC_PATH).'img/template_thumb/noimage.gif" alt="'.get_lang('NoTemplate preview').'"/>';
818
    }
819
}
820
821
/**
822
 * Add (or edit) a template. This function displays the form and also takes
823
 * care of uploading the image and storing the information in the database.
824
 *
825
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
826
 *
827
 * @version August 2008
828
 *
829
 * @since v1.8.6
830
 */
831
function addEditTemplate()
832
{
833
    $em = Database::getManager();
834
    $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
835
836
    $assetRepo = Container::getAssetRepository();
837
838
    /** @var SystemTemplate $template */
839
    $template = $id ? $em->find(SystemTemplate::class, $id) : new SystemTemplate();
840
841
    $form = new FormValidator(
842
        'template',
843
        'post',
844
        'settings.php?category=Templates&action='.Security::remove_XSS($_GET['action']).'&id='.$id
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['action']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

844
        'settings.php?category=Templates&action='./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['action']).'&id='.$id
Loading history...
845
    );
846
847
    // Setting the form elements: the header.
848
    if ('add' == $_GET['action']) {
849
        $title = get_lang('Add a template');
850
    } else {
851
        $title = get_lang('Template edition');
852
    }
853
    $form->addElement('header', '', $title);
854
855
    // Setting the form elements: the title of the template.
856
    $form->addText('title', get_lang('Title'), false);
857
    $form->addText('comment', get_lang('Description'), false);
858
859
    // Setting the form elements: the content of the template (wysiwyg editor).
860
    $form->addHtmlEditor(
861
        'template_text',
862
        get_lang('Text'),
863
        true,
864
        true,
865
        ['ToolbarSet' => 'Documents', 'Width' => '100%', 'Height' => '400']
866
    );
867
868
    // Setting the form elements: the form to upload an image to be used with the template.
869
    if (!$template->hasImage()) {
870
        // Picture
871
        $form->addFile(
872
            'template_image',
873
            get_lang('Add image'),
874
            ['id' => 'picture', 'class' => 'picture-form', 'crop_image' => true, 'crop_ratio' => '1 / 1']
875
        );
876
        $allowedPictureTypes = api_get_supported_image_extensions(false);
877
        $form->addRule('template_image', get_lang('Only PNG, JPG or GIF images allowed').' ('.implode(',', $allowedPictureTypes).')', 'filetype', $allowedPictureTypes);
878
    }
879
880
    // Setting the form elements: a little bit of information about the template image.
881
    $form->addElement('static', 'file_comment', '', get_lang('This image will represent the template in the templates list. It should be no larger than 100x70 pixels'));
882
883
    // Getting all the information of the template when editing a template.
884
    if ('edit' == $_GET['action']) {
885
        $defaults['template_id'] = $id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$defaults was never initialized. Although not strictly required by PHP, it is generally a good practice to add $defaults = array(); before regardless.
Loading history...
886
        $defaults['template_text'] = $template->getContent();
887
        // Forcing get_lang().
888
        $defaults['title'] = $template->getTitle();
889
        $defaults['comment'] = $template->getComment();
890
891
        // Adding an extra field: a hidden field with the id of the template we are editing.
892
        $form->addElement('hidden', 'template_id');
893
894
        // Adding an extra field: a preview of the image that is currently used.
895
896
        if ($template->hasImage()) {
897
            $imageUrl = $assetRepo->getAssetUrl($template->getImage());
898
            $form->addElement(
899
                'static',
900
                'template_image_preview',
901
                '',
902
                '<img src="'.$imageUrl
903
                    .'" alt="'.get_lang('Template preview')
904
                    .'"/>'
905
            );
906
            $form->addCheckBox('delete_image', null, get_lang('Delete picture'));
907
        } else {
908
            $form->addElement(
909
                'static',
910
                'template_image_preview',
911
                '',
912
                '<img src="'.api_get_path(WEB_PUBLIC_PATH).'img/template_thumb/noimage.gif" alt="'.get_lang('NoTemplate preview').'"/>'
913
            );
914
        }
915
916
        // Setting the information of the template that we are editing.
917
        $form->setDefaults($defaults);
918
    }
919
    // Setting the form elements: the submit button.
920
    $form->addButtonSave(get_lang('Validate'), 'submit');
921
922
    // Setting the rules: the required fields.
923
    if (!$template->hasImage()) {
924
        $form->addRule(
925
            'template_image',
926
            get_lang('Required field'),
927
            'required'
928
        );
929
        $form->addRule('title', get_lang('Required field'), 'required');
930
    }
931
932
    // if the form validates (complies to all rules) we save the information,
933
    // else we display the form again (with error message if needed)
934
    if ($form->validate()) {
935
        $check = Security::check_token('post');
936
937
        if ($check) {
938
            // Exporting the values.
939
            $values = $form->exportValues();
940
            $asset = null;
941
            if (isset($values['delete_image']) && !empty($id)) {
942
                deleteTemplateImage($id);
943
            }
944
945
            // Upload the file.
946
            if (!empty($_FILES['template_image']['name'])) {
947
                $picture = $_FILES['template_image'];
948
                if (!empty($picture['name'])) {
949
                    $asset = (new Asset())
950
                        ->setCategory(Asset::SYSTEM_TEMPLATE)
951
                        ->setTitle($picture['name'])
952
                    ;
953
                    if (!empty($values['picture_crop_result'])) {
954
                        $asset->setCrop($values['picture_crop_result']);
955
                    }
956
                    $asset = $assetRepo->createFromRequest($asset, $picture);
957
                }
958
            }
959
960
            // Store the information in the database (as insert or as update).
961
            $bootstrap = api_get_bootstrap_and_font_awesome();
962
            $viewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
963
964
            if ('add' == $_GET['action']) {
965
                $templateContent = '<head>'.$viewport.'<title>'.$values['title'].'</title>'.$bootstrap.'</head>'
966
                    .$values['template_text'];
967
                $template
968
                    ->setTitle($values['title'])
969
                    ->setComment(Security::remove_XSS($values['comment']))
970
                    ->setContent(Security::remove_XSS($templateContent, COURSEMANAGERLOWSECURITY))
971
                    ->setImage($asset);
972
                $em->persist($template);
973
                $em->flush();
974
975
                // Display a feedback message.
976
                echo Display::return_message(
977
                    get_lang('Template added'),
978
                    'confirm'
979
                );
980
                echo '<a href="settings.php?category=Templates&action=add">'.
981
                    Display::getMdiIcon(ObjectIcon::TEMPLATE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Add a template')).
982
                    '</a>';
983
            } else {
984
                $templateContent = '<head>'.$viewport.'<title>'.$values['title'].'</title>'.$bootstrap.'</head>'
985
                    .$values['template_text'];
986
987
                $template
988
                    ->setTitle($values['title'])
989
                    ->setContent(Security::remove_XSS($templateContent, COURSEMANAGERLOWSECURITY));
990
991
                if ($asset) {
992
                    $template->setImage($asset);
993
                }
994
995
                $em->persist($template);
996
                $em->flush();
997
998
                // Display a feedback message.
999
                echo Display::return_message(get_lang('Template edited'), 'confirm');
1000
            }
1001
        }
1002
        Security::clear_token();
1003
        displayTemplates();
1004
    } else {
1005
        $token = Security::get_token();
1006
        $form->addElement('hidden', 'sec_token');
1007
        $form->setConstants(['sec_token' => $token]);
1008
        // Display the form.
1009
        $form->display();
1010
    }
1011
}
1012
1013
/**
1014
 * Deletes the template picture as asset.
1015
 *
1016
 * @param int $id
1017
 */
1018
function deleteTemplateImage($id)
1019
{
1020
    $em = Database::getManager();
1021
1022
    /** @var SystemTemplate $template */
1023
    $template = $em->find(SystemTemplate::class, $id);
1024
1025
    if ($template && $template->hasImage()) {
1026
        $image = $template->getImage();
1027
        $em->remove($image);
1028
        $em->flush();
1029
    }
1030
}
1031
1032
/**
1033
 * Delete a template.
1034
 *
1035
 * @param int $id the id of the template that has to be deleted
1036
 *
1037
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
1038
 *
1039
 * @version August 2008
1040
 *
1041
 * @since v1.8.6
1042
 */
1043
function deleteTemplate($id)
1044
{
1045
    $id = intval($id);
1046
    // First we remove the image.
1047
    $table = Database::get_main_table('system_template');
1048
    $sql = "SELECT * FROM $table WHERE id = $id";
1049
    $result = Database::query($sql);
1050
    $row = Database::fetch_array($result);
1051
    if (!empty($row['image'])) {
1052
        @unlink(api_get_path(SYS_APP_PATH).'home/default_platform_document/template_thumb/'.$row['image']);
0 ignored issues
show
Bug introduced by
The constant SYS_APP_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1052
        /** @scrutinizer ignore-unhandled */ @unlink(api_get_path(SYS_APP_PATH).'home/default_platform_document/template_thumb/'.$row['image']);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1053
    }
1054
1055
    // Now we remove it from the database.
1056
    $sql = "DELETE FROM $table WHERE id = $id";
1057
    Database::query($sql);
1058
1059
    deleteTemplateImage($id);
1060
1061
    // Display a feedback message.
1062
    echo Display::return_message(get_lang('Template deleted'), 'confirm');
1063
}
1064
1065
/**
1066
 * @param array $settings
1067
 * @param array $settings_by_access_list
1068
 *
1069
 * @throws \Doctrine\ORM\ORMException
1070
 * @throws \Doctrine\ORM\OptimisticLockException
1071
 * @throws \Doctrine\ORM\TransactionRequiredException
1072
 *
1073
 * @return FormValidator
1074
 */
1075
function generateSettingsForm($settings, $settings_by_access_list)
1076
{
1077
    global $_configuration, $settings_to_avoid, $convert_byte_to_mega_list;
1078
    $em = Database::getManager();
1079
    $table_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS);
1080
1081
    $form = new FormValidator(
1082
        'settings',
1083
        'post',
1084
        'settings.php?category='.Security::remove_XSS($_GET['category'])
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['category']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

1084
        'settings.php?category='./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['category'])
Loading history...
1085
    );
1086
1087
    $form->addElement(
1088
        'hidden',
1089
        'search_field',
1090
        (!empty($_GET['search_field']) ? Security::remove_XSS($_GET['search_field']) : null)
1091
    );
1092
1093
    $url_id = api_get_current_access_url_id();
1094
    /*
1095
    if (!empty($_configuration['multiple_access_urls']) && api_is_global_platform_admin() && $url_id == 1) {
1096
        $group = array();
1097
        $group[] = $form->createElement('button', 'mark_all', get_lang('Select all'));
1098
        $group[] = $form->createElement('button', 'unmark_all', get_lang('Unselect all'));
1099
        $form->addGroup($group, 'buttons_in_action_right');
1100
    }*/
1101
1102
    $default_values = [];
1103
    $url_info = api_get_access_url($url_id);
1104
    $i = 0;
1105
    $addedSettings = [];
1106
    foreach ($settings as $row) {
1107
        if (in_array($row['variable'], array_keys($settings_to_avoid))) {
1108
            continue;
1109
        }
1110
1111
        if (in_array($row['variable'], $addedSettings)) {
1112
            continue;
1113
        }
1114
1115
        $addedSettings[] = $row['variable'];
1116
1117
        if (!empty($_configuration['multiple_access_urls'])) {
1118
            if (api_is_global_platform_admin()) {
1119
                if (0 == $row['access_url_locked']) {
1120
                    if (1 == $url_id) {
1121
                        if ('1' == $row['access_url_changeable']) {
1122
                            $form->addElement(
1123
                                'html',
1124
                                '<div class="float-right"><a class="share_this_setting" data_status = "0"  data_to_send = "'.$row['variable'].'" href="javascript:void(0);">'.
1125
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</a></div>'
1126
                            );
1127
                        } else {
1128
                            $form->addElement(
1129
                                'html',
1130
                                '<div class="float-right"><a class="share_this_setting" data_status = "1" data_to_send = "'.$row['variable'].'" href="javascript:void(0);">'.
1131
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</a></div>'
1132
                            );
1133
                        }
1134
                    } else {
1135
                        if ('1' == $row['access_url_changeable']) {
1136
                            $form->addElement(
1137
                                'html',
1138
                                '<div class="float-right">'.
1139
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</div>'
1140
                            );
1141
                        } else {
1142
                            $form->addElement(
1143
                                'html',
1144
                                '<div class="float-right">'.
1145
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</div>'
1146
                            );
1147
                        }
1148
                    }
1149
                }
1150
            }
1151
        }
1152
1153
        $hideme = [];
1154
        $hide_element = false;
1155
1156
        if (1 != $_configuration['access_url']) {
1157
            if (0 == $row['access_url_changeable']) {
1158
                // We hide the element in other cases (checkbox, radiobutton) we 'freeze' the element.
1159
                $hide_element = true;
1160
                $hideme = ['disabled'];
1161
            } elseif (1 == $url_info['active']) {
1162
                // We show the elements.
1163
                if (empty($row['variable'])) {
1164
                    $row['variable'] = 0;
1165
                }
1166
                if (empty($row['subkey'])) {
1167
                    $row['subkey'] = 0;
1168
                }
1169
                if (empty($row['category'])) {
1170
                    $row['category'] = 0;
1171
                }
1172
                if (isset($settings_by_access_list[$row['variable']]) &&
1173
                    isset($settings_by_access_list[$row['variable']][$row['subkey']]) &&
1174
                    is_array($settings_by_access_list[$row['variable']][$row['subkey']][$row['category']])
1175
                ) {
1176
                    // We are sure that the other site have a selected value.
1177
                    if ('' != $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']]['selected_value']) {
1178
                        $row['selected_value'] = $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']]['selected_value'];
1179
                    }
1180
                }
1181
                // There is no else{} statement because we load the default $row['selected_value'] of the main Chamilo site.
1182
            }
1183
        }
1184
1185
        switch ($row['type']) {
1186
            case 'textfield':
1187
                if (in_array($row['variable'], $convert_byte_to_mega_list)) {
1188
                    $form->addElement(
1189
                        'text',
1190
                        $row['variable'],
1191
                        [
1192
                            get_lang($row['title']),
1193
                            get_lang($row['comment']),
1194
                            get_lang('MB'),
1195
                        ],
1196
                        ['maxlength' => '8', 'aria-label' => get_lang($row['title'])]
1197
                    );
1198
                    $form->applyFilter($row['variable'], 'html_filter');
1199
                    $default_values[$row['variable']] = round($row['selected_value'] / 1024 / 1024, 1);
1200
                } elseif ('account_valid_duration' == $row['variable']) {
1201
                    $form->addElement(
1202
                        'text',
1203
                        $row['variable'],
1204
                        [
1205
                            get_lang($row['title']),
1206
                            get_lang($row['comment']),
1207
                        ],
1208
                        ['maxlength' => '5', 'aria-label' => get_lang($row['title'])]
1209
                    );
1210
                    $form->applyFilter($row['variable'], 'html_filter');
1211
1212
                    // For platform character set selection:
1213
                    // Conversion of the textfield to a select box with valid values.
1214
                    $default_values[$row['variable']] = $row['selected_value'];
1215
                } elseif ('platform_charset' == $row['variable']) {
1216
                    break;
1217
                } else {
1218
                    $hideme['class'] = 'col-md-4';
1219
                    $hideme['aria-label'] = get_lang($row['title']);
1220
                    $form->addElement(
1221
                        'text',
1222
                        $row['variable'],
1223
                        [
1224
                            get_lang($row['title']),
1225
                            get_lang($row['comment']),
1226
                        ],
1227
                        $hideme
1228
                    );
1229
                    $form->applyFilter($row['variable'], 'html_filter');
1230
                    $default_values[$row['variable']] = $row['selected_value'];
1231
                }
1232
                break;
1233
            case 'textarea':
1234
                if ('header_extra_content' == $row['variable']) {
1235
                    $file = api_get_home_path().'header_extra_content.txt';
0 ignored issues
show
Bug introduced by
The function api_get_home_path was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1235
                    $file = /** @scrutinizer ignore-call */ api_get_home_path().'header_extra_content.txt';
Loading history...
1236
                    $value = '';
1237
                    if (file_exists($file)) {
1238
                        $value = file_get_contents($file);
1239
                    }
1240
                    $form->addElement(
1241
                        'textarea',
1242
                        $row['variable'],
1243
                        [get_lang($row['title']), get_lang($row['comment'])],
1244
                        ['rows' => '10', 'id' => $row['variable']],
1245
                        $hideme
1246
                    );
1247
                    $default_values[$row['variable']] = $value;
1248
                } elseif ('footer_extra_content' == $row['variable']) {
1249
                    $file = api_get_home_path().'footer_extra_content.txt';
1250
                    $value = '';
1251
                    if (file_exists($file)) {
1252
                        $value = file_get_contents($file);
1253
                    }
1254
                    $form->addElement(
1255
                        'textarea',
1256
                        $row['variable'],
1257
                        [get_lang($row['title']), get_lang($row['comment'])],
1258
                        ['rows' => '10', 'id' => $row['variable']],
1259
                        $hideme
1260
                    );
1261
                    $default_values[$row['variable']] = $value;
1262
                } else {
1263
                    $form->addElement(
1264
                        'textarea',
1265
                        $row['variable'],
1266
                        [get_lang($row['title']),
1267
                        get_lang($row['comment']), ],
1268
                        ['rows' => '10', 'id' => $row['variable']],
1269
                        $hideme
1270
                    );
1271
                    $default_values[$row['variable']] = $row['selected_value'];
1272
                }
1273
                break;
1274
            case 'radio':
1275
                $values = api_get_settings_options($row['variable']);
1276
                $group = [];
1277
                if (is_array($values)) {
1278
                    foreach ($values as $key => $value) {
1279
                        $element = &$form->createElement(
1280
                            'radio',
1281
                            $row['variable'],
1282
                            '',
1283
                            get_lang($value['display_text']),
1284
                            $value['value']
1285
                        );
1286
                        if ($hide_element) {
1287
                            $element->freeze();
1288
                        }
1289
                        $group[] = $element;
1290
                    }
1291
                }
1292
                $form->addGroup(
1293
                    $group,
1294
                    $row['variable'],
1295
                    [get_lang($row['title']), get_lang($row['comment'])],
1296
                    null,
1297
                    false
1298
                );
1299
                $default_values[$row['variable']] = $row['selected_value'];
1300
                break;
1301
            case 'checkbox':
1302
                // 1. We collect all the options of this variable.
1303
                $sql = "SELECT * FROM $table_settings_current
1304
                        WHERE variable='".$row['variable']."' AND access_url =  1";
1305
1306
                $result = Database::query($sql);
1307
                $group = [];
1308
                while ($rowkeys = Database::fetch_array($result)) {
1309
                    // Profile tab option should be hidden when the social tool is enabled.
1310
                    if ('true' == api_get_setting('allow_social_tool')) {
1311
                        if ('show_tabs' === $rowkeys['variable'] && 'my_profile' === $rowkeys['subkey']) {
1312
                            continue;
1313
                        }
1314
                    }
1315
1316
                    // Hiding the gradebook option.
1317
                    if ('show_tabs' === $rowkeys['variable'] && 'my_gradebook' === $rowkeys['subkey']) {
1318
                        continue;
1319
                    }
1320
1321
                    $element = &$form->createElement(
1322
                        'checkbox',
1323
                        $rowkeys['subkey'],
1324
                        '',
1325
                        get_lang($rowkeys['subkeytext'])
1326
                    );
1327
1328
                    if (1 == $row['access_url_changeable']) {
1329
                        // 2. We look into the DB if there is a setting for a specific access_url.
1330
                        $access_url = $_configuration['access_url'];
1331
                        if (empty($access_url)) {
1332
                            $access_url = 1;
1333
                        }
1334
                        $sql = "SELECT selected_value FROM $table_settings_current
1335
                                WHERE
1336
                                    variable='".$rowkeys['variable']."' AND
1337
                                    subkey='".$rowkeys['subkey']."' AND
1338
                                    subkeytext='".$rowkeys['subkeytext']."' AND
1339
                                    access_url =  $access_url";
1340
                        $result_access = Database::query($sql);
1341
                        $row_access = Database::fetch_array($result_access);
1342
                        if ('true' === $row_access['selected_value'] && !$form->isSubmitted()) {
1343
                            $element->setChecked(true);
1344
                        }
1345
                    } else {
1346
                        if ('true' === $rowkeys['selected_value'] && !$form->isSubmitted()) {
1347
                            $element->setChecked(true);
1348
                        }
1349
                    }
1350
                    if ($hide_element) {
1351
                        $element->freeze();
1352
                    }
1353
                    $group[] = $element;
1354
                }
1355
                $form->addGroup(
1356
                    $group,
1357
                    $row['variable'],
1358
                    [get_lang($row['title']), get_lang($row['comment'])],
1359
                    null
1360
                );
1361
                break;
1362
            case 'link':
1363
                $form->addElement(
1364
                    'static',
1365
                    null,
1366
                    [get_lang($row['title']), get_lang($row['comment'])],
1367
                    get_lang('current value').' : '.$row['selected_value'],
1368
                    $hideme
1369
                );
1370
                break;
1371
            case 'select':
1372
                /*
1373
                * To populate the list of options, the select type dynamically calls a function that must be called select_ + the name of the variable being displayed.
1374
                * The functions being called must be added to the file settings.lib.php.
1375
                */
1376
                $form->addElement(
1377
                    'select',
1378
                    $row['variable'],
1379
                    [get_lang($row['title']), get_lang($row['comment'])],
1380
                    call_user_func('select_'.$row['variable']),
1381
                    $hideme
1382
                );
1383
                $default_values[$row['variable']] = $row['selected_value'];
1384
                break;
1385
            case 'custom':
1386
                break;
1387
            case 'select_course':
1388
                $courseSelectOptions = [];
1389
1390
                if (!empty($row['selected_value'])) {
1391
                    $course = $em->find('ChamiloCoreBundle:Course', $row['selected_value']);
1392
1393
                    $courseSelectOptions[$course->getId()] = $course->getTitle();
1394
                }
1395
1396
                $form->addElement(
1397
                    'select_ajax',
1398
                    $row['variable'],
1399
                    [get_lang($row['title']), get_lang($row['comment'])],
1400
                    $courseSelectOptions,
1401
                    ['url' => api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_course']
1402
                );
1403
                $default_values[$row['variable']] = $row['selected_value'];
1404
                break;
1405
        }
1406
1407
        switch ($row['variable']) {
1408
            case 'pdf_export_watermark_enable':
1409
                $url = PDF::get_watermark(null);
1410
1411
                if (false != $url) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $url of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
1412
                    $delete_url = '<a href="?delete_watermark">'.get_lang('Remove picture').' '.Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Remove picture')).'</a>';
1413
                    $form->addElement('html', '<div style="max-height:100px; max-width:100px; margin-left:162px; margin-bottom:10px; clear:both;"><img src="'.$url.'" style="margin-bottom:10px;" />'.$delete_url.'</div>');
1414
                }
1415
1416
                $form->addElement('file', 'pdf_export_watermark_path', get_lang('Upload a watermark image'));
1417
                $allowed_picture_types = ['jpg', 'jpeg', 'png', 'gif'];
1418
                $form->addRule(
1419
                    'pdf_export_watermark_path',
1420
                    get_lang('Only PNG, JPG or GIF images allowed').' ('.implode(',', $allowed_picture_types).')',
1421
                    'filetype',
1422
                    $allowed_picture_types
1423
                );
1424
1425
                break;
1426
            case 'timezone_value':
1427
                $timezone = $row['selected_value'];
1428
                if (empty($timezone)) {
1429
                    $timezone = api_get_timezone();
1430
                }
1431
                $form->addLabel('', sprintf(get_lang('The local time in the portal timezone (%s) is %s'), $timezone, api_get_local_time()));
1432
                break;
1433
        }
1434
    } // end for
1435
1436
    if (!empty($settings)) {
1437
        $form->setDefaults($default_values);
1438
    }
1439
    $form->addHtml('<div class="bottom_actions">');
1440
    $form->addButtonSave(get_lang('Save settings'));
1441
    $form->addHtml('</div>');
1442
1443
    return $form;
1444
}
1445
1446
/**
1447
 * Searches a platform setting in all categories except from the Plugins category.
1448
 *
1449
 * @param string $search
1450
 *
1451
 * @return array
1452
 */
1453
function searchSetting($search)
1454
{
1455
    if (empty($search)) {
1456
        return [];
1457
    }
1458
    $table_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS);
1459
    $sql = "SELECT * FROM $table_settings_current
1460
            WHERE category <> 'Plugins' ORDER BY id ASC ";
1461
    $result = Database::store_result(Database::query($sql), 'ASSOC');
1462
    $settings = [];
1463
1464
    $search = api_strtolower($search);
1465
1466
    if (!empty($result)) {
1467
        foreach ($result as $setting) {
1468
            $found = false;
1469
1470
            $title = api_strtolower(get_lang($setting['title']));
1471
            // try the title
1472
            if (false === strpos($title, $search)) {
1473
                $comment = api_strtolower(get_lang($setting['comment']));
1474
                //Try the comment
1475
                if (false === strpos($comment, $search)) {
1476
                    //Try the variable name
1477
                    if (false === strpos($setting['variable'], $search)) {
1478
                        continue;
1479
                    } else {
1480
                        $found = true;
1481
                    }
1482
                } else {
1483
                    $found = true;
1484
                }
1485
            } else {
1486
                $found = true;
1487
            }
1488
            if ($found) {
1489
                $settings[] = $setting;
1490
            }
1491
        }
1492
    }
1493
1494
    return $settings;
1495
}
1496
/**
1497
 * Helper function to generates a form elements group.
1498
 *
1499
 * @param object $form   The form where the elements group has to be added
1500
 * @param array  $values Values to browse through
1501
 *
1502
 * @return array
1503
 */
1504
function formGenerateElementsGroup($form, $values = [], $elementName)
1505
{
1506
    $group = [];
1507
    if (is_array($values)) {
1508
        foreach ($values as $key => $value) {
1509
            $element = &$form->createElement('radio', $elementName, '', get_lang($value['display_text']), $value['value']);
1510
            $group[] = $element;
1511
        }
1512
    }
1513
1514
    return $group;
1515
}
1516
/**
1517
 * Helper function with allowed file types for CSS.
1518
 *
1519
 * @return array Array of file types (no indexes)
1520
 */
1521
function getAllowedFileTypes()
1522
{
1523
    $allowedFiles = [
1524
        'css',
1525
        'zip',
1526
        'jpeg',
1527
        'jpg',
1528
        'png',
1529
        'gif',
1530
        'ico',
1531
        'psd',
1532
        'xcf',
1533
        'svg',
1534
        'webp',
1535
        'woff',
1536
        'woff2',
1537
    ];
1538
1539
    return $allowedFiles;
1540
}
1541
/**
1542
 * Helper function to set settings in the database.
1543
 *
1544
 * @param array $parameters List of values
1545
 * @param int   $accessUrl  The current access URL
1546
 */
1547
function setConfigurationSettingsInDatabase($parameters, $accessUrl)
1548
{
1549
    api_set_settings_category('Search', 'false', $accessUrl);
1550
    // Save the settings.
1551
    foreach ($parameters as $key => $value) {
1552
        api_set_setting($key, $value, null, null);
1553
    }
1554
}
1555
1556
/**
1557
 * Helper function to show the status of the search settings table.
1558
 *
1559
 * @param array $data Data to show
1560
 */
1561
function showSearchSettingsTable($data)
1562
{
1563
    echo Display::tag('h3', get_lang('Settings'));
1564
    $table = new SortableTableFromArray($data);
1565
    $table->set_header(0, get_lang('Setting'), false);
1566
    $table->set_header(1, get_lang('Status'), false);
1567
    echo $table->display();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $table->display() targeting SortableTable::display() 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...
1568
}
1569
/**
1570
 * Helper function to show status table for each command line tool installed.
1571
 */
1572
function showSearchToolsStatusTable()
1573
{
1574
    //@todo windows support
1575
    if (false == api_is_windows_os()) {
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...
1576
        $list_of_programs = ['pdftotext', 'ps2pdf', 'catdoc', 'html2text', 'unrtf', 'catppt', 'xls2csv'];
1577
        foreach ($list_of_programs as $program) {
1578
            $output = [];
1579
            $ret_val = null;
1580
            exec("which $program", $output, $ret_val);
1581
1582
            if (!$output) {
1583
                $output[] = '';
1584
            }
1585
1586
            $icon = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Not installed'));
1587
            if (!empty($output[0])) {
1588
                $icon = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Installed'));
1589
            }
1590
            $data2[] = [$program, $output[0], $icon];
1591
        }
1592
        echo Display::tag('h3', get_lang('Course Program</a>. If your course has no code, whatever the reason, invent one. For instance <i>INNOVATION</i> if the course is about Innovation Managements needed to convert files'));
1593
        $table = new SortableTableFromArray($data2);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data2 seems to be defined by a foreach iteration on line 1577. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1594
        $table->set_header(0, get_lang('Course Program</a>. If your course has no code, whatever the reason, invent one. For instance <i>INNOVATION</i> if the course is about Innovation Management'), false);
1595
        $table->set_header(1, get_lang('Path'), false);
1596
        $table->set_header(2, get_lang('Status'), false);
1597
        echo $table->display();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $table->display() targeting SortableTable::display() 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...
1598
    } else {
1599
        echo Display::return_message(
1600
            get_lang('You are using Chamilo in a Windows platform, sadly you can\'t convert documents in order to search the content using this tool'),
1601
            'warning'
1602
        );
1603
    }
1604
}
1605
/**
1606
 * Helper function to generate and show CSS Zip download message.
1607
 *
1608
 * @param string $style Style path
1609
 */
1610
function generateCSSDownloadLink($style)
1611
{
1612
    $arch = api_get_path(SYS_ARCHIVE_PATH).$style.'.zip';
1613
    $themeDir = Template::getThemeDir($style);
1614
    $dir = api_get_path(SYS_CSS_PATH).$themeDir;
1615
    $check = Security::check_abs_path(
1616
        $dir,
1617
        api_get_path(SYS_CSS_PATH).'themes'
1618
    );
1619
    if (is_dir($dir) && $check) {
1620
        $zip = new PclZip($arch);
1621
        // Remove path prefix except the style name and put file on disk
1622
        $zip->create($dir, PCLZIP_OPT_REMOVE_PATH, substr($dir, 0, -strlen($style)));
0 ignored issues
show
Bug introduced by
The constant PCLZIP_OPT_REMOVE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1623
        $url = api_get_path(WEB_CODE_PATH).'course_info/download.php?archive_path=&archive='.str_replace(api_get_path(SYS_ARCHIVE_PATH), '', $arch);
1624
1625
        //@TODO: use more generic script to download.
1626
        $str = '<a class="btn btn--primary btn-large" href="'.$url.'">'.get_lang('Download the file').'</a>';
1627
        echo Display::return_message($str, 'normal', false);
1628
    } else {
1629
        echo Display::return_message(get_lang('The file was not found'), 'warning');
1630
    }
1631
}
1632
1633
/**
1634
 * Get all settings of one category prepared for display in admin/settings.php.
1635
 *
1636
 * @param string $category
1637
 *
1638
 * @return array
1639
 */
1640
function getCategorySettings($category = '')
1641
{
1642
    $url_id = api_get_current_access_url_id();
1643
    $settings_by_access_list = [];
1644
1645
    if (1 == $url_id) {
1646
        $settings = api_get_settings($category, 'group', $url_id);
1647
    } else {
1648
        $url_info = api_get_access_url($url_id);
1649
        if (1 == $url_info['active']) {
1650
            $categoryToSearch = $category;
1651
            if ('search_setting' == $category) {
1652
                $categoryToSearch = '';
1653
            }
1654
            // The default settings of Chamilo
1655
            $settings = api_get_settings($categoryToSearch, 'group', 1, 0);
1656
            // The settings that are changeable from a particular site.
1657
            $settings_by_access = api_get_settings($categoryToSearch, 'group', $url_id, 1);
1658
1659
            foreach ($settings_by_access as $row) {
1660
                if (empty($row['variable'])) {
1661
                    $row['variable'] = 0;
1662
                }
1663
                if (empty($row['subkey'])) {
1664
                    $row['subkey'] = 0;
1665
                }
1666
                if (empty($row['category'])) {
1667
                    $row['category'] = 0;
1668
                }
1669
1670
                // One more validation if is changeable.
1671
                if (1 == $row['access_url_changeable']) {
1672
                    $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']] = $row;
1673
                } else {
1674
                    $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']] = [];
1675
                }
1676
            }
1677
        }
1678
    }
1679
1680
    if (isset($category) && 'search_setting' == $category) {
1681
        if (!empty($_REQUEST['search_field'])) {
1682
            $settings = searchSetting($_REQUEST['search_field']);
1683
        }
1684
    }
1685
1686
    return [
1687
        'settings' => $settings,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $settings does not seem to be defined for all execution paths leading up to this point.
Loading history...
1688
        'settings_by_access_list' => $settings_by_access_list,
1689
    ];
1690
}
1691