Passed
Pull Request — master (#6127)
by Angel Fernando Quiroz
09:20
created

storePlugins()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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