getCategorySettings()   C
last analyzed

Complexity

Conditions 12
Paths 12

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 29
nc 12
nop 1
dl 0
loc 49
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
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
    // 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">'.htmlspecialchars($plugin_info['title'] ?? $pluginName, ENT_QUOTES).'</td>';
180
        echo '<td class="p-3">'.htmlspecialchars($plugin_info['version'] ?? '0.0.0', ENT_QUOTES).'</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="'.htmlspecialchars($pluginName, ENT_QUOTES).'" 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="'.htmlspecialchars($pluginName, ENT_QUOTES).'" 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="'.htmlspecialchars($pluginName, ENT_QUOTES).'" 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 '<div id="page-loader" class="hidden fixed inset-0 bg-black/30 z-40">
228
            <div class="absolute inset-0 flex items-center justify-center">
229
              <div class="text-white text-sm text-center">
230
                <i class="mdi mdi-loading mdi-spin text-3xl block mb-2"></i>
231
                '.get_lang('Processing').'…
232
              </div>
233
            </div>
234
          </div>';
235
236
    echo '<script>
237
(function($){
238
  function showToast(message, type) {
239
    var bg = type === "success" ? "bg-green-600" : (type === "warning" ? "bg-yellow-600" : "bg-red-600");
240
    var $toast = $("<div/>", {
241
      class: "fixed top-4 right-4 z-50 text-white px-4 py-3 rounded shadow " + bg,
242
      text: message
243
    }).appendTo("body");
244
    setTimeout(function(){ $toast.fadeOut(300, function(){ $(this).remove(); }); }, 3500);
245
  }
246
247
  function actionLabel(a) {
248
    switch(a){
249
      case "install": return "'.get_lang('Installing').'";
250
      case "uninstall": return "'.get_lang('Uninstalling').'";
251
      case "enable": return "'.get_lang('Enabling').'";
252
      case "disable": return "'.get_lang('Disabling').'";
253
      default: return "'.get_lang('Processing').'";
254
    }
255
  }
256
257
  function showPageLoader(show){
258
    $("#page-loader").toggleClass("hidden", !show);
259
  }
260
261
  $(document).ready(function () {
262
    $(".plugin-action").on("click", function () {
263
      var $btn = $(this);
264
      if ($btn.data("busy")) return;
265
266
      var pluginName = $btn.data("plugin");
267
      var action = $btn.data("action");
268
      var originalHtml = $btn.html();
269
270
      $btn.data("busy", true)
271
          .attr("aria-busy", "true")
272
          .addClass("opacity-60 cursor-not-allowed")
273
          .html(\'<i class="mdi mdi-loading mdi-spin"></i> \' + actionLabel(action) + "...");
274
      $.ajax({
275
        type: "POST",
276
        url: "'.api_get_path(WEB_AJAX_PATH).'plugin.ajax.php",
277
        data: { a: action, plugin: pluginName },
278
        dataType: "json",
279
        timeout: 120000,
280
        beforeSend: function(){
281
          showToast(actionLabel(action) + "…", "warning");
282
        },
283
        success: function(data){
284
          if (data && data.success) {
285
            showToast("'.get_lang('Done').': " + action.toUpperCase(), "success");
286
            setTimeout(function(){ location.reload(); }, 500);
287
          } else {
288
            var msg = (data && (data.error || data.message)) ? (data.error || data.message) : "'.get_lang('Error').'";
289
            showToast("'.get_lang('Error').': " + msg, "error");
290
            $btn.html(originalHtml);
291
          }
292
        },
293
        error: function(xhr){
294
          var msg = "'.get_lang('Request failed').'";
295
          try {
296
            var j = JSON.parse(xhr.responseText);
297
            if (j && (j.error || j.message)) msg = j.error || j.message;
298
          } catch(e) {}
299
          showToast("'.get_lang('Error').': " + msg, "error");
300
          $btn.html(originalHtml);
301
        },
302
        complete: function(){
303
          $btn.data("busy", false)
304
              .removeAttr("aria-busy")
305
              .removeClass("opacity-60 cursor-not-allowed");
306
        }
307
      });
308
    });
309
  });
310
})(jQuery);
311
</script>';
312
}
313
314
/**
315
 * Creates the folder (if needed) and uploads the stylesheet in it.
316
 *
317
 * @param array $values  the values of the form
318
 * @param array $picture the values of the uploaded file
319
 *
320
 * @return bool
321
 *
322
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
323
 *
324
 * @version May 2008
325
 *
326
 * @since v1.8.5
327
 */
