Issues (1868)

public/main/admin/settings.lib.php (15 issues)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Asset;
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\SystemTemplate;
7
use Chamilo\CoreBundle\Enums\ActionIcon;
8
use Chamilo\CoreBundle\Enums\ObjectIcon;
9
use Chamilo\CoreBundle\Enums\StateIcon;
10
use Chamilo\CoreBundle\Framework\Container;
11
use ChamiloSession as Session;
12
use Symfony\Component\Filesystem\Filesystem;
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
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']).'">';
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
    $pluginRepo = Container::getPluginRepository();
135
136
    $allPlugins = (new AppPlugin())->read_plugins_from_path();
137
138
    // Header
139
    echo '<div class="mb-4 flex items-center justify-between">';
140
    echo '<h2 class="text-2xl font-semibold text-gray-90">'.get_lang('Manage Plugins').'</h2>';
141
    echo '<p class="text-gray-50 text-sm">'.get_lang('Install, activate or deactivate plugins easily.').'</p>';
142
    echo '</div>';
143
144
    echo '<table class="w-full border border-gray-25 rounded-lg shadow-md">';
145
    echo '<thead>';
146
    echo '<tr class="bg-gray-10 text-left">';
147
    echo '<th class="p-3 border-b border-gray-25">'.get_lang('Plugin').'</th>';
148
    echo '<th class="p-3 border-b border-gray-25">'.get_lang('Version').'</th>';
149
    echo '<th class="p-3 border-b border-gray-25">'.get_lang('Status').'</th>';
150
    echo '<th class="p-3 border-b border-gray-25 text-center">'.get_lang('Actions').'</th>';
151
    echo '</tr>';
152
    echo '</thead>';
153
    echo '<tbody>';
154
155
    foreach ($allPlugins as $pluginName) {
156
        $pluginInfoFile = api_get_path(SYS_PLUGIN_PATH).$pluginName.'/plugin.php';
157
158
        if (!file_exists($pluginInfoFile)) {
159
            continue;
160
        }
161
162
        $plugin_info = [];
163
164
        require $pluginInfoFile;
165
166
        $plugin = $pluginRepo->findOneByTitle($pluginName);
167
        $pluginConfiguration = $plugin?->getConfigurationsByAccessUrl(Container::getAccessUrlUtil()->getCurrent());
168
        $isInstalled = $plugin && $plugin->isInstalled();
169
        $isEnabled = $plugin && $pluginConfiguration && $pluginConfiguration->isActive();
170
171
        // Status badge
172
        $statusBadge = $isInstalled
173
            ? ($isEnabled
174
                ? '<span class="badge badge--success">'.get_lang('Enabled').'</span>'
175
                : '<span class="badge badge--warning">'.get_lang('Disabled').'</span>')
176
            : '<span class="badge badge--default">'.get_lang('Not Installed').'</span>';
177
178
        echo '<tr class="border-t border-gray-25 hover:bg-gray-15 transition duration-200">';
179
        echo '<td class="p-3 font-medium">'.$plugin_info['title'].'</td>';
180
        echo '<td class="p-3">'.$plugin_info['version'].'</td>';
181
        echo '<td class="p-3">'.$statusBadge.'</td>';
182
        echo '<td class="p-3 text-center">';
183
184
        echo '<div class="flex justify-center gap-2">';
185
186
        if ($isInstalled) {
187
            $toggleAction = $isEnabled ? 'disable' : 'enable';
188
            $toggleText = $isEnabled ? get_lang('Disable') : get_lang('Enable');
189
            $toggleColor = $isEnabled ? 'btn--plain' : 'btn--warning';
190
191
            $toggleIcon = $isEnabled ? 'mdi mdi-toggle-switch-off-outline' : 'mdi mdi-toggle-switch-outline';
192
193
            echo '<button class="plugin-action btn btn--sm '.$toggleColor.'"
194
                    data-plugin="'.$pluginName.'" data-action="'.$toggleAction.'">
195
                    <i class="'.$toggleIcon.'"></i> '.$toggleText.'
196
                  </button>';
197
198
            echo '<button class="plugin-action btn btn--sm btn--danger"
199
                    data-plugin="'.$pluginName.'" data-action="uninstall">
200
                    <i class="mdi mdi-trash-can-outline"></i> '.get_lang('Uninstall').'
201
                  </button>';
202
203
            $configureUrl = Container::getRouter()->generate(
204
                'legacy_main',
205
                ['name' => 'admin/configure_plugin.php', 'plugin' => $pluginName]
206
            );
207
208
            echo Display::url(
209
                get_lang('Configure'),
210
                $configureUrl,
211
                ['class' => 'btn btn--info btn--sm']
212
            );
213
        } else {
214
            echo '<button class="plugin-action btn btn--sm btn--success"
215
                    data-plugin="'.$pluginName.'" data-action="install">
216
                    <i class="mdi mdi-download"></i> '.get_lang('Install').'
217
                  </button>';
218
        }
219
220
        echo '</div>';
221
        echo '</td>';
222
        echo '</tr>';
223
    }
