formGenerateElementsGroup()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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