328
function uploadStylesheet($values, $picture)
329
{
330
    $result = false;
331
    // Valid name for the stylesheet folder.
332
    $style_name = api_preg_replace('/[^A-Za-z0-9]/', '', $values['name_stylesheet']);
333
    if (empty($style_name) || is_array($style_name)) {
334
        // The name of the uploaded stylesheet doesn't have the expected format
335
        return $result;
336
    }
337
    $cssToUpload = CSS_UPLOAD_PATH;
338
339
    // Check if a virtual instance vchamilo is used
340
    $virtualInstanceTheme = api_get_configuration_value('virtual_css_theme_folder');
341
    if (!empty($virtualInstanceTheme)) {
342
        $cssToUpload = $cssToUpload.$virtualInstanceTheme.'/';
343
    }
344
345
    // Create the folder if needed.
346
    if (!is_dir($cssToUpload.$style_name.'/')) {
347
        mkdir($cssToUpload.$style_name.'/', api_get_permissions_for_new_directories());
348
    }
349
350
    $info = pathinfo($picture['name']);
351
352
    if ('zip' == $info['extension']) {
353
        // Try to open the file and extract it in the theme.
354
        $zip = new ZipArchive();
355
        if ($zip->open($picture['tmp_name'])) {
356
            // Make sure all files inside the zip are images or css.
357
            $num_files = $zip->numFiles;
358
            $valid = true;
359
            $single_directory = true;
360
            $invalid_files = [];
361
362
            $allowedFiles = getAllowedFileTypes();
363
364
            for ($i = 0; $i < $num_files; $i++) {
365
                $file = $zip->statIndex($i);
366
                if ('/' != substr($file['name'], -1)) {
367
                    $path_parts = pathinfo($file['name']);
368
                    if (!in_array($path_parts['extension'], $allowedFiles)) {
369
                        $valid = false;
370
                        $invalid_files[] = $file['name'];
371
                    }
372
                }
373
374
                if (false === strpos($file['name'], '/')) {
375
                    $single_directory = false;
376
                }
377
            }
378
            if (!$valid) {
379
                $error_string = '<ul>';
380
                foreach ($invalid_files as $invalid_file) {
381
                    $error_string .= '<li>'.$invalid_file.'</li>';
382
                }
383
                $error_string .= '</ul>';
384
                echo Display::return_message(
385
                    get_lang('The only accepted extensions in the ZIP file are .jp(e)g, .png, .gif and .css.').$error_string,
386
                    'error',
387
                    false
388
                );
389
            } else {
390
                // If the zip does not contain a single directory, extract it.
391
                if (!$single_directory) {
392
                    // Extract zip file.
393
                    $zip->extractTo($cssToUpload.$style_name.'/');
394
                    $result = true;
395
                } else {
396
                    $extraction_path = $cssToUpload.$style_name.'/';
397
                    $mode = api_get_permissions_for_new_directories();
398
                    for ($i = 0; $i < $num_files; $i++) {
399
                        $entry = $zip->getNameIndex($i);
400
                        if ('/' == substr($entry, -1)) {
401
                            continue;
402
                        }
403
404
                        $pos_slash = strpos($entry, '/');
405
                        $entry_without_first_dir = substr($entry, $pos_slash + 1);
406
                        // If there is still a slash, we need to make sure the directories are created.
407
                        if (false !== strpos($entry_without_first_dir, '/')) {
408
                            if (!is_dir($extraction_path.dirname($entry_without_first_dir))) {
409
                                // Create it.
410
                                @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

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

1071
        /** @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...
1072
    }
1073
1074
    // Now we remove it from the database.
1075
    $sql = "DELETE FROM $table WHERE id = $id";
1076
    Database::query($sql);
1077
1078
    deleteTemplateImage($id);
1079
1080
    // Display a feedback message.
1081
    echo Display::return_message(get_lang('Template deleted'), 'confirm');
1082
}
1083
1084
/**
1085
 * @param array $settings
1086
 * @param array $settings_by_access_list
1087
 *
1088
 * @throws \Doctrine\ORM\ORMException
1089
 * @throws \Doctrine\ORM\OptimisticLockException
1090
 * @throws \Doctrine\ORM\TransactionRequiredException
1091
 *
1092
 * @return FormValidator
1093
 */
1094
function generateSettingsForm($settings, $settings_by_access_list)
1095
{
1096
    global $_configuration, $settings_to_avoid, $convert_byte_to_mega_list;
1097
    $em = Database::getManager();
1098
    $table_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS);
1099
1100
    $form = new FormValidator(
1101
        'settings',
1102
        'post',
1103
        'settings.php?category='.Security::remove_XSS($_GET['category'])
1104
    );
1105
1106
    $form->addElement(
1107
        'hidden',
1108
        'search_field',
1109
        (!empty($_GET['search_field']) ? Security::remove_XSS($_GET['search_field']) : null)
1110
    );
1111
1112
    $url_id = api_get_current_access_url_id();
1113
1114
    $default_values = [];
1115
    $url_info = api_get_access_url($url_id);
1116
    $i = 0;
1117
    $addedSettings = [];
1118
    foreach ($settings as $row) {
1119
        if (in_array($row['variable'], array_keys($settings_to_avoid))) {
1120
            continue;
1121
        }
1122
1123
        if (in_array($row['variable'], $addedSettings)) {
1124
            continue;
1125
        }
1126
1127
        $addedSettings[] = $row['variable'];
1128
1129
        if (api_get_multiple_access_url()) {
1130
            if (api_is_global_platform_admin()) {
1131
                if (0 == $row['access_url_locked']) {
1132
                    if (1 == $url_id) {
1133
                        if ('1' == $row['access_url_changeable']) {
1134
                            $form->addElement(
1135
                                'html',
1136
                                '<div class="float-right"><a class="share_this_setting" data_status = "0"  data_to_send = "'.$row['variable'].'" href="javascript:void(0);">'.
1137
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</a></div>'
1138
                            );
1139
                        } else {
1140
                            $form->addElement(
1141
                                'html',
1142
                                '<div class="float-right"><a class="share_this_setting" data_status = "1" data_to_send = "'.$row['variable'].'" href="javascript:void(0);">'.
1143
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</a></div>'
1144
                            );
1145
                        }
1146
                    } else {
1147
                        if ('1' == $row['access_url_changeable']) {
1148
                            $form->addElement(
1149
                                'html',
1150
                                '<div class="float-right">'.
1151
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</div>'
1152
                            );
1153
                        } else {
1154
                            $form->addElement(
1155
                                'html',
1156
                                '<div class="float-right">'.
1157
                                Display::getMdiIcon(StateIcon::SHARED_VISIBILITY, 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Change setting visibility for the other portals')).'</div>'
1158
                            );
1159
                        }
1160
                    }
1161
                }
1162
            }
1163
        }
1164
1165
        $hideme = [];
1166
        $hide_element = false;
1167
1168
        if (1 != $_configuration['access_url']) {
1169
            if (0 == $row['access_url_changeable']) {
1170
                // We hide the element in other cases (checkbox, radiobutton) we 'freeze' the element.
1171
                $hide_element = true;
1172
                $hideme = ['disabled'];
1173
            } elseif (1 == $url_info['active']) {
1174
                // We show the elements.
1175
                if (empty($row['variable'])) {
1176
                    $row['variable'] = 0;
1177
                }
1178
                if (empty($row['subkey'])) {
1179
                    $row['subkey'] = 0;
1180
                }
1181
                if (empty($row['category'])) {
1182
                    $row['category'] = 0;
1183
                }
1184
                if (isset($settings_by_access_list[$row['variable']]) &&
1185
                    isset($settings_by_access_list[$row['variable']][$row['subkey']]) &&
1186
                    is_array($settings_by_access_list[$row['variable']][$row['subkey']][$row['category']])
1187
                ) {
1188
                    // We are sure that the other site have a selected value.
1189
                    if ('' != $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']]['selected_value']) {
1190
                        $row['selected_value'] = $settings_by_access_list[$row['variable']][$row['subkey']][$row['category']]['selected_value'];
1191
                    }
1192
                }
1193
                // There is no else{} statement because we load the default $row['selected_value'] of the main Chamilo site.
1194
            }
1195
        }
1196
1197
        switch ($row['type']) {
1198
            case 'textfield':
1199
                if (in_array($row['variable'], $convert_byte_to_mega_list)) {
1200
                    $form->addElement(
1201
                        'text',
1202
                        $row['variable'],
1203
                        [
1204
                            get_lang($row['title']),
1205
                            get_lang($row['comment']),
1206
                            get_lang('MB'),
1207
                        ],
1208
                        ['maxlength' => '8', 'aria-label' => get_lang($row['title'])]
1209
                    );
1210
                    $form->applyFilter($row['variable'], 'html_filter');
1211
                    $default_values[$row['variable']] = round($row['selected_value'] / 1024 / 1024, 1);
1212
                } elseif ('account_valid_duration' == $row['variable']) {
1213
                    $form->addElement(
1214
                        'text',
1215
                        $row['variable'],
1216
                        [
1217
                            get_lang($row['title']),
1218
                            get_lang($row['comment']),
1219
                        ],
1220
                        ['maxlength' => '5', 'aria-label' => get_lang($row['title'])]
1221
                    );
1222
                    $form->applyFilter($row['variable'], 'html_filter');
1223
1224
                    // For platform character set selection:
1225
                    // Conversion of the textfield to a select box with valid values.
1226
                    $default_values[$row['variable']] = $row['selected_value'];
1227
                } elseif ('platform_charset' == $row['variable']) {
1228
                    break;
1229
                } else {
1230
                    $hideme['class'] = 'col-md-4';
1231
                    $hideme['aria-label'] = get_lang($row['title']);
1232
                    $form->addElement(
1233
                        'text',
1234
                        $row['variable'],
1235
                        [
1236
                            get_lang($row['title']),
1237
                            get_lang($row['comment']),
1238
                        ],
1239
                        $hideme
1240
                    );
1241
                    $form->applyFilter($row['variable'], 'html_filter');
1242
                    $default_values[$row['variable']] = $row['selected_value'];
1243
                }
1244
                break;
1245
            case 'textarea':
1246
                if ('header_extra_content' == $row['variable']) {
1247
                    $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

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