224
225
    echo '</tbody></table>';
226
227
    echo '<script>
228
    $(document).ready(function () {
229
        $(".plugin-action").click(function () {
230
            var pluginName = $(this).data("plugin");
231
            var action = $(this).data("action");
232
233
            $.post("'.api_get_path(WEB_AJAX_PATH).'plugin.ajax.php", { a: action, plugin: pluginName }, function(response) {
234
                var data = JSON.parse(response);
235
                if (data.success) {
236
                    location.reload();
237
                } else {
238
                    alert("Error: " + data.error);
239
                }
240
            });
241
        });
242
    });
243
    </script>';
244
}
245
246
/**
247
 * Creates the folder (if needed) and uploads the stylesheet in it.
248
 *
249
 * @param array $values  the values of the form
250
 * @param array $picture the values of the uploaded file
251
 *
252
 * @return bool
253
 *
254
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
255
 *
256
 * @version May 2008
257
 *
258
 * @since v1.8.5
259
 */
260
function uploadStylesheet($values, $picture)
261
{
262
    $result = false;
263
    // Valid name for the stylesheet folder.
264
    $style_name = api_preg_replace('/[^A-Za-z0-9]/', '', $values['name_stylesheet']);
265
    if (empty($style_name) || is_array($style_name)) {
266
        // The name of the uploaded stylesheet doesn't have the expected format
267
        return $result;
268
    }
269
    $cssToUpload = CSS_UPLOAD_PATH;
270
271
    // Check if a virtual instance vchamilo is used
272
    $virtualInstanceTheme = api_get_configuration_value('virtual_css_theme_folder');
273
    if (!empty($virtualInstanceTheme)) {
274
        $cssToUpload = $cssToUpload.$virtualInstanceTheme.'/';
275
    }
276
277
    // Create the folder if needed.
278
    if (!is_dir($cssToUpload.$style_name.'/')) {
279
        mkdir($cssToUpload.$style_name.'/', api_get_permissions_for_new_directories());
280
    }
281
282
    $info = pathinfo($picture['name']);
283
284
    if ('zip' == $info['extension']) {
285
        // Try to open the file and extract it in the theme.
286
        $zip = new ZipArchive();
287
        if ($zip->open($picture['tmp_name'])) {
288
            // Make sure all files inside the zip are images or css.
289
            $num_files = $zip->numFiles;
290
            $valid = true;
291
            $single_directory = true;
292
            $invalid_files = [];
293
294
            $allowedFiles = getAllowedFileTypes();
295
296
            for ($i = 0; $i < $num_files; $i++) {
297
                $file = $zip->statIndex($i);
298
                if ('/' != substr($file['name'], -1)) {
299
                    $path_parts = pathinfo($file['name']);
300
                    if (!in_array($path_parts['extension'], $allowedFiles)) {
301
                        $valid = false;
302
                        $invalid_files[] = $file['name'];
303
                    }
304
                }
305
306
                if (false === strpos($file['name'], '/')) {
307
                    $single_directory = false;
308
                }
309
            }
310
            if (!$valid) {
311
                $error_string = '<ul>';
312
                foreach ($invalid_files as $invalid_file) {
313
                    $error_string .= '<li>'.$invalid_file.'</li>';
314
                }
315
                $error_string .= '</ul>';
316
                echo Display::return_message(
317
                    get_lang('The only accepted extensions in the ZIP file are .jp(e)g, .png, .gif and .css.').$error_string,
318
                    'error',
319
                    false
320
                );
321
            } else {
322
                // If the zip does not contain a single directory, extract it.
323
                if (!$single_directory) {
324
                    // Extract zip file.
325
                    $zip->extractTo($cssToUpload.$style_name.'/');
326
                    $result = true;
327
                } else {
328
                    $extraction_path = $cssToUpload.$style_name.'/';
329
                    $mode = api_get_permissions_for_new_directories();
330
                    for ($i = 0; $i < $num_files; $i++) {
331
                        $entry = $zip->getNameIndex($i);
332
                        if ('/' == substr($entry, -1)) {
333
                            continue;
334
                        }
335
336
                        $pos_slash = strpos($entry, '/');
337
                        $entry_without_first_dir = substr($entry, $pos_slash + 1);
338
                        // If there is still a slash, we need to make sure the directories are created.
339
                        if (false !== strpos($entry_without_first_dir, '/')) {
340
                            if (!is_dir($extraction_path.dirname($entry_without_first_dir))) {
341
                                // Create it.
342
                                @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

342
                                /** @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...
343
                            }
344
                        }
345
346
                        $fp = $zip->getStream($entry);
347
                        $ofp = fopen($extraction_path.dirname($entry_without_first_dir).'/'.basename($entry), 'w');
348
349
                        while (!feof($fp)) {
350
                            fwrite($ofp, fread($fp, 8192));
351
                        }
352
353
                        fclose($fp);
354
                        fclose($ofp);
355
                    }
356
                    $result = true;
357
                }
358
            }
359
            $zip->close();
360
        } else {
361
            echo Display::return_message(get_lang('Error reading ZIP file').$info['extension'], 'error', false);
362
        }
363
    } else {
364
        // Simply move the file.
365
        move_uploaded_file($picture['tmp_name'], $cssToUpload.$style_name.'/'.$picture['name']);
366
        $result = true;
367
    }
368
369
    if ($result) {
370
        $fs = new Filesystem();
371
        $fs->mirror(
372
            CSS_UPLOAD_PATH,
373
            api_get_path(SYMFONY_SYS_PATH).'var/themes/',
374
            null,
375
            ['override' => true]
376
        );
377
    }
378
379
    return $result;
380
}
381
382
/**
383
 * Store plugin regions.
384
 */
385
function storeRegions()
386
{
387
    $plugin_obj = new AppPlugin();
388
389
    // Get a list of all current 'Plugins' settings
390
    $installed_plugins = $plugin_obj->getInstalledPlugins();
391
    $shortlist_installed = [];
392
    if (!empty($installed_plugins)) {
393
        foreach ($installed_plugins as $plugin) {
394
            if (isset($plugin['subkey'])) {
395
                $shortlist_installed[] = $plugin['subkey'];
396
            }
397
        }
398
    }
399
400
    $plugin_list = $plugin_obj->read_plugins_from_path();
401
402
    foreach ($plugin_list as $plugin) {
403
        if (isset($_POST['plugin_'.$plugin])) {
404
            $areas_to_installed = $_POST['plugin_'.$plugin];
405
            if (!empty($areas_to_installed)) {
406
                $plugin_obj->removeAllRegions($plugin);
407
                foreach ($areas_to_installed as $region) {
408
                    if (!empty($region) && '-1' != $region) {
409
                        $plugin_obj->add_to_region($plugin, $region);
410
                    }
411
                }
412
            }
413
        }
414
    }
415
}
416
417
/**
418
 * This function checks if the given style is a recognize style that exists in the css directory as
419
 * a standalone directory.
420
 *
421
 * @param string $style
422
 *
423
 * @return bool True if this style is recognized, false otherwise
424
 */
425
function isStyle($style)
426
{
427
    $themeList = api_get_themes();
428
429
    return in_array($style, array_keys($themeList));
430
}
431
432
/**
433
 * Search options
434
 * TODO: support for multiple site. aka $_configuration['access_url'] == 1.
435
 *
436
 * @author Marco Villegas <[email protected]>
437
 */
438
function handleSearch()
439
{
440
    global $SettingsStored, $_configuration;
441
442
    $search_enabled = api_get_setting('search_enabled');
443
444
    $form = new FormValidator(
445
        'search-options',
446
        'post',
447
        api_get_self().'?category=Search'
448
    );
449
    $values = api_get_settings_options('search_enabled');
450
    $form->addElement('header', null, get_lang('Fulltext search'));
451
452
    $group = formGenerateElementsGroup($form, $values, 'search_enabled');
453
454
    // SearchEnabledComment
455
    $form->addGroup(
456
        $group,
457
        'search_enabled',
458
        [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.')],
459
        null,
460
        false
461
    );
462
463
    $search_enabled = api_get_setting('search_enabled');
464
465
    if ($form->validate()) {
466
        $formValues = $form->exportValues();
467
        setConfigurationSettingsInDatabase($formValues, $_configuration['access_url']);
468
        $search_enabled = $formValues['search_enabled'];
469
        echo Display::return_message($SettingsStored, 'confirm');
470
    }
471
    $specific_fields = get_specific_field_list();
472
473
    if ('true' == $search_enabled) {
474
        $values = api_get_settings_options('search_show_unlinked_results');
475
        $group = formGenerateElementsGroup(
476
            $form,
477
            $values,
478
            'search_show_unlinked_results'
479
        );
480
        $form->addGroup(
481
            $group,
482
            'search_show_unlinked_results',
483
            [
484
                get_lang('Full-text search: show unlinked results'),
485
                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?'),
486
            ],
487
            null,
488
            false
489
        );
490
        $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...
491
492
        $sf_values = [];
493
        foreach ($specific_fields as $sf) {
494
            $sf_values[$sf['code']] = $sf['name'];
495
        }
496
        $url = Display::div(
497
            Display::url(
498
                get_lang('Add a specific search field'),
499
                'specific_fields.php'
500
            ),
501
            ['class' => 'sectioncomment']
502
        );
503
        if (empty($sf_values)) {
504
            $form->addElement('label', [get_lang('Specific Field for prefilter'), $url]);
505
        } else {
506
            $form->addElement(
507
                'select',
508
                'search_prefilter_prefix',
509
                [get_lang('Specific Field for prefilter'), $url],
510
                $sf_values,
511
                ''
512
            );
513
            $default_values['search_prefilter_prefix'] = api_get_setting('search_prefilter_prefix');
514
        }
515
    }
516
517
    $default_values['search_enabled'] = $search_enabled;
518
519
    $form->addButtonSave(get_lang('Save'));
520
    $form->setDefaults($default_values);
521
522
    echo '<div id="search-options-form">';
523
    $form->display();
524
    echo '</div>';
525
526
    if ('true' == $search_enabled) {
527
        //$xapianPath = api_get_path(SYS_UPLOAD_PATH).'plugins/xapian/searchdb';
528
529
        /*
530
        @todo Test the Xapian connection
531
        if (extension_loaded('xapian')) {
532
            require_once 'xapian.php';
533
            try {
534
                $db = new XapianDatabase($xapianPath.'/');
535
            } catch (Exception $e) {
536
                var_dump($e->getMessage());
537
            }
538
539
            require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php';
540
            require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php';
541
            require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
542
543
            $indexable = new IndexableChunk();
544
            $indexable->addValue("content", 'Test');
545
546
            $di = new ChamiloIndexer();
547
            $di->connectDb(NULL, NULL, 'english');
548
            $di->addChunk($indexable);
549
            $did = $di->index();
550
        }
551
        */
552
553
        $xapianLoaded = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
554
        $dir_exists = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
555
        $dir_is_writable = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
556
        $specific_fields_exists = Display::getMdiIcon(StateIcon::OPEN_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Validate'));
557
558
        //Testing specific fields
559
        if (empty($specific_fields)) {
560
            $specific_fields_exists = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Add a specific search field')
561
            );
562
        }
563
        //Testing xapian extension
564
        if (!extension_loaded('xapian')) {
565
            $xapianLoaded = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
566
        }
567
        //Testing xapian searchdb path
568
        if (!is_dir($xapianPath)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $xapianPath seems to be never defined.
Loading history...
569
            $dir_exists = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
570
        }
571
        //Testing xapian searchdb path is writable
572
        if (!is_writable($xapianPath)) {
573
            $dir_is_writable = Display::getMdiIcon(StateIcon::CLOSED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
574
        }
575
576
        $data = [];
577
        $data[] = [get_lang('Xapian module installed'), $xapianLoaded];
578
        $data[] = [get_lang('The directory exists').' - '.$xapianPath, $dir_exists];
579
        $data[] = [get_lang('Is writable').' - '.$xapianPath, $dir_is_writable];
580
        $data[] = [get_lang('Available custom search fields'), $specific_fields_exists];
581
582
        showSearchSettingsTable($data);
583
        showSearchToolsStatusTable();
584
    }
585
}
586
587
/**
588
 * Wrapper for the templates.
589
 *
590
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
591
 * @author Julio Montoya.
592
 *
593
 * @version August 2008
594
 *
595
 * @since v1.8.6
596
 */
597
function handleTemplates()
598
{
599
    /* Drive-by fix to avoid undefined var warnings, without repeating
600
     * isset() combos all over the place. */
601
    $action = isset($_GET['action']) ? $_GET['action'] : "invalid";
602
603
    if ('add' != $action) {
604
        echo '<div class="actions" style="margin-left: 1px;">';
605
        echo '<a href="settings.php?category=Templates&action=add">'.
606
                Display::getMdiIcon(ObjectIcon::TEMPLATE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Add a template')).'</a>';
607
        echo '</div>';
608
    }
609
610
    if ('add' == $action || ('edit' == $action && is_numeric($_GET['id']))) {
611
        addEditTemplate();
612
613
        // Add event to the system log.
614
        $user_id = api_get_user_id();
615
        $category = $_GET['category'];
616
        Event::addEvent(
617
            LOG_CONFIGURATION_SETTINGS_CHANGE,
618
            LOG_CONFIGURATION_SETTINGS_CATEGORY,
619
            $category,
620
            api_get_utc_datetime(),
621
            $user_id
622
        );
623
    } else {
624
        if ('delete' == $action && is_numeric($_GET['id'])) {
625
            deleteTemplate($_GET['id']);
626
627
            // Add event to the system log
628
            $user_id = api_get_user_id();
629
            $category = $_GET['category'];
630
            Event::addEvent(
631
                LOG_CONFIGURATION_SETTINGS_CHANGE,
632
                LOG_CONFIGURATION_SETTINGS_CATEGORY,
633
                $category,
634
                api_get_utc_datetime(),
635
                $user_id
636
            );
637
        }
638
        displayTemplates();
639
    }
640
}
641
642
/**
643
 * Display a sortable table with all the templates that the platform administrator has defined.
644
 *
645
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
646
 *
647
 * @version August 2008
648
 *
649
 * @since v1.8.6
650
 */
651
function displayTemplates()
652
{
653
    $table = new SortableTable(
654
        'templates',
655
        'getNumberOfTemplates',
656
        'getTemplateData',
657
        1
658
    );
659
    $table->set_additional_parameters(
660
        ['category' => Security::remove_XSS($_GET['category'])]
661
    );
662
    $table->set_header(0, get_lang('Image'), true, ['style' => 'width: 101px;']);
663
    $table->set_header(1, get_lang('Title'));
664
    $table->set_header(2, get_lang('Detail'), false, ['style' => 'width:50px;']);
665
    $table->set_column_filter(2, 'actionsFilter');
666
    $table->set_column_filter(0, 'searchImageFilter');
667
    $table->display();
668
}
669
670
/**
671
 * Gets the number of templates that are defined by the platform admin.
672
 *
673
 * @return int
674
 *
675
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
676
 *
677
 * @version August 2008
678
 *
679
 * @since v1.8.6
680
 */
681
function getNumberOfTemplates()
682
{
683
    // Database table definition.
684
    $table = Database::get_main_table('system_template');
685
686
    // The sql statement.
687
    $sql = "SELECT COUNT(id) AS total FROM $table";
688
    $result = Database::query($sql);
689
    $row = Database::fetch_array($result);
690
691
    // Returning the number of templates.
692
    return $row['total'];
693
}
694
695
/**
696
 * Gets all the template data for the sortable table.
697
 *
698
 * @param int    $from            the start of the limit statement
699
 * @param int    $number_of_items the number of elements that have to be retrieved from the database
700
 * @param int    $column          the column that is
701
 * @param string $direction       the sorting direction (ASC or DESC)
702
 *
703
 * @return array
704
 *
705
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
706
 *
707
 * @version August 2008
708
 *
709
 * @since v1.8.6
710
 */
711
function getTemplateData($from, $number_of_items, $column, $direction)
712
{
713
    // Database table definition.
714
    $table_system_template = Database::get_main_table('system_template');
715
716
    $from = (int) $from;
717
    $number_of_items = (int) $number_of_items;
718
    $column = (int) $column;
719
    $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
720
    // The sql statement.
721
    $sql = "SELECT id as col0, title as col1, id as col2 FROM $table_system_template";
722
    $sql .= " ORDER BY col$column $direction ";
723
    $sql .= " LIMIT $from,$number_of_items";
724
    $result = Database::query($sql);
725
    $return = [];
726
    while ($row = Database::fetch_array($result)) {
727
        $row['1'] = get_lang($row['1']);
728
        $return[] = $row;
729
    }
730
    // Returning all the information for the sortable table.
731
    return $return;
732
}
733
734
/**
735
 * display the edit and delete icons in the sortable table.
736
 *
737
 * @param int $id the id of the template
738
 *
739
 * @return string code for the link to edit and delete the template
740
 *
741
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
742
 *
743
 * @version August 2008
744
 *
745
 * @since v1.8.6
746
 */
747
function actionsFilter($id)
748
{
749
    $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>';
750
    $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>';
751
752
    return $return;
753
}
754
755
function searchImageFilter(int $id): string
756
{
757
    $em = Database::getManager();
758
759
    /** @var SystemTemplate $template */
760
    $template = $em->find(SystemTemplate::class, $id);
761
762
    if (null !== $template->getImage()) {
763
        $assetRepo = Container::getAssetRepository();
764
        $imageUrl = $assetRepo->getAssetUrl($template->getImage());
765
766
        return '<img src="'.$imageUrl.'" alt="'.get_lang('Template preview').'"/>';
767
    } else {
768
        return '<img src="'.api_get_path(WEB_PUBLIC_PATH).'img/template_thumb/noimage.gif" alt="'.get_lang('NoTemplate preview').'"/>';
769
    }
770
}
771
772
/**
773
 * Add (or edit) a template. This function displays the form and also takes
774
 * care of uploading the image and storing the information in the database.
775
 *
776
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
777
 *
778
 * @version August 2008
779
 *
780
 * @since v1.8.6
781
 */
782
function addEditTemplate()
783
{
784
    $em = Database::getManager();
785
    $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
786
787
    $assetRepo = Container::getAssetRepository();
788
789
    /** @var SystemTemplate $template */
790
    $template = $id ? $em->find(SystemTemplate::class, $id) : new SystemTemplate();
791
792
    $form = new FormValidator(
793
        'template',
794
        'post',
795
        'settings.php?category=Templates&action='.Security::remove_XSS($_GET['action']).'&id='.$id
796
    );
797
798
    // Setting the form elements: the header.
799
    if ('add' == $_GET['action']) {
800
        $title = get_lang('Add a template');
801
    } else {
802
        $title = get_lang('Template edition');
803
    }
804
    $form->addElement('header', '', $title);
805
806
    // Setting the form elements: the title of the template.
807
    $form->addText('title', get_lang('Title'), false);
808
    $form->addText('comment', get_lang('Description'), false);
809
810
    // Setting the form elements: the content of the template (wysiwyg editor).
811
    $form->addHtmlEditor(
812
        'template_text',
813
        get_lang('Text'),
814
        true,
815
        true,
816
        ['ToolbarSet' => 'Documents', 'Width' => '100%', 'Height' => '400']
817
    );
818
819
    // Setting the form elements: the form to upload an image to be used with the template.
820
    if (!$template->hasImage()) {
821
        // Picture
822
        $form->addFile(
823
            'template_image',
824
            get_lang('Add image'),
825
            ['id' => 'picture', 'class' => 'picture-form', 'crop_image' => true, 'crop_ratio' => '1 / 1']
826
        );
827
        $allowedPictureTypes = api_get_supported_image_extensions(false);
828
        $form->addRule('template_image', get_lang('Only PNG, JPG or GIF images allowed').' ('.implode(',', $allowedPictureTypes).')', 'filetype', $allowedPictureTypes);
829
    }
830
831
    // Setting the form elements: a little bit of information about the template image.
832
    $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'));
833
834
    // Getting all the information of the template when editing a template.
835
    if ('edit' == $_GET['action']) {
836
        $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...
837
        $defaults['template_text'] = $template->getContent();
838
        // Forcing get_lang().
839
        $defaults['title'] = $template->getTitle();
840
        $defaults['comment'] = $template->getComment();
841
842
        // Adding an extra field: a hidden field with the id of the template we are editing.
843
        $form->addElement('hidden', 'template_id');
844
845
        // Adding an extra field: a preview of the image that is currently used.
846
847
        if ($template->hasImage()) {
848
            $imageUrl = $assetRepo->getAssetUrl($template->getImage());
849
            $form->addElement(
850
                'static',
851
                'template_image_preview',
852
                '',
853
                '<img src="'.$imageUrl
854
                    .'" alt="'.get_lang('Template preview')
855
                    .'"/>'
856
            );
857
            $form->addCheckBox('delete_image', null, get_lang('Delete picture'));
858
        } else {
859
            $form->addElement(
860
                'static',
861
                'template_image_preview',
862
                '',
863
                '<img src="'.api_get_path(WEB_PUBLIC_PATH).'img/template_thumb/noimage.gif" alt="'.get_lang('NoTemplate preview').'"/>'
864
            );
865
        }
866
867
        // Setting the information of the template that we are editing.
868
        $form->setDefaults($defaults);
869
    }
870
    // Setting the form elements: the submit button.
871
    $form->addButtonSave(get_lang('Validate'), 'submit');
872
873
    // Setting the rules: the required fields.
874
    if (!$template->hasImage()) {
875
        $form->addRule(
876
            'template_image',
877
            get_lang('Required field'),
878
            'required'
879
        );
880
        $form->addRule('title', get_lang('Required field'), 'required');
881
    }
882
883
    // if the form validates (complies to all rules) we save the information,
884
    // else we display the form again (with error message if needed)
885
    if ($form->validate()) {
886
        $check = Security::check_token('post');
887
888
        if ($check) {
889
            // Exporting the values.
890
            $values = $form->exportValues();
891
            $asset = null;
892
            if (isset($values['delete_image']) && !empty($id)) {
893
                deleteTemplateImage($id);
894
            }
895
896
            // Upload the file.
897
            if (!empty($_FILES['template_image']['name'])) {
898
                $picture = $_FILES['template_image'];
899
                if (!empty($picture['name'])) {
900
                    $asset = (new Asset())
901
                        ->setCategory(Asset::SYSTEM_TEMPLATE)
902
                        ->setTitle($picture['name'])
903
                    ;
904
                    if (!empty($values['picture_crop_result'])) {
905
                        $asset->setCrop($values['picture_crop_result']);
906
                    }
907
                    $asset = $assetRepo->createFromRequest($asset, $picture);
908
                }
909
            }
910
911
            // Store the information in the database (as insert or as update).
912
            $bootstrap = api_get_bootstrap_and_font_awesome();
913
            $viewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
914
915
            if ('add' == $_GET['action']) {
916
                $templateContent = '<head>'.$viewport.'<title>'.$values['title'].'</title>'.$bootstrap.'</head>'
917
                    .$values['template_text'];
918
                $template
919
                    ->setTitle($values['title'])
920
                    ->setComment(Security::remove_XSS($values['comment']))
921
                    ->setContent(Security::remove_XSS($templateContent, COURSEMANAGERLOWSECURITY))
922
                    ->setImage($asset);
923
                $em->persist($template);
924
                $em->flush();
925
926
                // Display a feedback message.
927
                echo Display::return_message(
928
                    get_lang('Template added'),
929
                    'confirm'
930
                );
931
                echo '<a href="settings.php?category=Templates&action=add">'.
932
                    Display::getMdiIcon(ObjectIcon::TEMPLATE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Add a template')).
933
                    '</a>';
934
            } else {
935
                $templateContent = '<head>'.$viewport.'<title>'.$values['title'].'</title>'.$bootstrap.'</head>'
936
                    .$values['template_text'];
937
938
                $template
939
                    ->setTitle($values['title'])
940
                    ->setContent(Security::remove_XSS($templateContent, COURSEMANAGERLOWSECURITY));
941
942
                if ($asset) {
943
                    $template->setImage($asset);
944
                }
945
946
                $em->persist($template);
947
                $em->flush();
948
949
                // Display a feedback message.
950
                echo Display::return_message(get_lang('Template edited'), 'confirm');
951
            }
952
        }
953
        Security::clear_token();
954
        displayTemplates();
955
    } else {
956
        $token = Security::get_token();
957
        $form->addElement('hidden', 'sec_token');
958
        $form->setConstants(['sec_token' => $token]);
959
        // Display the form.
960
        $form->display();
961
    }
962
}
963
964
/**
965
 * Deletes the template picture as asset.
966
 *
967
 * @param int $id
968
 */
969
function deleteTemplateImage($id)
970
{
971
    $em = Database::getManager();
972
973
    /** @var SystemTemplate $template */
974
    $template = $em->find(SystemTemplate::class, $id);
975
976
    if ($template && $template->hasImage()) {
977
        $image = $template->getImage();
978
        $em->remove($image);
979
        $em->flush();
980
    }
981
}
982
983
/**
984
 * Delete a template.
985
 *
986
 * @param int $id the id of the template that has to be deleted
987
 *
988
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
989
 *
990
 * @version August 2008
991
 *
992
 * @since v1.8.6
993
 */
994
function deleteTemplate($id)
995
{
996
    $id = intval($id);
997
    // First we remove the image.
998
    $table = Database::get_main_table('system_template');
999
    $sql = "SELECT * FROM $table WHERE id = $id";
1000
    $result = Database::query($sql);
1001
    $row = Database::fetch_array($result);
1002
    if (!empty($row['image'])) {
1003
        @unlink(api_get_path(SYS_APP_PATH).'home/default_platform_document/template_thumb/'.$row['image']);
0 ignored issues
show
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

1003
        /** @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...
The constant SYS_APP_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1004
    }
1005
1006
    // Now we remove it from the database.
1007
    $sql = "DELETE FROM $table WHERE id = $id";
1008
    Database::query($sql);
1009
1010
    deleteTemplateImage($id);
1011
1012
    // Display a feedback message.
1013
    echo Display::return_message(get_lang('Template deleted'), 'confirm');
1014
}
1015
1016
/**
1017
 * @param array $settings
1018
 * @param array $settings_by_access_list
1019
 *
1020
 * @throws \Doctrine\ORM\ORMException
1021
 * @throws \Doctrine\ORM\OptimisticLockException
1022
 * @throws \Doctrine\ORM\TransactionRequiredException
1023
 *
1024
 * @return FormValidator
1025
 */
1026
function generateSettingsForm($settings, $settings_by_access_list)
1027
{
1028
    global $_configuration, $settings_to_avoid, $convert_byte_to_mega_list;
1029
    $em = Database::getManager();
1030
    $table_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS);
1031
1032
    $form = new FormValidator(
1033
        'settings',
1034
        'post',
1035
        'settings.php?category='.Security::remove_XSS($_GET['category'])
1036
    );
1037
1038
    $form->addElement(
1039
        'hidden',
1040
        'search_field',
1041
        (!empty($_GET['search_field']) ? Security::remove_XSS($_GET['search_field']) : null)
1042
    );
1043
1044
    $url_id = api_get_current_access_url_id();
1045
1046
    $default_values = [];
1047
    $url_info = api_get_access_url($url_id);
1048
    $i = 0;
1049
    $addedSettings = [];
1050
    foreach ($settings as $row) {
1051
        if (in_array($row['variable'], array_keys($settings_to_avoid))) {
1052
            continue;
1053
        }
1054
1055
        if (in_array($row['variable'], $addedSettings)) {
1056
            continue;
1057
        }
1058
1059
        $addedSettings[] = $row['variable'];
1060
1061
        if (api_get_multiple_access_url()) {
1062
            if (api_is_global_platform_admin()) {
1063
                if (0 == $row['access_url_locked']) {
1064
                    if (1 == $url_id) {
1065
                        if ('1' == $row['access_url_changeable']) {
1066
                            $form->addElement(
1067
                                'html',
1068
                                '<div class="float-right"><a class="share_this_setting" data_status = "0"  data_to_send = "'.$row['variable'].'" href="javascript:void(0);">'.
1069
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</a></div>'
1070
                            );
1071
                        } else {
1072
                            $form->addElement(
1073
                                'html',
1074
                                '<div class="float-right"><a class="share_this_setting" data_status = "1" data_to_send = "'.$row['variable'].'" href="javascript:void(0);">'.
1075
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</a></div>'
1076
                            );
1077
                        }
1078
                    } else {
1079
                        if ('1' == $row['access_url_changeable']) {
1080
                            $form->addElement(
1081
                                'html',
1082
                                '<div class="float-right">'.
1083
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</div>'
1084
                            );
1085
                        } else {
1086
                            $form->addElement(
1087
                                'html',
1088
                                '<div class="float-right">'.
1089
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</div>'
1090
                            );
1091
                        }
1092
                    }
1093
                }
1094
            }
1095
        }
1096
1097
        $hideme = [];
1098
        $hide_element = false;
1099
1100
        if (1 != $_configuration['access_url']) {
1101
            if (0 == $row['access_url_changeable']) {
1102
                // We hide the element in other cases (checkbox, radiobutton) we 'freeze' the element.
1103
                $hide_element = true;
1104
                $hideme = ['disabled'];
1105
            } elseif (1 == $url_info['active']) {
1106
                // We show the elements.
1107
                if (empty($row['variable'])) {
1108
                    $row['variable'] = 0;
1109
                }
1110
                if (empty($row['subkey'])) {
1111
                    $row['subkey'] = 0;
1112
                }
1113
                if (empty($row['category'])) {
1114
                    $row['category'] = 0;
1115
                }
1116
                if (isset($settings_by_access_list[$row['variable']]) &&
1117
                    isset($settings_by_access_list[$row['variable']][$row['subkey']]) &&
1118
                    is_array($settings_by_access_list[$row['variable']][$row['subkey']][$row['category']])
1119
                ) {
1120
                    // We are sure that the other site have a selected value.
1121
                    if ('' != $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']]['selected_value']) {
1122
                        $row['selected_value'] = $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']]['selected_value'];
1123
                    }
1124
                }
1125
                // There is no else{} statement because we load the default $row['selected_value'] of the main Chamilo site.
1126
            }
1127
        }
1128
1129
        switch ($row['type']) {
1130
            case 'textfield':
1131
                if (in_array($row['variable'], $convert_byte_to_mega_list)) {
1132
                    $form->addElement(
1133
                        'text',
1134
                        $row['variable'],
1135
                        [
1136
                            get_lang($row['title']),
1137
                            get_lang($row['comment']),
1138
                            get_lang('MB'),
1139
                        ],
1140
                        ['maxlength' => '8', 'aria-label' => get_lang($row['title'])]
1141
                    );
1142
                    $form->applyFilter($row['variable'], 'html_filter');
1143
                    $default_values[$row['variable']] = round($row['selected_value'] / 1024 / 1024, 1);
1144
                } elseif ('account_valid_duration' == $row['variable']) {
1145
                    $form->addElement(
1146
                        'text',
1147
                        $row['variable'],
1148
                        [
1149
                            get_lang($row['title']),
1150
                            get_lang($row['comment']),
1151
                        ],
1152
                        ['maxlength' => '5', 'aria-label' => get_lang($row['title'])]
1153
                    );
1154
                    $form->applyFilter($row['variable'], 'html_filter');
1155
1156
                    // For platform character set selection:
1157
                    // Conversion of the textfield to a select box with valid values.
1158
                    $default_values[$row['variable']] = $row['selected_value'];
1159
                } elseif ('platform_charset' == $row['variable']) {
1160
                    break;
1161
                } else {
1162
                    $hideme['class'] = 'col-md-4';
1163
                    $hideme['aria-label'] = get_lang($row['title']);
1164
                    $form->addElement(
1165
                        'text',
1166
                        $row['variable'],
1167
                        [
1168
                            get_lang($row['title']),
1169
                            get_lang($row['comment']),
1170
                        ],
1171
                        $hideme
1172
                    );
1173
                    $form->applyFilter($row['variable'], 'html_filter');
1174
                    $default_values[$row['variable']] = $row['selected_value'];
1175
                }
1176
                break;
1177
            case 'textarea':
1178
                if ('header_extra_content' == $row['variable']) {
1179
                    $file = api_get_home_path().'header_extra_content.txt';
0 ignored issues
show
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

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