Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Functions and classes used during installation, upgrades and for admin settings.
19
 *
20
 *  ADMIN SETTINGS TREE INTRODUCTION
21
 *
22
 *  This file performs the following tasks:
23
 *   -it defines the necessary objects and interfaces to build the Moodle
24
 *    admin hierarchy
25
 *   -it defines the admin_externalpage_setup()
26
 *
27
 *  ADMIN_SETTING OBJECTS
28
 *
29
 *  Moodle settings are represented by objects that inherit from the admin_setting
30
 *  class. These objects encapsulate how to read a setting, how to write a new value
31
 *  to a setting, and how to appropriately display the HTML to modify the setting.
32
 *
33
 *  ADMIN_SETTINGPAGE OBJECTS
34
 *
35
 *  The admin_setting objects are then grouped into admin_settingpages. The latter
36
 *  appear in the Moodle admin tree block. All interaction with admin_settingpage
37
 *  objects is handled by the admin/settings.php file.
38
 *
39
 *  ADMIN_EXTERNALPAGE OBJECTS
40
 *
41
 *  There are some settings in Moodle that are too complex to (efficiently) handle
42
 *  with admin_settingpages. (Consider, for example, user management and displaying
43
 *  lists of users.) In this case, we use the admin_externalpage object. This object
44
 *  places a link to an external PHP file in the admin tree block.
45
 *
46
 *  If you're using an admin_externalpage object for some settings, you can take
47
 *  advantage of the admin_externalpage_* functions. For example, suppose you wanted
48
 *  to add a foo.php file into admin. First off, you add the following line to
49
 *  admin/settings/first.php (at the end of the file) or to some other file in
50
 *  admin/settings:
51
 * <code>
52
 *     $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
53
 *         $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
54
 * </code>
55
 *
56
 *  Next, in foo.php, your file structure would resemble the following:
57
 * <code>
58
 *         require(__DIR__.'/../../config.php');
59
 *         require_once($CFG->libdir.'/adminlib.php');
60
 *         admin_externalpage_setup('foo');
61
 *         // functionality like processing form submissions goes here
62
 *         echo $OUTPUT->header();
63
 *         // your HTML goes here
64
 *         echo $OUTPUT->footer();
65
 * </code>
66
 *
67
 *  The admin_externalpage_setup() function call ensures the user is logged in,
68
 *  and makes sure that they have the proper role permission to access the page.
69
 *  It also configures all $PAGE properties needed for navigation.
70
 *
71
 *  ADMIN_CATEGORY OBJECTS
72
 *
73
 *  Above and beyond all this, we have admin_category objects. These objects
74
 *  appear as folders in the admin tree block. They contain admin_settingpage's,
75
 *  admin_externalpage's, and other admin_category's.
76
 *
77
 *  OTHER NOTES
78
 *
79
 *  admin_settingpage's, admin_externalpage's, and admin_category's all inherit
80
 *  from part_of_admin_tree (a pseudointerface). This interface insists that
81
 *  a class has a check_access method for access permissions, a locate method
82
 *  used to find a specific node in the admin tree and find parent path.
83
 *
84
 *  admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
85
 *  interface ensures that the class implements a recursive add function which
86
 *  accepts a part_of_admin_tree object and searches for the proper place to
87
 *  put it. parentable_part_of_admin_tree implies part_of_admin_tree.
88
 *
89
 *  Please note that the $this->name field of any part_of_admin_tree must be
90
 *  UNIQUE throughout the ENTIRE admin tree.
91
 *
92
 *  The $this->name field of an admin_setting object (which is *not* part_of_
93
 *  admin_tree) must be unique on the respective admin_settingpage where it is
94
 *  used.
95
 *
96
 * Original author: Vincenzo K. Marcovecchio
97
 * Maintainer:      Petr Skoda
98
 *
99
 * @package    core
100
 * @subpackage admin
101
 * @copyright  1999 onwards Martin Dougiamas  http://dougiamas.com
102
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
103
 */
104
 
105
use core_admin\local\settings\linkable_settings_page;
106
 
107
defined('MOODLE_INTERNAL') || die();
108
 
109
/// Add libraries
110
require_once($CFG->libdir.'/ddllib.php');
111
require_once($CFG->libdir.'/xmlize.php');
112
require_once($CFG->libdir.'/messagelib.php');
113
 
114
// Add classes, traits, and interfaces which should be autoloaded.
115
// The autoloader is configured late in setup.php, after ABORT_AFTER_CONFIG.
116
// This is also required where the setup system is not included at all.
117
require_once($CFG->dirroot.'/'.$CFG->admin.'/classes/local/settings/linkable_settings_page.php');
118
 
119
define('INSECURE_DATAROOT_WARNING', 1);
120
define('INSECURE_DATAROOT_ERROR', 2);
121
 
122
/**
123
 * Automatically clean-up all plugin data and remove the plugin DB tables
124
 *
125
 * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
126
 *
127
 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
128
 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
129
 * @uses global $OUTPUT to produce notices and other messages
130
 * @return void
131
 */
132
function uninstall_plugin($type, $name) {
133
    global $CFG, $DB, $OUTPUT;
134
 
135
    // This may take a long time.
136
    core_php_time_limit::raise();
137
 
138
    // Recursively uninstall all subplugins first.
139
    $subplugintypes = core_component::get_plugin_types_with_subplugins();
140
    if (isset($subplugintypes[$type])) {
141
        $base = core_component::get_plugin_directory($type, $name);
1441 ariadna 142
        $subplugins = \core\component::get_subplugins("{$type}_{$name}");
1 efrain 143
 
144
        if (!empty($subplugins)) {
145
            foreach (array_keys($subplugins) as $subplugintype) {
146
                $instances = core_component::get_plugin_list($subplugintype);
147
                foreach ($instances as $subpluginname => $notusedpluginpath) {
148
                    uninstall_plugin($subplugintype, $subpluginname);
149
                }
150
            }
151
        }
152
    }
153
 
154
    $component = $type . '_' . $name;  // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
155
 
156
    if ($type === 'mod') {
157
        $pluginname = $name;  // eg. 'forum'
158
        if (get_string_manager()->string_exists('modulename', $component)) {
159
            $strpluginname = get_string('modulename', $component);
160
        } else {
161
            $strpluginname = $component;
162
        }
163
 
164
    } else {
165
        $pluginname = $component;
166
        if (get_string_manager()->string_exists('pluginname', $component)) {
167
            $strpluginname = get_string('pluginname', $component);
168
        } else {
169
            $strpluginname = $component;
170
        }
171
    }
172
 
173
    echo $OUTPUT->heading($pluginname);
174
 
175
    // Delete all tag areas, collections and instances associated with this plugin.
176
    core_tag_area::uninstall($component);
177
 
178
    // Custom plugin uninstall.
179
    $plugindirectory = core_component::get_plugin_directory($type, $name);
180
    $uninstalllib = $plugindirectory . '/db/uninstall.php';
181
    if (file_exists($uninstalllib)) {
182
        require_once($uninstalllib);
183
        $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall';    // eg. 'xmldb_workshop_uninstall()'
184
        if (function_exists($uninstallfunction)) {
185
            // Do not verify result, let plugin complain if necessary.
186
            $uninstallfunction();
187
        }
188
    }
189
 
190
    // Specific plugin type cleanup.
191
    $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
192
    if ($plugininfo) {
193
        $plugininfo->uninstall_cleanup();
194
        core_plugin_manager::reset_caches();
195
    }
196
    $plugininfo = null;
197
 
198
    // Perform clean-up task common for all the plugin/subplugin types.
199
 
200
    // Delete the web service functions and pre-built services.
201
    \core_external\util::delete_service_descriptions($component);
202
 
203
    // delete calendar events
204
    $DB->delete_records('event', array('modulename' => $pluginname));
205
    $DB->delete_records('event', ['component' => $component]);
206
 
207
    // Delete scheduled tasks.
208
    $DB->delete_records('task_adhoc', ['component' => $component]);
209
    $DB->delete_records('task_scheduled', array('component' => $component));
210
 
211
    // Delete Inbound Message datakeys.
212
    $DB->delete_records_select('messageinbound_datakeys',
213
            'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
214
 
215
    // Delete Inbound Message handlers.
216
    $DB->delete_records('messageinbound_handlers', array('component' => $component));
217
 
218
    // delete all the logs
219
    $DB->delete_records('log', array('module' => $pluginname));
220
 
221
    // delete log_display information
222
    $DB->delete_records('log_display', array('component' => $component));
223
 
224
    // delete the module configuration records
225
    unset_all_config_for_plugin($component);
226
    if ($type === 'mod') {
227
        unset_all_config_for_plugin($pluginname);
228
    }
229
 
230
    // Wipe any xAPI state information.
231
    if (core_xapi\handler::supports_xapi($component)) {
232
        core_xapi\api::remove_states_from_component($component);
233
    }
234
 
235
    // delete message provider
236
    message_provider_uninstall($component);
237
 
238
    // delete the plugin tables
239
    $xmldbfilepath = $plugindirectory . '/db/install.xml';
240
    drop_plugin_tables($component, $xmldbfilepath, false);
241
    if ($type === 'mod' or $type === 'block') {
242
        // non-frankenstyle table prefixes
243
        drop_plugin_tables($name, $xmldbfilepath, false);
244
    }
245
 
246
    // delete the capabilities that were defined by this module
247
    capabilities_cleanup($component);
248
 
249
    // Delete all remaining files in the filepool owned by the component.
250
    $fs = get_file_storage();
251
    $fs->delete_component_files($component);
252
 
253
    // Finally purge all caches.
254
    purge_all_caches();
255
 
256
    // Invalidate the hash used for upgrade detections.
257
    set_config('allversionshash', '');
258
 
259
    echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
260
}
261
 
262
/**
263
 * Returns the version of installed component
264
 *
265
 * @param string $component component name
266
 * @param string $source either 'disk' or 'installed' - where to get the version information from
267
 * @return string|bool version number or false if the component is not found
268
 */
269
function get_component_version($component, $source='installed') {
270
    global $CFG, $DB;
271
 
272
    list($type, $name) = core_component::normalize_component($component);
273
 
274
    // moodle core or a core subsystem
275
    if ($type === 'core') {
276
        if ($source === 'installed') {
277
            if (empty($CFG->version)) {
278
                return false;
279
            } else {
280
                return $CFG->version;
281
            }
282
        } else {
283
            if (!is_readable($CFG->dirroot.'/version.php')) {
284
                return false;
285
            } else {
286
                $version = null; //initialize variable for IDEs
287
                include($CFG->dirroot.'/version.php');
288
                return $version;
289
            }
290
        }
291
    }
292
 
293
    // activity module
294
    if ($type === 'mod') {
295
        if ($source === 'installed') {
296
            if ($CFG->version < 2013092001.02) {
297
                return $DB->get_field('modules', 'version', array('name'=>$name));
298
            } else {
299
                return get_config('mod_'.$name, 'version');
300
            }
301
 
302
        } else {
303
            $mods = core_component::get_plugin_list('mod');
304
            if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
305
                return false;
306
            } else {
307
                $plugin = new stdClass();
308
                $plugin->version = null;
309
                $module = $plugin;
310
                include($mods[$name].'/version.php');
311
                return $plugin->version;
312
            }
313
        }
314
    }
315
 
316
    // block
317
    if ($type === 'block') {
318
        if ($source === 'installed') {
319
            if ($CFG->version < 2013092001.02) {
320
                return $DB->get_field('block', 'version', array('name'=>$name));
321
            } else {
322
                return get_config('block_'.$name, 'version');
323
            }
324
        } else {
325
            $blocks = core_component::get_plugin_list('block');
326
            if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
327
                return false;
328
            } else {
329
                $plugin = new stdclass();
330
                include($blocks[$name].'/version.php');
331
                return $plugin->version;
332
            }
333
        }
334
    }
335
 
336
    // all other plugin types
337
    if ($source === 'installed') {
338
        return get_config($type.'_'.$name, 'version');
339
    } else {
340
        $plugins = core_component::get_plugin_list($type);
341
        if (empty($plugins[$name])) {
342
            return false;
343
        } else {
344
            $plugin = new stdclass();
345
            include($plugins[$name].'/version.php');
346
            return $plugin->version;
347
        }
348
    }
349
}
350
 
351
/**
352
 * Delete all plugin tables
353
 *
354
 * @param string $name Name of plugin, used as table prefix
355
 * @param string $file Path to install.xml file
356
 * @param bool $feedback defaults to true
357
 * @return bool Always returns true
358
 */
359
function drop_plugin_tables($name, $file, $feedback=true) {
360
    global $CFG, $DB;
361
 
362
    // first try normal delete
363
    if (file_exists($file)) {
364
        $DB->get_manager()->delete_tables_from_xmldb_file($file);
365
        return true;
366
    }
367
 
368
    // then try to find all tables that start with name and are not in any xml file
369
    $used_tables = get_used_table_names();
370
 
371
    $tables = $DB->get_tables();
372
 
373
    /// Iterate over, fixing id fields as necessary
374
    foreach ($tables as $table) {
375
        if (in_array($table, $used_tables)) {
376
            continue;
377
        }
378
 
379
        if (strpos($table, $name) !== 0) {
380
            continue;
381
        }
382
 
383
        // found orphan table --> delete it
384
        if ($DB->get_manager()->table_exists($table)) {
385
            $xmldb_table = new xmldb_table($table);
386
            $DB->get_manager()->drop_table($xmldb_table);
387
        }
388
    }
389
 
390
    return true;
391
}
392
 
393
/**
394
 * Returns names of all known tables == tables that moodle knows about.
395
 *
396
 * @return array Array of lowercase table names
397
 */
398
function get_used_table_names() {
399
    $table_names = array();
400
    $dbdirs = get_db_directories();
401
 
402
    foreach ($dbdirs as $dbdir) {
403
        $file = $dbdir.'/install.xml';
404
 
405
        $xmldb_file = new xmldb_file($file);
406
 
407
        if (!$xmldb_file->fileExists()) {
408
            continue;
409
        }
410
 
411
        $loaded    = $xmldb_file->loadXMLStructure();
412
        $structure = $xmldb_file->getStructure();
413
 
414
        if ($loaded and $tables = $structure->getTables()) {
415
            foreach($tables as $table) {
416
                $table_names[] = strtolower($table->getName());
417
            }
418
        }
419
    }
420
 
421
    return $table_names;
422
}
423
 
424
/**
425
 * Returns list of all directories where we expect install.xml files
426
 * @return array Array of paths
427
 */
428
function get_db_directories() {
429
    global $CFG;
430
 
431
    $dbdirs = array();
432
 
433
    /// First, the main one (lib/db)
434
    $dbdirs[] = $CFG->libdir.'/db';
435
 
436
    /// Then, all the ones defined by core_component::get_plugin_types()
437
    $plugintypes = core_component::get_plugin_types();
438
    foreach ($plugintypes as $plugintype => $pluginbasedir) {
439
        if ($plugins = core_component::get_plugin_list($plugintype)) {
440
            foreach ($plugins as $plugin => $plugindir) {
441
                $dbdirs[] = $plugindir.'/db';
442
            }
443
        }
444
    }
445
 
446
    return $dbdirs;
447
}
448
 
449
/**
450
 * Try to obtain or release the cron lock.
451
 * @param string  $name  name of lock
452
 * @param int  $until timestamp when this lock considered stale, null means remove lock unconditionally
453
 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
454
 * @return bool true if lock obtained
455
 */
456
function set_cron_lock($name, $until, $ignorecurrent=false) {
457
    global $DB;
458
    if (empty($name)) {
459
        debugging("Tried to get a cron lock for a null fieldname");
460
        return false;
461
    }
462
 
463
    // remove lock by force == remove from config table
464
    if (is_null($until)) {
465
        set_config($name, null);
466
        return true;
467
    }
468
 
469
    if (!$ignorecurrent) {
470
        // read value from db - other processes might have changed it
471
        $value = $DB->get_field('config', 'value', array('name'=>$name));
472
 
473
        if ($value and $value > time()) {
474
            //lock active
475
            return false;
476
        }
477
    }
478
 
479
    set_config($name, $until);
480
    return true;
481
}
482
 
483
/**
484
 * Test if and critical warnings are present
485
 * @return bool
486
 */
487
function admin_critical_warnings_present() {
488
    global $SESSION;
489
 
490
    if (!has_capability('moodle/site:config', context_system::instance())) {
491
        return 0;
492
    }
493
 
494
    if (!isset($SESSION->admin_critical_warning)) {
495
        $SESSION->admin_critical_warning = 0;
496
        if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
497
            $SESSION->admin_critical_warning = 1;
498
        }
499
    }
500
 
501
    return $SESSION->admin_critical_warning;
502
}
503
 
504
/**
505
 * Detects if float supports at least 10 decimal digits
506
 *
507
 * Detects if float supports at least 10 decimal digits
508
 * and also if float-->string conversion works as expected.
509
 *
510
 * @return bool true if problem found
511
 */
512
function is_float_problem() {
513
    $num1 = 2009010200.01;
514
    $num2 = 2009010200.02;
515
 
516
    return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
517
}
518
 
519
/**
520
 * Try to verify that dataroot is not accessible from web.
521
 *
522
 * Try to verify that dataroot is not accessible from web.
523
 * It is not 100% correct but might help to reduce number of vulnerable sites.
524
 * Protection from httpd.conf and .htaccess is not detected properly.
525
 *
526
 * @uses INSECURE_DATAROOT_WARNING
527
 * @uses INSECURE_DATAROOT_ERROR
528
 * @param bool $fetchtest try to test public access by fetching file, default false
529
 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
530
 */
531
function is_dataroot_insecure($fetchtest=false) {
532
    global $CFG;
533
 
534
    $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
535
 
536
    $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
537
    $rp = strrev(trim($rp, '/'));
538
    $rp = explode('/', $rp);
539
    foreach($rp as $r) {
540
        if (strpos($siteroot, '/'.$r.'/') === 0) {
541
            $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
542
        } else {
543
            break; // probably alias root
544
        }
545
    }
546
 
547
    $siteroot = strrev($siteroot);
548
    $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
549
 
550
    if (strpos($dataroot, $siteroot) !== 0) {
551
        return false;
552
    }
553
 
554
    if (!$fetchtest) {
555
        return INSECURE_DATAROOT_WARNING;
556
    }
557
 
558
    // now try all methods to fetch a test file using http protocol
559
 
560
    $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
561
    preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
562
    $httpdocroot = $matches[1];
563
    $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
564
    make_upload_directory('diag');
565
    $testfile = $CFG->dataroot.'/diag/public.txt';
566
    if (!file_exists($testfile)) {
567
        file_put_contents($testfile, 'test file, do not delete');
568
        @chmod($testfile, $CFG->filepermissions);
569
    }
570
    $teststr = trim(file_get_contents($testfile));
571
    if (empty($teststr)) {
572
    // hmm, strange
573
        return INSECURE_DATAROOT_WARNING;
574
    }
575
 
576
    $testurl = $datarooturl.'/diag/public.txt';
577
    if (extension_loaded('curl') and
578
        !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
579
        !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
580
        ($ch = @curl_init($testurl)) !== false) {
581
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
582
        curl_setopt($ch, CURLOPT_HEADER, false);
583
        $data = curl_exec($ch);
584
        if (!curl_errno($ch)) {
585
            $data = trim($data);
586
            if ($data === $teststr) {
587
                curl_close($ch);
588
                return INSECURE_DATAROOT_ERROR;
589
            }
590
        }
591
        curl_close($ch);
592
    }
593
 
594
    if ($data = @file_get_contents($testurl)) {
595
        $data = trim($data);
596
        if ($data === $teststr) {
597
            return INSECURE_DATAROOT_ERROR;
598
        }
599
    }
600
 
601
    preg_match('|https?://([^/]+)|i', $testurl, $matches);
602
    $sitename = $matches[1];
603
    $error = 0;
604
    if ($fp = @fsockopen($sitename, 80, $error)) {
605
        preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
606
        $localurl = $matches[1];
607
        $out = "GET $localurl HTTP/1.1\r\n";
608
        $out .= "Host: $sitename\r\n";
609
        $out .= "Connection: Close\r\n\r\n";
610
        fwrite($fp, $out);
611
        $data = '';
612
        $incoming = false;
613
        while (!feof($fp)) {
614
            if ($incoming) {
615
                $data .= fgets($fp, 1024);
616
            } else if (@fgets($fp, 1024) === "\r\n") {
617
                    $incoming = true;
618
                }
619
        }
620
        fclose($fp);
621
        $data = trim($data);
622
        if ($data === $teststr) {
623
            return INSECURE_DATAROOT_ERROR;
624
        }
625
    }
626
 
627
    return INSECURE_DATAROOT_WARNING;
628
}
629
 
630
/**
631
 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
632
 */
633
function enable_cli_maintenance_mode() {
634
    global $CFG, $SITE;
635
 
636
    if (file_exists("$CFG->dataroot/climaintenance.html")) {
637
        unlink("$CFG->dataroot/climaintenance.html");
638
    }
639
 
640
    if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
641
        $data = $CFG->maintenance_message;
642
        $data = bootstrap_renderer::early_error_content($data, null, null, null);
643
        $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
644
 
645
    } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
646
        $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
647
 
648
    } else {
649
        $data = get_string('sitemaintenance', 'admin');
650
        $data = bootstrap_renderer::early_error_content($data, null, null, null);
651
        $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin',
652
            format_string($SITE->fullname, true, ['context' => context_system::instance()])), $data);
653
    }
654
 
655
    file_put_contents("$CFG->dataroot/climaintenance.html", $data);
656
    chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
657
}
658
 
659
/// CLASS DEFINITIONS /////////////////////////////////////////////////////////
660
 
661
 
662
/**
663
 * Interface for anything appearing in the admin tree
664
 *
665
 * The interface that is implemented by anything that appears in the admin tree
666
 * block. It forces inheriting classes to define a method for checking user permissions
667
 * and methods for finding something in the admin tree.
668
 *
669
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
670
 */
671
interface part_of_admin_tree {
672
 
673
/**
674
 * Finds a named part_of_admin_tree.
675
 *
676
 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
677
 * and not parentable_part_of_admin_tree, then this function should only check if
678
 * $this->name matches $name. If it does, it should return a reference to $this,
679
 * otherwise, it should return a reference to NULL.
680
 *
681
 * If a class inherits parentable_part_of_admin_tree, this method should be called
682
 * recursively on all child objects (assuming, of course, the parent object's name
683
 * doesn't match the search criterion).
684
 *
685
 * @param string $name The internal name of the part_of_admin_tree we're searching for.
686
 * @return mixed An object reference or a NULL reference.
687
 */
688
    public function locate($name);
689
 
690
    /**
691
     * Removes named part_of_admin_tree.
692
     *
693
     * @param string $name The internal name of the part_of_admin_tree we want to remove.
694
     * @return bool success.
695
     */
696
    public function prune($name);
697
 
698
    /**
699
     * Search using query
700
     * @param string $query
701
     * @return mixed array-object structure of found settings and pages
702
     */
703
    public function search($query);
704
 
705
    /**
706
     * Verifies current user's access to this part_of_admin_tree.
707
     *
708
     * Used to check if the current user has access to this part of the admin tree or
709
     * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
710
     * then this method is usually just a call to has_capability() in the site context.
711
     *
712
     * If a class inherits parentable_part_of_admin_tree, this method should return the
713
     * logical OR of the return of check_access() on all child objects.
714
     *
715
     * @return bool True if the user has access, false if she doesn't.
716
     */
717
    public function check_access();
718
 
719
    /**
720
     * Mostly useful for removing of some parts of the tree in admin tree block.
721
     *
722
     * @return bool True is hidden from normal list view
723
     */
724
    public function is_hidden();
725
 
726
    /**
727
     * Show we display Save button at the page bottom?
728
     * @return bool
729
     */
730
    public function show_save();
731
}
732
 
733
 
734
/**
735
 * Interface implemented by any part_of_admin_tree that has children.
736
 *
737
 * The interface implemented by any part_of_admin_tree that can be a parent
738
 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
739
 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
740
 * include an add method for adding other part_of_admin_tree objects as children.
741
 *
742
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
743
 */
744
interface parentable_part_of_admin_tree extends part_of_admin_tree {
745
 
746
/**
747
 * Adds a part_of_admin_tree object to the admin tree.
748
 *
749
 * Used to add a part_of_admin_tree object to this object or a child of this
750
 * object. $something should only be added if $destinationname matches
751
 * $this->name. If it doesn't, add should be called on child objects that are
752
 * also parentable_part_of_admin_tree's.
753
 *
754
 * $something should be appended as the last child in the $destinationname. If the
755
 * $beforesibling is specified, $something should be prepended to it. If the given
756
 * sibling is not found, $something should be appended to the end of $destinationname
757
 * and a developer debugging message should be displayed.
758
 *
759
 * @param string $destinationname The internal name of the new parent for $something.
760
 * @param part_of_admin_tree $something The object to be added.
761
 * @return bool True on success, false on failure.
762
 */
763
    public function add($destinationname, $something, $beforesibling = null);
764
 
765
}
766
 
767
 
768
/**
769
 * The object used to represent folders (a.k.a. categories) in the admin tree block.
770
 *
771
 * Each admin_category object contains a number of part_of_admin_tree objects.
772
 *
773
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
774
 */
775
class admin_category implements parentable_part_of_admin_tree, linkable_settings_page {
776
 
777
    /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
778
    protected $children;
779
    /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
780
    public $name;
781
    /** @var string The displayed name for this category. Usually obtained through get_string() */
782
    public $visiblename;
783
    /** @var bool Should this category be hidden in admin tree block? */
784
    public $hidden;
785
    /** @var mixed Either a string or an array or strings */
786
    public $path;
787
    /** @var mixed Either a string or an array or strings */
788
    public $visiblepath;
789
 
790
    /** @var array fast lookup category cache, all categories of one tree point to one cache */
791
    protected $category_cache;
792
 
793
    /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
794
    protected $sort = false;
795
    /** @var bool If set to true children will be sorted in ascending order. */
796
    protected $sortasc = true;
797
    /** @var bool If set to true sub categories and pages will be split and then sorted.. */
798
    protected $sortsplit = true;
799
    /** @var bool $sorted True if the children have been sorted and don't need resorting */
800
    protected $sorted = false;
801
 
802
    /**
803
     * Constructor for an empty admin category
804
     *
805
     * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
806
     * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
807
     * @param bool $hidden hide category in admin tree block, defaults to false
808
     */
809
    public function __construct($name, $visiblename, $hidden=false) {
810
        $this->children    = array();
811
        $this->name        = $name;
812
        $this->visiblename = $visiblename;
813
        $this->hidden      = $hidden;
814
    }
815
 
816
    /**
817
     * Get the URL to view this settings page.
818
     *
819
     * @return moodle_url
820
     */
821
    public function get_settings_page_url(): moodle_url {
822
        return new moodle_url(
823
            '/admin/category.php',
824
            [
825
                'category' => $this->name,
826
            ]
827
        );
828
    }
829
 
830
    /**
831
     * Returns a reference to the part_of_admin_tree object with internal name $name.
832
     *
833
     * @param string $name The internal name of the object we want.
834
     * @param bool $findpath initialize path and visiblepath arrays
835
     * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
836
     *                  defaults to false
837
     */
838
    public function locate($name, $findpath=false) {
839
        if (!isset($this->category_cache[$this->name])) {
840
            // somebody much have purged the cache
841
            $this->category_cache[$this->name] = $this;
842
        }
843
 
844
        if ($this->name == $name) {
845
            if ($findpath) {
846
                $this->visiblepath[] = $this->visiblename;
847
                $this->path[]        = $this->name;
848
            }
849
            return $this;
850
        }
851
 
852
        // quick category lookup
853
        if (!$findpath and isset($this->category_cache[$name])) {
854
            return $this->category_cache[$name];
855
        }
856
 
857
        $return = NULL;
858
        foreach($this->children as $childid=>$unused) {
859
            if ($return = $this->children[$childid]->locate($name, $findpath)) {
860
                break;
861
            }
862
        }
863
 
864
        if (!is_null($return) and $findpath) {
865
            $return->visiblepath[] = $this->visiblename;
866
            $return->path[]        = $this->name;
867
        }
868
 
869
        return $return;
870
    }
871
 
872
    /**
873
     * Search using query
874
     *
875
     * @param string query
876
     * @return mixed array-object structure of found settings and pages
877
     */
878
    public function search($query) {
879
        $result = array();
880
        foreach ($this->get_children() as $child) {
881
            $subsearch = $child->search($query);
882
            if (!is_array($subsearch)) {
883
                debugging('Incorrect search result from '.$child->name);
884
                continue;
885
            }
886
            $result = array_merge($result, $subsearch);
887
        }
888
        return $result;
889
    }
890
 
891
    /**
892
     * Removes part_of_admin_tree object with internal name $name.
893
     *
894
     * @param string $name The internal name of the object we want to remove.
895
     * @return bool success
896
     */
897
    public function prune($name) {
898
 
899
        if ($this->name == $name) {
900
            return false;  //can not remove itself
901
        }
902
 
903
        foreach($this->children as $precedence => $child) {
904
            if ($child->name == $name) {
905
                // clear cache and delete self
906
                while($this->category_cache) {
907
                    // delete the cache, but keep the original array address
908
                    array_pop($this->category_cache);
909
                }
910
                unset($this->children[$precedence]);
911
                return true;
912
            } else if ($this->children[$precedence]->prune($name)) {
913
                return true;
914
            }
915
        }
916
        return false;
917
    }
918
 
919
    /**
920
     * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
921
     *
922
     * By default the new part of the tree is appended as the last child of the parent. You
923
     * can specify a sibling node that the new part should be prepended to. If the given
924
     * sibling is not found, the part is appended to the end (as it would be by default) and
925
     * a developer debugging message is displayed.
926
     *
927
     * @throws coding_exception if the $beforesibling is empty string or is not string at all.
928
     * @param string $destinationame The internal name of the immediate parent that we want for $something.
929
     * @param mixed $something A part_of_admin_tree or setting instance to be added.
930
     * @param string $beforesibling The name of the parent's child the $something should be prepended to.
931
     * @return bool True if successfully added, false if $something can not be added.
932
     */
933
    public function add($parentname, $something, $beforesibling = null) {
934
        global $CFG;
935
 
936
        $parent = $this->locate($parentname);
937
        if (is_null($parent)) {
938
            debugging('parent does not exist!');
939
            return false;
940
        }
941
 
942
        if ($something instanceof part_of_admin_tree) {
943
            if (!($parent instanceof parentable_part_of_admin_tree)) {
944
                debugging('error - parts of tree can be inserted only into parentable parts');
945
                return false;
946
            }
947
            if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
948
                // The name of the node is already used, simply warn the developer that this should not happen.
949
                // It is intentional to check for the debug level before performing the check.
950
                debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
951
            }
952
            if (is_null($beforesibling)) {
953
                // Append $something as the parent's last child.
954
                $parent->children[] = $something;
955
            } else {
956
                if (!is_string($beforesibling) or trim($beforesibling) === '') {
957
                    throw new coding_exception('Unexpected value of the beforesibling parameter');
958
                }
959
                // Try to find the position of the sibling.
960
                $siblingposition = null;
961
                foreach ($parent->children as $childposition => $child) {
962
                    if ($child->name === $beforesibling) {
963
                        $siblingposition = $childposition;
964
                        break;
965
                    }
966
                }
967
                if (is_null($siblingposition)) {
968
                    debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
969
                    $parent->children[] = $something;
970
                } else {
971
                    $parent->children = array_merge(
972
                        array_slice($parent->children, 0, $siblingposition),
973
                        array($something),
974
                        array_slice($parent->children, $siblingposition)
975
                    );
976
                }
977
            }
978
            if ($something instanceof admin_category) {
979
                if (isset($this->category_cache[$something->name])) {
980
                    debugging('Duplicate admin category name: '.$something->name);
981
                } else {
982
                    $this->category_cache[$something->name] = $something;
983
                    $something->category_cache =& $this->category_cache;
984
                    foreach ($something->children as $child) {
985
                        // just in case somebody already added subcategories
986
                        if ($child instanceof admin_category) {
987
                            if (isset($this->category_cache[$child->name])) {
988
                                debugging('Duplicate admin category name: '.$child->name);
989
                            } else {
990
                                $this->category_cache[$child->name] = $child;
991
                                $child->category_cache =& $this->category_cache;
992
                            }
993
                        }
994
                    }
995
                }
996
            }
997
            return true;
998
 
999
        } else {
1000
            debugging('error - can not add this element');
1001
            return false;
1002
        }
1003
 
1004
    }
1005
 
1006
    /**
1007
     * Checks if the user has access to anything in this category.
1008
     *
1009
     * @return bool True if the user has access to at least one child in this category, false otherwise.
1010
     */
1011
    public function check_access() {
1012
        foreach ($this->children as $child) {
1013
            if ($child->check_access()) {
1014
                return true;
1015
            }
1016
        }
1017
        return false;
1018
    }
1019
 
1020
    /**
1021
     * Is this category hidden in admin tree block?
1022
     *
1023
     * @return bool True if hidden
1024
     */
1025
    public function is_hidden() {
1026
        return $this->hidden;
1027
    }
1028
 
1029
    /**
1030
     * Show we display Save button at the page bottom?
1031
     * @return bool
1032
     */
1033
    public function show_save() {
1034
        foreach ($this->children as $child) {
1035
            if ($child->show_save()) {
1036
                return true;
1037
            }
1038
        }
1039
        return false;
1040
    }
1041
 
1042
    /**
1043
     * Sets sorting on this category.
1044
     *
1045
     * Please note this function doesn't actually do the sorting.
1046
     * It can be called anytime.
1047
     * Sorting occurs when the user calls get_children.
1048
     * Code using the children array directly won't see the sorted results.
1049
     *
1050
     * @param bool $sort If set to true children will be sorted, if false they won't be.
1051
     * @param bool $asc If true sorting will be ascending, otherwise descending.
1052
     * @param bool $split If true we sort pages and sub categories separately.
1053
     */
1054
    public function set_sorting($sort, $asc = true, $split = true) {
1055
        $this->sort = (bool)$sort;
1056
        $this->sortasc = (bool)$asc;
1057
        $this->sortsplit = (bool)$split;
1058
    }
1059
 
1060
    /**
1061
     * Returns the children associated with this category.
1062
     *
1063
     * @return part_of_admin_tree[]
1064
     */
1065
    public function get_children() {
1066
        // If we should sort and it hasn't already been sorted.
1067
        if ($this->sort && !$this->sorted) {
1068
            if ($this->sortsplit) {
1069
                $categories = array();
1070
                $pages = array();
1071
                foreach ($this->children as $child) {
1072
                    if ($child instanceof admin_category) {
1073
                        $categories[] = $child;
1074
                    } else {
1075
                        $pages[] = $child;
1076
                    }
1077
                }
1078
                core_collator::asort_objects_by_property($categories, 'visiblename');
1079
                core_collator::asort_objects_by_property($pages, 'visiblename');
1080
                if (!$this->sortasc) {
1081
                    $categories = array_reverse($categories);
1082
                    $pages = array_reverse($pages);
1083
                }
1084
                $this->children = array_merge($pages, $categories);
1085
            } else {
1086
                core_collator::asort_objects_by_property($this->children, 'visiblename');
1087
                if (!$this->sortasc) {
1088
                    $this->children = array_reverse($this->children);
1089
                }
1090
            }
1091
            $this->sorted = true;
1092
        }
1093
        return $this->children;
1094
    }
1095
 
1096
    /**
1097
     * Magically gets a property from this object.
1098
     *
1099
     * @param $property
1100
     * @return part_of_admin_tree[]
1101
     * @throws coding_exception
1102
     */
1103
    public function __get($property) {
1104
        if ($property === 'children') {
1105
            return $this->get_children();
1106
        }
1107
        throw new coding_exception('Invalid property requested.');
1108
    }
1109
 
1110
    /**
1111
     * Magically sets a property against this object.
1112
     *
1113
     * @param string $property
1114
     * @param mixed $value
1115
     * @throws coding_exception
1116
     */
1117
    public function __set($property, $value) {
1118
        if ($property === 'children') {
1119
            $this->sorted = false;
1120
            $this->children = $value;
1121
        } else {
1122
            throw new coding_exception('Invalid property requested.');
1123
        }
1124
    }
1125
 
1126
    /**
1127
     * Checks if an inaccessible property is set.
1128
     *
1129
     * @param string $property
1130
     * @return bool
1131
     * @throws coding_exception
1132
     */
1133
    public function __isset($property) {
1134
        if ($property === 'children') {
1135
            return isset($this->children);
1136
        }
1137
        throw new coding_exception('Invalid property requested.');
1138
    }
1139
}
1140
 
1141
 
1142
/**
1143
 * Root of admin settings tree, does not have any parent.
1144
 *
1145
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1146
 */
1147
class admin_root extends admin_category {
1148
/** @var array List of errors */
1149
    public $errors;
1150
    /** @var string search query */
1151
    public $search;
1152
    /** @var bool full tree flag - true means all settings required, false only pages required */
1153
    public $fulltree;
1154
    /** @var bool flag indicating loaded tree */
1155
    public $loaded;
1156
    /** @var mixed site custom defaults overriding defaults in settings files*/
1157
    public $custom_defaults;
1158
 
1159
    /**
1160
     * @param bool $fulltree true means all settings required,
1161
     *                            false only pages required
1162
     */
1163
    public function __construct($fulltree) {
1164
        global $CFG;
1165
 
1166
        parent::__construct('root', get_string('administration'), false);
1167
        $this->errors   = array();
1168
        $this->search   = '';
1169
        $this->fulltree = $fulltree;
1170
        $this->loaded   = false;
1171
 
1172
        $this->category_cache = array();
1173
 
1174
        // load custom defaults if found
1175
        $this->custom_defaults = null;
1176
        $defaultsfile = "$CFG->dirroot/local/defaults.php";
1177
        if (is_readable($defaultsfile)) {
1178
            $defaults = array();
1179
            include($defaultsfile);
1180
            if (is_array($defaults) and count($defaults)) {
1181
                $this->custom_defaults = $defaults;
1182
            }
1183
        }
1184
    }
1185
 
1186
    /**
1187
     * Empties children array, and sets loaded to false
1188
     *
1189
     * @param bool $requirefulltree
1190
     */
1191
    public function purge_children($requirefulltree) {
1192
        $this->children = array();
1193
        $this->fulltree = ($requirefulltree || $this->fulltree);
1194
        $this->loaded   = false;
1195
        //break circular dependencies - this helps PHP 5.2
1196
        while($this->category_cache) {
1197
            array_pop($this->category_cache);
1198
        }
1199
        $this->category_cache = array();
1200
    }
1201
}
1202
 
1203
 
1204
/**
1205
 * Links external PHP pages into the admin tree.
1206
 *
1207
 * See detailed usage example at the top of this document (adminlib.php)
1208
 *
1209
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1210
 */
1211
class admin_externalpage implements part_of_admin_tree, linkable_settings_page {
1212
 
1213
    /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1214
    public $name;
1215
 
1216
    /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1217
    public $visiblename;
1218
 
1219
    /** @var string The external URL that we should link to when someone requests this external page. */
1220
    public $url;
1221
 
1222
    /** @var array The role capability/permission a user must have to access this external page. */
1223
    public $req_capability;
1224
 
1225
    /** @var object The context in which capability/permission should be checked, default is site context. */
1226
    public $context;
1227
 
1228
    /** @var bool hidden in admin tree block. */
1229
    public $hidden;
1230
 
1231
    /** @var mixed either string or array of string */
1232
    public $path;
1233
 
1234
    /** @var array list of visible names of page parents */
1235
    public $visiblepath;
1236
 
1237
    /**
1238
     * Constructor for adding an external page into the admin tree.
1239
     *
1240
     * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1241
     * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1242
     * @param string $url The external URL that we should link to when someone requests this external page.
1243
     * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1244
     * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1245
     * @param stdClass $context The context the page relates to. Not sure what happens
1246
     *      if you specify something other than system or front page. Defaults to system.
1247
     */
1248
    public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1249
        $this->name        = $name;
1250
        $this->visiblename = $visiblename;
1251
        $this->url         = $url;
1252
        if (is_array($req_capability)) {
1253
            $this->req_capability = $req_capability;
1254
        } else {
1255
            $this->req_capability = array($req_capability);
1256
        }
1257
        $this->hidden = $hidden;
1258
        $this->context = $context;
1259
    }
1260
 
1261
    /**
1262
     * Get the URL to view this settings page.
1263
     *
1264
     * @return moodle_url
1265
     */
1266
    public function get_settings_page_url(): moodle_url {
1267
        return new moodle_url($this->url);
1268
    }
1269
 
1270
    /**
1271
     * Returns a reference to the part_of_admin_tree object with internal name $name.
1272
     *
1273
     * @param string $name The internal name of the object we want.
1274
     * @param bool $findpath defaults to false
1275
     * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1276
     */
1277
    public function locate($name, $findpath=false) {
1278
        if ($this->name == $name) {
1279
            if ($findpath) {
1280
                $this->visiblepath = array($this->visiblename);
1281
                $this->path        = array($this->name);
1282
            }
1283
            return $this;
1284
        } else {
1285
            $return = NULL;
1286
            return $return;
1287
        }
1288
    }
1289
 
1290
    /**
1291
     * This function always returns false, required function by interface
1292
     *
1293
     * @param string $name
1294
     * @return false
1295
     */
1296
    public function prune($name) {
1297
        return false;
1298
    }
1299
 
1300
    /**
1301
     * Search using query
1302
     *
1303
     * @param string $query
1304
     * @return mixed array-object structure of found settings and pages
1305
     */
1306
    public function search($query) {
1307
        $found = false;
1308
        if (strpos(strtolower($this->name), $query) !== false) {
1309
            $found = true;
1310
        } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1311
                $found = true;
1312
            }
1313
        if ($found) {
1314
            $result = new stdClass();
1315
            $result->page     = $this;
1316
            $result->settings = array();
1317
            return array($this->name => $result);
1318
        } else {
1319
            return array();
1320
        }
1321
    }
1322
 
1323
    /**
1324
     * Determines if the current user has access to this external page based on $this->req_capability.
1325
     *
1326
     * @return bool True if user has access, false otherwise.
1327
     */
1328
    public function check_access() {
1329
        global $CFG;
1330
        $context = empty($this->context) ? context_system::instance() : $this->context;
1331
        foreach($this->req_capability as $cap) {
1332
            if (has_capability($cap, $context)) {
1333
                return true;
1334
            }
1335
        }
1336
        return false;
1337
    }
1338
 
1339
    /**
1340
     * Is this external page hidden in admin tree block?
1341
     *
1342
     * @return bool True if hidden
1343
     */
1344
    public function is_hidden() {
1345
        return $this->hidden;
1346
    }
1347
 
1348
    /**
1349
     * Show we display Save button at the page bottom?
1350
     * @return bool
1351
     */
1352
    public function show_save() {
1353
        return false;
1354
    }
1355
}
1356
 
1357
/**
1358
 * Used to store details of the dependency between two settings elements.
1359
 *
1360
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1361
 * @copyright 2017 Davo Smith, Synergy Learning
1362
 */
1363
class admin_settingdependency {
1364
    /** @var string the name of the setting to be shown/hidden */
1365
    public $settingname;
1366
    /** @var string the setting this is dependent on */
1367
    public $dependenton;
1368
    /** @var string the condition to show/hide the element */
1369
    public $condition;
1370
    /** @var string the value to compare against */
1371
    public $value;
1372
 
1373
    /** @var string[] list of valid conditions */
1374
    private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1375
 
1376
    /**
1377
     * admin_settingdependency constructor.
1378
     * @param string $settingname
1379
     * @param string $dependenton
1380
     * @param string $condition
1381
     * @param string $value
1382
     * @throws \coding_exception
1383
     */
1384
    public function __construct($settingname, $dependenton, $condition, $value) {
1385
        $this->settingname = $this->parse_name($settingname);
1386
        $this->dependenton = $this->parse_name($dependenton);
1387
        $this->condition = $condition;
1388
        $this->value = $value;
1389
 
1390
        if (!in_array($this->condition, self::$validconditions)) {
1391
            throw new coding_exception("Invalid condition '$condition'");
1392
        }
1393
    }
1394
 
1395
    /**
1396
     * Convert the setting name into the form field name.
1397
     * @param string $name
1398
     * @return string
1399
     */
1400
    private function parse_name($name) {
1401
        $bits = explode('/', $name);
1402
        $name = array_pop($bits);
1403
        $plugin = '';
1404
        if ($bits) {
1405
            $plugin = array_pop($bits);
1406
            if ($plugin === 'moodle') {
1407
                $plugin = '';
1408
            }
1409
        }
1410
        return 's_'.$plugin.'_'.$name;
1411
    }
1412
 
1413
    /**
1414
     * Gather together all the dependencies in a format suitable for initialising javascript
1415
     * @param admin_settingdependency[] $dependencies
1416
     * @return array
1417
     */
1418
    public static function prepare_for_javascript($dependencies) {
1419
        $result = [];
1420
        foreach ($dependencies as $d) {
1421
            if (!isset($result[$d->dependenton])) {
1422
                $result[$d->dependenton] = [];
1423
            }
1424
            if (!isset($result[$d->dependenton][$d->condition])) {
1425
                $result[$d->dependenton][$d->condition] = [];
1426
            }
1427
            if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1428
                $result[$d->dependenton][$d->condition][$d->value] = [];
1429
            }
1430
            $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1431
        }
1432
        return $result;
1433
    }
1434
}
1435
 
1436
/**
1437
 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1438
 *
1439
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1440
 */
1441
class admin_settingpage implements part_of_admin_tree, linkable_settings_page {
1442
 
1443
    /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1444
    public $name;
1445
 
1446
    /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1447
    public $visiblename;
1448
 
1449
    /** @var mixed An array of admin_setting objects that are part of this setting page. */
1450
    public $settings;
1451
 
1452
    /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1453
    protected $dependencies = [];
1454
 
1455
    /** @var array The role capability/permission a user must have to access this external page. */
1456
    public $req_capability;
1457
 
1458
    /** @var object The context in which capability/permission should be checked, default is site context. */
1459
    public $context;
1460
 
1461
    /** @var bool hidden in admin tree block. */
1462
    public $hidden;
1463
 
1464
    /** @var mixed string of paths or array of strings of paths */
1465
    public $path;
1466
 
1467
    /** @var array list of visible names of page parents */
1468
    public $visiblepath;
1469
 
1470
    /**
1471
     * see admin_settingpage for details of this function
1472
     *
1473
     * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1474
     * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1475
     * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1476
     * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1477
     * @param stdClass $context The context the page relates to. Not sure what happens
1478
     *      if you specify something other than system or front page. Defaults to system.
1479
     */
1480
    public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1481
        $this->settings    = new stdClass();
1482
        $this->name        = $name;
1483
        $this->visiblename = $visiblename;
1484
        if (is_array($req_capability)) {
1485
            $this->req_capability = $req_capability;
1486
        } else {
1487
            $this->req_capability = array($req_capability);
1488
        }
1489
        $this->hidden      = $hidden;
1490
        $this->context     = $context;
1491
    }
1492
 
1493
    /**
1494
     * Get the URL to view this page.
1495
     *
1496
     * @return moodle_url
1497
     */
1498
    public function get_settings_page_url(): moodle_url {
1499
        return new moodle_url(
1500
            '/admin/settings.php',
1501
            [
1502
                'section' => $this->name,
1503
            ]
1504
        );
1505
    }
1506
 
1507
    /**
1508
     * see admin_category
1509
     *
1510
     * @param string $name
1511
     * @param bool $findpath
1512
     * @return mixed Object (this) if name ==  this->name, else returns null
1513
     */
1514
    public function locate($name, $findpath=false) {
1515
        if ($this->name == $name) {
1516
            if ($findpath) {
1517
                $this->visiblepath = array($this->visiblename);
1518
                $this->path        = array($this->name);
1519
            }
1520
            return $this;
1521
        } else {
1522
            $return = NULL;
1523
            return $return;
1524
        }
1525
    }
1526
 
1527
    /**
1528
     * Search string in settings page.
1529
     *
1530
     * @param string $query
1531
     * @return array
1532
     */
1533
    public function search($query) {
1534
        $found = array();
1535
 
1536
        foreach ($this->settings as $setting) {
1537
            if ($setting->is_related($query)) {
1538
                $found[] = $setting;
1539
            }
1540
        }
1541
 
1542
        if ($found) {
1543
            $result = new stdClass();
1544
            $result->page     = $this;
1545
            $result->settings = $found;
1546
            return array($this->name => $result);
1547
        }
1548
 
1549
        $found = false;
1550
        if (strpos(strtolower($this->name), $query) !== false) {
1551
            $found = true;
1552
        } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1553
                $found = true;
1554
            }
1555
        if ($found) {
1556
            $result = new stdClass();
1557
            $result->page     = $this;
1558
            $result->settings = array();
1559
            return array($this->name => $result);
1560
        } else {
1561
            return array();
1562
        }
1563
    }
1564
 
1565
    /**
1566
     * This function always returns false, required by interface
1567
     *
1568
     * @param string $name
1569
     * @return bool Always false
1570
     */
1571
    public function prune($name) {
1572
        return false;
1573
    }
1574
 
1575
    /**
1576
     * adds an admin_setting to this admin_settingpage
1577
     *
1578
     * not the same as add for admin_category. adds an admin_setting to this admin_settingpage. settings appear (on the settingpage) in the order in which they're added
1579
     * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1580
     *
1581
     * @param object $setting is the admin_setting object you want to add
1582
     * @return bool true if successful, false if not
1583
     */
1584
    public function add($setting) {
1585
        if (!($setting instanceof admin_setting)) {
1586
            debugging('error - not a setting instance');
1587
            return false;
1588
        }
1589
 
1590
        $name = $setting->name;
1591
        if ($setting->plugin) {
1592
            $name = $setting->plugin . $name;
1593
        }
1594
        $this->settings->{$name} = $setting;
1595
        return true;
1596
    }
1597
 
1598
    /**
1599
     * Hide the named setting if the specified condition is matched.
1600
     *
1601
     * @param string $settingname
1602
     * @param string $dependenton
1603
     * @param string $condition
1604
     * @param string $value
1605
     */
1606
    public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1607
        $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1608
 
1609
        // Reformat the dependency name to the plugin | name format used in the display.
1610
        $dependenton = str_replace('/', ' | ', $dependenton);
1611
 
1612
        // Let the setting know, so it can be displayed underneath.
1613
        $findname = str_replace('/', '', $settingname);
1614
        foreach ($this->settings as $name => $setting) {
1615
            if ($name === $findname) {
1616
                $setting->add_dependent_on($dependenton);
1617
            }
1618
        }
1619
    }
1620
 
1621
    /**
1622
     * see admin_externalpage
1623
     *
1624
     * @return bool Returns true for yes false for no
1625
     */
1626
    public function check_access() {
1627
        global $CFG;
1628
        $context = empty($this->context) ? context_system::instance() : $this->context;
1629
        foreach($this->req_capability as $cap) {
1630
            if (has_capability($cap, $context)) {
1631
                return true;
1632
            }
1633
        }
1634
        return false;
1635
    }
1636
 
1637
    /**
1638
     * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1639
     * @return string Returns an XHTML string
1640
     */
1641
    public function output_html() {
1642
        $adminroot = admin_get_root();
1643
        $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1644
        foreach($this->settings as $setting) {
1645
            $fullname = $setting->get_full_name();
1646
            if (array_key_exists($fullname, $adminroot->errors)) {
1647
                $data = $adminroot->errors[$fullname]->data;
1648
            } else {
1649
                $data = $setting->get_setting();
1650
                // do not use defaults if settings not available - upgrade settings handles the defaults!
1651
            }
1652
            $return .= $setting->output_html($data);
1653
        }
1654
        $return .= '</fieldset>';
1655
        return $return;
1656
    }
1657
 
1658
    /**
1659
     * Is this settings page hidden in admin tree block?
1660
     *
1661
     * @return bool True if hidden
1662
     */
1663
    public function is_hidden() {
1664
        return $this->hidden;
1665
    }
1666
 
1667
    /**
1668
     * Show we display Save button at the page bottom?
1669
     * @return bool
1670
     */
1671
    public function show_save() {
1672
        foreach($this->settings as $setting) {
1673
            if (empty($setting->nosave)) {
1674
                return true;
1675
            }
1676
        }
1677
        return false;
1678
    }
1679
 
1680
    /**
1681
     * Should any of the settings on this page be shown / hidden based on conditions?
1682
     * @return bool
1683
     */
1684
    public function has_dependencies() {
1685
        return (bool)$this->dependencies;
1686
    }
1687
 
1688
    /**
1689
     * Format the setting show/hide conditions ready to initialise the page javascript
1690
     * @return array
1691
     */
1692
    public function get_dependencies_for_javascript() {
1693
        if (!$this->has_dependencies()) {
1694
            return [];
1695
        }
1696
        return admin_settingdependency::prepare_for_javascript($this->dependencies);
1697
    }
1698
}
1699
 
1700
 
1701
/**
1702
 * Admin settings class. Only exists on setting pages.
1703
 * Read & write happens at this level; no authentication.
1704
 *
1705
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1706
 */
1707
abstract class admin_setting {
1708
    /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1709
    public $name;
1710
    /** @var lang_string|string localised name */
1711
    public $visiblename;
1712
    /** @var string localised long description in Markdown format */
1713
    public $description;
1714
    /** @var mixed Can be string or array of string */
1715
    public $defaultsetting;
1716
    /** @var ?callable */
1717
    public $updatedcallback;
1718
    /** @var mixed can be String or Null.  Null means main config table */
1719
    public $plugin; // null means main config table
1720
    /** @var bool true indicates this setting does not actually save anything, just information */
1721
    public $nosave = false;
1722
    /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1723
    public $affectsmodinfo = false;
1724
    /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1725
    private $flags = array();
1726
    /** @var bool Whether this field must be forced LTR. */
1727
    private $forceltr = null;
1728
    /** @var array list of other settings that may cause this setting to be hidden */
1729
    private $dependenton = [];
1730
    /** @var bool Whether this setting uses a custom form control */
1731
    protected $customcontrol = false;
1732
    /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
1733
    public $paramtype;
1734
 
1735
    /**
1736
     * Constructor
1737
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1738
     *                     or 'myplugin/mysetting' for ones in config_plugins.
1739
     * @param string $visiblename localised name
1740
     * @param string $description localised long description
1741
     * @param mixed $defaultsetting string or array depending on implementation
1742
     */
1743
    public function __construct($name, $visiblename, $description, $defaultsetting) {
1744
        $this->parse_setting_name($name);
1745
        $this->visiblename    = $visiblename;
1746
        $this->description    = $description;
1747
        $this->defaultsetting = $defaultsetting;
1748
    }
1749
 
1750
    /**
1751
     * Generic function to add a flag to this admin setting.
1752
     *
1753
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1754
     * @param bool $default - The default for the flag
1755
     * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1756
     * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1757
     */
1758
    protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1759
        if (empty($this->flags[$shortname])) {
1760
            $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1761
        } else {
1762
            $this->flags[$shortname]->set_options($enabled, $default);
1763
        }
1764
    }
1765
 
1766
    /**
1767
     * Set the enabled options flag on this admin setting.
1768
     *
1769
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1770
     * @param bool $default - The default for the flag
1771
     */
1772
    public function set_enabled_flag_options($enabled, $default) {
1773
        $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1774
    }
1775
 
1776
    /**
1777
     * Set the advanced options flag on this admin setting.
1778
     *
1779
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1780
     * @param bool $default - The default for the flag
1781
     */
1782
    public function set_advanced_flag_options($enabled, $default) {
1783
        $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1784
    }
1785
 
1786
 
1787
    /**
1788
     * Set the locked options flag on this admin setting.
1789
     *
1790
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1791
     * @param bool $default - The default for the flag
1792
     */
1793
    public function set_locked_flag_options($enabled, $default) {
1794
        $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1795
    }
1796
 
1797
    /**
1798
     * Set the required options flag on this admin setting.
1799
     *
1800
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1801
     * @param bool $default - The default for the flag.
1802
     */
1803
    public function set_required_flag_options($enabled, $default) {
1804
        $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1805
    }
1806
 
1807
    /**
1808
     * Is this option forced in config.php?
1809
     *
1810
     * @return bool
1811
     */
1812
    public function is_readonly(): bool {
1813
        global $CFG;
1814
 
1815
        if (empty($this->plugin)) {
1816
            if ($this->is_forceable() && array_key_exists($this->name, $CFG->config_php_settings)) {
1817
                return true;
1818
            }
1819
        } else {
1820
            if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
1821
                and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
1822
                return true;
1823
            }
1824
        }
1825
        return false;
1826
    }
1827
 
1828
    /**
1829
     * Get the currently saved value for a setting flag
1830
     *
1831
     * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1832
     * @return bool
1833
     */
1834
    public function get_setting_flag_value(admin_setting_flag $flag) {
1835
        $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1836
        if (!isset($value)) {
1837
            $value = $flag->get_default();
1838
        }
1839
 
1840
        return !empty($value);
1841
    }
1842
 
1843
    /**
1844
     * Get the list of defaults for the flags on this setting.
1845
     *
1846
     * @param array of strings describing the defaults for this setting. This is appended to by this function.
1847
     */
1848
    public function get_setting_flag_defaults(& $defaults) {
1849
        foreach ($this->flags as $flag) {
1850
            if ($flag->is_enabled() && $flag->get_default()) {
1851
                $defaults[] = $flag->get_displayname();
1852
            }
1853
        }
1854
    }
1855
 
1856
    /**
1857
     * Output the input fields for the advanced and locked flags on this setting.
1858
     *
1859
     * @param bool $adv - The current value of the advanced flag.
1860
     * @param bool $locked - The current value of the locked flag.
1861
     * @return string $output - The html for the flags.
1862
     */
1863
    public function output_setting_flags() {
1864
        $output = '';
1865
 
1866
        foreach ($this->flags as $flag) {
1867
            if ($flag->is_enabled()) {
1868
                $output .= $flag->output_setting_flag($this);
1869
            }
1870
        }
1871
 
1872
        if (!empty($output)) {
1873
            return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1874
        }
1875
        return $output;
1876
    }
1877
 
1878
    /**
1879
     * Write the values of the flags for this admin setting.
1880
     *
1881
     * @param ?array $data - The data submitted from the form or null to set the default value for new installs.
1882
     * @return bool - true if successful.
1883
     */
1884
    public function write_setting_flags($data) {
1885
        $result = true;
1886
        foreach ($this->flags as $flag) {
1887
            $result = $result && $flag->write_setting_flag($this, $data);
1888
        }
1889
        return $result;
1890
    }
1891
 
1892
    /**
1893
     * Set up $this->name and potentially $this->plugin
1894
     *
1895
     * Set up $this->name and possibly $this->plugin based on whether $name looks
1896
     * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1897
     * on the names, that is, output a developer debug warning if the name
1898
     * contains anything other than [a-zA-Z0-9_]+.
1899
     *
1900
     * @param string $name the setting name passed in to the constructor.
1901
     */
1902
    private function parse_setting_name($name) {
1903
        $bits = explode('/', $name);
1904
        if (count($bits) > 2) {
1905
            throw new moodle_exception('invalidadminsettingname', '', '', $name);
1906
        }
1907
        $this->name = array_pop($bits);
1908
        if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1909
            throw new moodle_exception('invalidadminsettingname', '', '', $name);
1910
        }
1911
        if (!empty($bits)) {
1912
            $this->plugin = array_pop($bits);
1913
            if ($this->plugin === 'moodle') {
1914
                $this->plugin = null;
1915
            } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1916
                    throw new moodle_exception('invalidadminsettingname', '', '', $name);
1917
                }
1918
        }
1919
    }
1920
 
1921
    /**
1922
     * Returns the fullname prefixed by the plugin
1923
     * @return string
1924
     */
1925
    public function get_full_name() {
1926
        return 's_'.$this->plugin.'_'.$this->name;
1927
    }
1928
 
1929
    /**
1930
     * Returns the ID string based on plugin and name
1931
     * @return string
1932
     */
1933
    public function get_id() {
1934
        return 'id_s_'.$this->plugin.'_'.$this->name;
1935
    }
1936
 
1937
    /**
1938
     * @param bool $affectsmodinfo If true, changes to this setting will
1939
     *   cause the course cache to be rebuilt
1940
     */
1941
    public function set_affects_modinfo($affectsmodinfo) {
1942
        $this->affectsmodinfo = $affectsmodinfo;
1943
    }
1944
 
1945
    /**
1946
     * Returns the config if possible
1947
     *
1948
     * @return mixed returns config if successful else null
1949
     */
1950
    public function config_read($name) {
1951
        global $CFG;
1952
        if (!empty($this->plugin)) {
1953
            $value = get_config($this->plugin, $name);
1954
            return $value === false ? NULL : $value;
1955
 
1956
        } else {
1957
            if (isset($CFG->$name)) {
1958
                return $CFG->$name;
1959
            } else {
1960
                return NULL;
1961
            }
1962
        }
1963
    }
1964
 
1965
    /**
1966
     * Used to set a config pair and log change
1967
     *
1968
     * @param string $name
1969
     * @param mixed $value Gets converted to string if not null
1970
     * @return bool Write setting to config table
1971
     */
1972
    public function config_write($name, $value) {
1973
        global $DB, $USER, $CFG;
1974
 
1975
        if ($this->nosave) {
1976
            return true;
1977
        }
1978
 
1979
        // make sure it is a real change
1980
        $oldvalue = get_config($this->plugin, $name);
1981
        $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1982
        $value = is_null($value) ? null : (string)$value;
1983
 
1984
        if ($oldvalue === $value) {
1985
            return true;
1986
        }
1987
 
1988
        // store change
1989
        set_config($name, $value, $this->plugin);
1990
 
1991
        // Some admin settings affect course modinfo
1992
        if ($this->affectsmodinfo) {
1993
            // Clear course cache for all courses
1994
            rebuild_course_cache(0, true);
1995
        }
1996
 
1997
        $this->add_to_config_log($name, $oldvalue, $value);
1998
 
1999
        return true; // BC only
2000
    }
2001
 
2002
    /**
2003
     * Log config changes if necessary.
2004
     * @param string $name
2005
     * @param string $oldvalue
2006
     * @param string $value
2007
     */
2008
    protected function add_to_config_log($name, $oldvalue, $value) {
2009
        add_to_config_log($name, $oldvalue, $value, $this->plugin);
2010
    }
2011
 
2012
    /**
2013
     * Returns current value of this setting
2014
     * @return mixed array or string depending on instance, NULL means not set yet
2015
     */
2016
    abstract public function get_setting();
2017
 
2018
    /**
2019
     * Returns default setting if exists
2020
     * @return mixed array or string depending on instance; NULL means no default, user must supply
2021
     */
2022
    public function get_defaultsetting() {
2023
        $adminroot =  admin_get_root(false, false);
2024
        if (!empty($adminroot->custom_defaults)) {
2025
            $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
2026
            if (isset($adminroot->custom_defaults[$plugin])) {
2027
                if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
2028
                    return $adminroot->custom_defaults[$plugin][$this->name];
2029
                }
2030
            }
2031
        }
2032
        return $this->defaultsetting;
2033
    }
2034
 
2035
    /**
2036
     * Store new setting
2037
     *
2038
     * @param mixed $data string or array, must not be NULL
2039
     * @return string empty string if ok, string error message otherwise
2040
     */
2041
    abstract public function write_setting($data);
2042
 
2043
    /**
2044
     * Return part of form with setting
2045
     * This function should always be overwritten
2046
     *
2047
     * @param mixed $data array or string depending on setting
2048
     * @param string $query
2049
     * @return string
2050
     */
2051
    public function output_html($data, $query='') {
2052
    // should be overridden
2053
        return;
2054
    }
2055
 
2056
    /**
2057
     * Function called if setting updated - cleanup, cache reset, etc.
2058
     * @param callable $functionname Sets the function name
2059
     * @return void
2060
     */
2061
    public function set_updatedcallback($functionname) {
2062
        $this->updatedcallback = $functionname;
2063
    }
2064
 
2065
    /**
2066
     * Execute postupdatecallback if necessary.
2067
     * @param mixed $original original value before write_setting()
2068
     * @return bool true if changed, false if not.
2069
     */
2070
    public function post_write_settings($original) {
2071
        // Comparison must work for arrays too.
2072
        if (serialize($original) === serialize($this->get_setting())) {
2073
            return false;
2074
        }
2075
 
2076
        $callbackfunction = $this->updatedcallback;
2077
        if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2078
            $callbackfunction($this->get_full_name());
2079
        }
2080
        return true;
2081
    }
2082
 
2083
    /**
2084
     * Is setting related to query text - used when searching
2085
     * @param string $query
2086
     * @return bool
2087
     */
2088
    public function is_related($query) {
2089
        if (strpos(strtolower($this->name), $query) !== false) {
2090
            return true;
2091
        }
2092
        if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2093
            return true;
2094
        }
2095
        if (strpos(core_text::strtolower($this->description), $query) !== false) {
2096
            return true;
2097
        }
2098
        $current = $this->get_setting();
2099
        if (!is_null($current)) {
2100
            if (is_string($current)) {
2101
                if (strpos(core_text::strtolower($current), $query) !== false) {
2102
                    return true;
2103
                }
2104
            }
2105
        }
2106
        $default = $this->get_defaultsetting();
2107
        if (!is_null($default)) {
2108
            if (is_string($default)) {
2109
                if (strpos(core_text::strtolower($default), $query) !== false) {
2110
                    return true;
2111
                }
2112
            }
2113
        }
2114
        return false;
2115
    }
2116
 
2117
    /**
2118
     * Get whether this should be displayed in LTR mode.
2119
     *
2120
     * @return bool|null
2121
     */
2122
    public function get_force_ltr() {
2123
        return $this->forceltr;
2124
    }
2125
 
2126
    /**
2127
     * Set whether to force LTR or not.
2128
     *
2129
     * @param bool $value True when forced, false when not force, null when unknown.
2130
     */
2131
    public function set_force_ltr($value) {
2132
        $this->forceltr = $value;
2133
    }
2134
 
2135
    /**
2136
     * Add a setting to the list of those that could cause this one to be hidden
2137
     * @param string $dependenton
2138
     */
2139
    public function add_dependent_on($dependenton) {
2140
        $this->dependenton[] = $dependenton;
2141
    }
2142
 
2143
    /**
2144
     * Get a list of the settings that could cause this one to be hidden.
2145
     * @return array
2146
     */
2147
    public function get_dependent_on() {
2148
        return $this->dependenton;
2149
    }
2150
 
2151
    /**
2152
     * Whether this setting uses a custom form control.
2153
     * This function is especially useful to decide if we should render a label element for this setting or not.
2154
     *
2155
     * @return bool
2156
     */
2157
    public function has_custom_form_control(): bool {
2158
        return $this->customcontrol;
2159
    }
2160
 
2161
    /**
2162
     * Whether the setting can be overridden in config.php.
2163
     *
2164
     * Returning true will allow the setting to be defined and overridden in config.php.
2165
     * Returning false will prevent the config setting from being overridden even when it gets defined in config.php.
2166
     *
2167
     * @return bool
2168
     */
2169
    public function is_forceable(): bool {
2170
        return true;
2171
    }
2172
}
2173
 
2174
/**
2175
 * An additional option that can be applied to an admin setting.
2176
 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2177
 *
2178
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2179
 */
2180
class admin_setting_flag {
2181
    /** @var bool Flag to indicate if this option can be toggled for this setting */
2182
    private $enabled = false;
2183
    /** @var bool Flag to indicate if this option defaults to true or false */
2184
    private $default = false;
2185
    /** @var string Short string used to create setting name - e.g. 'adv' */
2186
    private $shortname = '';
2187
    /** @var string String used as the label for this flag */
2188
    private $displayname = '';
2189
    /** @var bool Checkbox for this flag is displayed in admin page */
2190
    const ENABLED = true;
2191
    /** @var bool Checkbox for this flag is not displayed in admin page */
2192
    const DISABLED = false;
2193
 
2194
    /**
2195
     * Constructor
2196
     *
2197
     * @param bool $enabled Can this option can be toggled.
2198
     *                      Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2199
     * @param bool $default The default checked state for this setting option.
2200
     * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2201
     * @param string $displayname The displayname of this flag. Used as a label for the flag.
2202
     */
2203
    public function __construct($enabled, $default, $shortname, $displayname) {
2204
        $this->shortname = $shortname;
2205
        $this->displayname = $displayname;
2206
        $this->set_options($enabled, $default);
2207
    }
2208
 
2209
    /**
2210
     * Update the values of this setting options class
2211
     *
2212
     * @param bool $enabled Can this option can be toggled.
2213
     *                      Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2214
     * @param bool $default The default checked state for this setting option.
2215
     */
2216
    public function set_options($enabled, $default) {
2217
        $this->enabled = $enabled;
2218
        $this->default = $default;
2219
    }
2220
 
2221
    /**
2222
     * Should this option appear in the interface and be toggleable?
2223
     *
2224
     * @return bool Is it enabled?
2225
     */
2226
    public function is_enabled() {
2227
        return $this->enabled;
2228
    }
2229
 
2230
    /**
2231
     * Should this option be checked by default?
2232
     *
2233
     * @return bool Is it on by default?
2234
     */
2235
    public function get_default() {
2236
        return $this->default;
2237
    }
2238
 
2239
    /**
2240
     * Return the short name for this flag. e.g. 'adv' or 'locked'
2241
     *
2242
     * @return string
2243
     */
2244
    public function get_shortname() {
2245
        return $this->shortname;
2246
    }
2247
 
2248
    /**
2249
     * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2250
     *
2251
     * @return string
2252
     */
2253
    public function get_displayname() {
2254
        return $this->displayname;
2255
    }
2256
 
2257
    /**
2258
     * Save the submitted data for this flag - or set it to the default if $data is null.
2259
     *
2260
     * @param admin_setting $setting - The admin setting for this flag
2261
     * @param array $data - The data submitted from the form or null to set the default value for new installs.
2262
     * @return bool
2263
     */
2264
    public function write_setting_flag(admin_setting $setting, $data) {
2265
        $result = true;
2266
        if ($this->is_enabled()) {
2267
            if (!isset($data)) {
2268
                $value = $this->get_default();
2269
            } else {
2270
                $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2271
            }
2272
            $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2273
        }
2274
 
2275
        return $result;
2276
 
2277
    }
2278
 
2279
    /**
2280
     * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2281
     *
2282
     * @param admin_setting $setting - The admin setting for this flag
2283
     * @return string - The html for the checkbox.
2284
     */
2285
    public function output_setting_flag(admin_setting $setting) {
2286
        global $OUTPUT;
2287
 
2288
        $value = $setting->get_setting_flag_value($this);
2289
 
2290
        $context = new stdClass();
2291
        $context->id = $setting->get_id() . '_' . $this->get_shortname();
2292
        $context->name = $setting->get_full_name() .  '_' . $this->get_shortname();
2293
        $context->value = 1;
2294
        $context->checked = $value ? true : false;
2295
        $context->label = $this->get_displayname();
2296
 
2297
        return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2298
    }
2299
}
2300
 
2301
 
2302
/**
2303
 * No setting - just heading and text.
2304
 *
2305
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2306
 */
2307
class admin_setting_heading extends admin_setting {
2308
 
2309
    /**
2310
     * not a setting, just text
2311
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2312
     * @param string $heading heading
2313
     * @param string $information text in box
2314
     */
2315
    public function __construct($name, $heading, $information) {
2316
        $this->nosave = true;
2317
        parent::__construct($name, $heading, $information, '');
2318
    }
2319
 
2320
    /**
2321
     * Always returns true
2322
     * @return bool Always returns true
2323
     */
2324
    public function get_setting() {
2325
        return true;
2326
    }
2327
 
2328
    /**
2329
     * Always returns true
2330
     * @return bool Always returns true
2331
     */
2332
    public function get_defaultsetting() {
2333
        return true;
2334
    }
2335
 
2336
    /**
2337
     * Never write settings
2338
     * @return string Always returns an empty string
2339
     */
2340
    public function write_setting($data) {
2341
    // do not write any setting
2342
        return '';
2343
    }
2344
 
2345
    /**
2346
     * Returns an HTML string
2347
     * @return string Returns an HTML string
2348
     */
2349
    public function output_html($data, $query='') {
2350
        global $OUTPUT;
2351
        $context = new stdClass();
2352
        $context->title = $this->visiblename;
2353
        $context->description = $this->description;
2354
        $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2355
        return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2356
    }
2357
}
2358
 
2359
/**
2360
 * No setting - just name and description in same row.
2361
 *
2362
 * @copyright 2018 onwards Amaia Anabitarte
2363
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2364
 */
2365
class admin_setting_description extends admin_setting {
2366
 
2367
    /**
2368
     * Not a setting, just text
2369
     *
2370
     * @param string $name
2371
     * @param string $visiblename
2372
     * @param string $description
2373
     */
2374
    public function __construct($name, $visiblename, $description) {
2375
        $this->nosave = true;
2376
        parent::__construct($name, $visiblename, $description, '');
2377
    }
2378
 
2379
    /**
2380
     * Always returns true
2381
     *
2382
     * @return bool Always returns true
2383
     */
2384
    public function get_setting() {
2385
        return true;
2386
    }
2387
 
2388
    /**
2389
     * Always returns true
2390
     *
2391
     * @return bool Always returns true
2392
     */
2393
    public function get_defaultsetting() {
2394
        return true;
2395
    }
2396
 
2397
    /**
2398
     * Never write settings
2399
     *
2400
     * @param mixed $data Gets converted to str for comparison against yes value
2401
     * @return string Always returns an empty string
2402
     */
2403
    public function write_setting($data) {
2404
        // Do not write any setting.
2405
        return '';
2406
    }
2407
 
2408
    /**
2409
     * Returns an HTML string
2410
     *
2411
     * @param string $data
2412
     * @param string $query
2413
     * @return string Returns an HTML string
2414
     */
2415
    public function output_html($data, $query='') {
2416
        global $OUTPUT;
2417
 
2418
        $context = new stdClass();
2419
        $context->title = $this->visiblename;
2420
        $context->description = $this->description;
2421
 
2422
        return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2423
    }
2424
}
2425
 
2426
 
2427
 
2428
/**
2429
 * The most flexible setting, the user enters text.
2430
 *
2431
 * This type of field should be used for config settings which are using
2432
 * English words and are not localised (passwords, database name, list of values, ...).
2433
 *
2434
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2435
 */
2436
class admin_setting_configtext extends admin_setting {
2437
 
2438
    /** @var int default field size */
2439
    public $size;
2440
    /** @var array List of arbitrary data attributes */
2441
    protected $datavalues = [];
2442
 
2443
    /**
2444
     * Config text constructor
2445
     *
2446
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2447
     * @param string $visiblename localised
2448
     * @param string $description long localised info
2449
     * @param string $defaultsetting
2450
     * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2451
     * @param int $size default field size
2452
     */
2453
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2454
        $this->paramtype = $paramtype;
2455
        if (!is_null($size)) {
2456
            $this->size  = $size;
2457
        } else {
2458
            $this->size  = ($paramtype === PARAM_INT) ? 5 : 30;
2459
        }
2460
        parent::__construct($name, $visiblename, $description, $defaultsetting);
2461
    }
2462
 
2463
    /**
2464
     * Get whether this should be displayed in LTR mode.
2465
     *
2466
     * Try to guess from the PARAM type unless specifically set.
2467
     */
2468
    public function get_force_ltr() {
2469
        $forceltr = parent::get_force_ltr();
2470
        if ($forceltr === null) {
2471
            return !is_rtl_compatible($this->paramtype);
2472
        }
2473
        return $forceltr;
2474
    }
2475
 
2476
    /**
2477
     * Return the setting
2478
     *
2479
     * @return mixed returns config if successful else null
2480
     */
2481
    public function get_setting() {
2482
        return $this->config_read($this->name);
2483
    }
2484
 
2485
    public function write_setting($data) {
2486
        if ($this->paramtype === PARAM_INT and $data === '') {
2487
        // do not complain if '' used instead of 0
2488
            $data = 0;
2489
        }
2490
        // $data is a string
2491
        $validated = $this->validate($data);
2492
        if ($validated !== true) {
2493
            return $validated;
2494
        }
2495
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2496
    }
2497
 
2498
    /**
2499
     * Validate data before storage
2500
     * @param string data
2501
     * @return mixed true if ok string if error found
2502
     */
2503
    public function validate($data) {
2504
        // allow paramtype to be a custom regex if it is the form of /pattern/
2505
        if (preg_match('#^/.*/$#', $this->paramtype)) {
2506
            if (preg_match($this->paramtype, $data)) {
2507
                return true;
2508
            } else {
2509
                return get_string('validateerror', 'admin');
2510
            }
2511
 
2512
        } else if ($this->paramtype === PARAM_RAW) {
2513
            return true;
2514
 
2515
        } else {
2516
            $cleaned = clean_param($data, $this->paramtype);
2517
            if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2518
                return true;
2519
            } else {
2520
                return get_string('validateerror', 'admin');
2521
            }
2522
        }
2523
    }
2524
 
2525
    /**
2526
     * Set arbitrary data attributes for template.
2527
     *
2528
     * @param string $key Attribute key for template.
2529
     * @param string $value Attribute value for template.
2530
     */
2531
    public function set_data_attribute(string $key, string $value): void {
2532
        $this->datavalues[] = [
2533
            'key' => $key,
2534
            'value' => $value,
2535
        ];
2536
    }
2537
 
2538
    /**
2539
     * Return an XHTML string for the setting
2540
     * @return string Returns an XHTML string
2541
     */
2542
    public function output_html($data, $query = '') {
2543
        global $OUTPUT;
2544
 
2545
        $default = $this->get_defaultsetting();
2546
        $context = (object) [
2547
            'size' => $this->size,
2548
            'id' => $this->get_id(),
2549
            'name' => $this->get_full_name(),
2550
            'value' => $data,
2551
            'forceltr' => $this->get_force_ltr(),
2552
            'readonly' => $this->is_readonly(),
2553
            'data' => $this->datavalues,
2554
            'maxcharacter' => array_key_exists('validation-max-length', $this->datavalues),
2555
        ];
2556
        $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2557
 
2558
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2559
    }
2560
}
2561
 
2562
/**
2563
 * Text input with a maximum length constraint.
2564
 *
2565
 * @copyright 2015 onwards Ankit Agarwal
2566
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2567
 */
2568
class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2569
 
2570
    /** @var int maximum number of chars allowed. */
2571
    protected $maxlength;
2572
 
2573
    /**
2574
     * Config text constructor
2575
     *
2576
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2577
     *                     or 'myplugin/mysetting' for ones in config_plugins.
2578
     * @param string $visiblename localised
2579
     * @param string $description long localised info
2580
     * @param string $defaultsetting
2581
     * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2582
     * @param int $size default field size
2583
     * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2584
     */
2585
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2586
                                $size=null, $maxlength = 0) {
2587
        $this->maxlength = $maxlength;
2588
        $this->set_data_attribute('validation-max-length', $maxlength);
2589
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2590
    }
2591
 
2592
    /**
2593
     * Validate data before storage
2594
     *
2595
     * @param string $data data
2596
     * @return mixed true if ok string if error found
2597
     */
2598
    public function validate($data) {
2599
        $parentvalidation = parent::validate($data);
2600
        if ($parentvalidation === true) {
2601
            if ($this->maxlength > 0) {
2602
                // Max length check.
2603
                $length = core_text::strlen($data);
2604
                if ($length > $this->maxlength) {
2605
                    return get_string('maximumchars', 'moodle',  $this->maxlength);
2606
                }
2607
                return true;
2608
            } else {
2609
                return true; // No max length check needed.
2610
            }
2611
        } else {
2612
            return $parentvalidation;
2613
        }
2614
    }
2615
 
2616
    /**
2617
     * Return an XHTML string for the setting.
2618
     *
2619
     * @param string $data data.
2620
     * @param string $query query statement.
2621
     * @return string Returns an XHTML string
2622
     */
2623
    public function output_html($data, $query = ''): string {
2624
        global $PAGE;
2625
        $PAGE->requires->js_call_amd('core_form/configtext_maxlength', 'init');
2626
 
2627
        return parent::output_html($data, $query);
2628
    }
2629
}
2630
 
2631
/**
2632
 * General text area without html editor.
2633
 *
2634
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2635
 */
2636
class admin_setting_configtextarea extends admin_setting_configtext {
2637
    private $rows;
2638
    private $cols;
2639
 
2640
    /**
2641
     * @param string $name
2642
     * @param string $visiblename
2643
     * @param string $description
2644
     * @param mixed $defaultsetting string or array
2645
     * @param mixed $paramtype
2646
     * @param string $cols The number of columns to make the editor
2647
     * @param string $rows The number of rows to make the editor
2648
     */
2649
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2650
        $this->rows = $rows;
2651
        $this->cols = $cols;
2652
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2653
    }
2654
 
2655
    /**
2656
     * Returns an XHTML string for the editor
2657
     *
2658
     * @param string $data
2659
     * @param string $query
2660
     * @return string XHTML string for the editor
2661
     */
2662
    public function output_html($data, $query='') {
2663
        global $OUTPUT;
2664
 
2665
        $default = $this->get_defaultsetting();
2666
        $defaultinfo = $default;
2667
        if (!is_null($default) and $default !== '') {
2668
            $defaultinfo = "\n".$default;
2669
        }
2670
 
2671
        $context = (object) [
2672
            'cols' => $this->cols,
2673
            'rows' => $this->rows,
2674
            'id' => $this->get_id(),
2675
            'name' => $this->get_full_name(),
2676
            'value' => $data,
2677
            'forceltr' => $this->get_force_ltr(),
2678
            'readonly' => $this->is_readonly(),
2679
        ];
2680
        $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2681
 
2682
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2683
    }
2684
}
2685
 
2686
/**
2687
 * General text area with html editor.
2688
 */
2689
class admin_setting_confightmleditor extends admin_setting_configtextarea {
2690
 
2691
    /**
2692
     * @param string $name
2693
     * @param string $visiblename
2694
     * @param string $description
2695
     * @param mixed $defaultsetting string or array
2696
     * @param mixed $paramtype
2697
     */
2698
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2699
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2700
        $this->set_force_ltr(false);
2701
        editors_head_setup();
2702
    }
2703
 
2704
    /**
2705
     * Returns an XHTML string for the editor
2706
     *
2707
     * @param string $data
2708
     * @param string $query
2709
     * @return string XHTML string for the editor
2710
     */
2711
    public function output_html($data, $query='') {
2712
        $editor = editors_get_preferred_editor(FORMAT_HTML);
2713
        $editor->set_text($data);
2714
        $editor->use_editor($this->get_id(), array('noclean'=>true));
2715
        return parent::output_html($data, $query);
2716
    }
2717
 
2718
    /**
2719
     * Checks if data has empty html.
2720
     *
2721
     * @param string $data
2722
     * @return string Empty when no errors.
2723
     */
2724
    public function write_setting($data) {
2725
        if (trim(html_to_text($data)) === '') {
2726
            $data = '';
2727
        }
2728
        return parent::write_setting($data);
2729
    }
2730
}
2731
 
2732
 
2733
/**
2734
 * Password field, allows unmasking of password
2735
 *
2736
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2737
 */
2738
class admin_setting_configpasswordunmask extends admin_setting_configtext {
2739
 
2740
    /**
2741
     * Constructor
2742
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2743
     * @param string $visiblename localised
2744
     * @param string $description long localised info
2745
     * @param string $defaultsetting default password
2746
     */
2747
    public function __construct($name, $visiblename, $description, $defaultsetting) {
2748
        parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2749
    }
2750
 
2751
    /**
2752
     * Log config changes if necessary.
2753
     * @param string $name
2754
     * @param string $oldvalue
2755
     * @param string $value
2756
     */
2757
    protected function add_to_config_log($name, $oldvalue, $value) {
2758
        if ($value !== '') {
2759
            $value = '********';
2760
        }
2761
        if ($oldvalue !== '' and $oldvalue !== null) {
2762
            $oldvalue = '********';
2763
        }
2764
        parent::add_to_config_log($name, $oldvalue, $value);
2765
    }
2766
 
2767
    /**
2768
     * Returns HTML for the field.
2769
     *
2770
     * @param   string  $data       Value for the field
2771
     * @param   string  $query      Passed as final argument for format_admin_setting
2772
     * @return  string              Rendered HTML
2773
     */
2774
    public function output_html($data, $query='') {
2775
        global $OUTPUT;
2776
 
2777
        $context = (object) [
2778
            'id' => $this->get_id(),
2779
            'name' => $this->get_full_name(),
2780
            'size' => $this->size,
2781
            'value' => $this->is_readonly() ? null : $data,
2782
            'forceltr' => $this->get_force_ltr(),
2783
            'readonly' => $this->is_readonly(),
2784
        ];
2785
        $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2786
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2787
    }
2788
}
2789
 
2790
/**
2791
 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2792
 *
2793
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2794
 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2795
 */
2796
class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2797
 
2798
    /**
2799
     * Constructor
2800
     *
2801
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2802
     * @param string $visiblename localised
2803
     * @param string $description long localised info
2804
     * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2805
     */
2806
    public function __construct($name, $visiblename, $description, $defaultsetting) {
2807
        parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2808
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2809
    }
2810
}
2811
 
2812
/**
2813
 * Admin setting class for encrypted values using secure encryption.
2814
 *
2815
 * @copyright 2019 The Open University
2816
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2817
 */
2818
class admin_setting_encryptedpassword extends admin_setting {
2819
 
2820
    /**
2821
     * Constructor. Same as parent except that the default value is always an empty string.
2822
     *
2823
     * @param string $name Internal name used in config table
2824
     * @param string $visiblename Name shown on form
2825
     * @param string $description Description that appears below field
2826
     */
2827
    public function __construct(string $name, string $visiblename, string $description) {
2828
        parent::__construct($name, $visiblename, $description, '');
2829
    }
2830
 
2831
    public function get_setting() {
2832
        return $this->config_read($this->name);
2833
    }
2834
 
2835
    public function write_setting($data) {
2836
        $data = trim($data);
2837
        if ($data === '') {
2838
            // Value can really be set to nothing.
2839
            $savedata = '';
2840
        } else {
2841
            // Encrypt value before saving it.
2842
            $savedata = \core\encryption::encrypt($data);
2843
        }
2844
        return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
2845
    }
2846
 
2847
    public function output_html($data, $query='') {
2848
        global $OUTPUT;
2849
 
2850
        $default = $this->get_defaultsetting();
2851
        $context = (object) [
2852
            'id' => $this->get_id(),
2853
            'name' => $this->get_full_name(),
2854
            'set' => $data !== '',
2855
            'novalue' => $this->get_setting() === null
2856
        ];
2857
        $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
2858
 
2859
        return format_admin_setting($this, $this->visiblename, $element, $this->description,
2860
                true, '', $default, $query);
2861
    }
2862
}
2863
 
2864
/**
2865
 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2866
 * Note: Only advanced makes sense right now - locked does not.
2867
 *
2868
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2869
 */
2870
class admin_setting_configempty extends admin_setting_configtext {
2871
 
2872
    /**
2873
     * @param string $name
2874
     * @param string $visiblename
2875
     * @param string $description
2876
     */
2877
    public function __construct($name, $visiblename, $description) {
2878
        parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2879
    }
2880
 
2881
    /**
2882
     * Returns an XHTML string for the hidden field
2883
     *
2884
     * @param string $data
2885
     * @param string $query
2886
     * @return string XHTML string for the editor
2887
     */
2888
    public function output_html($data, $query='') {
2889
        global $OUTPUT;
2890
 
2891
        $context = (object) [
2892
            'id' => $this->get_id(),
2893
            'name' => $this->get_full_name()
2894
        ];
2895
        $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2896
 
2897
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2898
    }
2899
}
2900
 
2901
 
2902
/**
2903
 * Path to directory
2904
 *
2905
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2906
 */
2907
class admin_setting_configfile extends admin_setting_configtext {
2908
    /**
2909
     * Constructor
2910
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2911
     * @param string $visiblename localised
2912
     * @param string $description long localised info
2913
     * @param string $defaultdirectory default directory location
2914
     */
2915
    public function __construct($name, $visiblename, $description, $defaultdirectory) {
2916
        parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2917
    }
2918
 
2919
    /**
2920
     * Returns XHTML for the field
2921
     *
2922
     * Returns XHTML for the field and also checks whether the file
2923
     * specified in $data exists using file_exists()
2924
     *
2925
     * @param string $data File name and path to use in value attr
2926
     * @param string $query
2927
     * @return string XHTML field
2928
     */
2929
    public function output_html($data, $query='') {
2930
        global $CFG, $OUTPUT;
2931
 
2932
        $default = $this->get_defaultsetting();
2933
        $context = (object) [
2934
            'id' => $this->get_id(),
2935
            'name' => $this->get_full_name(),
2936
            'size' => $this->size,
2937
            'value' => $data,
2938
            'showvalidity' => !empty($data),
2939
            'valid' => $data && file_exists($data),
2940
            'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
2941
            'forceltr' => $this->get_force_ltr(),
2942
        ];
2943
 
2944
        if ($context->readonly) {
2945
            $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2946
        }
2947
 
2948
        $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2949
 
2950
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2951
    }
2952
 
2953
    /**
2954
     * Checks if execpatch has been disabled in config.php
2955
     */
2956
    public function write_setting($data) {
2957
        global $CFG;
2958
        if (!empty($CFG->preventexecpath)) {
2959
            if ($this->get_setting() === null) {
2960
                // Use default during installation.
2961
                $data = $this->get_defaultsetting();
2962
                if ($data === null) {
2963
                    $data = '';
2964
                }
2965
            } else {
2966
                return '';
2967
            }
2968
        }
2969
        return parent::write_setting($data);
2970
    }
2971
 
2972
}
2973
 
2974
 
2975
/**
2976
 * Path to executable file
2977
 *
2978
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2979
 */
2980
class admin_setting_configexecutable extends admin_setting_configfile {
2981
 
2982
    /**
2983
     * Returns an XHTML field
2984
     *
2985
     * @param string $data This is the value for the field
2986
     * @param string $query
2987
     * @return string XHTML field
2988
     */
2989
    public function output_html($data, $query='') {
2990
        global $CFG, $OUTPUT;
2991
        $default = $this->get_defaultsetting();
2992
        require_once("$CFG->libdir/filelib.php");
2993
 
2994
        $context = (object) [
2995
            'id' => $this->get_id(),
2996
            'name' => $this->get_full_name(),
2997
            'size' => $this->size,
2998
            'value' => $data,
2999
            'showvalidity' => !empty($data),
3000
            'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
3001
            'readonly' => !empty($CFG->preventexecpath),
3002
            'forceltr' => $this->get_force_ltr()
3003
        ];
3004
 
3005
        if (!empty($CFG->preventexecpath)) {
3006
            $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
3007
        }
3008
 
3009
        $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
3010
 
3011
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
3012
    }
3013
}
3014
 
3015
 
3016
/**
3017
 * Path to directory
3018
 *
3019
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3020
 */
3021
class admin_setting_configdirectory extends admin_setting_configfile {
3022
 
3023
    /**
3024
     * Returns an XHTML field
3025
     *
3026
     * @param string $data This is the value for the field
3027
     * @param string $query
3028
     * @return string XHTML
3029
     */
3030
    public function output_html($data, $query='') {
3031
        global $CFG, $OUTPUT;
3032
        $default = $this->get_defaultsetting();
3033
 
3034
        $context = (object) [
3035
            'id' => $this->get_id(),
3036
            'name' => $this->get_full_name(),
3037
            'size' => $this->size,
3038
            'value' => $data,
3039
            'showvalidity' => !empty($data),
3040
            'valid' => $data && file_exists($data) && is_dir($data),
3041
            'readonly' => !empty($CFG->preventexecpath),
3042
            'forceltr' => $this->get_force_ltr()
3043
        ];
3044
 
3045
        if (!empty($CFG->preventexecpath)) {
3046
            $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
3047
        }
3048
 
3049
        $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
3050
 
3051
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
3052
    }
3053
}
3054
 
3055
 
3056
/**
3057
 * Checkbox
3058
 *
3059
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3060
 */
3061
class admin_setting_configcheckbox extends admin_setting {
3062
    /** @var string Value used when checked */
3063
    public $yes;
3064
    /** @var string Value used when not checked */
3065
    public $no;
3066
 
3067
    /**
3068
     * Constructor
3069
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3070
     * @param string $visiblename localised
3071
     * @param string $description long localised info
3072
     * @param string $defaultsetting
3073
     * @param string $yes value used when checked
3074
     * @param string $no value used when not checked
3075
     */
3076
    public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
3077
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3078
        $this->yes = (string)$yes;
3079
        $this->no  = (string)$no;
3080
    }
3081
 
3082
    /**
3083
     * Retrieves the current setting using the objects name
3084
     *
3085
     * @return string
3086
     */
3087
    public function get_setting() {
3088
        return $this->config_read($this->name);
3089
    }
3090
 
3091
    /**
3092
     * Sets the value for the setting
3093
     *
3094
     * Sets the value for the setting to either the yes or no values
3095
     * of the object by comparing $data to yes
3096
     *
3097
     * @param mixed $data Gets converted to str for comparison against yes value
3098
     * @return string empty string or error
3099
     */
3100
    public function write_setting($data) {
3101
        if ((string)$data === $this->yes) { // convert to strings before comparison
3102
            $data = $this->yes;
3103
        } else {
3104
            $data = $this->no;
3105
        }
3106
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3107
    }
3108
 
3109
    /**
3110
     * Returns an XHTML checkbox field
3111
     *
3112
     * @param string $data If $data matches yes then checkbox is checked
3113
     * @param string $query
3114
     * @return string XHTML field
3115
     */
3116
    public function output_html($data, $query='') {
3117
        global $OUTPUT;
3118
 
3119
        $context = (object) [
3120
            'id' => $this->get_id(),
3121
            'name' => $this->get_full_name(),
3122
            'no' => $this->no,
3123
            'value' => $this->yes,
3124
            'checked' => (string) $data === $this->yes,
3125
            'readonly' => $this->is_readonly(),
3126
        ];
3127
 
3128
        $default = $this->get_defaultsetting();
3129
        if (!is_null($default)) {
3130
            if ((string)$default === $this->yes) {
3131
                $defaultinfo = get_string('checkboxyes', 'admin');
3132
            } else {
3133
                $defaultinfo = get_string('checkboxno', 'admin');
3134
            }
3135
        } else {
3136
            $defaultinfo = NULL;
3137
        }
3138
 
3139
        $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
3140
 
3141
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3142
    }
3143
}
3144
 
3145
 
3146
/**
3147
 * Multiple checkboxes, each represents different value, stored in csv format
3148
 *
3149
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3150
 */
3151
class admin_setting_configmulticheckbox extends admin_setting {
3152
    /** @var callable|null Loader function for choices */
3153
    protected $choiceloader = null;
3154
 
3155
    /** @var array Array of choices value=>label. */
3156
    public $choices;
3157
 
3158
    /**
3159
     * Constructor: uses parent::__construct
3160
     *
3161
     * The $choices parameter may be either an array of $value => $label format,
3162
     * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and
3163
     * returns an array in that format.
3164
     *
3165
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3166
     * @param string $visiblename localised
3167
     * @param string $description long localised info
3168
     * @param ?array $defaultsetting array of selected
3169
     * @param array|callable|null $choices array of $value => $label for each checkbox, or a callback
3170
     */
3171
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3172
        if (is_array($choices)) {
3173
            $this->choices = $choices;
3174
        }
3175
        if (is_callable($choices)) {
3176
            $this->choiceloader = $choices;
3177
        }
3178
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3179
    }
3180
 
3181
    /**
3182
     * This function may be used in ancestors for lazy loading of choices
3183
     *
3184
     * Override this method if loading of choices is expensive, such
3185
     * as when it requires multiple db requests.
3186
     *
3187
     * @return bool true if loaded, false if error
3188
     */
3189
    public function load_choices() {
3190
        if ($this->choiceloader) {
3191
            if (!is_array($this->choices)) {
3192
                $this->choices = call_user_func($this->choiceloader);
3193
            }
3194
        }
3195
        return true;
3196
    }
3197
 
3198
    /**
3199
     * Is setting related to query text - used when searching
3200
     *
3201
     * @param string $query
3202
     * @return bool true on related, false on not or failure
3203
     */
3204
    public function is_related($query) {
3205
        if (!$this->load_choices() or empty($this->choices)) {
3206
            return false;
3207
        }
3208
        if (parent::is_related($query)) {
3209
            return true;
3210
        }
3211
 
3212
        foreach ($this->choices as $desc) {
3213
            if (strpos(core_text::strtolower($desc), $query) !== false) {
3214
                return true;
3215
            }
3216
        }
3217
        return false;
3218
    }
3219
 
3220
    /**
3221
     * Returns the current setting if it is set
3222
     *
3223
     * @return mixed null if null, else an array
3224
     */
3225
    public function get_setting() {
3226
        $result = $this->config_read($this->name);
3227
 
3228
        if (is_null($result)) {
3229
            return NULL;
3230
        }
3231
        if ($result === '') {
3232
            return array();
3233
        }
3234
        $enabled = explode(',', $result);
3235
        $setting = array();
3236
        foreach ($enabled as $option) {
3237
            $setting[$option] = 1;
3238
        }
3239
        return $setting;
3240
    }
3241
 
3242
    /**
3243
     * Saves the setting(s) provided in $data
3244
     *
3245
     * @param array $data An array of data, if not array returns empty str
3246
     * @return mixed empty string on useless data or bool true=success, false=failed
3247
     */
3248
    public function write_setting($data) {
3249
        if (!is_array($data)) {
3250
            return ''; // ignore it
3251
        }
3252
        if (!$this->load_choices() or empty($this->choices)) {
3253
            return '';
3254
        }
3255
        unset($data['xxxxx']);
3256
        $result = array();
3257
        foreach ($data as $key => $value) {
3258
            if ($value and array_key_exists($key, $this->choices)) {
3259
                $result[] = $key;
3260
            }
3261
        }
3262
        return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3263
    }
3264
 
3265
    /**
3266
     * Returns XHTML field(s) as required by choices
3267
     *
3268
     * Relies on data being an array should data ever be another valid vartype with
3269
     * acceptable value this may cause a warning/error
3270
     * if (!is_array($data)) would fix the problem
3271
     *
3272
     * @todo Add vartype handling to ensure $data is an array
3273
     *
3274
     * @param array $data An array of checked values
3275
     * @param string $query
3276
     * @return string XHTML field
3277
     */
3278
    public function output_html($data, $query='') {
3279
        global $OUTPUT;
3280
 
3281
        if (!$this->load_choices() or empty($this->choices)) {
3282
            return '';
3283
        }
3284
 
3285
        $default = $this->get_defaultsetting();
3286
        if (is_null($default)) {
3287
            $default = array();
3288
        }
3289
        if (is_null($data)) {
3290
            $data = array();
3291
        }
3292
 
3293
        $context = (object) [
3294
            'id' => $this->get_id(),
3295
            'name' => $this->get_full_name(),
3296
            'readonly' => $this->is_readonly(),
3297
        ];
3298
 
3299
        $options = array();
3300
        $defaults = array();
3301
        foreach ($this->choices as $key => $description) {
3302
            if (!empty($default[$key])) {
3303
                $defaults[] = $description;
3304
            }
3305
 
3306
            $options[] = [
3307
                'key' => $key,
3308
                'checked' => !empty($data[$key]),
3309
                'label' => highlightfast($query, $description)
3310
            ];
3311
        }
3312
 
3313
        if (is_null($default)) {
3314
            $defaultinfo = null;
3315
        } else if (!empty($defaults)) {
3316
            $defaultinfo = implode(', ', $defaults);
3317
        } else {
3318
            $defaultinfo = get_string('none');
3319
        }
3320
 
3321
        $context->options = $options;
3322
        $context->hasoptions = !empty($options);
3323
 
3324
        $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3325
 
3326
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3327
 
3328
    }
3329
}
3330
 
3331
 
3332
/**
3333
 * Multiple checkboxes 2, value stored as string 00101011
3334
 *
3335
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3336
 */
3337
class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3338
 
3339
    /**
3340
     * Returns the setting if set
3341
     *
3342
     * @return mixed null if not set, else an array of set settings
3343
     */
3344
    public function get_setting() {
3345
        $result = $this->config_read($this->name);
3346
        if (is_null($result)) {
3347
            return NULL;
3348
        }
3349
        if (!$this->load_choices()) {
3350
            return NULL;
3351
        }
3352
        $result = str_pad($result, count($this->choices), '0');
3353
        $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3354
        $setting = array();
3355
        foreach ($this->choices as $key=>$unused) {
3356
            $value = array_shift($result);
3357
            if ($value) {
3358
                $setting[$key] = 1;
3359
            }
3360
        }
3361
        return $setting;
3362
    }
3363
 
3364
    /**
3365
     * Save setting(s) provided in $data param
3366
     *
3367
     * @param array $data An array of settings to save
3368
     * @return mixed empty string for bad data or bool true=>success, false=>error
3369
     */
3370
    public function write_setting($data) {
3371
        if (!is_array($data)) {
3372
            return ''; // ignore it
3373
        }
3374
        if (!$this->load_choices() or empty($this->choices)) {
3375
            return '';
3376
        }
3377
        $result = '';
3378
        foreach ($this->choices as $key=>$unused) {
3379
            if (!empty($data[$key])) {
3380
                $result .= '1';
3381
            } else {
3382
                $result .= '0';
3383
            }
3384
        }
3385
        return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3386
    }
3387
}
3388
 
3389
 
3390
/**
3391
 * Select one value from list
3392
 *
3393
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3394
 */
3395
class admin_setting_configselect extends admin_setting {
3396
    /** @var array Array of choices value=>label */
3397
    public $choices;
3398
    /** @var array Array of choices grouped using optgroups */
3399
    public $optgroups;
3400
    /** @var callable|null Loader function for choices */
3401
    protected $choiceloader = null;
3402
    /** @var callable|null Validation function */
3403
    protected $validatefunction = null;
3404
 
3405
    /**
3406
     * Constructor.
3407
     *
3408
     * If you want to lazy-load the choices, pass a callback function that returns a choice
3409
     * array for the $choices parameter.
3410
     *
3411
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3412
     * @param string $visiblename localised
3413
     * @param string $description long localised info
3414
     * @param string|int|array $defaultsetting
3415
     * @param array|callable|null $choices array of $value=>$label for each selection, or callback
3416
     */
3417
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3418
        // Look for optgroup and single options.
3419
        if (is_array($choices)) {
3420
            $this->choices = [];
3421
            foreach ($choices as $key => $val) {
3422
                if (is_array($val)) {
3423
                    $this->optgroups[$key] = $val;
3424
                    $this->choices = array_merge($this->choices, $val);
3425
                } else {
3426
                    $this->choices[$key] = $val;
3427
                }
3428
            }
3429
        }
3430
        if (is_callable($choices)) {
3431
            $this->choiceloader = $choices;
3432
        }
3433
 
3434
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3435
    }
3436
 
3437
    /**
3438
     * Sets a validate function.
3439
     *
3440
     * The callback will be passed one parameter, the new setting value, and should return either
3441
     * an empty string '' if the value is OK, or an error message if not.
3442
     *
3443
     * @param callable|null $validatefunction Validate function or null to clear
3444
     * @since Moodle 3.10
3445
     */
3446
    public function set_validate_function(?callable $validatefunction = null) {
3447
        $this->validatefunction = $validatefunction;
3448
    }
3449
 
3450
    /**
3451
     * This function may be used in ancestors for lazy loading of choices
3452
     *
3453
     * Override this method if loading of choices is expensive, such
3454
     * as when it requires multiple db requests.
3455
     *
3456
     * @return bool true if loaded, false if error
3457
     */
3458
    public function load_choices() {
3459
        if ($this->choiceloader) {
3460
            if (!is_array($this->choices)) {
3461
                $this->choices = call_user_func($this->choiceloader);
3462
            }
3463
            return true;
3464
        }
3465
        return true;
3466
    }
3467
 
3468
    /**
3469
     * Check if this is $query is related to a choice
3470
     *
3471
     * @param string $query
3472
     * @return bool true if related, false if not
3473
     */
3474
    public function is_related($query) {
3475
        if (parent::is_related($query)) {
3476
            return true;
3477
        }
3478
        if (!$this->load_choices()) {
3479
            return false;
3480
        }
3481
        foreach ($this->choices as $key=>$value) {
3482
            if (strpos(core_text::strtolower($key), $query) !== false) {
3483
                return true;
3484
            }
3485
            if (strpos(core_text::strtolower($value), $query) !== false) {
3486
                return true;
3487
            }
3488
        }
3489
        return false;
3490
    }
3491
 
3492
    /**
3493
     * Return the setting
3494
     *
3495
     * @return mixed returns config if successful else null
3496
     */
3497
    public function get_setting() {
3498
        return $this->config_read($this->name);
3499
    }
3500
 
3501
    /**
3502
     * Save a setting
3503
     *
3504
     * @param string $data
3505
     * @return string empty of error string
3506
     */
3507
    public function write_setting($data) {
3508
        if (!$this->load_choices() or empty($this->choices)) {
3509
            return '';
3510
        }
3511
        if (!array_key_exists($data, $this->choices)) {
3512
            return ''; // ignore it
3513
        }
3514
 
3515
        // Validate the new setting.
3516
        $error = $this->validate_setting($data);
3517
        if ($error) {
3518
            return $error;
3519
        }
3520
 
3521
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3522
    }
3523
 
3524
    /**
3525
     * Validate the setting. This uses the callback function if provided; subclasses could override
3526
     * to carry out validation directly in the class.
3527
     *
3528
     * @param string $data New value being set
3529
     * @return string Empty string if valid, or error message text
3530
     * @since Moodle 3.10
3531
     */
3532
    protected function validate_setting(string $data): string {
3533
        // If validation function is specified, call it now.
3534
        if ($this->validatefunction) {
3535
            return call_user_func($this->validatefunction, $data);
3536
        } else {
3537
            return '';
3538
        }
3539
    }
3540
 
3541
    /**
3542
     * Returns XHTML select field
3543
     *
3544
     * Ensure the options are loaded, and generate the XHTML for the select
3545
     * element and any warning message. Separating this out from output_html
3546
     * makes it easier to subclass this class.
3547
     *
3548
     * @param string $data the option to show as selected.
3549
     * @param string $current the currently selected option in the database, null if none.
3550
     * @param string $default the default selected option.
3551
     * @return array the HTML for the select element, and a warning message.
3552
     * @deprecated since Moodle 3.2
3553
     */
3554
    public function output_select_html($data, $current, $default, $extraname = '') {
3555
        debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3556
    }
3557
 
3558
    /**
3559
     * Returns XHTML select field and wrapping div(s)
3560
     *
3561
     * @see output_select_html()
3562
     *
3563
     * @param string $data the option to show as selected
3564
     * @param string $query
3565
     * @return string XHTML field and wrapping div
3566
     */
3567
    public function output_html($data, $query='') {
3568
        global $OUTPUT;
3569
 
3570
        $default = $this->get_defaultsetting();
3571
        $current = $this->get_setting();
3572
 
3573
        if (!$this->load_choices() || empty($this->choices)) {
3574
            return '';
3575
        }
3576
 
3577
        $context = (object) [
3578
            'id' => $this->get_id(),
3579
            'name' => $this->get_full_name(),
3580
        ];
3581
 
3582
        if (!is_null($default) && array_key_exists($default, $this->choices)) {
3583
            $defaultinfo = $this->choices[$default];
3584
        } else {
3585
            $defaultinfo = NULL;
3586
        }
3587
 
3588
        // Warnings.
3589
        $warning = '';
3590
        if ($current === null) {
3591
            // First run.
3592
        } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3593
            // No warning.
3594
        } else if (!array_key_exists($current, $this->choices)) {
3595
            $warning = get_string('warningcurrentsetting', 'admin', $current);
3596
            if (!is_null($default) && $data == $current) {
3597
                $data = $default; // Use default instead of first value when showing the form.
3598
            }
3599
        }
3600
 
3601
        $options = [];
3602
        $template = 'core_admin/setting_configselect';
3603
 
3604
        if (!empty($this->optgroups)) {
3605
            $optgroups = [];
3606
            foreach ($this->optgroups as $label => $choices) {
3607
                $optgroup = array('label' => $label, 'options' => []);
3608
                foreach ($choices as $value => $name) {
3609
                    $optgroup['options'][] = [
3610
                        'value' => $value,
3611
                        'name' => $name,
3612
                        'selected' => (string) $value == $data
3613
                    ];
3614
                    unset($this->choices[$value]);
3615
                }
3616
                $optgroups[] = $optgroup;
3617
            }
3618
            $context->options = $options;
3619
            $context->optgroups = $optgroups;
3620
            $template = 'core_admin/setting_configselect_optgroup';
3621
        }
3622
 
3623
        foreach ($this->choices as $value => $name) {
3624
            $options[] = [
3625
                'value' => $value,
3626
                'name' => $name,
3627
                'selected' => (string) $value == $data
3628
            ];
3629
        }
3630
        $context->options = $options;
3631
        $context->readonly = $this->is_readonly();
3632
 
3633
        $element = $OUTPUT->render_from_template($template, $context);
3634
 
3635
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3636
    }
3637
}
3638
 
3639
/**
3640
 * Select multiple items from list
3641
 *
3642
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3643
 */
3644
class admin_setting_configmultiselect extends admin_setting_configselect {
3645
    /**
3646
     * Constructor
3647
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3648
     * @param string $visiblename localised
3649
     * @param string $description long localised info
3650
     * @param array $defaultsetting array of selected items
3651
     * @param ?array $choices array of $value=>$label for each list item
3652
     */
3653
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3654
        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3655
    }
3656
 
3657
    /**
3658
     * Returns the select setting(s)
3659
     *
3660
     * @return mixed null or array. Null if no settings else array of setting(s)
3661
     */
3662
    public function get_setting() {
3663
        $result = $this->config_read($this->name);
3664
        if (is_null($result)) {
3665
            return NULL;
3666
        }
3667
        if ($result === '') {
3668
            return array();
3669
        }
3670
        return explode(',', $result);
3671
    }
3672
 
3673
    /**
3674
     * Saves setting(s) provided through $data
3675
     *
3676
     * Potential bug in the works should anyone call with this function
3677
     * using a vartype that is not an array
3678
     *
3679
     * @param array $data
3680
     */
3681
    public function write_setting($data) {
3682
        if (!is_array($data)) {
3683
            return ''; //ignore it
3684
        }
3685
        if (!$this->load_choices() or empty($this->choices)) {
3686
            return '';
3687
        }
3688
 
3689
        unset($data['xxxxx']);
3690
 
3691
        $save = array();
3692
        foreach ($data as $value) {
3693
            if (!array_key_exists($value, $this->choices)) {
3694
                continue; // ignore it
3695
            }
3696
            $save[] = $value;
3697
        }
3698
 
3699
        return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3700
    }
3701
 
3702
    /**
3703
     * Is setting related to query text - used when searching
3704
     *
3705
     * @param string $query
3706
     * @return bool true if related, false if not
3707
     */
3708
    public function is_related($query) {
3709
        if (!$this->load_choices() or empty($this->choices)) {
3710
            return false;
3711
        }
3712
        if (parent::is_related($query)) {
3713
            return true;
3714
        }
3715
 
3716
        foreach ($this->choices as $desc) {
3717
            if (strpos(core_text::strtolower($desc), $query) !== false) {
3718
                return true;
3719
            }
3720
        }
3721
        return false;
3722
    }
3723
 
3724
    /**
3725
     * Returns XHTML multi-select field
3726
     *
3727
     * @todo Add vartype handling to ensure $data is an array
3728
     * @param array $data Array of values to select by default
3729
     * @param string $query
3730
     * @return string XHTML multi-select field
3731
     */
3732
    public function output_html($data, $query='') {
3733
        global $OUTPUT;
3734
 
3735
        if (!$this->load_choices() or empty($this->choices)) {
3736
            return '';
3737
        }
3738
 
3739
        $default = $this->get_defaultsetting();
3740
        if (is_null($default)) {
3741
            $default = array();
3742
        }
3743
        if (is_null($data)) {
3744
            $data = array();
3745
        }
3746
 
3747
        $context = (object) [
3748
            'id' => $this->get_id(),
3749
            'name' => $this->get_full_name(),
3750
            'size' => min(10, count($this->choices))
3751
        ];
3752
 
3753
        $defaults = [];
3754
        $options = [];
3755
        $template = 'core_admin/setting_configmultiselect';
3756
 
3757
        if (!empty($this->optgroups)) {
3758
            $optgroups = [];
3759
            foreach ($this->optgroups as $label => $choices) {
3760
                $optgroup = array('label' => $label, 'options' => []);
3761
                foreach ($choices as $value => $name) {
3762
                    if (in_array($value, $default)) {
3763
                        $defaults[] = $name;
3764
                    }
3765
                    $optgroup['options'][] = [
3766
                        'value' => $value,
3767
                        'name' => $name,
3768
                        'selected' => in_array($value, $data)
3769
                    ];
3770
                    unset($this->choices[$value]);
3771
                }
3772
                $optgroups[] = $optgroup;
3773
            }
3774
            $context->optgroups = $optgroups;
3775
            $template = 'core_admin/setting_configmultiselect_optgroup';
3776
        }
3777
 
3778
        foreach ($this->choices as $value => $name) {
3779
            if (in_array($value, $default)) {
3780
                $defaults[] = $name;
3781
            }
3782
            $options[] = [
3783
                'value' => $value,
3784
                'name' => $name,
3785
                'selected' => in_array($value, $data)
3786
            ];
3787
        }
3788
        $context->options = $options;
3789
        $context->readonly = $this->is_readonly();
3790
 
3791
        if (is_null($default)) {
3792
            $defaultinfo = NULL;
3793
        } if (!empty($defaults)) {
3794
            $defaultinfo = implode(', ', $defaults);
3795
        } else {
3796
            $defaultinfo = get_string('none');
3797
        }
3798
 
3799
        $element = $OUTPUT->render_from_template($template, $context);
3800
 
3801
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3802
    }
3803
}
3804
 
3805
/**
3806
 * Time selector
3807
 *
3808
 * This is a liiitle bit messy. we're using two selects, but we're returning
3809
 * them as an array named after $name (so we only use $name2 internally for the setting)
3810
 *
3811
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3812
 */
3813
class admin_setting_configtime extends admin_setting {
3814
    /** @var string Used for setting second select (minutes) */
3815
    public $name2;
3816
 
3817
    /**
3818
     * Constructor
3819
     * @param string $hoursname setting for hours
3820
     * @param string $minutesname setting for hours
3821
     * @param string $visiblename localised
3822
     * @param string $description long localised info
3823
     * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3824
     */
3825
    public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3826
        $this->name2 = $minutesname;
3827
        parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3828
    }
3829
 
3830
    /**
3831
     * Get the selected time
3832
     *
3833
     * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3834
     */
3835
    public function get_setting() {
3836
        $result1 = $this->config_read($this->name);
3837
        $result2 = $this->config_read($this->name2);
3838
        if (is_null($result1) or is_null($result2)) {
3839
            return NULL;
3840
        }
3841
 
3842
        return array('h' => $result1, 'm' => $result2);
3843
    }
3844
 
3845
    /**
3846
     * Store the time (hours and minutes)
3847
     *
3848
     * @param array $data Must be form 'h'=>xx, 'm'=>xx
3849
     * @return string error message or empty string on success
3850
     */
3851
    public function write_setting($data) {
3852
        if (!is_array($data)) {
3853
            return '';
3854
        }
3855
 
3856
        $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3857
        return ($result ? '' : get_string('errorsetting', 'admin'));
3858
    }
3859
 
3860
    /**
3861
     * Returns XHTML time select fields
3862
     *
3863
     * @param array $data Must be form 'h'=>xx, 'm'=>xx
3864
     * @param string $query
3865
     * @return string XHTML time select fields and wrapping div(s)
3866
     */
3867
    public function output_html($data, $query='') {
3868
        global $OUTPUT;
3869
 
3870
        $default = $this->get_defaultsetting();
3871
        if (is_array($default)) {
3872
            $defaultinfo = $default['h'].':'.$default['m'];
3873
        } else {
3874
            $defaultinfo = NULL;
3875
        }
3876
 
3877
        $context = (object) [
3878
            'id' => $this->get_id(),
3879
            'name' => $this->get_full_name(),
3880
            'readonly' => $this->is_readonly(),
3881
            'hours' => array_map(function($i) use ($data) {
3882
                return [
3883
                    'value' => $i,
3884
                    'name' => $i,
3885
                    'selected' => $i == $data['h']
3886
                ];
3887
            }, range(0, 23)),
3888
            'minutes' => array_map(function($i) use ($data) {
3889
                return [
3890
                    'value' => $i,
3891
                    'name' => $i,
3892
                    'selected' => $i == $data['m']
3893
                ];
3894
            }, range(0, 59, 5))
3895
        ];
3896
 
3897
        $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3898
 
3899
        return format_admin_setting($this, $this->visiblename, $element, $this->description,
3900
            $this->get_id() . 'h', '', $defaultinfo, $query);
3901
    }
3902
 
3903
}
3904
 
3905
 
3906
/**
3907
 * Seconds duration setting.
3908
 *
3909
 * @copyright 2012 Petr Skoda (http://skodak.org)
3910
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3911
 */
3912
class admin_setting_configduration extends admin_setting {
3913
 
3914
    /** @var int default duration unit */
3915
    protected $defaultunit;
3916
    /** @var callable|null Validation function */
3917
    protected $validatefunction = null;
3918
 
3919
    /** @var int The minimum allowed value */
3920
    protected int $minduration = 0;
3921
 
3922
    /** @var null|int The maximum allowed value */
3923
    protected null|int $maxduration = null;
3924
 
3925
    /**
3926
     * Constructor
3927
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3928
     *                     or 'myplugin/mysetting' for ones in config_plugins.
3929
     * @param string $visiblename localised name
3930
     * @param string $description localised long description
3931
     * @param mixed $defaultsetting string or array depending on implementation
3932
     * @param int $defaultunit - day, week, etc. (in seconds)
3933
     */
3934
    public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3935
        if (is_number($defaultsetting)) {
3936
            $defaultsetting = self::parse_seconds($defaultsetting);
3937
        }
3938
        $units = self::get_units();
3939
        if (isset($units[$defaultunit])) {
3940
            $this->defaultunit = $defaultunit;
3941
        } else {
3942
            $this->defaultunit = 86400;
3943
        }
3944
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3945
    }
3946
 
3947
    /**
3948
     * Set the minimum allowed value.
3949
     * This must be at least 0.
3950
     *
3951
     * @param int $duration
3952
     */
3953
    public function set_min_duration(int $duration): void {
3954
        if ($duration < 0) {
3955
            throw new coding_exception('The minimum duration must be at least 0.');
3956
        }
3957
 
3958
        $this->minduration = $duration;
3959
    }
3960
 
3961
    /**
3962
     * Set the maximum allowed value.
3963
     *
3964
     * A value of null will disable the maximum duration value.
3965
     *
3966
     * @param int|null $duration
3967
     */
3968
    public function set_max_duration(?int $duration): void {
3969
        $this->maxduration = $duration;
3970
    }
3971
 
3972
    /**
3973
     * Sets a validate function.
3974
     *
3975
     * The callback will be passed one parameter, the new setting value, and should return either
3976
     * an empty string '' if the value is OK, or an error message if not.
3977
     *
3978
     * @param callable|null $validatefunction Validate function or null to clear
3979
     * @since Moodle 3.10
3980
     */
3981
    public function set_validate_function(?callable $validatefunction = null) {
3982
        $this->validatefunction = $validatefunction;
3983
    }
3984
 
3985
    /**
3986
     * Validate the setting. This uses the callback function if provided; subclasses could override
3987
     * to carry out validation directly in the class.
3988
     *
3989
     * @param int $data New value being set
3990
     * @return string Empty string if valid, or error message text
3991
     * @since Moodle 3.10
3992
     */
3993
    protected function validate_setting(int $data): string {
3994
        if ($data < $this->minduration) {
3995
            return get_string(
3996
                'configduration_low',
3997
                'admin',
3998
                self::get_duration_text($this->minduration, get_string('numseconds', 'core', 0))
3999
            );
4000
        }
4001
 
4002
        if ($this->maxduration && $data > $this->maxduration) {
4003
            return get_string('configduration_high', 'admin', self::get_duration_text($this->maxduration));
4004
        }
4005
 
4006
        // If validation function is specified, call it now.
4007
        if ($this->validatefunction) {
4008
            return call_user_func($this->validatefunction, $data);
4009
        }
4010
        return '';
4011
    }
4012
 
4013
    /**
4014
     * Returns selectable units.
4015
     * @static
4016
     * @return array
4017
     */
4018
    protected static function get_units() {
4019
        return array(
4020
            604800 => get_string('weeks'),
4021
            86400 => get_string('days'),
4022
            3600 => get_string('hours'),
4023
            60 => get_string('minutes'),
4024
            1 => get_string('seconds'),
4025
        );
4026
    }
4027
 
4028
    /**
4029
     * Converts seconds to some more user friendly string.
4030
     * @static
4031
     * @param int $seconds
4032
     * @param null|string The value to use when the duration is empty. If not specified, a "None" value is used.
4033
     * @return string
4034
     */
4035
    protected static function get_duration_text(int $seconds, ?string $emptyvalue = null): string {
4036
        if (empty($seconds)) {
4037
            if ($emptyvalue !== null) {
4038
                return $emptyvalue;
4039
            }
4040
            return get_string('none');
4041
        }
4042
        $data = self::parse_seconds($seconds);
4043
        switch ($data['u']) {
4044
            case (60*60*24*7):
4045
                return get_string('numweeks', '', $data['v']);
4046
            case (60*60*24):
4047
                return get_string('numdays', '', $data['v']);
4048
            case (60*60):
4049
                return get_string('numhours', '', $data['v']);
4050
            case (60):
4051
                return get_string('numminutes', '', $data['v']);
4052
            default:
4053
                return get_string('numseconds', '', $data['v']*$data['u']);
4054
        }
4055
    }
4056
 
4057
    /**
4058
     * Finds suitable units for given duration.
4059
     * @static
4060
     * @param int $seconds
4061
     * @return array
4062
     */
4063
    protected static function parse_seconds($seconds) {
4064
        foreach (self::get_units() as $unit => $unused) {
4065
            if ($seconds % $unit === 0) {
4066
                return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
4067
            }
4068
        }
4069
        return array('v'=>(int)$seconds, 'u'=>1);
4070
    }
4071
 
4072
    /**
4073
     * Get the selected duration as array.
4074
     *
4075
     * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
4076
     */
4077
    public function get_setting() {
4078
        $seconds = $this->config_read($this->name);
4079
        if (is_null($seconds)) {
4080
            return null;
4081
        }
4082
 
4083
        return self::parse_seconds($seconds);
4084
    }
4085
 
4086
    /**
4087
     * Store the duration as seconds.
4088
     *
4089
     * @param array $data Must be form 'h'=>xx, 'm'=>xx
4090
     * @return string error message or empty string on success
4091
     */
4092
    public function write_setting($data) {
4093
        if (!is_array($data)) {
4094
            return '';
4095
        }
4096
 
4097
        $unit = (int)$data['u'];
4098
        $value = (int)$data['v'];
4099
        $seconds = $value * $unit;
4100
 
4101
        // Validate the new setting.
4102
        $error = $this->validate_setting($seconds);
4103
        if ($error) {
4104
            return $error;
4105
        }
4106
 
4107
        $result = $this->config_write($this->name, $seconds);
4108
        return ($result ? '' : get_string('errorsetting', 'admin'));
4109
    }
4110
 
4111
    /**
4112
     * Returns duration text+select fields.
4113
     *
4114
     * @param array $data Must be form 'v'=>xx, 'u'=>xx
4115
     * @param string $query
4116
     * @return string duration text+select fields and wrapping div(s)
4117
     */
4118
    public function output_html($data, $query='') {
4119
        global $OUTPUT;
4120
 
4121
        $default = $this->get_defaultsetting();
4122
        if (is_number($default)) {
4123
            $defaultinfo = self::get_duration_text($default);
4124
        } else if (is_array($default)) {
4125
            $defaultinfo = self::get_duration_text($default['v']*$default['u']);
4126
        } else {
4127
            $defaultinfo = null;
4128
        }
4129
 
4130
        $inputid = $this->get_id() . 'v';
4131
        $units = array_filter(self::get_units(), function($unit): bool {
4132
            if (!$this->maxduration) {
4133
                // No duration limit. All units are valid.
4134
                return true;
4135
            }
4136
 
4137
            return $unit <= $this->maxduration;
4138
        }, ARRAY_FILTER_USE_KEY);
4139
 
4140
        $defaultunit = $this->defaultunit;
4141
 
4142
        $context = (object) [
4143
            'id' => $this->get_id(),
4144
            'name' => $this->get_full_name(),
4145
            'value' => $data['v'] ?? '',
4146
            'readonly' => $this->is_readonly(),
4147
            'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
4148
                return [
4149
                    'value' => $unit,
4150
                    'name' => $units[$unit],
4151
                    'selected' => isset($data) && (($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u'])
4152
                ];
4153
            }, array_keys($units))
4154
        ];
4155
 
4156
        $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
4157
 
4158
        return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
4159
    }
4160
}
4161
 
4162
 
4163
/**
4164
 * Seconds duration setting with an advanced checkbox, that controls a additional
4165
 * $name.'_adv' setting.
4166
 *
4167
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4168
 * @copyright 2014 The Open University
4169
 */
4170
class admin_setting_configduration_with_advanced extends admin_setting_configduration {
4171
    /**
4172
     * Constructor
4173
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
4174
     *                     or 'myplugin/mysetting' for ones in config_plugins.
4175
     * @param string $visiblename localised name
4176
     * @param string $description localised long description
4177
     * @param array  $defaultsetting array of int value, and bool whether it is
4178
     *                     is advanced by default.
4179
     * @param int $defaultunit - day, week, etc. (in seconds)
4180
     */
4181
    public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
4182
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
4183
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4184
    }
4185
}
4186
 
4187
 
4188
/**
4189
 * Used to validate a textarea used for ip addresses
4190
 *
4191
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4192
 * @copyright 2011 Petr Skoda (http://skodak.org)
4193
 */
4194
class admin_setting_configiplist extends admin_setting_configtextarea {
4195
 
4196
    /**
4197
     * Validate the contents of the textarea as IP addresses
4198
     *
4199
     * Used to validate a new line separated list of IP addresses collected from
4200
     * a textarea control
4201
     *
4202
     * @param string $data A list of IP Addresses separated by new lines
4203
     * @return mixed bool true for success or string:error on failure
4204
     */
4205
    public function validate($data) {
4206
        if(!empty($data)) {
4207
            $lines = explode("\n", $data);
4208
        } else {
4209
            return true;
4210
        }
4211
        $result = true;
4212
        $badips = array();
4213
        foreach ($lines as $line) {
4214
            $tokens = explode('#', $line);
4215
            $ip = trim($tokens[0]);
4216
            if (empty($ip)) {
4217
                continue;
4218
            }
4219
            if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
4220
                preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
4221
                preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
4222
            } else {
4223
                $result = false;
4224
                $badips[] = $ip;
4225
            }
4226
        }
4227
        if($result) {
4228
            return true;
4229
        } else {
4230
            return get_string('validateiperror', 'admin', join(', ', $badips));
4231
        }
4232
    }
4233
}
4234
 
4235
/**
4236
 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
4237
 *
4238
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4239
 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4240
 */
4241
class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
4242
 
4243
    /**
4244
     * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
4245
     * Used to validate a new line separated list of entries collected from a textarea control.
4246
     *
4247
     * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
4248
     * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
4249
     * via the get_setting() method, which has been overriden.
4250
     *
4251
     * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
4252
     * @return mixed bool true for success or string:error on failure
4253
     */
4254
    public function validate($data) {
4255
        if (empty($data)) {
4256
            return true;
4257
        }
4258
        $entries = explode("\n", $data);
4259
        $badentries = [];
4260
 
4261
        foreach ($entries as $key => $entry) {
4262
            $entry = trim($entry);
4263
            if (empty($entry)) {
4264
                return get_string('validateemptylineerror', 'admin');
4265
            }
4266
 
4267
            // Validate each string entry against the supported formats.
4268
            if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
4269
                    || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
4270
                    || \core\ip_utils::is_domain_matching_pattern($entry)) {
4271
                continue;
4272
            }
4273
 
4274
            // Otherwise, the entry is invalid.
4275
            $badentries[] = $entry;
4276
        }
4277
 
4278
        if ($badentries) {
4279
            return get_string('validateerrorlist', 'admin', join(', ', $badentries));
4280
        }
4281
        return true;
4282
    }
4283
 
4284
    /**
4285
     * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
4286
     *
4287
     * @param string $data the setting data, as sent from the web form.
4288
     * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
4289
     */
4290
    protected function ace_encode($data) {
4291
        if (empty($data)) {
4292
            return $data;
4293
        }
4294
        $entries = explode("\n", $data);
4295
        foreach ($entries as $key => $entry) {
4296
            $entry = trim($entry);
4297
            // This regex matches any string that has non-ascii character.
4298
            if (preg_match('/[^\x00-\x7f]/', $entry)) {
4299
                // If we can convert the unicode string to an idn, do so.
4300
                // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
4301
                $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4302
                $entries[$key] = $val ? $val : $entry;
4303
            }
4304
        }
4305
        return implode("\n", $entries);
4306
    }
4307
 
4308
    /**
4309
     * Decode any ascii-encoded domain names back to their utf-8 representation for display.
4310
     *
4311
     * @param string $data the setting data, as found in the database.
4312
     * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
4313
     */
4314
    protected function ace_decode($data) {
4315
        $entries = explode("\n", $data);
4316
        foreach ($entries as $key => $entry) {
4317
            $entry = trim($entry);
4318
            if (strpos($entry, 'xn--') !== false) {
4319
                $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4320
            }
4321
        }
4322
        return implode("\n", $entries);
4323
    }
4324
 
4325
    /**
4326
     * Override, providing utf8-decoding for ascii-encoded IDN strings.
4327
     *
4328
     * @return mixed returns punycode-converted setting string if successful, else null.
4329
     */
4330
    public function get_setting() {
4331
        // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4332
        $data = $this->config_read($this->name);
4333
        if (function_exists('idn_to_utf8') && !is_null($data)) {
4334
            $data = $this->ace_decode($data);
4335
        }
4336
        return $data;
4337
    }
4338
 
4339
    /**
4340
     * Override, providing ascii-encoding for utf8 (native) IDN strings.
4341
     *
4342
     * @param string $data
4343
     * @return string
4344
     */
4345
    public function write_setting($data) {
4346
        if ($this->paramtype === PARAM_INT and $data === '') {
4347
            // Do not complain if '' used instead of 0.
4348
            $data = 0;
4349
        }
4350
 
4351
        // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4352
        if (function_exists('idn_to_ascii')) {
4353
            $data = $this->ace_encode($data);
4354
        }
4355
 
4356
        $validated = $this->validate($data);
4357
        if ($validated !== true) {
4358
            return $validated;
4359
        }
4360
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4361
    }
4362
}
4363
 
4364
/**
4365
 * Used to validate a textarea used for port numbers.
4366
 *
4367
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4368
 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4369
 */
4370
class admin_setting_configportlist extends admin_setting_configtextarea {
4371
 
4372
    /**
4373
     * Validate the contents of the textarea as port numbers.
4374
     * Used to validate a new line separated list of ports collected from a textarea control.
4375
     *
4376
     * @param string $data A list of ports separated by new lines
4377
     * @return mixed bool true for success or string:error on failure
4378
     */
4379
    public function validate($data) {
4380
        if (empty($data)) {
4381
            return true;
4382
        }
4383
        $ports = explode("\n", $data);
4384
        $badentries = [];
4385
        foreach ($ports as $port) {
4386
            $port = trim($port);
4387
            if (empty($port)) {
4388
                return get_string('validateemptylineerror', 'admin');
4389
            }
4390
 
4391
            // Is the string a valid integer number?
4392
            if (strval(intval($port)) !== $port || intval($port) <= 0) {
4393
                $badentries[] = $port;
4394
            }
4395
        }
4396
        if ($badentries) {
4397
            return get_string('validateerrorlist', 'admin', $badentries);
4398
        }
4399
        return true;
4400
    }
4401
}
4402
 
4403
 
4404
/**
4405
 * An admin setting for selecting one or more users who have a capability
4406
 * in the system context
4407
 *
4408
 * An admin setting for selecting one or more users, who have a particular capability
4409
 * in the system context. Warning, make sure the list will never be too long. There is
4410
 * no paging or searching of this list.
4411
 *
4412
 * To correctly get a list of users from this config setting, you need to call the
4413
 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4414
 *
4415
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4416
 */
4417
class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4418
    /** @var string The capabilities name */
4419
    protected $capability;
4420
    /** @var int include admin users too */
4421
    protected $includeadmins;
4422
 
4423
    /**
4424
     * Constructor.
4425
     *
4426
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4427
     * @param string $visiblename localised name
4428
     * @param string $description localised long description
4429
     * @param array $defaultsetting array of usernames
4430
     * @param string $capability string capability name.
4431
     * @param bool $includeadmins include administrators
4432
     */
4433
    function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4434
        $this->capability    = $capability;
4435
        $this->includeadmins = $includeadmins;
4436
        parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4437
    }
4438
 
4439
    /**
4440
     * Load all of the uses who have the capability into choice array
4441
     *
4442
     * @return bool Always returns true
4443
     */
4444
    function load_choices() {
4445
        if (is_array($this->choices)) {
4446
            return true;
4447
        }
4448
        list($sort, $sortparams) = users_order_by_sql('u');
4449
        if (!empty($sortparams)) {
4450
            throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4451
                    'This is unexpected, and a problem because there is no way to pass these ' .
4452
                    'parameters to get_users_by_capability. See MDL-34657.');
4453
        }
4454
        $userfieldsapi = \core_user\fields::for_name();
4455
        $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
4456
        $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4457
        $this->choices = array(
4458
            '$@NONE@$' => get_string('nobody'),
4459
            '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4460
        );
4461
        if ($this->includeadmins) {
4462
            $admins = get_admins();
4463
            foreach ($admins as $user) {
4464
                $this->choices[$user->id] = fullname($user);
4465
            }
4466
        }
4467
        if (is_array($users)) {
4468
            foreach ($users as $user) {
4469
                $this->choices[$user->id] = fullname($user);
4470
            }
4471
        }
4472
        return true;
4473
    }
4474
 
4475
    /**
4476
     * Returns the default setting for class
4477
     *
4478
     * @return mixed Array, or string. Empty string if no default
4479
     */
4480
    public function get_defaultsetting() {
4481
        $this->load_choices();
4482
        $defaultsetting = parent::get_defaultsetting();
4483
        if (empty($defaultsetting)) {
4484
            return array('$@NONE@$');
4485
        } else if (array_key_exists($defaultsetting, $this->choices)) {
4486
                return $defaultsetting;
4487
            } else {
4488
                return '';
4489
            }
4490
    }
4491
 
4492
    /**
4493
     * Returns the current setting
4494
     *
4495
     * @return mixed array or string
4496
     */
4497
    public function get_setting() {
4498
        $result = parent::get_setting();
4499
        if ($result === null) {
4500
            // this is necessary for settings upgrade
4501
            return null;
4502
        }
4503
        if (empty($result)) {
4504
            $result = array('$@NONE@$');
4505
        }
4506
        return $result;
4507
    }
4508
 
4509
    /**
4510
     * Save the chosen setting provided as $data
4511
     *
4512
     * @param array $data
4513
     * @return mixed string or array
4514
     */
4515
    public function write_setting($data) {
4516
    // If all is selected, remove any explicit options.
4517
        if (in_array('$@ALL@$', $data)) {
4518
            $data = array('$@ALL@$');
4519
        }
4520
        // None never needs to be written to the DB.
4521
        if (in_array('$@NONE@$', $data)) {
4522
            unset($data[array_search('$@NONE@$', $data)]);
4523
        }
4524
        return parent::write_setting($data);
4525
    }
4526
}
4527
 
4528
 
4529
/**
4530
 * Special checkbox for calendar - resets SESSION vars.
4531
 *
4532
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4533
 */
4534
class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4535
    /**
4536
     * Calls the parent::__construct with default values
4537
     *
4538
     * name =>  calendar_adminseesall
4539
     * visiblename => get_string('adminseesall', 'admin')
4540
     * description => get_string('helpadminseesall', 'admin')
4541
     * defaultsetting => 0
4542
     */
4543
    public function __construct() {
4544
        parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4545
            get_string('helpadminseesall', 'admin'), '0');
4546
    }
4547
 
4548
    /**
4549
     * Stores the setting passed in $data
4550
     *
4551
     * @param mixed gets converted to string for comparison
4552
     * @return string empty string or error message
4553
     */
4554
    public function write_setting($data) {
4555
        global $SESSION;
4556
        return parent::write_setting($data);
4557
    }
4558
}
4559
 
4560
/**
4561
 * Special select for settings that are altered in setup.php and can not be altered on the fly
4562
 *
4563
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4564
 */
4565
class admin_setting_special_selectsetup extends admin_setting_configselect {
4566
    /**
4567
     * Reads the setting directly from the database
4568
     *
4569
     * @return mixed
4570
     */
4571
    public function get_setting() {
4572
    // read directly from db!
4573
        return get_config(NULL, $this->name);
4574
    }
4575
 
4576
    /**
4577
     * Save the setting passed in $data
4578
     *
4579
     * @param string $data The setting to save
4580
     * @return string empty or error message
4581
     */
4582
    public function write_setting($data) {
4583
        global $CFG;
4584
        // do not change active CFG setting!
4585
        $current = $CFG->{$this->name};
4586
        $result = parent::write_setting($data);
4587
        $CFG->{$this->name} = $current;
4588
        return $result;
4589
    }
4590
}
4591
 
4592
 
4593
/**
4594
 * Special select for frontpage - stores data in course table
4595
 *
4596
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4597
 */
4598
class admin_setting_sitesetselect extends admin_setting_configselect {
4599
    /**
4600
     * Returns the site name for the selected site
4601
     *
4602
     * @see get_site()
4603
     * @return string The site name of the selected site
4604
     */
4605
    public function get_setting() {
4606
        $site = course_get_format(get_site())->get_course();
4607
        return $site->{$this->name};
4608
    }
4609
 
4610
    /**
4611
     * Updates the database and save the setting
4612
     *
4613
     * @param string data
4614
     * @return string empty or error message
4615
     */
4616
    public function write_setting($data) {
4617
        global $DB, $SITE, $COURSE;
4618
        if (!in_array($data, array_keys($this->choices))) {
4619
            return get_string('errorsetting', 'admin');
4620
        }
4621
        $record = new stdClass();
4622
        $record->id           = SITEID;
4623
        $temp                 = $this->name;
4624
        $record->$temp        = $data;
4625
        $record->timemodified = time();
4626
 
4627
        course_get_format($SITE)->update_course_format_options($record);
4628
        $DB->update_record('course', $record);
4629
 
4630
        // Reset caches.
4631
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4632
        if ($SITE->id == $COURSE->id) {
4633
            $COURSE = $SITE;
4634
        }
4635
        core_courseformat\base::reset_course_cache($SITE->id);
4636
 
4637
        return '';
4638
 
4639
    }
4640
 
4641
    /**
4642
     * admin_setting_sitesetselect is not meant to be overridden in config.php.
4643
     *
4644
     * @return bool
4645
     */
4646
    public function is_forceable(): bool {
4647
        return false;
4648
    }
4649
}
4650
 
4651
 
4652
/**
4653
 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4654
 * block to hidden.
4655
 *
4656
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4657
 */
4658
class admin_setting_bloglevel extends admin_setting_configselect {
4659
    /**
4660
     * Updates the database and save the setting
4661
     *
4662
     * @param string data
4663
     * @return string empty or error message
4664
     */
4665
    public function write_setting($data) {
4666
        global $DB, $CFG;
4667
        if ($data == 0) {
4668
            $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4669
            foreach ($blogblocks as $block) {
4670
                $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4671
            }
4672
        } else {
4673
            // reenable all blocks only when switching from disabled blogs
4674
            if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4675
                $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4676
                foreach ($blogblocks as $block) {
4677
                    $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4678
                }
4679
            }
4680
        }
4681
        return parent::write_setting($data);
4682
    }
4683
}
4684
 
4685
 
4686
/**
4687
 * Special select - lists on the frontpage - hacky
4688
 *
4689
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4690
 */
4691
class admin_setting_courselist_frontpage extends admin_setting {
4692
 
4693
    /** @var array Array of choices value=>label. */
4694
    public $choices;
4695
 
4696
    /**
4697
     * Construct override, requires one param
4698
     *
4699
     * @param bool $loggedin Is the user logged in
4700
     */
4701
    public function __construct($loggedin) {
4702
        global $CFG;
4703
        require_once($CFG->dirroot.'/course/lib.php');
4704
        $name        = 'frontpage'.($loggedin ? 'loggedin' : '');
4705
        $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4706
        $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4707
        $defaults    = array(FRONTPAGEALLCOURSELIST);
4708
        parent::__construct($name, $visiblename, $description, $defaults);
4709
    }
4710
 
4711
    /**
4712
     * Loads the choices available
4713
     *
4714
     * @return bool always returns true
4715
     */
4716
    public function load_choices() {
4717
        if (is_array($this->choices)) {
4718
            return true;
4719
        }
4720
        $this->choices = array(FRONTPAGENEWS          => get_string('frontpagenews'),
4721
            FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4722
            FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4723
            FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4724
            FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4725
            FRONTPAGECOURSESEARCH  => get_string('frontpagecoursesearch'),
4726
            'none'                 => get_string('none'));
4727
        if ($this->name === 'frontpage') {
4728
            unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4729
        }
4730
        return true;
4731
    }
4732
 
4733
    /**
4734
     * Returns the selected settings
4735
     *
4736
     * @param mixed array or setting or null
4737
     */
4738
    public function get_setting() {
4739
        $result = $this->config_read($this->name);
4740
        if (is_null($result)) {
4741
            return NULL;
4742
        }
4743
        if ($result === '') {
4744
            return array();
4745
        }
4746
        return explode(',', $result);
4747
    }
4748
 
4749
    /**
4750
     * Save the selected options
4751
     *
4752
     * @param array $data
4753
     * @return mixed empty string (data is not an array) or bool true=success false=failure
4754
     */
4755
    public function write_setting($data) {
4756
        if (!is_array($data)) {
4757
            return '';
4758
        }
4759
        $this->load_choices();
4760
        $save = array();
4761
        foreach($data as $datum) {
4762
            if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4763
                continue;
4764
            }
4765
            $save[$datum] = $datum; // no duplicates
4766
        }
4767
        return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4768
    }
4769
 
4770
    /**
4771
     * Return XHTML select field and wrapping div
4772
     *
4773
     * @todo Add vartype handling to make sure $data is an array
4774
     * @param array $data Array of elements to select by default
4775
     * @return string XHTML select field and wrapping div
4776
     */
4777
    public function output_html($data, $query='') {
4778
        global $OUTPUT;
4779
 
4780
        $this->load_choices();
4781
        $currentsetting = array();
4782
        foreach ($data as $key) {
4783
            if ($key != 'none' and array_key_exists($key, $this->choices)) {
4784
                $currentsetting[] = $key; // already selected first
4785
            }
4786
        }
4787
 
4788
        $context = (object) [
4789
            'id' => $this->get_id(),
4790
            'name' => $this->get_full_name(),
4791
        ];
4792
 
4793
        $options = $this->choices;
4794
        $selects = [];
4795
        for ($i = 0; $i < count($this->choices) - 1; $i++) {
4796
            if (!array_key_exists($i, $currentsetting)) {
4797
                $currentsetting[$i] = 'none';
4798
            }
4799
            $selects[] = [
4800
                'key' => $i,
4801
                'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4802
                    return [
4803
                        'name' => $options[$option],
4804
                        'value' => $option,
4805
                        'selected' => $currentsetting[$i] == $option
4806
                    ];
4807
                }, array_keys($options))
4808
            ];
4809
        }
4810
        $context->selects = $selects;
4811
 
4812
        $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4813
 
4814
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4815
    }
4816
}
4817
 
4818
 
4819
/**
4820
 * Special checkbox for frontpage - stores data in course table
4821
 *
4822
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4823
 */
4824
class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4825
    /**
4826
     * Returns the current sites name
4827
     *
4828
     * @return string
4829
     */
4830
    public function get_setting() {
4831
        $site = course_get_format(get_site())->get_course();
4832
        return $site->{$this->name};
4833
    }
4834
 
4835
    /**
4836
     * Save the selected setting
4837
     *
4838
     * @param string $data The selected site
4839
     * @return string empty string or error message
4840
     */
4841
    public function write_setting($data) {
4842
        global $DB, $SITE, $COURSE;
4843
        $record = new stdClass();
4844
        $record->id            = $SITE->id;
4845
        $record->{$this->name} = ($data == '1' ? 1 : 0);
4846
        $record->timemodified  = time();
4847
 
4848
        course_get_format($SITE)->update_course_format_options($record);
4849
        $DB->update_record('course', $record);
4850
 
4851
        // Reset caches.
4852
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4853
        if ($SITE->id == $COURSE->id) {
4854
            $COURSE = $SITE;
4855
        }
4856
        core_courseformat\base::reset_course_cache($SITE->id);
4857
 
4858
        return '';
4859
    }
4860
 
4861
    /**
4862
     * admin_setting_sitesetcheckbox is not meant to be overridden in config.php.
4863
     *
4864
     * @return bool
4865
     */
4866
    public function is_forceable(): bool {
4867
        return false;
4868
    }
4869
}
4870
 
4871
/**
4872
 * Special text for frontpage - stores data in course table.
4873
 * Empty string means not set here. Manual setting is required.
4874
 *
4875
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4876
 */
4877
class admin_setting_sitesettext extends admin_setting_configtext {
4878
 
4879
    /**
4880
     * Constructor.
4881
     */
4882
    public function __construct() {
4883
        call_user_func_array([parent::class, '__construct'], func_get_args());
4884
        $this->set_force_ltr(false);
4885
    }
4886
 
4887
    /**
4888
     * Return the current setting
4889
     *
4890
     * @return mixed string or null
4891
     */
4892
    public function get_setting() {
4893
        $site = course_get_format(get_site())->get_course();
4894
        return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4895
    }
4896
 
4897
    /**
4898
     * Validate the selected data
4899
     *
4900
     * @param string $data The selected value to validate
4901
     * @return mixed true or message string
4902
     */
4903
    public function validate($data) {
4904
        global $DB, $SITE;
4905
        $cleaned = clean_param($data, PARAM_TEXT);
4906
        if ($cleaned === '') {
4907
            return get_string('required');
4908
        }
4909
        if ($this->name ==='shortname' &&
4910
                $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4911
            return get_string('shortnametaken', 'error', $data);
4912
        }
4913
        if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4914
            return true;
4915
        } else {
4916
            return get_string('validateerror', 'admin');
4917
        }
4918
    }
4919
 
4920
    /**
4921
     * Save the selected setting
4922
     *
4923
     * @param string $data The selected value
4924
     * @return string empty or error message
4925
     */
4926
    public function write_setting($data) {
4927
        global $DB, $SITE, $COURSE;
4928
        $data = trim($data);
4929
        $validated = $this->validate($data);
4930
        if ($validated !== true) {
4931
            return $validated;
4932
        }
4933
 
4934
        $record = new stdClass();
4935
        $record->id            = $SITE->id;
4936
        $record->{$this->name} = $data;
4937
        $record->timemodified  = time();
4938
 
4939
        course_get_format($SITE)->update_course_format_options($record);
4940
        $DB->update_record('course', $record);
4941
 
4942
        // Reset caches.
4943
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4944
        if ($SITE->id == $COURSE->id) {
4945
            $COURSE = $SITE;
4946
        }
4947
        core_courseformat\base::reset_course_cache($SITE->id);
4948
 
4949
        return '';
4950
    }
4951
 
4952
    /**
4953
     * admin_setting_sitesettext is not meant to be overridden in config.php.
4954
     *
4955
     * @return bool
4956
     */
4957
    public function is_forceable(): bool {
4958
        return false;
4959
    }
4960
}
4961
 
4962
 
4963
/**
4964
 * This type of field should be used for mandatory config settings.
4965
 *
4966
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4967
 */
4968
class admin_setting_requiredtext extends admin_setting_configtext {
4969
 
4970
    /**
4971
     * Validate data before storage.
4972
     *
4973
     * @param string $data The string to be validated.
4974
     * @return bool|string true for success or error string if invalid.
4975
     */
4976
    public function validate($data) {
4977
        $cleaned = clean_param($data, PARAM_TEXT);
4978
        if ($cleaned === '') {
4979
            return get_string('required');
4980
        }
4981
 
4982
        return parent::validate($data);
4983
    }
4984
}
4985
 
4986
/**
4987
 * This type of field should be used for mandatory config settings where setting password is required.
4988
 *
4989
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4990
 */
4991
class admin_setting_requiredpasswordunmask extends admin_setting_configpasswordunmask {
4992
 
4993
    /**
4994
     * Validate data before storage.
4995
     *
4996
     * @param string $data The string to be validated.
4997
     * @return bool|string true for success or error string if invalid.
4998
     */
4999
    public function validate($data) {
5000
        $cleaned = clean_param($data, PARAM_TEXT);
5001
        if ($cleaned === '') {
5002
            return get_string('required');
5003
        }
5004
 
5005
        return parent::validate($data);
5006
    }
5007
}
5008
 
5009
/**
5010
 * Special text editor for site description.
5011
 *
5012
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5013
 */
5014
class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
5015
 
5016
    /**
5017
     * Calls parent::__construct with specific arguments
5018
     */
5019
    public function __construct() {
5020
        parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
5021
            PARAM_RAW, 60, 15);
5022
    }
5023
 
5024
    /**
5025
     * Return the current setting
5026
     * @return string The current setting
5027
     */
5028
    public function get_setting() {
5029
        $site = course_get_format(get_site())->get_course();
5030
        return $site->{$this->name};
5031
    }
5032
 
5033
    /**
5034
     * Save the new setting
5035
     *
5036
     * @param string $data The new value to save
5037
     * @return string empty or error message
5038
     */
5039
    public function write_setting($data) {
5040
        global $DB, $SITE, $COURSE;
5041
        $record = new stdClass();
5042
        $record->id            = $SITE->id;
5043
        $record->{$this->name} = $data;
5044
        $record->timemodified  = time();
5045
 
5046
        course_get_format($SITE)->update_course_format_options($record);
5047
        $DB->update_record('course', $record);
5048
 
5049
        // Reset caches.
5050
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
5051
        if ($SITE->id == $COURSE->id) {
5052
            $COURSE = $SITE;
5053
        }
5054
        core_courseformat\base::reset_course_cache($SITE->id);
5055
 
5056
        return '';
5057
    }
5058
 
5059
    /**
5060
     * admin_setting_special_frontpagedesc is not meant to be overridden in config.php.
5061
     *
5062
     * @return bool
5063
     */
5064
    public function is_forceable(): bool {
5065
        return false;
5066
    }
5067
}
5068
 
5069
 
5070
/**
5071
 * Administration interface for emoticon_manager settings.
5072
 *
5073
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5074
 */
5075
class admin_setting_emoticons extends admin_setting {
5076
 
5077
    /**
5078
     * Calls parent::__construct with specific args
5079
     */
5080
    public function __construct() {
5081
        global $CFG;
5082
 
5083
        $manager = get_emoticon_manager();
5084
        $defaults = $this->prepare_form_data($manager->default_emoticons());
5085
        parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
5086
    }
5087
 
5088
    /**
5089
     * Return the current setting(s)
5090
     *
5091
     * @return ?array Current settings array
5092
     */
5093
    public function get_setting() {
5094
        global $CFG;
5095
 
5096
        $manager = get_emoticon_manager();
5097
 
5098
        $config = $this->config_read($this->name);
5099
        if (is_null($config)) {
5100
            return null;
5101
        }
5102
 
5103
        $config = $manager->decode_stored_config($config);
5104
        if (is_null($config)) {
5105
            return null;
5106
        }
5107
 
5108
        return $this->prepare_form_data($config);
5109
    }
5110
 
5111
    /**
5112
     * Save selected settings
5113
     *
5114
     * @param array $data Array of settings to save
5115
     * @return string error message or empty string on success
5116
     */
5117
    public function write_setting($data) {
5118
 
5119
        $manager = get_emoticon_manager();
5120
        $emoticons = $this->process_form_data($data);
5121
 
5122
        if ($emoticons === false) {
5123
            return false;
5124
        }
5125
 
5126
        if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
5127
            return ''; // success
5128
        } else {
5129
            return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
5130
        }
5131
    }
5132
 
5133
    /**
5134
     * Return XHTML field(s) for options
5135
     *
5136
     * @param array $data Array of options to set in HTML
5137
     * @return string XHTML string for the fields and wrapping div(s)
5138
     */
5139
    public function output_html($data, $query='') {
5140
        global $OUTPUT;
5141
 
5142
        $context = (object) [
5143
            'name' => $this->get_full_name(),
5144
            'emoticons' => [],
5145
            'forceltr' => true,
5146
        ];
5147
 
5148
        $i = 0;
5149
        foreach ($data as $field => $value) {
5150
 
5151
            // When $i == 0: text.
5152
            // When $i == 1: imagename.
5153
            // When $i == 2: imagecomponent.
5154
            // When $i == 3: altidentifier.
5155
            // When $i == 4: altcomponent.
5156
            $fields[$i] = (object) [
5157
                'field' => $field,
5158
                'value' => $value,
5159
                'index' => $i
5160
            ];
5161
            $i++;
5162
 
5163
            if ($i > 4) {
5164
                $icon = null;
5165
                if (!empty($fields[1]->value)) {
5166
                    if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
5167
                        $alt = get_string($fields[3]->value, $fields[4]->value);
5168
                    } else {
5169
                        $alt = $fields[0]->value;
5170
                    }
5171
                    $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
5172
                }
5173
                $context->emoticons[] = [
5174
                    'fields' => $fields,
5175
                    'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
5176
                ];
5177
                $fields = [];
5178
                $i = 0;
5179
            }
5180
        }
5181
 
5182
        $context->reseturl = new moodle_url('/admin/resetemoticons.php');
5183
        $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
5184
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5185
    }
5186
 
5187
    /**
5188
     * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
5189
     *
5190
     * @see self::process_form_data()
5191
     * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
5192
     * @return array of form fields and their values
5193
     */
5194
    protected function prepare_form_data(array $emoticons) {
5195
 
5196
        $form = array();
5197
        $i = 0;
5198
        foreach ($emoticons as $emoticon) {
5199
            $form['text'.$i]            = $emoticon->text;
5200
            $form['imagename'.$i]       = $emoticon->imagename;
5201
            $form['imagecomponent'.$i]  = $emoticon->imagecomponent;
5202
            $form['altidentifier'.$i]   = $emoticon->altidentifier;
5203
            $form['altcomponent'.$i]    = $emoticon->altcomponent;
5204
            $i++;
5205
        }
5206
        // add one more blank field set for new object
5207
        $form['text'.$i]            = '';
5208
        $form['imagename'.$i]       = '';
5209
        $form['imagecomponent'.$i]  = '';
5210
        $form['altidentifier'.$i]   = '';
5211
        $form['altcomponent'.$i]    = '';
5212
 
5213
        return $form;
5214
    }
5215
 
5216
    /**
5217
     * Converts the data from admin settings form into an array of emoticon objects
5218
     *
5219
     * @see self::prepare_form_data()
5220
     * @param array $data array of admin form fields and values
5221
     * @return false|array of emoticon objects
5222
     */
5223
    protected function process_form_data(array $form) {
5224
 
5225
        $count = count($form); // number of form field values
5226
 
5227
        if ($count % 5) {
5228
            // we must get five fields per emoticon object
5229
            return false;
5230
        }
5231
 
5232
        $emoticons = array();
5233
        for ($i = 0; $i < $count / 5; $i++) {
5234
            $emoticon                   = new stdClass();
5235
            $emoticon->text             = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
5236
            $emoticon->imagename        = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
5237
            $emoticon->imagecomponent   = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
5238
            $emoticon->altidentifier    = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
5239
            $emoticon->altcomponent     = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
5240
 
5241
            if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
5242
                // prevent from breaking http://url.addresses by accident
5243
                $emoticon->text = '';
5244
            }
5245
 
5246
            if (strlen($emoticon->text) < 2) {
5247
                // do not allow single character emoticons
5248
                $emoticon->text = '';
5249
            }
5250
 
5251
            if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
5252
                // emoticon text must contain some non-alphanumeric character to prevent
5253
                // breaking HTML tags
5254
                $emoticon->text = '';
5255
            }
5256
 
5257
            if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
5258
                $emoticons[] = $emoticon;
5259
            }
5260
        }
5261
        return $emoticons;
5262
    }
5263
 
5264
}
5265
 
5266
 
5267
/**
5268
 * Special setting for limiting of the list of available languages.
5269
 *
5270
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5271
 */
5272
class admin_setting_langlist extends admin_setting_configtext {
5273
    /**
5274
     * Calls parent::__construct with specific arguments
5275
     */
5276
    public function __construct() {
5277
        parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
5278
    }
5279
 
5280
    /**
5281
     * Validate that each language identifier exists on the site
5282
     *
5283
     * @param string $data
5284
     * @return bool|string True if validation successful, otherwise error string
5285
     */
5286
    public function validate($data) {
5287
        $parentcheck = parent::validate($data);
5288
        if ($parentcheck !== true) {
5289
            return $parentcheck;
5290
        }
5291
 
5292
        if ($data === '') {
5293
            return true;
5294
        }
5295
 
5296
        // Normalize language identifiers.
5297
        $langcodes = array_map('trim', explode(',', $data));
5298
        foreach ($langcodes as $langcode) {
5299
            // If the langcode contains optional alias, split it out.
5300
            [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
5301
 
5302
            if (!get_string_manager()->translation_exists($langcode)) {
5303
                return get_string('invalidlanguagecode', 'error', $langcode);
5304
            }
5305
        }
5306
 
5307
        return true;
5308
    }
5309
 
5310
    /**
5311
     * Save the new setting
5312
     *
5313
     * @param string $data The new setting
5314
     * @return string error message or empty string on success
5315
     */
5316
    public function write_setting($data) {
5317
        $return = parent::write_setting($data);
5318
        get_string_manager()->reset_caches();
5319
        return $return;
5320
    }
5321
}
5322
 
5323
 
5324
/**
5325
 * Allows to specify comma separated list of known country codes.
5326
 *
5327
 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
5328
 * known codes.
5329
 *
5330
 * @package     core
5331
 * @category    admin
5332
 * @copyright   2020 David Mudrák <david@moodle.com>
5333
 * @license     https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5334
 */
5335
class admin_setting_countrycodes extends admin_setting_configtext {
5336
 
5337
    /**
5338
     * Construct the instance of the setting.
5339
     *
5340
     * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
5341
     * @param lang_string|string $visiblename Language string with the field label text.
5342
     * @param lang_string|string $description Language string with the field description text.
5343
     * @param string $defaultsetting Default value of the setting.
5344
     * @param int $size Input text field size.
5345
     */
5346
    public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
5347
        parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
5348
    }
5349
 
5350
    /**
5351
     * Validate the setting value before storing it.
5352
     *
5353
     * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
5354
     * a comma separated list of such words.
5355
     *
5356
     * @param string $data Value inserted into the setting field.
5357
     * @return bool|string True if the value is OK, error string otherwise.
5358
     */
5359
    public function validate($data) {
5360
 
5361
        $parentcheck = parent::validate($data);
5362
 
5363
        if ($parentcheck !== true) {
5364
            return $parentcheck;
5365
        }
5366
 
5367
        if ($data === '') {
5368
            return true;
5369
        }
5370
 
5371
        $allcountries = get_string_manager()->get_list_of_countries(true);
5372
 
5373
        foreach (explode(',', $data) as $code) {
5374
            if (!isset($allcountries[$code])) {
5375
                return get_string('invalidcountrycode', 'core_error', $code);
5376
            }
5377
        }
5378
 
5379
        return true;
5380
    }
5381
}
5382
 
5383
 
5384
/**
5385
 * Selection of one of the recognised countries using the list
5386
 * returned by {@link get_list_of_countries()}.
5387
 *
5388
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5389
 */
5390
class admin_settings_country_select extends admin_setting_configselect {
5391
    protected $includeall;
5392
    public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
5393
        $this->includeall = $includeall;
5394
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
5395
    }
5396
 
5397
    /**
5398
     * Lazy-load the available choices for the select box
5399
     */
5400
    public function load_choices() {
5401
        global $CFG;
5402
        if (is_array($this->choices)) {
5403
            return true;
5404
        }
5405
        $this->choices = array_merge(
5406
                array('0' => get_string('choosedots')),
5407
                get_string_manager()->get_list_of_countries($this->includeall));
5408
        return true;
5409
    }
5410
}
5411
 
5412
 
5413
/**
5414
 * admin_setting_configselect for the default number of sections in a course,
5415
 * simply so we can lazy-load the choices.
5416
 *
5417
 * @copyright 2011 The Open University
5418
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5419
 */
5420
class admin_settings_num_course_sections extends admin_setting_configselect {
5421
    public function __construct($name, $visiblename, $description, $defaultsetting) {
5422
        parent::__construct($name, $visiblename, $description, $defaultsetting, array());
5423
    }
5424
 
5425
    /** Lazy-load the available choices for the select box */
5426
    public function load_choices() {
5427
        $max = get_config('moodlecourse', 'maxsections');
5428
        if (!isset($max) || !is_numeric($max)) {
5429
            $max = 52;
5430
        }
5431
        for ($i = 0; $i <= $max; $i++) {
5432
            $this->choices[$i] = "$i";
5433
        }
5434
        return true;
5435
    }
5436
}
5437
 
5438
 
5439
/**
5440
 * Course category selection
5441
 *
5442
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5443
 */
5444
class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete {
5445
    /**
5446
     * Calls parent::__construct with specific arguments
5447
     */
5448
    public function __construct($name, $visiblename, $description, $defaultsetting = 1) {
5449
        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null);
5450
    }
5451
 
5452
    /**
5453
     * Load the available choices for the select box
5454
     *
5455
     * @return bool
5456
     */
5457
    public function load_choices() {
5458
        if (is_array($this->choices)) {
5459
            return true;
5460
        }
5461
        $this->choices = core_course_category::make_categories_list('', 0, ' / ');
5462
        return true;
5463
    }
5464
}
5465
 
5466
 
5467
/**
5468
 * Special control for selecting days to backup
5469
 *
5470
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5471
 */
5472
class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
5473
    /**
5474
     * Calls parent::__construct with specific arguments
5475
     */
5476
    public function __construct() {
5477
        parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
5478
        $this->plugin = 'backup';
5479
    }
5480
 
5481
    /**
5482
     * Load the available choices for the select box
5483
     *
5484
     * @return bool Always returns true
5485
     */
5486
    public function load_choices() {
5487
        if (is_array($this->choices)) {
5488
            return true;
5489
        }
5490
        $this->choices = array();
5491
        $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5492
        foreach ($days as $day) {
5493
            $this->choices[$day] = get_string($day, 'calendar');
5494
        }
5495
        return true;
5496
    }
5497
}
5498
 
5499
/**
5500
 * Special setting for backup auto destination.
5501
 *
5502
 * @package    core
5503
 * @subpackage admin
5504
 * @copyright  2014 Frédéric Massart - FMCorz.net
5505
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5506
 */
5507
class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5508
 
5509
    /**
5510
     * Calls parent::__construct with specific arguments.
5511
     */
5512
    public function __construct() {
5513
        parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5514
    }
5515
 
5516
    /**
5517
     * Check if the directory must be set, depending on backup/backup_auto_storage.
5518
     *
5519
     * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5520
     * there will be conflicts if this validation happens before the other one.
5521
     *
5522
     * @param string $data Form data.
5523
     * @return string Empty when no errors.
5524
     */
5525
    public function write_setting($data) {
5526
        $storage = (int) get_config('backup', 'backup_auto_storage');
5527
        if ($storage !== 0) {
5528
            if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5529
                // The directory must exist and be writable.
5530
                return get_string('backuperrorinvaliddestination');
5531
            }
5532
        }
5533
        return parent::write_setting($data);
5534
    }
5535
}
5536
 
5537
 
5538
/**
5539
 * Special debug setting
5540
 *
5541
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5542
 */
5543
class admin_setting_special_debug extends admin_setting_configselect {
5544
    /**
5545
     * Calls parent::__construct with specific arguments
5546
     */
5547
    public function __construct() {
5548
        parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5549
    }
5550
 
5551
    /**
5552
     * Load the available choices for the select box
5553
     *
5554
     * @return bool
5555
     */
5556
    public function load_choices() {
5557
        if (is_array($this->choices)) {
5558
            return true;
5559
        }
5560
        $this->choices = array(DEBUG_NONE      => get_string('debugnone', 'admin'),
5561
            DEBUG_MINIMAL   => get_string('debugminimal', 'admin'),
5562
            DEBUG_NORMAL    => get_string('debugnormal', 'admin'),
5563
            DEBUG_ALL       => get_string('debugall', 'admin'),
5564
            DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5565
        return true;
5566
    }
5567
}
5568
 
5569
 
5570
/**
5571
 * Special admin control
5572
 *
5573
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5574
 */
5575
class admin_setting_special_calendar_weekend extends admin_setting {
5576
    /**
5577
     * Calls parent::__construct with specific arguments
5578
     */
5579
    public function __construct() {
5580
        $name = 'calendar_weekend';
5581
        $visiblename = get_string('calendar_weekend', 'admin');
5582
        $description = get_string('helpweekenddays', 'admin');
5583
        $default = array ('0', '6'); // Saturdays and Sundays
5584
        parent::__construct($name, $visiblename, $description, $default);
5585
    }
5586
 
5587
    /**
5588
     * Gets the current settings as an array
5589
     *
5590
     * @return mixed Null if none, else array of settings
5591
     */
5592
    public function get_setting() {
5593
        $result = $this->config_read($this->name);
5594
        if (is_null($result)) {
5595
            return NULL;
5596
        }
5597
        if ($result === '') {
5598
            return array();
5599
        }
5600
        $settings = array();
5601
        for ($i=0; $i<7; $i++) {
5602
            if ($result & (1 << $i)) {
5603
                $settings[] = $i;
5604
            }
5605
        }
5606
        return $settings;
5607
    }
5608
 
5609
    /**
5610
     * Save the new settings
5611
     *
5612
     * @param array $data Array of new settings
5613
     * @return string error message or empty string on success
5614
     */
5615
    public function write_setting($data) {
5616
        if (!is_array($data)) {
5617
            return '';
5618
        }
5619
        unset($data['xxxxx']);
5620
        $result = 0;
5621
        foreach($data as $index) {
5622
            $result |= 1 << $index;
5623
        }
5624
        return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5625
    }
5626
 
5627
    /**
5628
     * Return XHTML to display the control
5629
     *
5630
     * @param array $data array of selected days
5631
     * @param string $query
5632
     * @return string XHTML for display (field + wrapping div(s)
5633
     */
5634
    public function output_html($data, $query='') {
5635
        global $OUTPUT;
5636
 
5637
        // The order matters very much because of the implied numeric keys.
5638
        $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5639
        $context = (object) [
5640
            'name' => $this->get_full_name(),
5641
            'id' => $this->get_id(),
5642
            'days' => array_map(function($index) use ($days, $data) {
5643
                return [
5644
                    'index' => $index,
5645
                    'label' => get_string($days[$index], 'calendar'),
5646
                    'checked' => in_array($index, $data)
5647
                ];
5648
            }, array_keys($days))
5649
        ];
5650
 
5651
        $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5652
 
5653
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5654
 
5655
    }
5656
}
5657
 
5658
 
5659
/**
5660
 * Admin setting that allows a user to pick a behaviour.
5661
 *
5662
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5663
 */
5664
class admin_setting_question_behaviour extends admin_setting_configselect {
5665
    /**
5666
     * @param string $name name of config variable
5667
     * @param string $visiblename display name
5668
     * @param string $description description
5669
     * @param string $default default.
5670
     */
5671
    public function __construct($name, $visiblename, $description, $default) {
5672
        parent::__construct($name, $visiblename, $description, $default, null);
5673
    }
5674
 
5675
    /**
5676
     * Load list of behaviours as choices
5677
     * @return bool true => success, false => error.
5678
     */
5679
    public function load_choices() {
5680
        global $CFG;
5681
        require_once($CFG->dirroot . '/question/engine/lib.php');
5682
        $this->choices = question_engine::get_behaviour_options('');
5683
        return true;
5684
    }
5685
}
5686
 
5687
 
5688
/**
5689
 * Admin setting that allows a user to pick appropriate roles for something.
5690
 *
5691
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5692
 */
5693
class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5694
    /** @var array Array of capabilities which identify roles */
5695
    private $types;
5696
 
5697
    /**
5698
     * @param string $name Name of config variable
5699
     * @param string $visiblename Display name
5700
     * @param string $description Description
5701
     * @param array $types Array of archetypes which identify
5702
     *              roles that will be enabled by default.
5703
     */
5704
    public function __construct($name, $visiblename, $description, $types) {
5705
        parent::__construct($name, $visiblename, $description, NULL, NULL);
5706
        $this->types = $types;
5707
    }
5708
 
5709
    /**
5710
     * Load roles as choices
5711
     *
5712
     * @return bool true=>success, false=>error
5713
     */
5714
    public function load_choices() {
5715
        global $CFG, $DB;
5716
        if (during_initial_install()) {
5717
            return false;
5718
        }
5719
        if (is_array($this->choices)) {
5720
            return true;
5721
        }
5722
        if ($roles = get_all_roles()) {
5723
            $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5724
            return true;
5725
        } else {
5726
            return false;
5727
        }
5728
    }
5729
 
5730
    /**
5731
     * Return the default setting for this control
5732
     *
5733
     * @return ?array Array of default settings
5734
     */
5735
    public function get_defaultsetting() {
5736
        global $CFG;
5737
 
5738
        if (during_initial_install()) {
5739
            return null;
5740
        }
5741
        $result = array();
5742
        foreach($this->types as $archetype) {
5743
            if ($caproles = get_archetype_roles($archetype)) {
5744
                foreach ($caproles as $caprole) {
5745
                    $result[$caprole->id] = 1;
5746
                }
5747
            }
5748
        }
5749
        return $result;
5750
    }
5751
}
5752
 
5753
 
5754
/**
5755
 * Admin setting that is a list of installed filter plugins.
5756
 *
5757
 * @copyright 2015 The Open University
5758
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5759
 */
5760
class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5761
 
5762
    /**
5763
     * Constructor
5764
     *
5765
     * @param string $name unique ascii name, either 'mysetting' for settings
5766
     *      that in config, or 'myplugin/mysetting' for ones in config_plugins.
5767
     * @param string $visiblename localised name
5768
     * @param string $description localised long description
5769
     * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5770
     */
5771
    public function __construct($name, $visiblename, $description, $default) {
5772
        if (empty($default)) {
5773
            $default = array();
5774
        }
5775
        $this->load_choices();
5776
        foreach ($default as $plugin) {
5777
            if (!isset($this->choices[$plugin])) {
5778
                unset($default[$plugin]);
5779
            }
5780
        }
5781
        parent::__construct($name, $visiblename, $description, $default, null);
5782
    }
5783
 
5784
    public function load_choices() {
5785
        if (is_array($this->choices)) {
5786
            return true;
5787
        }
5788
        $this->choices = array();
5789
 
5790
        foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5791
            $this->choices[$plugin] = filter_get_name($plugin);
5792
        }
5793
        return true;
5794
    }
5795
}
5796
 
5797
 
5798
/**
5799
 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5800
 *
5801
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5802
 */
5803
class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5804
    /**
5805
     * Constructor
5806
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5807
     * @param string $visiblename localised
5808
     * @param string $description long localised info
5809
     * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5810
     * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5811
     * @param int $size default field size
5812
     */
5813
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5814
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5815
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5816
    }
5817
}
5818
 
5819
 
5820
/**
5821
 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5822
 *
5823
 * @copyright 2009 Petr Skoda (http://skodak.org)
5824
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5825
 */
5826
class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5827
 
5828
    /**
5829
     * Constructor
5830
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5831
     * @param string $visiblename localised
5832
     * @param string $description long localised info
5833
     * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5834
     * @param string $yes value used when checked
5835
     * @param string $no value used when not checked
5836
     */
5837
    public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5838
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5839
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5840
    }
5841
 
5842
}
5843
 
5844
 
5845
/**
5846
 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5847
 *
5848
 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5849
 *
5850
 * @copyright 2010 Sam Hemelryk
5851
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5852
 */
5853
class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5854
    /**
5855
     * Constructor
5856
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5857
     * @param string $visiblename localised
5858
     * @param string $description long localised info
5859
     * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5860
     * @param string $yes value used when checked
5861
     * @param string $no value used when not checked
5862
     */
5863
    public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5864
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5865
        $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5866
    }
5867
 
5868
}
5869
 
5870
/**
5871
 * Autocomplete as you type form element.
5872
 *
5873
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5874
 */
5875
class admin_setting_configselect_autocomplete extends admin_setting_configselect {
5876
    /** @var boolean $tags Should we allow typing new entries to the field? */
5877
    protected $tags = false;
5878
    /** @var string $ajax Name of an AMD module to send/process ajax requests. */
5879
    protected $ajax = '';
5880
    /** @var string $placeholder Placeholder text for an empty list. */
5881
    protected $placeholder = '';
5882
    /** @var bool $casesensitive Whether the search has to be case-sensitive. */
5883
    protected $casesensitive = false;
5884
    /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */
5885
    protected $showsuggestions = true;
5886
    /** @var string $noselectionstring String that is shown when there are no selections. */
5887
    protected $noselectionstring = '';
5888
 
5889
    /**
5890
     * Returns XHTML select field and wrapping div(s)
5891
     *
5892
     * @see output_select_html()
5893
     *
5894
     * @param string $data the option to show as selected
5895
     * @param string $query
5896
     * @return string XHTML field and wrapping div
5897
     */
5898
    public function output_html($data, $query='') {
5899
        global $PAGE;
5900
 
5901
        $html = parent::output_html($data, $query);
5902
 
5903
        if ($html === '') {
5904
            return $html;
5905
        }
5906
 
5907
        $this->placeholder = get_string('search');
5908
 
5909
        $params = array('#' . $this->get_id(), $this->tags, $this->ajax,
5910
            $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring);
5911
 
5912
        // Load autocomplete wrapper for select2 library.
5913
        $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params);
5914
 
5915
        return $html;
5916
    }
5917
}
5918
 
5919
/**
5920
 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5921
 *
5922
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5923
 */
5924
class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5925
    /**
5926
     * Calls parent::__construct with specific arguments
5927
     */
5928
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5929
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5930
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5931
    }
5932
 
5933
}
5934
 
5935
/**
5936
 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5937
 *
5938
 * @copyright 2017 Marina Glancy
5939
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5940
 */
5941
class admin_setting_configselect_with_lock extends admin_setting_configselect {
5942
    /**
5943
     * Constructor
5944
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5945
     *     or 'myplugin/mysetting' for ones in config_plugins.
5946
     * @param string $visiblename localised
5947
     * @param string $description long localised info
5948
     * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5949
     * @param array $choices array of $value=>$label for each selection
5950
     */
5951
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5952
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5953
        $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5954
    }
5955
}
5956
 
5957
 
5958
/**
5959
 * Graded roles in gradebook
5960
 *
5961
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5962
 */
5963
class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5964
    /**
5965
     * Calls parent::__construct with specific arguments
5966
     */
5967
    public function __construct() {
5968
        parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5969
            get_string('configgradebookroles', 'admin'),
5970
            array('student'));
5971
    }
5972
}
5973
 
5974
 
5975
/**
5976
 *
5977
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5978
 */
5979
class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5980
    /**
5981
     * Saves the new settings passed in $data
5982
     *
5983
     * @param string $data
5984
     * @return mixed string or Array
5985
     */
5986
    public function write_setting($data) {
5987
        global $CFG, $DB;
5988
 
5989
        $oldvalue  = $this->config_read($this->name);
5990
        $return    = parent::write_setting($data);
5991
        $newvalue  = $this->config_read($this->name);
5992
 
5993
        if ($oldvalue !== $newvalue) {
5994
        // force full regrading
5995
            $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
5996
        }
5997
 
5998
        return $return;
5999
    }
6000
}
6001
 
6002
 
6003
/**
6004
 * Which roles to show on course description page
6005
 *
6006
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6007
 */
6008
class admin_setting_special_coursecontact extends admin_setting_pickroles {
6009
    /**
6010
     * Calls parent::__construct with specific arguments
6011
     */
6012
    public function __construct() {
6013
        parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
6014
            get_string('coursecontact_desc', 'admin'),
6015
            array('editingteacher'));
6016
        $this->set_updatedcallback(function (){
6017
            cache::make('core', 'coursecontacts')->purge();
6018
        });
6019
    }
6020
}
6021
 
6022
 
6023
/**
6024
 *
6025
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6026
 */
6027
class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
6028
    /**
6029
     * Calls parent::__construct with specific arguments
6030
     */
6031
    public function __construct() {
6032
        parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
6033
            get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
6034
    }
6035
 
6036
    /**
6037
     * Old syntax of class constructor. Deprecated in PHP7.
6038
     *
6039
     * @deprecated since Moodle 3.1
6040
     */
6041
    public function admin_setting_special_gradelimiting() {
6042
        debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
6043
        self::__construct();
6044
    }
6045
 
6046
    /**
6047
     * Force site regrading
6048
     */
6049
    function regrade_all() {
6050
        global $CFG;
6051
        require_once("$CFG->libdir/gradelib.php");
6052
        grade_force_site_regrading();
6053
    }
6054
 
6055
    /**
6056
     * Saves the new settings
6057
     *
6058
     * @param mixed $data
6059
     * @return string empty string or error message
6060
     */
6061
    function write_setting($data) {
6062
        $previous = $this->get_setting();
6063
 
6064
        if ($previous === null) {
6065
            if ($data) {
6066
                $this->regrade_all();
6067
            }
6068
        } else {
6069
            if ($data != $previous) {
6070
                $this->regrade_all();
6071
            }
6072
        }
6073
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6074
    }
6075
 
6076
}
6077
 
6078
/**
6079
 * Special setting for $CFG->grade_minmaxtouse.
6080
 *
6081
 * @package    core
6082
 * @copyright  2015 Frédéric Massart - FMCorz.net
6083
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6084
 */
6085
class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
6086
 
6087
    /**
6088
     * Constructor.
6089
     */
6090
    public function __construct() {
6091
        parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
6092
            new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
6093
            array(
6094
                GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
6095
                GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
6096
            )
6097
        );
6098
    }
6099
 
6100
    /**
6101
     * Saves the new setting.
6102
     *
6103
     * @param mixed $data
6104
     * @return string empty string or error message
6105
     */
6106
    function write_setting($data) {
6107
        global $CFG;
6108
 
6109
        $previous = $this->get_setting();
6110
        $result = parent::write_setting($data);
6111
 
6112
        // If saved and the value has changed.
6113
        if (empty($result) && $previous != $data) {
6114
            require_once($CFG->libdir . '/gradelib.php');
6115
            grade_force_site_regrading();
6116
        }
6117
 
6118
        return $result;
6119
    }
6120
 
6121
}
6122
 
6123
 
6124
/**
6125
 * Primary grade export plugin - has state tracking.
6126
 *
6127
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6128
 */
6129
class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
6130
    /**
6131
     * Calls parent::__construct with specific arguments
6132
     */
6133
    public function __construct() {
6134
        parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
6135
            get_string('configgradeexport', 'admin'), array(), NULL);
6136
    }
6137
 
6138
    /**
6139
     * Load the available choices for the multicheckbox
6140
     *
6141
     * @return bool always returns true
6142
     */
6143
    public function load_choices() {
6144
        if (is_array($this->choices)) {
6145
            return true;
6146
        }
6147
        $this->choices = array();
6148
 
6149
        if ($plugins = core_component::get_plugin_list('gradeexport')) {
6150
            foreach($plugins as $plugin => $unused) {
6151
                $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
6152
            }
6153
        }
6154
        return true;
6155
    }
6156
}
6157
 
6158
/**
6159
 * A setting for the default grade export plugin.
6160
 *
6161
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6162
 */
6163
class admin_setting_special_gradeexportdefault extends admin_setting_configselect {
6164
    /**
6165
     * Calls parent::__construct with specific arguments
6166
     */
6167
    public function __construct() {
6168
        parent::__construct('gradeexport_default', get_string('gradeexportdefault', 'admin'),
6169
                get_string('configgradeexportdefault', 'admin'), null, null);
6170
    }
6171
 
6172
    /**
6173
     * Returns the default option
6174
     *
6175
     * @return string default option
6176
     */
6177
    public function get_defaultsetting() {
6178
        $this->load_choices();
6179
        $defaultsetting = parent::get_defaultsetting();
6180
        if (array_key_exists($defaultsetting, $this->choices)) {
6181
            return $defaultsetting;
6182
        } else {
6183
            return array_key_first($this->choices);
6184
        }
6185
    }
6186
 
6187
    /**
6188
     * Load the available choices for the configselect
6189
     *
6190
     * @return bool always returns true
6191
     */
6192
    public function load_choices() {
6193
        if (is_array($this->choices)) {
6194
            return true;
6195
        }
6196
        $this->choices = [];
6197
 
6198
        if ($plugins = core_component::get_plugin_list('gradeexport')) {
6199
            foreach ($plugins as $plugin => $unused) {
6200
                $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
6201
            }
6202
        }
6203
        return true;
6204
    }
6205
}
6206
 
6207
/**
6208
 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
6209
 *
6210
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6211
 */
6212
class admin_setting_special_gradepointdefault extends admin_setting_configtext {
6213
    /**
6214
     * Config gradepointmax constructor
6215
     *
6216
     * @param string $name Overidden by "gradepointmax"
6217
     * @param string $visiblename Overridden by "gradepointmax" language string.
6218
     * @param string $description Overridden by "gradepointmax_help" language string.
6219
     * @param string $defaultsetting Not used, overridden by 100.
6220
     * @param mixed $paramtype Overridden by PARAM_INT.
6221
     * @param int $size Overridden by 5.
6222
     */
6223
    public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
6224
        $name = 'gradepointdefault';
6225
        $visiblename = get_string('gradepointdefault', 'grades');
6226
        $description = get_string('gradepointdefault_help', 'grades');
6227
        $defaultsetting = 100;
6228
        $paramtype = PARAM_INT;
6229
        $size = 5;
6230
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6231
    }
6232
 
6233
    /**
6234
     * Validate data before storage
6235
     * @param string $data The submitted data
6236
     * @return bool|string true if ok, string if error found
6237
     */
6238
    public function validate($data) {
6239
        global $CFG;
6240
        if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
6241
            return true;
6242
        } else {
6243
            return get_string('gradepointdefault_validateerror', 'grades');
6244
        }
6245
    }
6246
}
6247
 
6248
 
6249
/**
6250
 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
6251
 *
6252
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6253
 */
6254
class admin_setting_special_gradepointmax extends admin_setting_configtext {
6255
 
6256
    /**
6257
     * Config gradepointmax constructor
6258
     *
6259
     * @param string $name Overidden by "gradepointmax"
6260
     * @param string $visiblename Overridden by "gradepointmax" language string.
6261
     * @param string $description Overridden by "gradepointmax_help" language string.
6262
     * @param string $defaultsetting Not used, overridden by 100.
6263
     * @param mixed $paramtype Overridden by PARAM_INT.
6264
     * @param int $size Overridden by 5.
6265
     */
6266
    public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
6267
        $name = 'gradepointmax';
6268
        $visiblename = get_string('gradepointmax', 'grades');
6269
        $description = get_string('gradepointmax_help', 'grades');
6270
        $defaultsetting = 100;
6271
        $paramtype = PARAM_INT;
6272
        $size = 5;
6273
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6274
    }
6275
 
6276
    /**
6277
     * Save the selected setting
6278
     *
6279
     * @param string $data The selected site
6280
     * @return string empty string or error message
6281
     */
6282
    public function write_setting($data) {
6283
        if ($data === '') {
6284
            $data = (int)$this->defaultsetting;
6285
        } else {
6286
            $data = $data;
6287
        }
6288
        return parent::write_setting($data);
6289
    }
6290
 
6291
    /**
6292
     * Validate data before storage
6293
     * @param string $data The submitted data
6294
     * @return bool|string true if ok, string if error found
6295
     */
6296
    public function validate($data) {
6297
        if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
6298
            return true;
6299
        } else {
6300
            return get_string('gradepointmax_validateerror', 'grades');
6301
        }
6302
    }
6303
 
6304
    /**
6305
     * Return an XHTML string for the setting
6306
     * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6307
     * @param string $query search query to be highlighted
6308
     * @return string XHTML to display control
6309
     */
6310
    public function output_html($data, $query = '') {
6311
        global $OUTPUT;
6312
 
6313
        $default = $this->get_defaultsetting();
6314
        $context = (object) [
6315
            'size' => $this->size,
6316
            'id' => $this->get_id(),
6317
            'name' => $this->get_full_name(),
6318
            'value' => $data,
6319
            'attributes' => [
6320
                'maxlength' => 5
6321
            ],
6322
            'forceltr' => $this->get_force_ltr()
6323
        ];
6324
        $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
6325
 
6326
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
6327
    }
6328
}
6329
 
6330
 
6331
/**
6332
 * Grade category settings
6333
 *
6334
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6335
 */
6336
class admin_setting_gradecat_combo extends admin_setting {
6337
 
6338
    /** @var array Array of choices value=>label. */
6339
    public $choices;
6340
 
6341
    /**
6342
     * Sets choices and calls parent::__construct with passed arguments
6343
     * @param string $name
6344
     * @param string $visiblename
6345
     * @param string $description
6346
     * @param mixed $defaultsetting string or array depending on implementation
6347
     * @param array $choices An array of choices for the control
6348
     */
6349
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
6350
        $this->choices = $choices;
6351
        parent::__construct($name, $visiblename, $description, $defaultsetting);
6352
    }
6353
 
6354
    /**
6355
     * Return the current setting(s) array
6356
     *
6357
     * @return ?array Array of value=>xx, forced=>xx
6358
     */
6359
    public function get_setting() {
6360
        global $CFG;
6361
 
6362
        $value = $this->config_read($this->name);
6363
        $flag  = $this->config_read($this->name.'_flag');
6364
 
6365
        if (is_null($value) or is_null($flag)) {
6366
            return NULL;
6367
        }
6368
 
6369
        // Bitwise operation is still required, in cases where unused 'advanced' flag is still set.
6370
        $flag   = (int)$flag;
6371
        $forced = (bool)(1 & $flag); // First bit.
6372
 
6373
        return array('value' => $value, 'forced' => $forced);
6374
    }
6375
 
6376
    /**
6377
     * Save the new settings passed in $data
6378
     *
6379
     * @todo Add vartype handling to ensure $data is array
6380
     * @param array $data Associative array of value=>xx, forced=>xx
6381
     * @return string empty or error message
6382
     */
6383
    public function write_setting($data) {
6384
        global $CFG;
6385
 
6386
        $value = $data['value'];
6387
        $forced = empty($data['forced']) ? 0 : 1;
6388
 
6389
        if (!in_array($value, array_keys($this->choices))) {
6390
            return 'Error setting ';
6391
        }
6392
 
6393
        $oldvalue = $this->config_read($this->name);
6394
        $oldflag = (int)$this->config_read($this->name.'_flag');
6395
        $oldforced = (1 & $oldflag); // first bit
6396
 
6397
        $result1 = $this->config_write($this->name, $value);
6398
        $result2 = $this->config_write($this->name.'_flag', $forced);
6399
 
6400
        // force regrade if needed
6401
        if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
6402
            require_once($CFG->libdir.'/gradelib.php');
6403
            grade_category::updated_forced_settings();
6404
        }
6405
 
6406
        if ($result1 and $result2) {
6407
            return '';
6408
        } else {
6409
            return get_string('errorsetting', 'admin');
6410
        }
6411
    }
6412
 
6413
    /**
6414
     * Return XHTML to display the field and wrapping div
6415
     *
6416
     * @todo Add vartype handling to ensure $data is array
6417
     * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6418
     * @param string $query
6419
     * @return string XHTML to display control
6420
     */
6421
    public function output_html($data, $query='') {
6422
        global $OUTPUT;
6423
 
6424
        $value  = $data['value'];
6425
 
6426
        $default = $this->get_defaultsetting();
6427
        if (!is_null($default)) {
6428
            $defaultinfo = array();
6429
            if (isset($this->choices[$default['value']])) {
6430
                $defaultinfo[] = $this->choices[$default['value']];
6431
            }
6432
            if (!empty($default['forced'])) {
6433
                $defaultinfo[] = get_string('force');
6434
            }
6435
            $defaultinfo = implode(', ', $defaultinfo);
6436
 
6437
        } else {
6438
            $defaultinfo = NULL;
6439
        }
6440
 
6441
        $options = $this->choices;
6442
        $context = (object) [
6443
            'id' => $this->get_id(),
6444
            'name' => $this->get_full_name(),
6445
            'forced' => !empty($data['forced']),
6446
            'options' => array_map(function($option) use ($options, $value) {
6447
                return [
6448
                    'value' => $option,
6449
                    'name' => $options[$option],
6450
                    'selected' => $option == $value
6451
                ];
6452
            }, array_keys($options)),
6453
        ];
6454
 
6455
        $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
6456
 
6457
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
6458
    }
6459
}
6460
 
6461
 
6462
/**
6463
 * Selection of grade report in user profiles
6464
 *
6465
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6466
 */
6467
class admin_setting_grade_profilereport extends admin_setting_configselect {
6468
    /**
6469
     * Calls parent::__construct with specific arguments
6470
     */
6471
    public function __construct() {
6472
        parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
6473
    }
6474
 
6475
    /**
6476
     * Loads an array of choices for the configselect control
6477
     *
6478
     * @return bool always return true
6479
     */
6480
    public function load_choices() {
6481
        if (is_array($this->choices)) {
6482
            return true;
6483
        }
6484
        $this->choices = array();
6485
 
6486
        global $CFG;
6487
        require_once($CFG->libdir.'/gradelib.php');
6488
 
6489
        foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6490
            if (file_exists($plugindir.'/lib.php')) {
6491
                require_once($plugindir.'/lib.php');
6492
                $functionname = 'grade_report_'.$plugin.'_profilereport';
6493
                if (function_exists($functionname)) {
6494
                    $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
6495
                }
6496
            }
6497
        }
6498
        return true;
6499
    }
6500
}
6501
 
6502
/**
6503
 * Provides a selection of grade reports to be used for "grades".
6504
 *
6505
 * @copyright 2015 Adrian Greeve <adrian@moodle.com>
6506
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6507
 */
6508
class admin_setting_my_grades_report extends admin_setting_configselect {
6509
 
6510
    /**
6511
     * Calls parent::__construct with specific arguments.
6512
     */
6513
    public function __construct() {
6514
        parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
6515
                new lang_string('mygrades_desc', 'grades'), 'overview', null);
6516
    }
6517
 
6518
    /**
6519
     * Loads an array of choices for the configselect control.
6520
     *
6521
     * @return bool always returns true.
6522
     */
6523
    public function load_choices() {
6524
        global $CFG; // Remove this line and behold the horror of behat test failures!
6525
        $this->choices = array();
6526
        foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6527
            if (file_exists($plugindir . '/lib.php')) {
6528
                require_once($plugindir . '/lib.php');
6529
                // Check to see if the class exists. Check the correct plugin convention first.
6530
                if (class_exists('gradereport_' . $plugin)) {
6531
                    $classname = 'gradereport_' . $plugin;
6532
                } else if (class_exists('grade_report_' . $plugin)) {
6533
                    // We are using the old plugin naming convention.
6534
                    $classname = 'grade_report_' . $plugin;
6535
                } else {
6536
                    continue;
6537
                }
6538
                if ($classname::supports_mygrades()) {
6539
                    $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
6540
                }
6541
            }
6542
        }
6543
        // Add an option to specify an external url.
6544
        $this->choices['external'] = get_string('externalurl', 'grades');
6545
        return true;
6546
    }
6547
}
6548
 
6549
/**
6550
 * Special class for register auth selection
6551
 *
6552
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6553
 */
6554
class admin_setting_special_registerauth extends admin_setting_configselect {
6555
    /**
6556
     * Calls parent::__construct with specific arguments
6557
     */
6558
    public function __construct() {
6559
        parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
6560
    }
6561
 
6562
    /**
6563
     * Returns the default option
6564
     *
6565
     * @return string empty or default option
6566
     */
6567
    public function get_defaultsetting() {
6568
        $this->load_choices();
6569
        $defaultsetting = parent::get_defaultsetting();
6570
        if (array_key_exists($defaultsetting, $this->choices)) {
6571
            return $defaultsetting;
6572
        } else {
6573
            return '';
6574
        }
6575
    }
6576
 
6577
    /**
6578
     * Loads the possible choices for the array
6579
     *
6580
     * @return bool always returns true
6581
     */
6582
    public function load_choices() {
6583
        global $CFG;
6584
 
6585
        if (is_array($this->choices)) {
6586
            return true;
6587
        }
6588
        $this->choices = array();
6589
        $this->choices[''] = get_string('disable');
6590
 
6591
        $authsenabled = get_enabled_auth_plugins();
6592
 
6593
        foreach ($authsenabled as $auth) {
6594
            $authplugin = get_auth_plugin($auth);
6595
            if (!$authplugin->can_signup()) {
6596
                continue;
6597
            }
6598
            // Get the auth title (from core or own auth lang files)
6599
            $authtitle = $authplugin->get_title();
6600
            $this->choices[$auth] = $authtitle;
6601
        }
6602
        return true;
6603
    }
6604
}
6605
 
6606
 
6607
/**
6608
 * General plugins manager
6609
 */
6610
class admin_page_pluginsoverview extends admin_externalpage {
6611
 
6612
    /**
6613
     * Sets basic information about the external page
6614
     */
6615
    public function __construct() {
6616
        global $CFG;
6617
        parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6618
            "$CFG->wwwroot/$CFG->admin/plugins.php");
6619
    }
6620
}
6621
 
6622
/**
6623
 * Module manage page
6624
 *
6625
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6626
 */
6627
class admin_page_managemods extends admin_externalpage {
6628
    /**
6629
     * Calls parent::__construct with specific arguments
6630
     */
6631
    public function __construct() {
6632
        global $CFG;
6633
        parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6634
    }
6635
 
6636
    /**
6637
     * Try to find the specified module
6638
     *
6639
     * @param string $query The module to search for
6640
     * @return array
6641
     */
6642
    public function search($query) {
6643
        global $CFG, $DB;
6644
        if ($result = parent::search($query)) {
6645
            return $result;
6646
        }
6647
 
6648
        $found = false;
6649
        if ($modules = $DB->get_records('modules')) {
6650
            foreach ($modules as $module) {
6651
                if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6652
                    continue;
6653
                }
6654
                if (strpos($module->name, $query) !== false) {
6655
                    $found = true;
6656
                    break;
6657
                }
6658
                $strmodulename = get_string('modulename', $module->name);
6659
                if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6660
                    $found = true;
6661
                    break;
6662
                }
6663
            }
6664
        }
6665
        if ($found) {
6666
            $result = new stdClass();
6667
            $result->page     = $this;
6668
            $result->settings = array();
6669
            return array($this->name => $result);
6670
        } else {
6671
            return array();
6672
        }
6673
    }
6674
}
6675
 
6676
 
6677
/**
6678
 * Special class for enrol plugins management.
6679
 *
6680
 * @copyright 2010 Petr Skoda {@link http://skodak.org}
6681
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6682
 */
6683
class admin_setting_manageenrols extends admin_setting {
6684
    /**
6685
     * Calls parent::__construct with specific arguments
6686
     */
6687
    public function __construct() {
6688
        $this->nosave = true;
6689
        parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6690
    }
6691
 
6692
    /**
6693
     * Always returns true, does nothing
6694
     *
6695
     * @return true
6696
     */
6697
    public function get_setting() {
6698
        return true;
6699
    }
6700
 
6701
    /**
6702
     * Always returns true, does nothing
6703
     *
6704
     * @return true
6705
     */
6706
    public function get_defaultsetting() {
6707
        return true;
6708
    }
6709
 
6710
    /**
6711
     * Always returns '', does not write anything
6712
     *
6713
     * @return string Always returns ''
6714
     */
6715
    public function write_setting($data) {
6716
    // do not write any setting
6717
        return '';
6718
    }
6719
 
6720
    /**
6721
     * Checks if $query is one of the available enrol plugins
6722
     *
6723
     * @param string $query The string to search for
6724
     * @return bool Returns true if found, false if not
6725
     */
6726
    public function is_related($query) {
6727
        if (parent::is_related($query)) {
6728
            return true;
6729
        }
6730
 
6731
        $query = core_text::strtolower($query);
6732
        $enrols = enrol_get_plugins(false);
6733
        foreach ($enrols as $name=>$enrol) {
6734
            $localised = get_string('pluginname', 'enrol_'.$name);
6735
            if (strpos(core_text::strtolower($name), $query) !== false) {
6736
                return true;
6737
            }
6738
            if (strpos(core_text::strtolower($localised), $query) !== false) {
6739
                return true;
6740
            }
6741
        }
6742
        return false;
6743
    }
6744
 
6745
    /**
6746
     * Builds the XHTML to display the control
6747
     *
6748
     * @param string $data Unused
6749
     * @param string $query
6750
     * @return string
6751
     */
6752
    public function output_html($data, $query='') {
6753
        global $CFG, $OUTPUT, $DB, $PAGE;
6754
 
6755
        // Display strings.
6756
        $strup        = get_string('up');
6757
        $strdown      = get_string('down');
6758
        $strsettings  = get_string('settings');
6759
        $strenable    = get_string('enable');
6760
        $strdisable   = get_string('disable');
6761
        $struninstall = get_string('uninstallplugin', 'core_admin');
6762
        $strusage     = get_string('enrolusage', 'enrol');
6763
        $strversion   = get_string('version');
6764
        $strtest      = get_string('testsettings', 'core_enrol');
6765
 
6766
        $pluginmanager = core_plugin_manager::instance();
6767
 
6768
        $enrols_available = enrol_get_plugins(false);
6769
        $active_enrols    = enrol_get_plugins(true);
6770
 
6771
        $allenrols = array();
6772
        foreach ($active_enrols as $key=>$enrol) {
6773
            $allenrols[$key] = true;
6774
        }
6775
        foreach ($enrols_available as $key=>$enrol) {
6776
            $allenrols[$key] = true;
6777
        }
6778
        // Now find all borked plugins and at least allow then to uninstall.
6779
        $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6780
        foreach ($condidates as $candidate) {
6781
            if (empty($allenrols[$candidate])) {
6782
                $allenrols[$candidate] = true;
6783
            }
6784
        }
6785
 
6786
        $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6787
        $return .= $OUTPUT->box_start('generalbox enrolsui');
6788
 
6789
        $table = new html_table();
6790
        $table->head  = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6791
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6792
        $table->id = 'courseenrolmentplugins';
6793
        $table->attributes['class'] = 'admintable generaltable';
6794
        $table->data  = array();
6795
 
6796
        // Iterate through enrol plugins and add to the display table.
6797
        $updowncount = 1;
6798
        $enrolcount = count($active_enrols);
6799
        $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6800
        $printed = array();
6801
        foreach($allenrols as $enrol => $unused) {
6802
            $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6803
            $version = get_config('enrol_'.$enrol, 'version');
6804
            if ($version === false) {
6805
                $version = '';
6806
            }
6807
 
6808
            if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6809
                $name = get_string('pluginname', 'enrol_'.$enrol);
6810
            } else {
6811
                $name = $enrol;
6812
            }
6813
            // Usage.
6814
            $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6815
            $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6816
            $usage = "$ci / $cp";
6817
 
6818
            // Hide/show links.
6819
            $class = '';
6820
            if (isset($active_enrols[$enrol])) {
6821
                $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6822
                $hideshow = "<a href=\"$aurl\">";
6823
                $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6824
                $enabled = true;
6825
                $displayname = $name;
6826
            } else if (isset($enrols_available[$enrol])) {
6827
                $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6828
                $hideshow = "<a href=\"$aurl\">";
6829
                $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6830
                $enabled = false;
6831
                $displayname = $name;
6832
                $class = 'dimmed_text';
6833
            } else {
6834
                $hideshow = '';
6835
                $enabled = false;
6836
                $displayname = '<span class="notifyproblem">'.$name.'</span>';
6837
            }
6838
            if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6839
                $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6840
            } else {
6841
                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6842
            }
6843
 
6844
            // Up/down link (only if enrol is enabled).
6845
            $updown = '';
6846
            if ($enabled) {
6847
                if ($updowncount > 1) {
6848
                    $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6849
                    $updown .= "<a href=\"$aurl\">";
6850
                    $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6851
                } else {
6852
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
6853
                }
6854
                if ($updowncount < $enrolcount) {
6855
                    $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6856
                    $updown .= "<a href=\"$aurl\">";
6857
                    $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6858
                } else {
6859
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
6860
                }
6861
                ++$updowncount;
6862
            }
6863
 
6864
            // Add settings link.
6865
            if (!$version) {
6866
                $settings = '';
6867
            } else if ($surl = $plugininfo->get_settings_url()) {
6868
                $settings = html_writer::link($surl, $strsettings);
6869
            } else {
6870
                $settings = '';
6871
            }
6872
 
6873
            // Add uninstall info.
6874
            $uninstall = '';
6875
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6876
                $uninstall = html_writer::link($uninstallurl, $struninstall);
6877
            }
6878
 
6879
            $test = '';
6880
            if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6881
                $testsettingsurl = new moodle_url('/enrol/test_settings.php', ['enrol' => $enrol]);
6882
                $test = html_writer::link($testsettingsurl, $strtest);
6883
            }
6884
 
6885
            // Add a row to the table.
6886
            $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6887
            if ($class) {
6888
                $row->attributes['class'] = $class;
6889
            }
6890
            $table->data[] = $row;
6891
 
6892
            $printed[$enrol] = true;
6893
        }
6894
 
6895
        $return .= html_writer::table($table);
6896
        $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6897
        $return .= $OUTPUT->box_end();
6898
        return highlight($query, $return);
6899
    }
6900
}
6901
 
6902
 
6903
/**
6904
 * Blocks manage page
6905
 *
6906
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6907
 */
6908
class admin_page_manageblocks extends admin_externalpage {
6909
    /**
6910
     * Calls parent::__construct with specific arguments
6911
     */
6912
    public function __construct() {
6913
        global $CFG;
6914
        parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6915
    }
6916
 
6917
    /**
6918
     * Search for a specific block
6919
     *
6920
     * @param string $query The string to search for
6921
     * @return array
6922
     */
6923
    public function search($query) {
6924
        global $CFG, $DB;
6925
        if ($result = parent::search($query)) {
6926
            return $result;
6927
        }
6928
 
6929
        $found = false;
6930
        if ($blocks = $DB->get_records('block')) {
6931
            foreach ($blocks as $block) {
6932
                if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6933
                    continue;
6934
                }
6935
                if (strpos($block->name, $query) !== false) {
6936
                    $found = true;
6937
                    break;
6938
                }
6939
                $strblockname = get_string('pluginname', 'block_'.$block->name);
6940
                if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6941
                    $found = true;
6942
                    break;
6943
                }
6944
            }
6945
        }
6946
        if ($found) {
6947
            $result = new stdClass();
6948
            $result->page     = $this;
6949
            $result->settings = array();
6950
            return array($this->name => $result);
6951
        } else {
6952
            return array();
6953
        }
6954
    }
6955
}
6956
 
6957
/**
6958
 * Message outputs configuration
6959
 *
6960
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6961
 */
6962
class admin_page_managemessageoutputs extends admin_externalpage {
6963
    /**
6964
     * Calls parent::__construct with specific arguments
6965
     */
6966
    public function __construct() {
6967
        global $CFG;
6968
        parent::__construct('managemessageoutputs',
6969
            get_string('defaultmessageoutputs', 'message'),
6970
            new moodle_url('/admin/message.php')
6971
        );
6972
    }
6973
 
6974
    /**
6975
     * Search for a specific message processor
6976
     *
6977
     * @param string $query The string to search for
6978
     * @return array
6979
     */
6980
    public function search($query) {
6981
        global $CFG, $DB;
6982
        if ($result = parent::search($query)) {
6983
            return $result;
6984
        }
6985
 
6986
        $found = false;
6987
        if ($processors = get_message_processors()) {
6988
            foreach ($processors as $processor) {
6989
                if (!$processor->available) {
6990
                    continue;
6991
                }
6992
                if (strpos($processor->name, $query) !== false) {
6993
                    $found = true;
6994
                    break;
6995
                }
6996
                $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
6997
                if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
6998
                    $found = true;
6999
                    break;
7000
                }
7001
            }
7002
        }
7003
        if ($found) {
7004
            $result = new stdClass();
7005
            $result->page     = $this;
7006
            $result->settings = array();
7007
            return array($this->name => $result);
7008
        } else {
7009
            return array();
7010
        }
7011
    }
7012
}
7013
 
7014
/**
7015
 * Manage question behaviours page
7016
 *
7017
 * @copyright  2011 The Open University
7018
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7019
 */
7020
class admin_page_manageqbehaviours extends admin_externalpage {
7021
    /**
7022
     * Constructor
7023
     */
7024
    public function __construct() {
7025
        global $CFG;
7026
        parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
7027
                new moodle_url('/admin/qbehaviours.php'));
7028
    }
7029
 
7030
    /**
7031
     * Search question behaviours for the specified string
7032
     *
7033
     * @param string $query The string to search for in question behaviours
7034
     * @return array
7035
     */
7036
    public function search($query) {
7037
        global $CFG;
7038
        if ($result = parent::search($query)) {
7039
            return $result;
7040
        }
7041
 
7042
        $found = false;
7043
        require_once($CFG->dirroot . '/question/engine/lib.php');
7044
        foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
7045
            if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
7046
                    $query) !== false) {
7047
                $found = true;
7048
                break;
7049
            }
7050
        }
7051
        if ($found) {
7052
            $result = new stdClass();
7053
            $result->page     = $this;
7054
            $result->settings = array();
7055
            return array($this->name => $result);
7056
        } else {
7057
            return array();
7058
        }
7059
    }
7060
}
7061
 
7062
 
7063
/**
7064
 * Question type manage page
7065
 *
7066
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7067
 */
7068
class admin_page_manageqtypes extends admin_externalpage {
7069
    /**
7070
     * Calls parent::__construct with specific arguments
7071
     */
7072
    public function __construct() {
7073
        global $CFG;
7074
        parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
7075
                new moodle_url('/admin/qtypes.php'));
7076
    }
7077
 
7078
    /**
7079
     * Search question types for the specified string
7080
     *
7081
     * @param string $query The string to search for in question types
7082
     * @return array
7083
     */
7084
    public function search($query) {
7085
        global $CFG;
7086
        if ($result = parent::search($query)) {
7087
            return $result;
7088
        }
7089
 
7090
        $found = false;
7091
        require_once($CFG->dirroot . '/question/engine/bank.php');
7092
        foreach (question_bank::get_all_qtypes() as $qtype) {
7093
            if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
7094
                $found = true;
7095
                break;
7096
            }
7097
        }
7098
        if ($found) {
7099
            $result = new stdClass();
7100
            $result->page     = $this;
7101
            $result->settings = array();
7102
            return array($this->name => $result);
7103
        } else {
7104
            return array();
7105
        }
7106
    }
7107
}
7108
 
7109
 
7110
class admin_page_manageportfolios extends admin_externalpage {
7111
    /**
7112
     * Calls parent::__construct with specific arguments
7113
     */
7114
    public function __construct() {
7115
        global $CFG;
7116
        parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
7117
                "$CFG->wwwroot/$CFG->admin/portfolio.php");
7118
    }
7119
 
7120
    /**
7121
     * Searches page for the specified string.
7122
     * @param string $query The string to search for
7123
     * @return array
7124
     */
7125
    public function search($query) {
7126
        global $CFG;
7127
        if ($result = parent::search($query)) {
7128
            return $result;
7129
        }
7130
 
7131
        $found = false;
7132
        $portfolios = core_component::get_plugin_list('portfolio');
7133
        foreach ($portfolios as $p => $dir) {
7134
            if (strpos($p, $query) !== false) {
7135
                $found = true;
7136
                break;
7137
            }
7138
        }
7139
        if (!$found) {
7140
            foreach (portfolio_instances(false, false) as $instance) {
7141
                $title = $instance->get('name');
7142
                if (strpos(core_text::strtolower($title), $query) !== false) {
7143
                    $found = true;
7144
                    break;
7145
                }
7146
            }
7147
        }
7148
 
7149
        if ($found) {
7150
            $result = new stdClass();
7151
            $result->page     = $this;
7152
            $result->settings = array();
7153
            return array($this->name => $result);
7154
        } else {
7155
            return array();
7156
        }
7157
    }
7158
}
7159
 
7160
 
7161
class admin_page_managerepositories extends admin_externalpage {
7162
    /**
7163
     * Calls parent::__construct with specific arguments
7164
     */
7165
    public function __construct() {
7166
        global $CFG;
7167
        parent::__construct('managerepositories', get_string('manage',
7168
                'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
7169
    }
7170
 
7171
    /**
7172
     * Searches page for the specified string.
7173
     * @param string $query The string to search for
7174
     * @return array
7175
     */
7176
    public function search($query) {
7177
        global $CFG;
7178
        if ($result = parent::search($query)) {
7179
            return $result;
7180
        }
7181
 
7182
        $found = false;
7183
        $repositories= core_component::get_plugin_list('repository');
7184
        foreach ($repositories as $p => $dir) {
7185
            if (strpos($p, $query) !== false) {
7186
                $found = true;
7187
                break;
7188
            }
7189
        }
7190
        if (!$found) {
7191
            foreach (repository::get_types() as $instance) {
7192
                $title = $instance->get_typename();
7193
                if (strpos(core_text::strtolower($title), $query) !== false) {
7194
                    $found = true;
7195
                    break;
7196
                }
7197
            }
7198
        }
7199
 
7200
        if ($found) {
7201
            $result = new stdClass();
7202
            $result->page     = $this;
7203
            $result->settings = array();
7204
            return array($this->name => $result);
7205
        } else {
7206
            return array();
7207
        }
7208
    }
7209
}
7210
 
7211
 
7212
/**
7213
 * Special class for authentication administration.
7214
 *
7215
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7216
 */
7217
class admin_setting_manageauths extends admin_setting {
7218
    /**
7219
     * Calls parent::__construct with specific arguments
7220
     */
7221
    public function __construct() {
7222
        $this->nosave = true;
7223
        parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
7224
    }
7225
 
7226
    /**
7227
     * Always returns true
7228
     *
7229
     * @return true
7230
     */
7231
    public function get_setting() {
7232
        return true;
7233
    }
7234
 
7235
    /**
7236
     * Always returns true
7237
     *
7238
     * @return true
7239
     */
7240
    public function get_defaultsetting() {
7241
        return true;
7242
    }
7243
 
7244
    /**
7245
     * Always returns '' and doesn't write anything
7246
     *
7247
     * @return string Always returns ''
7248
     */
7249
    public function write_setting($data) {
7250
    // do not write any setting
7251
        return '';
7252
    }
7253
 
7254
    /**
7255
     * Search to find if Query is related to auth plugin
7256
     *
7257
     * @param string $query The string to search for
7258
     * @return bool true for related false for not
7259
     */
7260
    public function is_related($query) {
7261
        if (parent::is_related($query)) {
7262
            return true;
7263
        }
7264
 
7265
        $authsavailable = core_component::get_plugin_list('auth');
7266
        foreach ($authsavailable as $auth => $dir) {
7267
            if (strpos($auth, $query) !== false) {
7268
                return true;
7269
            }
7270
            $authplugin = get_auth_plugin($auth);
7271
            $authtitle = $authplugin->get_title();
7272
            if (strpos(core_text::strtolower($authtitle), $query) !== false) {
7273
                return true;
7274
            }
7275
        }
7276
        return false;
7277
    }
7278
 
7279
    /**
7280
     * Return XHTML to display control
7281
     *
7282
     * @param mixed $data Unused
7283
     * @param string $query
7284
     * @return string highlight
7285
     */
7286
    public function output_html($data, $query='') {
7287
        global $CFG, $OUTPUT, $DB;
7288
 
7289
        // display strings
7290
        $txt = get_strings(array('authenticationplugins', 'users', 'administration',
7291
            'settings', 'edit', 'name', 'enable', 'disable',
7292
            'up', 'down', 'none', 'users'));
7293
        $txt->updown = "$txt->up/$txt->down";
7294
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7295
        $txt->testsettings = get_string('testsettings', 'core_auth');
7296
 
7297
        $authsavailable = core_component::get_plugin_list('auth');
7298
        get_enabled_auth_plugins(true); // fix the list of enabled auths
7299
        if (empty($CFG->auth)) {
7300
            $authsenabled = array();
7301
        } else {
7302
            $authsenabled = explode(',', $CFG->auth);
7303
        }
7304
 
7305
        // construct the display array, with enabled auth plugins at the top, in order
7306
        $displayauths = array();
7307
        $registrationauths = array();
7308
        $registrationauths[''] = $txt->disable;
7309
        $authplugins = array();
7310
        foreach ($authsenabled as $auth) {
7311
            $authplugin = get_auth_plugin($auth);
7312
            $authplugins[$auth] = $authplugin;
7313
            /// Get the auth title (from core or own auth lang files)
7314
            $authtitle = $authplugin->get_title();
7315
            /// Apply titles
7316
            $displayauths[$auth] = $authtitle;
7317
            if ($authplugin->can_signup()) {
7318
                $registrationauths[$auth] = $authtitle;
7319
            }
7320
        }
7321
 
7322
        foreach ($authsavailable as $auth => $dir) {
7323
            if (array_key_exists($auth, $displayauths)) {
7324
                continue; //already in the list
7325
            }
7326
            $authplugin = get_auth_plugin($auth);
7327
            $authplugins[$auth] = $authplugin;
7328
            /// Get the auth title (from core or own auth lang files)
7329
            $authtitle = $authplugin->get_title();
7330
            /// Apply titles
7331
            $displayauths[$auth] = $authtitle;
7332
            if ($authplugin->can_signup()) {
7333
                $registrationauths[$auth] = $authtitle;
7334
            }
7335
        }
7336
 
7337
        $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
7338
        $return .= $OUTPUT->box_start('generalbox authsui');
7339
 
7340
        $table = new html_table();
7341
        $table->head  = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
7342
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7343
        $table->data  = array();
7344
        $table->attributes['class'] = 'admintable generaltable';
7345
        $table->id = 'manageauthtable';
7346
 
7347
        //add always enabled plugins first
7348
        $displayname = $displayauths['manual'];
7349
        $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
7350
        $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
7351
        $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
7352
        $displayname = $displayauths['nologin'];
7353
        $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
7354
        $table->data[] = array($displayname, $usercount, '', '', '', '', '');
7355
 
7356
 
7357
        // iterate through auth plugins and add to the display table
7358
        $updowncount = 1;
7359
        $authcount = count($authsenabled);
7360
        $url = "auth.php?sesskey=" . sesskey();
7361
        foreach ($displayauths as $auth => $name) {
7362
            if ($auth == 'manual' or $auth == 'nologin') {
7363
                continue;
7364
            }
7365
            $class = '';
7366
            // hide/show link
7367
            if (in_array($auth, $authsenabled)) {
7368
                $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
7369
                $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7370
                $enabled = true;
7371
                $displayname = $name;
7372
            }
7373
            else {
7374
                $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
7375
                $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7376
                $enabled = false;
7377
                $displayname = $name;
7378
                $class = 'dimmed_text';
7379
            }
7380
 
7381
            $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
7382
 
7383
            // up/down link (only if auth is enabled)
7384
            $updown = '';
7385
            if ($enabled) {
7386
                if ($updowncount > 1) {
7387
                    $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
7388
                    $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7389
                }
7390
                else {
7391
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
7392
                }
7393
                if ($updowncount < $authcount) {
7394
                    $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
7395
                    $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7396
                }
7397
                else {
7398
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
7399
                }
7400
                ++ $updowncount;
7401
            }
7402
 
7403
            // settings link
7404
            if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
7405
                $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
7406
            } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
7407
                throw new \coding_exception('config.html is no longer supported, please use settings.php instead.');
7408
            } else {
7409
                $settings = '';
7410
            }
7411
 
7412
            // Uninstall link.
7413
            $uninstall = '';
7414
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
7415
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7416
            }
7417
 
7418
            $test = '';
7419
            if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
7420
                $testurl = new moodle_url('/auth/test_settings.php', ['auth' => $auth]);
7421
                $test = html_writer::link($testurl, $txt->testsettings);
7422
            }
7423
 
7424
            // Add a row to the table.
7425
            $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
7426
            if ($class) {
7427
                $row->attributes['class'] = $class;
7428
            }
7429
            $table->data[] = $row;
7430
        }
7431
        $return .= html_writer::table($table);
7432
        $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
7433
        $return .= $OUTPUT->box_end();
7434
        return highlight($query, $return);
7435
    }
7436
}
7437
 
7438
/**
7439
 * Special class for antiviruses administration.
7440
 *
7441
 * @copyright  2015 Ruslan Kabalin, Lancaster University.
7442
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7443
 */
7444
class admin_setting_manageantiviruses extends admin_setting {
7445
    /**
7446
     * Calls parent::__construct with specific arguments
7447
     */
7448
    public function __construct() {
7449
        $this->nosave = true;
7450
        parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
7451
    }
7452
 
7453
    /**
7454
     * Always returns true, does nothing
7455
     *
7456
     * @return true
7457
     */
7458
    public function get_setting() {
7459
        return true;
7460
    }
7461
 
7462
    /**
7463
     * Always returns true, does nothing
7464
     *
7465
     * @return true
7466
     */
7467
    public function get_defaultsetting() {
7468
        return true;
7469
    }
7470
 
7471
    /**
7472
     * Always returns '', does not write anything
7473
     *
7474
     * @param string $data Unused
7475
     * @return string Always returns ''
7476
     */
7477
    public function write_setting($data) {
7478
        // Do not write any setting.
7479
        return '';
7480
    }
7481
 
7482
    /**
7483
     * Checks if $query is one of the available editors
7484
     *
7485
     * @param string $query The string to search for
7486
     * @return bool Returns true if found, false if not
7487
     */
7488
    public function is_related($query) {
7489
        if (parent::is_related($query)) {
7490
            return true;
7491
        }
7492
 
7493
        $antivirusesavailable = \core\antivirus\manager::get_available();
7494
        foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
7495
            if (strpos($antivirus, $query) !== false) {
7496
                return true;
7497
            }
7498
            if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
7499
                return true;
7500
            }
7501
        }
7502
        return false;
7503
    }
7504
 
7505
    /**
7506
     * Builds the XHTML to display the control
7507
     *
7508
     * @param string $data Unused
7509
     * @param string $query
7510
     * @return string
7511
     */
7512
    public function output_html($data, $query='') {
7513
        global $CFG, $OUTPUT;
7514
 
7515
        // Display strings.
7516
        $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7517
            'up', 'down', 'none'));
7518
        $struninstall = get_string('uninstallplugin', 'core_admin');
7519
 
7520
        $txt->updown = "$txt->up/$txt->down";
7521
 
7522
        $antivirusesavailable = \core\antivirus\manager::get_available();
7523
        $activeantiviruses = explode(',', $CFG->antiviruses);
7524
 
7525
        $activeantiviruses = array_reverse($activeantiviruses);
7526
        foreach ($activeantiviruses as $key => $antivirus) {
7527
            if (empty($antivirusesavailable[$antivirus])) {
7528
                unset($activeantiviruses[$key]);
7529
            } else {
7530
                $name = $antivirusesavailable[$antivirus];
7531
                unset($antivirusesavailable[$antivirus]);
7532
                $antivirusesavailable[$antivirus] = $name;
7533
            }
7534
        }
7535
        $antivirusesavailable = array_reverse($antivirusesavailable, true);
7536
        $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
7537
        $return .= $OUTPUT->box_start('generalbox antivirusesui');
7538
 
7539
        $table = new html_table();
7540
        $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7541
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7542
        $table->id = 'antivirusmanagement';
7543
        $table->attributes['class'] = 'admintable generaltable';
7544
        $table->data  = array();
7545
 
7546
        // Iterate through auth plugins and add to the display table.
7547
        $updowncount = 1;
7548
        $antiviruscount = count($activeantiviruses);
7549
        $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
7550
        foreach ($antivirusesavailable as $antivirus => $name) {
7551
            // Hide/show link.
7552
            $class = '';
7553
            if (in_array($antivirus, $activeantiviruses)) {
7554
                $hideshowurl = $baseurl;
7555
                $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
7556
                $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
7557
                $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7558
                $enabled = true;
7559
                $displayname = $name;
7560
            } else {
7561
                $hideshowurl = $baseurl;
7562
                $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
7563
                $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
7564
                $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7565
                $enabled = false;
7566
                $displayname = $name;
7567
                $class = 'dimmed_text';
7568
            }
7569
 
7570
            // Up/down link.
7571
            $updown = '';
7572
            if ($enabled) {
7573
                if ($updowncount > 1) {
7574
                    $updownurl = $baseurl;
7575
                    $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
7576
                    $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
7577
                    $updown = html_writer::link($updownurl, $updownimg);
7578
                } else {
7579
                    $updownimg = $OUTPUT->spacer();
7580
                }
7581
                if ($updowncount < $antiviruscount) {
7582
                    $updownurl = $baseurl;
7583
                    $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
7584
                    $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
7585
                    $updown = html_writer::link($updownurl, $updownimg);
7586
                } else {
7587
                    $updownimg = $OUTPUT->spacer();
7588
                }
7589
                ++ $updowncount;
7590
            }
7591
 
7592
            // Settings link.
7593
            if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
7594
                $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
7595
                $settings = html_writer::link($eurl, $txt->settings);
7596
            } else {
7597
                $settings = '';
7598
            }
7599
 
7600
            $uninstall = '';
7601
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
7602
                $uninstall = html_writer::link($uninstallurl, $struninstall);
7603
            }
7604
 
7605
            // Add a row to the table.
7606
            $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7607
            if ($class) {
7608
                $row->attributes['class'] = $class;
7609
            }
7610
            $table->data[] = $row;
7611
        }
7612
        $return .= html_writer::table($table);
7613
        $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
7614
        $return .= $OUTPUT->box_end();
7615
        return highlight($query, $return);
7616
    }
7617
}
7618
 
7619
/**
7620
 * Course formats manager. Allows to enable/disable formats and jump to settings
7621
 */
7622
class admin_setting_manageformats extends admin_setting {
7623
 
7624
    /**
7625
     * Calls parent::__construct with specific arguments
7626
     */
7627
    public function __construct() {
7628
        $this->nosave = true;
7629
        parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7630
    }
7631
 
7632
    /**
7633
     * Always returns true
7634
     *
7635
     * @return true
7636
     */
7637
    public function get_setting() {
7638
        return true;
7639
    }
7640
 
7641
    /**
7642
     * Always returns true
7643
     *
7644
     * @return true
7645
     */
7646
    public function get_defaultsetting() {
7647
        return true;
7648
    }
7649
 
7650
    /**
7651
     * Always returns '' and doesn't write anything
7652
     *
7653
     * @param mixed $data string or array, must not be NULL
7654
     * @return string Always returns ''
7655
     */
7656
    public function write_setting($data) {
7657
        // do not write any setting
7658
        return '';
7659
    }
7660
 
7661
    /**
7662
     * Search to find if Query is related to format plugin
7663
     *
7664
     * @param string $query The string to search for
7665
     * @return bool true for related false for not
7666
     */
7667
    public function is_related($query) {
7668
        if (parent::is_related($query)) {
7669
            return true;
7670
        }
7671
        $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7672
        foreach ($formats as $format) {
7673
            if (strpos($format->component, $query) !== false ||
7674
                    strpos(core_text::strtolower($format->displayname), $query) !== false) {
7675
                return true;
7676
            }
7677
        }
7678
        return false;
7679
    }
7680
 
7681
    /**
7682
     * Return XHTML to display control
7683
     *
7684
     * @param mixed $data Unused
7685
     * @param string $query
7686
     * @return string highlight
7687
     */
7688
    public function output_html($data, $query='') {
7689
        global $CFG, $OUTPUT;
7690
        $return = '';
7691
        $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7692
        $return .= $OUTPUT->box_start('generalbox formatsui');
7693
 
7694
        $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7695
 
7696
        // display strings
7697
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7698
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7699
        $txt->updown = "$txt->up/$txt->down";
7700
 
7701
        $table = new html_table();
7702
        $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7703
        $table->align = array('left', 'center', 'center', 'center', 'center');
7704
        $table->attributes['class'] = 'manageformattable generaltable admintable';
7705
        $table->data  = array();
7706
 
7707
        $cnt = 0;
7708
        $defaultformat = get_config('moodlecourse', 'format');
7709
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7710
        foreach ($formats as $format) {
7711
            $url = new moodle_url('/admin/courseformats.php',
7712
                    array('sesskey' => sesskey(), 'format' => $format->name));
7713
            $isdefault = '';
7714
            $class = '';
7715
            if ($format->is_enabled()) {
7716
                $strformatname = $format->displayname;
7717
                if ($defaultformat === $format->name) {
7718
                    $hideshow = $txt->default;
7719
                } else {
7720
                    $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7721
                            $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7722
                }
7723
            } else {
7724
                $strformatname = $format->displayname;
7725
                $class = 'dimmed_text';
7726
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7727
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7728
            }
7729
            $updown = '';
7730
            if ($cnt) {
7731
                $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7732
                    $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7733
            } else {
7734
                $updown .= $spacer;
7735
            }
7736
            if ($cnt < count($formats) - 1) {
7737
                $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7738
                    $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7739
            } else {
7740
                $updown .= $spacer;
7741
            }
7742
            $cnt++;
7743
            $settings = '';
7744
            if ($format->get_settings_url()) {
7745
                $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7746
            }
7747
            $uninstall = '';
7748
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7749
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7750
            }
7751
            $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7752
            if ($class) {
7753
                $row->attributes['class'] = $class;
7754
            }
7755
            $table->data[] = $row;
7756
        }
7757
        $return .= html_writer::table($table);
7758
        $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7759
        $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7760
        $return .= $OUTPUT->box_end();
7761
        return highlight($query, $return);
7762
    }
7763
}
7764
 
7765
/**
7766
 * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7767
 *
7768
 * @package    core
7769
 * @copyright  2018 Toni Barbera
7770
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7771
 */
7772
class admin_setting_managecustomfields extends admin_setting {
7773
 
7774
    /**
7775
     * Calls parent::__construct with specific arguments
7776
     */
7777
    public function __construct() {
7778
        $this->nosave = true;
7779
        parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7780
    }
7781
 
7782
    /**
7783
     * Always returns true
7784
     *
7785
     * @return true
7786
     */
7787
    public function get_setting() {
7788
        return true;
7789
    }
7790
 
7791
    /**
7792
     * Always returns true
7793
     *
7794
     * @return true
7795
     */
7796
    public function get_defaultsetting() {
7797
        return true;
7798
    }
7799
 
7800
    /**
7801
     * Always returns '' and doesn't write anything
7802
     *
7803
     * @param mixed $data string or array, must not be NULL
7804
     * @return string Always returns ''
7805
     */
7806
    public function write_setting($data) {
7807
        // Do not write any setting.
7808
        return '';
7809
    }
7810
 
7811
    /**
7812
     * Search to find if Query is related to format plugin
7813
     *
7814
     * @param string $query The string to search for
7815
     * @return bool true for related false for not
7816
     */
7817
    public function is_related($query) {
7818
        if (parent::is_related($query)) {
7819
            return true;
7820
        }
7821
        $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7822
        foreach ($formats as $format) {
7823
            if (strpos($format->component, $query) !== false ||
7824
                    strpos(core_text::strtolower($format->displayname), $query) !== false) {
7825
                return true;
7826
            }
7827
        }
7828
        return false;
7829
    }
7830
 
7831
    /**
7832
     * Return XHTML to display control
7833
     *
7834
     * @param mixed $data Unused
7835
     * @param string $query
7836
     * @return string highlight
7837
     */
7838
    public function output_html($data, $query='') {
7839
        global $CFG, $OUTPUT;
7840
        $return = '';
7841
        $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7842
        $return .= $OUTPUT->box_start('generalbox customfieldsui');
7843
 
7844
        $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7845
 
7846
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7847
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7848
        $txt->updown = "$txt->up/$txt->down";
7849
 
7850
        $table = new html_table();
7851
        $table->head  = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7852
        $table->align = array('left', 'center', 'center', 'center');
7853
        $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7854
        $table->data  = array();
7855
 
7856
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7857
        foreach ($fields as $field) {
7858
            $url = new moodle_url('/admin/customfields.php',
7859
                    array('sesskey' => sesskey(), 'field' => $field->name));
7860
 
7861
            if ($field->is_enabled()) {
7862
                $strfieldname = $field->displayname;
7863
                $class = '';
7864
                $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7865
                        $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7866
            } else {
7867
                $strfieldname = $field->displayname;
7868
                $class = 'dimmed_text';
7869
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7870
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7871
            }
7872
            $settings = '';
7873
            if ($field->get_settings_url()) {
7874
                $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7875
            }
7876
            $uninstall = '';
7877
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7878
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7879
            }
7880
            $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7881
            $row->attributes['class'] = $class;
7882
            $table->data[] = $row;
7883
        }
7884
        $return .= html_writer::table($table);
7885
        $return .= $OUTPUT->box_end();
7886
        return highlight($query, $return);
7887
    }
7888
}
7889
 
7890
/**
7891
 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7892
 *
7893
 * @copyright  2016 Brendan Heywood (brendan@catalyst-au.net)
7894
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7895
 */
7896
class admin_setting_managedataformats extends admin_setting {
7897
 
7898
    /**
7899
     * Calls parent::__construct with specific arguments
7900
     */
7901
    public function __construct() {
7902
        $this->nosave = true;
7903
        parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7904
    }
7905
 
7906
    /**
7907
     * Always returns true
7908
     *
7909
     * @return true
7910
     */
7911
    public function get_setting() {
7912
        return true;
7913
    }
7914
 
7915
    /**
7916
     * Always returns true
7917
     *
7918
     * @return true
7919
     */
7920
    public function get_defaultsetting() {
7921
        return true;
7922
    }
7923
 
7924
    /**
7925
     * Always returns '' and doesn't write anything
7926
     *
7927
     * @param mixed $data string or array, must not be NULL
7928
     * @return string Always returns ''
7929
     */
7930
    public function write_setting($data) {
7931
        // Do not write any setting.
7932
        return '';
7933
    }
7934
 
7935
    /**
7936
     * Search to find if Query is related to format plugin
7937
     *
7938
     * @param string $query The string to search for
7939
     * @return bool true for related false for not
7940
     */
7941
    public function is_related($query) {
7942
        if (parent::is_related($query)) {
7943
            return true;
7944
        }
7945
        $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7946
        foreach ($formats as $format) {
7947
            if (strpos($format->component, $query) !== false ||
7948
                    strpos(core_text::strtolower($format->displayname), $query) !== false) {
7949
                return true;
7950
            }
7951
        }
7952
        return false;
7953
    }
7954
 
7955
    /**
7956
     * Return XHTML to display control
7957
     *
7958
     * @param mixed $data Unused
7959
     * @param string $query
7960
     * @return string highlight
7961
     */
7962
    public function output_html($data, $query='') {
7963
        global $CFG, $OUTPUT;
7964
        $return = '';
7965
 
7966
        $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7967
 
7968
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7969
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7970
        $txt->updown = "$txt->up/$txt->down";
7971
 
7972
        $table = new html_table();
7973
        $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7974
        $table->align = array('left', 'center', 'center', 'center', 'center');
7975
        $table->attributes['class'] = 'manageformattable generaltable admintable';
7976
        $table->data  = array();
7977
 
7978
        $cnt = 0;
7979
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7980
        $totalenabled = 0;
7981
        foreach ($formats as $format) {
7982
            if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7983
                $totalenabled++;
7984
            }
7985
        }
7986
        foreach ($formats as $format) {
7987
            $status = $format->get_status();
7988
            $url = new moodle_url('/admin/dataformats.php',
7989
                    array('sesskey' => sesskey(), 'name' => $format->name));
7990
 
7991
            $class = '';
7992
            if ($format->is_enabled()) {
7993
                $strformatname = $format->displayname;
7994
                if ($totalenabled == 1&& $format->is_enabled()) {
7995
                    $hideshow = '';
7996
                } else {
7997
                    $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7998
                        $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7999
                }
8000
            } else {
8001
                $class = 'dimmed_text';
8002
                $strformatname = $format->displayname;
8003
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8004
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8005
            }
8006
 
8007
            $updown = '';
8008
            if ($cnt) {
8009
                $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8010
                    $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8011
            } else {
8012
                $updown .= $spacer;
8013
            }
8014
            if ($cnt < count($formats) - 1) {
8015
                $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8016
                    $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8017
            } else {
8018
                $updown .= $spacer;
8019
            }
8020
 
8021
            $uninstall = '';
8022
            if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8023
                $uninstall = get_string('status_missing', 'core_plugin');
8024
            } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8025
                $uninstall = get_string('status_new', 'core_plugin');
8026
            } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
8027
                if ($totalenabled != 1 || !$format->is_enabled()) {
8028
                    $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8029
                }
8030
            }
8031
 
8032
            $settings = '';
8033
            if ($format->get_settings_url()) {
8034
                $settings = html_writer::link($format->get_settings_url(), $txt->settings);
8035
            }
8036
 
8037
            $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
8038
            if ($class) {
8039
                $row->attributes['class'] = $class;
8040
            }
8041
            $table->data[] = $row;
8042
            $cnt++;
8043
        }
8044
        $return .= html_writer::table($table);
8045
        return highlight($query, $return);
8046
    }
8047
}
8048
 
8049
/**
8050
 * Special class for filter administration.
8051
 *
8052
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8053
 */
8054
class admin_page_managefilters extends admin_externalpage {
8055
    /**
8056
     * Calls parent::__construct with specific arguments
8057
     */
8058
    public function __construct() {
8059
        global $CFG;
8060
        parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
8061
    }
8062
 
8063
    /**
8064
     * Searches all installed filters for specified filter
8065
     *
8066
     * @param string $query The filter(string) to search for
8067
     * @param string $query
8068
     */
8069
    public function search($query) {
8070
        global $CFG;
8071
        if ($result = parent::search($query)) {
8072
            return $result;
8073
        }
8074
 
8075
        $found = false;
8076
        $filternames = filter_get_all_installed();
8077
        foreach ($filternames as $path => $strfiltername) {
8078
            if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
8079
                $found = true;
8080
                break;
8081
            }
8082
            if (strpos($path, $query) !== false) {
8083
                $found = true;
8084
                break;
8085
            }
8086
        }
8087
 
8088
        if ($found) {
8089
            $result = new stdClass;
8090
            $result->page = $this;
8091
            $result->settings = array();
8092
            return array($this->name => $result);
8093
        } else {
8094
            return array();
8095
        }
8096
    }
8097
}
8098
 
8099
/**
8100
 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8101
 * Requires a get_rank method on the plugininfo class for sorting.
8102
 *
8103
 * @copyright 2017 Damyon Wiese
8104
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8105
 */
8106
abstract class admin_setting_manage_plugins extends admin_setting {
8107
 
8108
    /**
8109
     * Get the admin settings section name (just a unique string)
8110
     *
8111
     * @return string
8112
     */
8113
    public function get_section_name() {
8114
        return 'manage' . $this->get_plugin_type() . 'plugins';
8115
    }
8116
 
8117
    /**
8118
     * Get the admin settings section title (use get_string).
8119
     *
8120
     * @return string
8121
     */
8122
    abstract public function get_section_title();
8123
 
8124
    /**
8125
     * Get the type of plugin to manage.
8126
     *
8127
     * @return string
8128
     */
8129
    abstract public function get_plugin_type();
8130
 
8131
    /**
8132
     * Get the name of the second column.
8133
     *
8134
     * @return string
8135
     */
8136
    public function get_info_column_name() {
8137
        return '';
8138
    }
8139
 
8140
    /**
8141
     * Get the type of plugin to manage.
8142
     *
8143
     * @param \core\plugininfo\base $plugininfo The plugin info class.
8144
     * @return string
8145
     */
8146
    abstract public function get_info_column($plugininfo);
8147
 
8148
    /**
8149
     * Calls parent::__construct with specific arguments
8150
     */
8151
    public function __construct() {
8152
        $this->nosave = true;
8153
        parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
8154
    }
8155
 
8156
    /**
8157
     * Always returns true, does nothing
8158
     *
8159
     * @return true
8160
     */
8161
    public function get_setting() {
8162
        return true;
8163
    }
8164
 
8165
    /**
8166
     * Always returns true, does nothing
8167
     *
8168
     * @return true
8169
     */
8170
    public function get_defaultsetting() {
8171
        return true;
8172
    }
8173
 
8174
    /**
8175
     * Always returns '', does not write anything
8176
     *
8177
     * @param mixed $data
8178
     * @return string Always returns ''
8179
     */
8180
    public function write_setting($data) {
8181
        // Do not write any setting.
8182
        return '';
8183
    }
8184
 
8185
    /**
8186
     * Checks if $query is one of the available plugins of this type
8187
     *
8188
     * @param string $query The string to search for
8189
     * @return bool Returns true if found, false if not
8190
     */
8191
    public function is_related($query) {
8192
        if (parent::is_related($query)) {
8193
            return true;
8194
        }
8195
 
8196
        $query = core_text::strtolower($query);
8197
        $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
8198
        foreach ($plugins as $name => $plugin) {
8199
            $localised = $plugin->displayname;
8200
            if (strpos(core_text::strtolower($name), $query) !== false) {
8201
                return true;
8202
            }
8203
            if (strpos(core_text::strtolower($localised), $query) !== false) {
8204
                return true;
8205
            }
8206
        }
8207
        return false;
8208
    }
8209
 
8210
    /**
8211
     * The URL for the management page for this plugintype.
8212
     *
8213
     * @return moodle_url
8214
     */
8215
    protected function get_manage_url() {
8216
        return new moodle_url('/admin/updatesetting.php');
8217
    }
8218
 
8219
    /**
8220
     * Builds the HTML to display the control.
8221
     *
8222
     * @param string $data Unused
8223
     * @param string $query
8224
     * @return string
8225
     */
8226
    public function output_html($data, $query = '') {
8227
        global $CFG, $OUTPUT, $DB, $PAGE;
8228
 
8229
        $context = (object) [
8230
            'manageurl' => new moodle_url($this->get_manage_url(), [
8231
                    'type' => $this->get_plugin_type(),
8232
                    'sesskey' => sesskey(),
8233
                ]),
8234
            'infocolumnname' => $this->get_info_column_name(),
8235
            'plugins' => [],
8236
        ];
8237
 
8238
        $pluginmanager = core_plugin_manager::instance();
8239
        $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
8240
        $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
8241
        $plugins = array_merge($enabled, $allplugins);
8242
        foreach ($plugins as $key => $plugin) {
8243
            $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
8244
 
8245
            $pluginkey = (object) [
8246
                'plugin' => $plugin->displayname,
8247
                'enabled' => $plugin->is_enabled(),
8248
                'togglelink' => '',
8249
                'moveuplink' => '',
8250
                'movedownlink' => '',
8251
                'settingslink' => $plugin->get_settings_url(),
8252
                'uninstalllink' => '',
8253
                'info' => '',
8254
            ];
8255
 
8256
            // Enable/Disable link.
8257
            $togglelink = new moodle_url($pluginlink);
8258
            if ($plugin->is_enabled()) {
8259
                $toggletarget = false;
8260
                $togglelink->param('action', 'disable');
8261
 
8262
                if (count($context->plugins)) {
8263
                    // This is not the first plugin.
8264
                    $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
8265
                }
8266
 
8267
                if (count($enabled) > count($context->plugins) + 1) {
8268
                    // This is not the last plugin.
8269
                    $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
8270
                }
8271
 
8272
                $pluginkey->info = $this->get_info_column($plugin);
8273
            } else {
8274
                $toggletarget = true;
8275
                $togglelink->param('action', 'enable');
8276
            }
8277
 
8278
            $pluginkey->toggletarget = $toggletarget;
8279
            $pluginkey->togglelink = $togglelink;
8280
 
8281
            $frankenstyle = $plugin->type . '_' . $plugin->name;
8282
            if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
8283
                // This plugin supports uninstallation.
8284
                $pluginkey->uninstalllink = $uninstalllink;
8285
            }
8286
 
8287
            if (!empty($this->get_info_column_name())) {
8288
                // This plugintype has an info column.
8289
                $pluginkey->info = $this->get_info_column($plugin);
8290
            }
8291
 
8292
            $context->plugins[] = $pluginkey;
8293
        }
8294
 
8295
        $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8296
        return highlight($query, $str);
8297
    }
8298
}
8299
 
8300
/**
8301
 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8302
 * Requires a get_rank method on the plugininfo class for sorting.
8303
 *
8304
 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8305
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8306
 */
8307
class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8308
    public function get_section_title() {
8309
        return get_string('type_fileconverter_plural', 'plugin');
8310
    }
8311
 
8312
    public function get_plugin_type() {
8313
        return 'fileconverter';
8314
    }
8315
 
8316
    public function get_info_column_name() {
8317
        return get_string('supportedconversions', 'plugin');
8318
    }
8319
 
8320
    public function get_info_column($plugininfo) {
8321
        return $plugininfo->get_supported_conversions();
8322
    }
8323
}
8324
 
8325
/**
8326
 * Special class for media player plugins management.
8327
 *
8328
 * @copyright 2016 Marina Glancy
8329
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8330
 */
8331
class admin_setting_managemediaplayers extends admin_setting {
8332
    /**
8333
     * Calls parent::__construct with specific arguments
8334
     */
8335
    public function __construct() {
8336
        $this->nosave = true;
8337
        parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8338
    }
8339
 
8340
    /**
8341
     * Always returns true, does nothing
8342
     *
8343
     * @return true
8344
     */
8345
    public function get_setting() {
8346
        return true;
8347
    }
8348
 
8349
    /**
8350
     * Always returns true, does nothing
8351
     *
8352
     * @return true
8353
     */
8354
    public function get_defaultsetting() {
8355
        return true;
8356
    }
8357
 
8358
    /**
8359
     * Always returns '', does not write anything
8360
     *
8361
     * @param mixed $data
8362
     * @return string Always returns ''
8363
     */
8364
    public function write_setting($data) {
8365
        // Do not write any setting.
8366
        return '';
8367
    }
8368
 
8369
    /**
8370
     * Checks if $query is one of the available enrol plugins
8371
     *
8372
     * @param string $query The string to search for
8373
     * @return bool Returns true if found, false if not
8374
     */
8375
    public function is_related($query) {
8376
        if (parent::is_related($query)) {
8377
            return true;
8378
        }
8379
 
8380
        $query = core_text::strtolower($query);
8381
        $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8382
        foreach ($plugins as $name => $plugin) {
8383
            $localised = $plugin->displayname;
8384
            if (strpos(core_text::strtolower($name), $query) !== false) {
8385
                return true;
8386
            }
8387
            if (strpos(core_text::strtolower($localised), $query) !== false) {
8388
                return true;
8389
            }
8390
        }
8391
        return false;
8392
    }
8393
 
8394
    /**
8395
     * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8396
     * @return \core\plugininfo\media[]
8397
     */
8398
    protected function get_sorted_plugins() {
8399
        $pluginmanager = core_plugin_manager::instance();
8400
 
8401
        $plugins = $pluginmanager->get_plugins_of_type('media');
8402
        $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8403
 
8404
        // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8405
        \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8406
 
8407
        $order = array_values($enabledplugins);
8408
        $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8409
 
8410
        $sortedplugins = array();
8411
        foreach ($order as $name) {
8412
            $sortedplugins[$name] = $plugins[$name];
8413
        }
8414
 
8415
        return $sortedplugins;
8416
    }
8417
 
8418
    /**
8419
     * Builds the XHTML to display the control
8420
     *
8421
     * @param string $data Unused
8422
     * @param string $query
8423
     * @return string
8424
     */
8425
    public function output_html($data, $query='') {
8426
        global $CFG, $OUTPUT, $DB, $PAGE;
8427
 
8428
        // Display strings.
8429
        $strup        = get_string('up');
8430
        $strdown      = get_string('down');
8431
        $strsettings  = get_string('settings');
8432
        $strenable    = get_string('enable');
8433
        $strdisable   = get_string('disable');
8434
        $struninstall = get_string('uninstallplugin', 'core_admin');
8435
        $strversion   = get_string('version');
8436
        $strname      = get_string('name');
8437
        $strsupports  = get_string('supports', 'core_media');
8438
 
8439
        $pluginmanager = core_plugin_manager::instance();
8440
 
8441
        $plugins = $this->get_sorted_plugins();
8442
        $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8443
 
8444
        $return = $OUTPUT->box_start('generalbox mediaplayersui');
8445
 
8446
        $table = new html_table();
8447
        $table->head  = array($strname, $strsupports, $strversion,
8448
            $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8449
        $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8450
            'centeralign', 'centeralign', 'centeralign', 'centeralign');
8451
        $table->id = 'mediaplayerplugins';
8452
        $table->attributes['class'] = 'admintable generaltable';
8453
        $table->data  = array();
8454
 
8455
        // Iterate through media plugins and add to the display table.
8456
        $updowncount = 1;
8457
        $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8458
        $printed = array();
8459
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8460
 
8461
        $usedextensions = [];
8462
        foreach ($plugins as $name => $plugin) {
8463
            $url->param('media', $name);
8464
            /** @var \core\plugininfo\media $plugininfo */
8465
            $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8466
            $version = $plugininfo->versiondb;
8467
            $supports = $plugininfo->supports($usedextensions);
8468
 
8469
            // Hide/show links.
8470
            $class = '';
8471
            if (!$plugininfo->is_installed_and_upgraded()) {
8472
                $hideshow = '';
8473
                $enabled = false;
8474
                $displayname = '<span class="notifyproblem">'.$name.'</span>';
8475
            } else {
8476
                $enabled = $plugininfo->is_enabled();
8477
                if ($enabled) {
8478
                    $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8479
                        $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8480
                } else {
8481
                    $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8482
                        $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8483
                    $class = 'dimmed_text';
8484
                }
8485
                $displayname = $plugin->displayname;
8486
                if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8487
                    $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8488
                }
8489
            }
8490
            if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8491
                $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8492
            } else {
8493
                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8494
            }
8495
 
8496
            // Up/down link (only if enrol is enabled).
8497
            $updown = '';
8498
            if ($enabled) {
8499
                if ($updowncount > 1) {
8500
                    $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8501
                        $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8502
                } else {
8503
                    $updown = $spacer;
8504
                }
8505
                if ($updowncount < count($enabledplugins)) {
8506
                    $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8507
                        $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8508
                } else {
8509
                    $updown .= $spacer;
8510
                }
8511
                ++$updowncount;
8512
            }
8513
 
8514
            $uninstall = '';
8515
            $status = $plugininfo->get_status();
8516
            if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8517
                $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8518
            }
8519
            if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8520
                $uninstall = get_string('status_new', 'core_plugin');
8521
            } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8522
                $uninstall .= html_writer::link($uninstallurl, $struninstall);
8523
            }
8524
 
8525
            $settings = '';
8526
            if ($plugininfo->get_settings_url()) {
8527
                $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8528
            }
8529
 
8530
            // Add a row to the table.
8531
            $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8532
            if ($class) {
8533
                $row->attributes['class'] = $class;
8534
            }
8535
            $table->data[] = $row;
8536
 
8537
            $printed[$name] = true;
8538
        }
8539
 
8540
        $return .= html_writer::table($table);
8541
        $return .= $OUTPUT->box_end();
8542
        return highlight($query, $return);
8543
    }
8544
}
8545
 
8546
 
8547
/**
8548
 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8549
 *
8550
 * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
8551
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8552
 */
8553
class admin_setting_managecontentbankcontenttypes extends admin_setting {
8554
 
8555
    /**
8556
     * Calls parent::__construct with specific arguments
8557
     */
8558
    public function __construct() {
8559
        $this->nosave = true;
8560
        parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8561
    }
8562
 
8563
    /**
8564
     * Always returns true
8565
     *
8566
     * @return true
8567
     */
8568
    public function get_setting() {
8569
        return true;
8570
    }
8571
 
8572
    /**
8573
     * Always returns true
8574
     *
8575
     * @return true
8576
     */
8577
    public function get_defaultsetting() {
8578
        return true;
8579
    }
8580
 
8581
    /**
8582
     * Always returns '' and doesn't write anything
8583
     *
8584
     * @param mixed $data string or array, must not be NULL
8585
     * @return string Always returns ''
8586
     */
8587
    public function write_setting($data) {
8588
        // Do not write any setting.
8589
        return '';
8590
    }
8591
 
8592
    /**
8593
     * Search to find if Query is related to content bank plugin
8594
     *
8595
     * @param string $query The string to search for
8596
     * @return bool true for related false for not
8597
     */
8598
    public function is_related($query) {
8599
        if (parent::is_related($query)) {
8600
            return true;
8601
        }
8602
        $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8603
        foreach ($types as $type) {
8604
            if (strpos($type->component, $query) !== false ||
8605
                strpos(core_text::strtolower($type->displayname), $query) !== false) {
8606
                return true;
8607
            }
8608
        }
8609
        return false;
8610
    }
8611
 
8612
    /**
8613
     * Return XHTML to display control
8614
     *
8615
     * @param mixed $data Unused
8616
     * @param string $query
8617
     * @return string highlight
8618
     */
8619
    public function output_html($data, $query='') {
8620
        global $CFG, $OUTPUT;
8621
        $return = '';
8622
 
8623
        $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8624
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8625
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8626
 
8627
        $table = new html_table();
8628
        $table->head  = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8629
        $table->align = array('left', 'center', 'center', 'center', 'center');
8630
        $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8631
        $table->data  = array();
8632
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8633
 
8634
        $totalenabled = 0;
8635
        $count = 0;
8636
        foreach ($types as $type) {
8637
            if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8638
                $totalenabled++;
8639
            }
8640
        }
8641
 
8642
        foreach ($types as $type) {
8643
            $url = new moodle_url('/admin/contentbank.php',
8644
                array('sesskey' => sesskey(), 'name' => $type->name));
8645
 
8646
            $class = '';
8647
            $strtypename = $type->displayname;
8648
            if ($type->is_enabled()) {
8649
                $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8650
                    $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8651
            } else {
8652
                $class = 'dimmed_text';
8653
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8654
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8655
            }
8656
 
8657
            $updown = '';
8658
            if ($count) {
8659
                $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8660
                        $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8661
            } else {
8662
                $updown .= $spacer;
8663
            }
8664
            if ($count < count($types) - 1) {
8665
                $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8666
                        $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8667
            } else {
8668
                $updown .= $spacer;
8669
            }
8670
 
8671
            $settings = '';
8672
            if ($type->get_settings_url()) {
8673
                $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8674
            }
8675
 
8676
            $uninstall = '';
8677
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8678
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8679
            }
8680
 
8681
            $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8682
            if ($class) {
8683
                $row->attributes['class'] = $class;
8684
            }
8685
            $table->data[] = $row;
8686
            $count++;
8687
        }
8688
        $return .= html_writer::table($table);
8689
        return highlight($query, $return);
8690
    }
8691
}
8692
 
8693
/**
8694
 * Initialise admin page - this function does require login and permission
8695
 * checks specified in page definition.
8696
 *
8697
 * This function must be called on each admin page before other code.
8698
 *
8699
 * @global moodle_page $PAGE
8700
 *
8701
 * @param string $section name of page
8702
 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8703
 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8704
 *      added to the turn blocks editing on/off form, so this page reloads correctly.
8705
 * @param string $actualurl if the actual page being viewed is not the normal one for this
8706
 *      page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8707
 * @param array $options Additional options that can be specified for page setup.
8708
 *      pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8709
 *      nosearch - Do not display search bar
8710
 */
1441 ariadna 8711
function admin_externalpage_setup($section, $extrabutton = '', ?array $extraurlparams = null, $actualurl = '', array $options = array()) {
1 efrain 8712
    global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8713
 
8714
    $PAGE->set_context(null); // hack - set context to something, by default to system context
8715
 
8716
    $site = get_site();
8717
    require_login(null, false);
8718
 
8719
    if (!empty($options['pagelayout'])) {
8720
        // A specific page layout has been requested.
8721
        $PAGE->set_pagelayout($options['pagelayout']);
8722
    } else if ($section === 'upgradesettings') {
8723
        $PAGE->set_pagelayout('maintenance');
8724
    } else {
8725
        $PAGE->set_pagelayout('admin');
8726
    }
8727
 
8728
    $adminroot = admin_get_root(false, false); // settings not required for external pages
8729
    $extpage = $adminroot->locate($section, true);
8730
 
8731
    $hassiteconfig = has_capability('moodle/site:config', context_system::instance());
8732
    if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8733
        // The requested section isn't in the admin tree
8734
        // It could be because the user has inadequate capapbilities or because the section doesn't exist
8735
        if (!$hassiteconfig) {
8736
            // The requested section could depend on a different capability
8737
            // but most likely the user has inadequate capabilities
8738
            throw new \moodle_exception('accessdenied', 'admin');
8739
        } else {
8740
            throw new \moodle_exception('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8741
        }
8742
    }
8743
 
8744
    // this eliminates our need to authenticate on the actual pages
8745
    if (!$extpage->check_access()) {
8746
        throw new \moodle_exception('accessdenied', 'admin');
8747
        die;
8748
    }
8749
 
8750
    navigation_node::require_admin_tree();
8751
 
8752
    // $PAGE->set_extra_button($extrabutton); TODO
8753
 
8754
    if (!$actualurl) {
8755
        $actualurl = $extpage->url;
8756
    }
8757
 
8758
    $PAGE->set_url($actualurl, $extraurlparams);
8759
    if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8760
        $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8761
    }
8762
 
8763
    if (empty($SITE->fullname) || empty($SITE->shortname)) {
8764
        // During initial install.
8765
        $strinstallation = get_string('installation', 'install');
8766
        $strsettings = get_string('settings');
8767
        $PAGE->navbar->add($strsettings);
8768
        $PAGE->set_title($strinstallation);
8769
        $PAGE->set_heading($strinstallation);
8770
        $PAGE->set_cacheable(false);
8771
        return;
8772
    }
8773
 
8774
    // Locate the current item on the navigation and make it active when found.
8775
    $path = $extpage->path;
8776
    $node = $PAGE->settingsnav;
8777
    while ($node && count($path) > 0) {
8778
        $node = $node->get(array_pop($path));
8779
    }
8780
    if ($node) {
8781
        $node->make_active();
8782
    }
8783
 
8784
    // Normal case.
8785
    $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8786
    if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8787
        $USER->editing = $adminediting;
8788
    }
8789
 
8790
    if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
8791
        if ($PAGE->user_is_editing()) {
8792
            $caption = get_string('blockseditoff');
8793
            $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8794
        } else {
8795
            $caption = get_string('blocksediton');
8796
            $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8797
        }
8798
        $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8799
    }
8800
 
8801
    $PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $extpage->visiblepath));
8802
    $PAGE->set_heading($SITE->fullname);
8803
 
8804
    if ($hassiteconfig && empty($options['nosearch'])) {
8805
        $PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [
8806
            'action' => new moodle_url('/admin/search.php'),
8807
            'query' => $PAGE->url->get_param('query'),
8808
        ]));
8809
    }
8810
 
8811
    // prevent caching in nav block
8812
    $PAGE->navigation->clear_cache();
8813
}
8814
 
8815
/**
8816
 * Returns the reference to admin tree root
8817
 *
8818
 * @return object admin_root object
8819
 */
8820
function admin_get_root($reload=false, $requirefulltree=true) {
8821
    global $CFG, $DB, $OUTPUT, $ADMIN;
8822
 
8823
    if (is_null($ADMIN)) {
8824
    // create the admin tree!
8825
        $ADMIN = new admin_root($requirefulltree);
8826
    }
8827
 
8828
    if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8829
        $ADMIN->purge_children($requirefulltree);
8830
    }
8831
 
8832
    if (!$ADMIN->loaded) {
8833
    // we process this file first to create categories first and in correct order
8834
        require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8835
 
8836
        // now we process all other files in admin/settings to build the admin tree
8837
        foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8838
            if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8839
                continue;
8840
            }
8841
            if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8842
            // plugins are loaded last - they may insert pages anywhere
8843
                continue;
8844
            }
8845
            require($file);
8846
        }
8847
        require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8848
 
8849
        $ADMIN->loaded = true;
8850
    }
8851
 
8852
    return $ADMIN;
8853
}
8854
 
8855
/// settings utility functions
8856
 
8857
/**
8858
 * This function applies default settings recursively.
8859
 *
8860
 * Because setting the defaults of some settings can enable other settings,
8861
 * this function calls itself repeatedly (max 4 times) until no more new settings are saved.
8862
 *
8863
 * NOTE: previous "internal" parameters $admindefaultsettings, $settingsoutput were removed in Moodle 4.3.
8864
 *
8865
 * @param part_of_admin_tree|null $node NULL means apply all settings with repeated recursion
8866
 * @param bool $unconditional if true overrides all values with defaults (true for installation, false for CLI upgrade)
8867
 * @return array The names and values of the applied setting defaults
8868
 */
8869
function admin_apply_default_settings(?part_of_admin_tree $node = null, bool $unconditional = true): array {
8870
    if (is_null($node)) {
8871
        // This function relies heavily on config cache, so we need to enable in-memory caches if it
8872
        // is used during install when normal caching is disabled.
8873
        $token = new \core_cache\allow_temporary_caches(); // Value not used intentionally, see its destructor.
8874
 
8875
        core_plugin_manager::reset_caches();
8876
        $root = admin_get_root(true, true);
8877
        $saved = admin_apply_default_settings($root, $unconditional);
8878
        if (!$saved) {
8879
            return [];
8880
        }
8881
 
8882
        for ($i = 1; $i <= 3; $i++) {
8883
            core_plugin_manager::reset_caches();
8884
            $root = admin_get_root(true, true);
8885
            // No need to force defaults in repeated runs.
8886
            $moresaved = admin_apply_default_settings($root, false);
8887
            if (!$moresaved) {
8888
                // No more setting defaults to save.
8889
                return $saved;
8890
            }
8891
            $saved += $moresaved;
8892
        }
8893
 
8894
        // We should not get here unless there are some problematic settings.php files.
8895
        core_plugin_manager::reset_caches();
8896
        return $saved;
8897
    }
8898
 
8899
    // Recursive applying of defaults in admin tree.
8900
    $saved = [];
8901
    if ($node instanceof admin_category) {
8902
        foreach ($node->children as $child) {
8903
            if ($child === null) {
8904
                // This should not happen,
8905
                // this is to prevent theoretical infinite loops.
8906
                continue;
8907
            }
8908
            if ($child instanceof admin_externalpage) {
8909
                continue;
8910
            }
8911
            $saved += admin_apply_default_settings($child, $unconditional);
8912
        }
8913
 
8914
    } else if ($node instanceof admin_settingpage) {
8915
        /** @var admin_setting $setting */
8916
        foreach ((array)$node->settings as $setting) {
8917
            if ($setting->nosave) {
8918
                // Not a real setting, must be a heading or description.
8919
                continue;
8920
            }
8921
            if (!$unconditional && !is_null($setting->get_setting())) {
8922
                // Do not override existing defaults.
8923
                continue;
8924
            }
8925
            $defaultsetting = $setting->get_defaultsetting();
8926
            if (is_null($defaultsetting)) {
8927
                // No value yet - default maybe applied after admin user creation or in upgradesettings.
8928
                continue;
8929
            }
8930
            // This should be unique-enough setting name that matches administration UI.
8931
            if ($setting->plugin === null) {
8932
                $settingname = $setting->name;
8933
            } else {
8934
                $settingname = $setting->plugin . '/' . $setting->name;
8935
            }
8936
            // Set the default for this setting.
8937
            $error = $setting->write_setting($defaultsetting);
8938
            if ($error === '') {
8939
                $setting->write_setting_flags(null);
8940
                if (is_int($defaultsetting) || $defaultsetting instanceof lang_string
8941
                    || $defaultsetting instanceof moodle_url) {
8942
                    $defaultsetting = (string)$defaultsetting;
8943
                }
8944
                $saved[$settingname] = $defaultsetting;
8945
            } else {
8946
                debugging("Error applying default setting '$settingname': " . $error, DEBUG_DEVELOPER);
8947
            }
8948
        }
8949
    }
8950
 
8951
    return $saved;
8952
}
8953
 
8954
/**
8955
 * Store changed settings, this function updates the errors variable in $ADMIN
8956
 *
8957
 * @param object $formdata from form
8958
 * @return int number of changed settings
8959
 */
8960
function admin_write_settings($formdata) {
8961
    global $CFG, $SITE, $DB;
8962
 
8963
    $olddbsessions = !empty($CFG->dbsessions);
8964
    $formdata = (array)$formdata;
8965
 
8966
    $data = array();
8967
    foreach ($formdata as $fullname=>$value) {
8968
        if (strpos($fullname, 's_') !== 0) {
8969
            continue; // not a config value
8970
        }
8971
        $data[$fullname] = $value;
8972
    }
8973
 
8974
    $adminroot = admin_get_root();
8975
    $settings = admin_find_write_settings($adminroot, $data);
8976
 
8977
    $count = 0;
8978
    foreach ($settings as $fullname=>$setting) {
8979
        /** @var $setting admin_setting */
8980
        $original = $setting->get_setting();
8981
        $error = $setting->write_setting($data[$fullname]);
8982
        if ($error !== '') {
8983
            $adminroot->errors[$fullname] = new stdClass();
8984
            $adminroot->errors[$fullname]->data  = $data[$fullname];
8985
            $adminroot->errors[$fullname]->id    = $setting->get_id();
8986
            $adminroot->errors[$fullname]->error = $error;
8987
        } else {
8988
            $setting->write_setting_flags($data);
8989
        }
8990
        if ($setting->post_write_settings($original)) {
8991
            $count++;
8992
        }
8993
    }
8994
 
8995
    if ($olddbsessions != !empty($CFG->dbsessions)) {
8996
        require_logout();
8997
    }
8998
 
8999
    // Now update $SITE - just update the fields, in case other people have a
9000
    // a reference to it (e.g. $PAGE, $COURSE).
9001
    $newsite = $DB->get_record('course', array('id'=>$SITE->id));
9002
    foreach (get_object_vars($newsite) as $field => $value) {
9003
        $SITE->$field = $value;
9004
    }
9005
 
9006
    // now reload all settings - some of them might depend on the changed
9007
    admin_get_root(true);
9008
    return $count;
9009
}
9010
 
9011
/**
9012
 * Internal recursive function - finds all settings from submitted form
9013
 *
9014
 * @param object $node Instance of admin_category, or admin_settingpage
9015
 * @param array $data
9016
 * @return array
9017
 */
9018
function admin_find_write_settings($node, $data) {
9019
    $return = array();
9020
 
9021
    if (empty($data)) {
9022
        return $return;
9023
    }
9024
 
9025
    if ($node instanceof admin_category) {
9026
        if ($node->check_access()) {
9027
            $entries = array_keys($node->children);
9028
            foreach ($entries as $entry) {
9029
                $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
9030
            }
9031
        }
9032
 
9033
    } else if ($node instanceof admin_settingpage) {
9034
        if ($node->check_access()) {
9035
            foreach ($node->settings as $setting) {
9036
                $fullname = $setting->get_full_name();
9037
                if (array_key_exists($fullname, $data)) {
9038
                    $return[$fullname] = $setting;
9039
                }
9040
            }
9041
        }
9042
 
9043
    }
9044
 
9045
    return $return;
9046
}
9047
 
9048
/**
9049
 * Internal function - prints the search results
9050
 *
9051
 * @param string $query String to search for
9052
 * @return string empty or XHTML
9053
 */
9054
function admin_search_settings_html($query) {
9055
    global $CFG, $OUTPUT, $PAGE;
9056
 
9057
    if (core_text::strlen($query) < 2) {
9058
        return '';
9059
    }
9060
    $query = core_text::strtolower($query);
9061
 
9062
    $adminroot = admin_get_root();
9063
    $findings = $adminroot->search($query);
9064
    $savebutton = false;
9065
 
9066
    $tpldata = (object) [
9067
        'actionurl' => $PAGE->url->out(false),
9068
        'results' => [],
9069
        'sesskey' => sesskey(),
9070
    ];
9071
 
9072
    foreach ($findings as $found) {
9073
        $page     = $found->page;
9074
        $settings = $found->settings;
9075
        if ($page->is_hidden()) {
9076
        // hidden pages are not displayed in search results
9077
            continue;
9078
        }
9079
 
9080
        $heading = highlight($query, $page->visiblename);
9081
        $headingurl = null;
9082
        if ($page instanceof admin_externalpage) {
9083
            $headingurl = new moodle_url($page->url);
9084
        } else if ($page instanceof admin_settingpage) {
9085
            $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
9086
        } else {
9087
            continue;
9088
        }
9089
 
9090
        // Locate the page in the admin root and populate its visiblepath attribute.
9091
        $path = array();
9092
        $located = $adminroot->locate($page->name, true);
9093
        if ($located) {
9094
            foreach ($located->visiblepath as $pathitem) {
9095
                array_unshift($path, (string) $pathitem);
9096
            }
9097
        }
9098
 
9099
        $sectionsettings = [];
9100
        if (!empty($settings)) {
9101
            foreach ($settings as $setting) {
9102
                if (empty($setting->nosave)) {
9103
                    $savebutton = true;
9104
                }
9105
                $fullname = $setting->get_full_name();
9106
                if (array_key_exists($fullname, $adminroot->errors)) {
9107
                    $data = $adminroot->errors[$fullname]->data;
9108
                } else {
9109
                    $data = $setting->get_setting();
9110
                // do not use defaults if settings not available - upgradesettings handles the defaults!
9111
                }
9112
                $sectionsettings[] = $setting->output_html($data, $query);
9113
            }
9114
        }
9115
 
9116
        $tpldata->results[] = (object) [
9117
            'title' => $heading,
9118
            'path' => $path,
9119
            'url' => $headingurl->out(false),
9120
            'settings' => $sectionsettings
9121
        ];
9122
    }
9123
 
9124
    $tpldata->showsave = $savebutton;
9125
    $tpldata->hasresults = !empty($tpldata->results);
9126
 
9127
    return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
9128
}
9129
 
9130
/**
9131
 * Internal function - returns arrays of html pages with uninitialised settings
9132
 *
9133
 * @param object $node Instance of admin_category or admin_settingpage
9134
 * @return array
9135
 */
9136
function admin_output_new_settings_by_page($node) {
9137
    global $OUTPUT;
9138
    $return = array();
9139
 
9140
    if ($node instanceof admin_category) {
9141
        $entries = array_keys($node->children);
9142
        foreach ($entries as $entry) {
9143
            $return += admin_output_new_settings_by_page($node->children[$entry]);
9144
        }
9145
 
9146
    } else if ($node instanceof admin_settingpage) {
9147
            $newsettings = array();
9148
            foreach ($node->settings as $setting) {
9149
                if (is_null($setting->get_setting())) {
9150
                    $newsettings[] = $setting;
9151
                }
9152
            }
9153
            if (count($newsettings) > 0) {
9154
                $adminroot = admin_get_root();
9155
                $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
9156
                $page .= '<fieldset class="adminsettings">'."\n";
9157
                foreach ($newsettings as $setting) {
9158
                    $fullname = $setting->get_full_name();
9159
                    if (array_key_exists($fullname, $adminroot->errors)) {
9160
                        $data = $adminroot->errors[$fullname]->data;
9161
                    } else {
9162
                        $data = $setting->get_setting();
9163
                        if (is_null($data)) {
9164
                            $data = $setting->get_defaultsetting();
9165
                        }
9166
                    }
9167
                    $page .= '<div class="clearer"><!-- --></div>'."\n";
9168
                    $page .= $setting->output_html($data);
9169
                }
9170
                $page .= '</fieldset>';
9171
                $return[$node->name] = $page;
9172
            }
9173
        }
9174
 
9175
    return $return;
9176
}
9177
 
9178
/**
9179
 * Format admin settings
9180
 *
9181
 * @param object $setting
9182
 * @param string $title label element
9183
 * @param string $form form fragment, html code - not highlighted automatically
9184
 * @param string $description
9185
 * @param mixed $label link label to id, true by default or string being the label to connect it to
9186
 * @param string $warning warning text
9187
 * @param ?string $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
9188
 * @param string $query search query to be highlighted
9189
 * @return string XHTML
9190
 */
9191
function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
9192
    global $CFG, $OUTPUT;
9193
 
9194
    $context = (object) [
9195
        'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
9196
        'fullname' => $setting->get_full_name(),
9197
    ];
9198
 
9199
    // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
9200
    if ($label === true) {
9201
        $context->labelfor = $setting->get_id();
9202
    } else if ($label === false) {
9203
        $context->labelfor = '';
9204
    } else {
9205
        $context->labelfor = $label;
9206
    }
9207
 
9208
    $form .= $setting->output_setting_flags();
9209
 
9210
    $context->warning = $warning;
9211
    $context->override = '';
9212
    if (empty($setting->plugin)) {
9213
        if ($setting->is_forceable() && array_key_exists($setting->name, $CFG->config_php_settings)) {
9214
            $context->override = get_string('configoverride', 'admin');
9215
        }
9216
    } else {
9217
        if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
9218
            $context->override = get_string('configoverride', 'admin');
9219
        }
9220
    }
9221
 
9222
    $defaults = array();
9223
    if (!is_null($defaultinfo)) {
9224
        if ($defaultinfo === '') {
9225
            $defaultinfo = get_string('emptysettingvalue', 'admin');
9226
        }
9227
        $defaults[] = $defaultinfo;
9228
    }
9229
 
9230
    $context->default = null;
9231
    $setting->get_setting_flag_defaults($defaults);
9232
    if (!empty($defaults)) {
9233
        $defaultinfo = implode(', ', $defaults);
9234
        $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
9235
        $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
9236
    }
9237
 
9238
 
9239
    $context->error = '';
9240
    $adminroot = admin_get_root();
9241
    if (array_key_exists($context->fullname, $adminroot->errors)) {
9242
        $context->error = $adminroot->errors[$context->fullname]->error;
9243
    }
9244
 
9245
    if ($dependenton = $setting->get_dependent_on()) {
9246
        $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
9247
    }
9248
 
9249
    $context->id = 'admin-' . $setting->name;
9250
    $context->title = highlightfast($query, $title);
9251
    $context->name = highlightfast($query, $context->name);
9252
    $context->description = highlight($query, markdown_to_html($description));
9253
    $context->element = $form;
9254
    $context->forceltr = $setting->get_force_ltr();
9255
    $context->customcontrol = $setting->has_custom_form_control();
9256
 
9257
    return $OUTPUT->render_from_template('core_admin/setting', $context);
9258
}
9259
 
9260
/**
9261
 * Based on find_new_settings{@link ()}  in upgradesettings.php
9262
 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
9263
 *
9264
 * @param object $node Instance of admin_category, or admin_settingpage
9265
 * @return boolean true if any settings haven't been initialised, false if they all have
9266
 */
9267
function any_new_admin_settings($node) {
9268
 
9269
    if ($node instanceof admin_category) {
9270
        $entries = array_keys($node->children);
9271
        foreach ($entries as $entry) {
9272
            if (any_new_admin_settings($node->children[$entry])) {
9273
                return true;
9274
            }
9275
        }
9276
 
9277
    } else if ($node instanceof admin_settingpage) {
9278
            foreach ($node->settings as $setting) {
9279
                if ($setting->get_setting() === NULL) {
9280
                    return true;
9281
                }
9282
            }
9283
        }
9284
 
9285
    return false;
9286
}
9287
 
9288
/**
9289
 * Given a table and optionally a column name should replaces be done?
9290
 *
9291
 * @param string $table name
9292
 * @param string $column name
9293
 * @return bool success or fail
9294
 */
9295
function db_should_replace($table, $column = '', $additionalskiptables = ''): bool {
9296
 
9297
    // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing...
9298
    $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
9299
        'events_queue', 'repository_instance_config', 'block_instances', 'files'];
9300
 
9301
    // Additional skip tables.
9302
    if (!empty($additionalskiptables)) {
9303
        $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '',  $additionalskiptables)));
9304
    }
9305
 
9306
    // Don't process these.
9307
    if (in_array($table, $skiptables)) {
9308
        return false;
9309
    }
9310
 
9311
    // To be safe never replace inside a table that looks related to logging.
9312
    if (preg_match('/(^|_)logs?($|_)/', $table)) {
9313
        return false;
9314
    }
9315
 
9316
    // Do column based exclusions.
9317
    if (!empty($column)) {
9318
        // Don't touch anything that looks like a hash.
9319
        if (preg_match('/hash$/', $column)) {
9320
            return false;
9321
        }
9322
    }
9323
 
9324
    return true;
9325
}
9326
 
9327
/**
9328
 * Moved from admin/replace.php so that we can use this in cron
9329
 *
9330
 * @param string $search string to look for
9331
 * @param string $replace string to replace
9332
 * @return bool success or fail
9333
 */
9334
function db_replace($search, $replace, $additionalskiptables = '') {
9335
    global $DB, $CFG, $OUTPUT;
9336
 
9337
    // Turn off time limits, sometimes upgrades can be slow.
9338
    core_php_time_limit::raise();
9339
 
9340
    if (!$tables = $DB->get_tables() ) {    // No tables yet at all.
9341
        return false;
9342
    }
9343
    foreach ($tables as $table) {
9344
 
9345
        if (!db_should_replace($table, '', $additionalskiptables)) {
9346
            continue;
9347
        }
9348
 
9349
        if ($columns = $DB->get_columns($table)) {
9350
            $DB->set_debug(true);
9351
            foreach ($columns as $column) {
9352
                if (!db_should_replace($table, $column->name)) {
9353
                    continue;
9354
                }
9355
                $DB->replace_all_text($table, $column, $search, $replace);
9356
            }
9357
            $DB->set_debug(false);
9358
        }
9359
    }
9360
 
9361
    // delete modinfo caches
9362
    rebuild_course_cache(0, true);
9363
 
9364
    // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9365
    $blocks = core_component::get_plugin_list('block');
9366
    foreach ($blocks as $blockname=>$fullblock) {
9367
        if ($blockname === 'NEWBLOCK') {   // Someone has unzipped the template, ignore it
9368
            continue;
9369
        }
9370
 
9371
        if (!is_readable($fullblock.'/lib.php')) {
9372
            continue;
9373
        }
9374
 
9375
        $function = 'block_'.$blockname.'_global_db_replace';
9376
        include_once($fullblock.'/lib.php');
9377
        if (!function_exists($function)) {
9378
            continue;
9379
        }
9380
 
9381
        echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9382
        $function($search, $replace);
9383
        echo $OUTPUT->notification("...finished", 'notifysuccess');
9384
    }
9385
 
9386
    // Trigger an event.
9387
    $eventargs = [
9388
        'context' => context_system::instance(),
9389
        'other' => [
9390
            'search' => $search,
9391
            'replace' => $replace
9392
        ]
9393
    ];
9394
    $event = \core\event\database_text_field_content_replaced::create($eventargs);
9395
    $event->trigger();
9396
 
9397
    purge_all_caches();
9398
 
9399
    return true;
9400
}
9401
 
9402
/**
9403
 * Manage repository settings
9404
 *
9405
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9406
 */
9407
class admin_setting_managerepository extends admin_setting {
9408
/** @var string */
9409
    private $baseurl;
9410
 
9411
    /**
9412
     * calls parent::__construct with specific arguments
9413
     */
9414
    public function __construct() {
9415
        global $CFG;
9416
        parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9417
        $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9418
    }
9419
 
9420
    /**
9421
     * Always returns true, does nothing
9422
     *
9423
     * @return true
9424
     */
9425
    public function get_setting() {
9426
        return true;
9427
    }
9428
 
9429
    /**
9430
     * Always returns true does nothing
9431
     *
9432
     * @return true
9433
     */
9434
    public function get_defaultsetting() {
9435
        return true;
9436
    }
9437
 
9438
    /**
9439
     * Always returns s_managerepository
9440
     *
9441
     * @return string Always return 's_managerepository'
9442
     */
9443
    public function get_full_name() {
9444
        return 's_managerepository';
9445
    }
9446
 
9447
    /**
9448
     * Always returns '' doesn't do anything
9449
     */
9450
    public function write_setting($data) {
9451
        $url = $this->baseurl . '&amp;new=' . $data;
9452
        return '';
9453
    // TODO
9454
    // Should not use redirect and exit here
9455
    // Find a better way to do this.
9456
    // redirect($url);
9457
    // exit;
9458
    }
9459
 
9460
    /**
9461
     * Searches repository plugins for one that matches $query
9462
     *
9463
     * @param string $query The string to search for
9464
     * @return bool true if found, false if not
9465
     */
9466
    public function is_related($query) {
9467
        if (parent::is_related($query)) {
9468
            return true;
9469
        }
9470
 
9471
        $repositories= core_component::get_plugin_list('repository');
9472
        foreach ($repositories as $p => $dir) {
9473
            if (strpos($p, $query) !== false) {
9474
                return true;
9475
            }
9476
        }
9477
        foreach (repository::get_types() as $instance) {
9478
            $title = $instance->get_typename();
9479
            if (strpos(core_text::strtolower($title), $query) !== false) {
9480
                return true;
9481
            }
9482
        }
9483
        return false;
9484
    }
9485
 
9486
    /**
9487
     * Helper function that generates a moodle_url object
9488
     * relevant to the repository
9489
     */
9490
 
9491
    function repository_action_url($repository) {
9492
        return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9493
    }
9494
 
9495
    /**
9496
     * Builds XHTML to display the control
9497
     *
9498
     * @param string $data Unused
9499
     * @param string $query
9500
     * @return string XHTML
9501
     */
9502
    public function output_html($data, $query='') {
9503
        global $CFG, $USER, $OUTPUT;
9504
 
9505
        // Get strings that are used
9506
        $strshow = get_string('on', 'repository');
9507
        $strhide = get_string('off', 'repository');
9508
        $strdelete = get_string('disabled', 'repository');
9509
 
9510
        $actionchoicesforexisting = array(
9511
            'show' => $strshow,
9512
            'hide' => $strhide,
9513
            'delete' => $strdelete
9514
        );
9515
 
9516
        $actionchoicesfornew = array(
9517
            'newon' => $strshow,
9518
            'newoff' => $strhide,
9519
            'delete' => $strdelete
9520
        );
9521
 
9522
        $return = '';
9523
        $return .= $OUTPUT->box_start('generalbox');
9524
 
9525
        // Set strings that are used multiple times
9526
        $settingsstr = get_string('settings');
9527
        $disablestr = get_string('disable');
9528
 
9529
        // Table to list plug-ins
9530
        $table = new html_table();
9531
        $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9532
        $table->align = array('left', 'center', 'center', 'center', 'center');
9533
        $table->data = array();
9534
 
9535
        // Get list of used plug-ins
9536
        $repositorytypes = repository::get_types();
9537
        if (!empty($repositorytypes)) {
9538
            // Array to store plugins being used
9539
            $alreadyplugins = array();
9540
            $totalrepositorytypes = count($repositorytypes);
9541
            $updowncount = 1;
9542
            foreach ($repositorytypes as $i) {
9543
                $settings = '';
9544
                $typename = $i->get_typename();
9545
                // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9546
                $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9547
                $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9548
 
9549
                if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9550
                    // Calculate number of instances in order to display them for the Moodle administrator
9551
                    if (!empty($instanceoptionnames)) {
9552
                        $params = array();
9553
                        $params['context'] = array(context_system::instance());
9554
                        $params['onlyvisible'] = false;
9555
                        $params['type'] = $typename;
9556
                        $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9557
                        // site instances
9558
                        $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9559
                        $params['context'] = array();
9560
                        $instances = repository::static_function($typename, 'get_instances', $params);
9561
                        $courseinstances = array();
9562
                        $userinstances = array();
9563
 
9564
                        foreach ($instances as $instance) {
9565
                            $repocontext = context::instance_by_id($instance->instance->contextid);
9566
                            if ($repocontext->contextlevel == CONTEXT_COURSE) {
9567
                                $courseinstances[] = $instance;
9568
                            } else if ($repocontext->contextlevel == CONTEXT_USER) {
9569
                                $userinstances[] = $instance;
9570
                            }
9571
                        }
9572
                        // course instances
9573
                        $instancenumber = count($courseinstances);
9574
                        $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9575
 
9576
                        // user private instances
9577
                        $instancenumber =  count($userinstances);
9578
                        $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9579
                    } else {
9580
                        $admininstancenumbertext = "";
9581
                        $courseinstancenumbertext = "";
9582
                        $userinstancenumbertext = "";
9583
                    }
9584
 
9585
                    $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9586
 
9587
                    $settings .= $OUTPUT->container_start('mdl-left');
9588
                    $settings .= '<br/>';
9589
                    $settings .= $admininstancenumbertext;
9590
                    $settings .= '<br/>';
9591
                    $settings .= $courseinstancenumbertext;
9592
                    $settings .= '<br/>';
9593
                    $settings .= $userinstancenumbertext;
9594
                    $settings .= $OUTPUT->container_end();
9595
                }
9596
                // Get the current visibility
9597
                if ($i->get_visible()) {
9598
                    $currentaction = 'show';
9599
                } else {
9600
                    $currentaction = 'hide';
9601
                }
9602
 
9603
                $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9604
 
9605
                // Display up/down link
9606
                $updown = '';
9607
                // Should be done with CSS instead.
9608
                $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9609
 
9610
                if ($updowncount > 1) {
9611
                    $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9612
                    $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9613
                }
9614
                else {
9615
                    $updown .= $spacer;
9616
                }
9617
                if ($updowncount < $totalrepositorytypes) {
9618
                    $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9619
                    $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9620
                }
9621
                else {
9622
                    $updown .= $spacer;
9623
                }
9624
 
9625
                $updowncount++;
9626
 
9627
                $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9628
 
9629
                if (!in_array($typename, $alreadyplugins)) {
9630
                    $alreadyplugins[] = $typename;
9631
                }
9632
            }
9633
        }
9634
 
9635
        // Get all the plugins that exist on disk
9636
        $plugins = core_component::get_plugin_list('repository');
9637
        if (!empty($plugins)) {
9638
            foreach ($plugins as $plugin => $dir) {
9639
                // Check that it has not already been listed
9640
                if (!in_array($plugin, $alreadyplugins)) {
9641
                    $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9642
                    $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9643
                }
9644
            }
9645
        }
9646
 
9647
        $return .= html_writer::table($table);
9648
        $return .= $OUTPUT->box_end();
9649
        return highlight($query, $return);
9650
    }
9651
}
9652
 
9653
/**
9654
 * Special checkbox for enable mobile web service
9655
 * If enable then we store the service id of the mobile service into config table
9656
 * If disable then we unstore the service id from the config table
9657
 */
9658
class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9659
 
9660
    /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9661
    private $restuse;
9662
 
9663
    /**
9664
     * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9665
     *
9666
     * @return boolean
9667
     */
9668
    private function is_protocol_cap_allowed() {
9669
        global $DB, $CFG;
9670
 
9671
        // If the $this->restuse variable is not set, it needs to be set.
9672
        if (empty($this->restuse) and $this->restuse!==false) {
9673
            $params = array();
9674
            $params['permission'] = CAP_ALLOW;
9675
            $params['roleid'] = $CFG->defaultuserroleid;
9676
            $params['capability'] = 'webservice/rest:use';
9677
            $this->restuse = $DB->record_exists('role_capabilities', $params);
9678
        }
9679
 
9680
        return $this->restuse;
9681
    }
9682
 
9683
    /**
9684
     * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9685
     * @param bool $status true to allow, false to not set
9686
     */
9687
    private function set_protocol_cap($status) {
9688
        global $CFG;
9689
        if ($status and !$this->is_protocol_cap_allowed()) {
9690
            //need to allow the cap
9691
            $permission = CAP_ALLOW;
9692
            $assign = true;
9693
        } else if (!$status and $this->is_protocol_cap_allowed()){
9694
            //need to disallow the cap
9695
            $permission = CAP_INHERIT;
9696
            $assign = true;
9697
        }
9698
        if (!empty($assign)) {
9699
            $systemcontext = context_system::instance();
9700
            assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9701
        }
9702
    }
9703
 
9704
    /**
9705
     * Builds XHTML to display the control.
9706
     * The main purpose of this overloading is to display a warning when https
9707
     * is not supported by the server
9708
     * @param string $data Unused
9709
     * @param string $query
9710
     * @return string XHTML
9711
     */
9712
    public function output_html($data, $query='') {
9713
        global $OUTPUT;
9714
        $html = parent::output_html($data, $query);
9715
 
9716
        if ((string)$data === $this->yes) {
9717
            $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9718
            foreach ($notifications as $notification) {
9719
                $message = get_string($notification[0], $notification[1]);
9720
                $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9721
            }
9722
        }
9723
 
9724
        return $html;
9725
    }
9726
 
9727
    /**
9728
     * Retrieves the current setting using the objects name
9729
     *
9730
     * @return string
9731
     */
9732
    public function get_setting() {
9733
        global $CFG;
9734
 
9735
        // First check if is not set.
9736
        $result = $this->config_read($this->name);
9737
        if (is_null($result)) {
9738
            return null;
9739
        }
9740
 
9741
        // For install cli script, $CFG->defaultuserroleid is not set so return 0
9742
        // Or if web services aren't enabled this can't be,
9743
        if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9744
            return 0;
9745
        }
9746
 
9747
        require_once($CFG->dirroot . '/webservice/lib.php');
9748
        $webservicemanager = new webservice();
9749
        $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9750
        if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9751
            return $result;
9752
        } else {
9753
            return 0;
9754
        }
9755
    }
9756
 
9757
    /**
9758
     * Save the selected setting
9759
     *
9760
     * @param string $data The selected site
9761
     * @return string empty string or error message
9762
     */
9763
    public function write_setting($data) {
9764
        global $DB, $CFG;
9765
 
9766
        //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9767
        if (empty($CFG->defaultuserroleid)) {
9768
            return '';
9769
        }
9770
 
9771
        $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9772
 
9773
        require_once($CFG->dirroot . '/webservice/lib.php');
9774
        $webservicemanager = new webservice();
9775
 
9776
        $updateprotocol = false;
9777
        if ((string)$data === $this->yes) {
9778
             //code run when enable mobile web service
9779
             //enable web service systeme if necessary
9780
             set_config('enablewebservices', true);
9781
 
9782
             //enable mobile service
9783
             $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9784
             $mobileservice->enabled = 1;
9785
             $webservicemanager->update_external_service($mobileservice);
9786
 
9787
             // Enable REST server.
9788
             $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9789
 
9790
             if (!in_array('rest', $activeprotocols)) {
9791
                 $activeprotocols[] = 'rest';
9792
                 $updateprotocol = true;
9793
             }
9794
 
9795
             if ($updateprotocol) {
9796
                 set_config('webserviceprotocols', implode(',', $activeprotocols));
9797
             }
9798
 
9799
             // Allow rest:use capability for authenticated user.
9800
             $this->set_protocol_cap(true);
9801
         } else {
9802
             // Disable the mobile service.
9803
             $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9804
             $mobileservice->enabled = 0;
9805
             $webservicemanager->update_external_service($mobileservice);
9806
         }
9807
 
9808
        return (parent::write_setting($data));
9809
    }
9810
}
9811
 
9812
/**
9813
 * Special class for management of external services
9814
 *
9815
 * @author Petr Skoda (skodak)
9816
 */
9817
class admin_setting_manageexternalservices extends admin_setting {
9818
    /**
9819
     * Calls parent::__construct with specific arguments
9820
     */
9821
    public function __construct() {
9822
        $this->nosave = true;
9823
        parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9824
    }
9825
 
9826
    /**
9827
     * Always returns true, does nothing
9828
     *
9829
     * @return true
9830
     */
9831
    public function get_setting() {
9832
        return true;
9833
    }
9834
 
9835
    /**
9836
     * Always returns true, does nothing
9837
     *
9838
     * @return true
9839
     */
9840
    public function get_defaultsetting() {
9841
        return true;
9842
    }
9843
 
9844
    /**
9845
     * Always returns '', does not write anything
9846
     *
9847
     * @return string Always returns ''
9848
     */
9849
    public function write_setting($data) {
9850
    // do not write any setting
9851
        return '';
9852
    }
9853
 
9854
    /**
9855
     * Checks if $query is one of the available external services
9856
     *
9857
     * @param string $query The string to search for
9858
     * @return bool Returns true if found, false if not
9859
     */
9860
    public function is_related($query) {
9861
        global $DB;
9862
 
9863
        if (parent::is_related($query)) {
9864
            return true;
9865
        }
9866
 
9867
        $services = $DB->get_records('external_services', array(), 'id, name');
9868
        foreach ($services as $service) {
9869
            if (strpos(core_text::strtolower($service->name), $query) !== false) {
9870
                return true;
9871
            }
9872
        }
9873
        return false;
9874
    }
9875
 
9876
    /**
9877
     * Builds the XHTML to display the control
9878
     *
9879
     * @param string $data Unused
9880
     * @param string $query
9881
     * @return string
9882
     */
9883
    public function output_html($data, $query='') {
9884
        global $CFG, $OUTPUT, $DB;
9885
 
9886
        // display strings
9887
        $stradministration = get_string('administration');
9888
        $stredit = get_string('edit');
9889
        $strservice = get_string('externalservice', 'webservice');
9890
        $strdelete = get_string('delete');
9891
        $strplugin = get_string('plugin', 'admin');
9892
        $stradd = get_string('add');
9893
        $strfunctions = get_string('functions', 'webservice');
9894
        $strusers = get_string('users');
9895
        $strserviceusers = get_string('serviceusers', 'webservice');
9896
 
9897
        $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9898
        $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9899
        $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9900
 
9901
        // built in services
9902
         $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9903
         $return = "";
9904
         if (!empty($services)) {
9905
            $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9906
 
9907
 
9908
 
9909
            $table = new html_table();
9910
            $table->head  = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9911
            $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9912
            $table->id = 'builtinservices';
9913
            $table->attributes['class'] = 'admintable externalservices generaltable';
9914
            $table->data  = array();
9915
 
9916
            // iterate through auth plugins and add to the display table
9917
            foreach ($services as $service) {
9918
                $name = $service->name;
9919
 
9920
                // hide/show link
9921
                if ($service->enabled) {
9922
                    $displayname = "<span>$name</span>";
9923
                } else {
9924
                    $displayname = "<span class=\"dimmed_text\">$name</span>";
9925
                }
9926
 
9927
                $plugin = $service->component;
9928
 
9929
                $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9930
 
9931
                if ($service->restrictedusers) {
9932
                    $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9933
                } else {
9934
                    $users = get_string('allusers', 'webservice');
9935
                }
9936
 
9937
                $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9938
 
9939
                // add a row to the table
9940
                $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9941
            }
9942
            $return .= html_writer::table($table);
9943
        }
9944
 
9945
        // Custom services
9946
        $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9947
        $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9948
 
9949
        $table = new html_table();
9950
        $table->head  = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9951
        $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9952
        $table->id = 'customservices';
9953
        $table->attributes['class'] = 'admintable externalservices generaltable';
9954
        $table->data  = array();
9955
 
9956
        // iterate through auth plugins and add to the display table
9957
        foreach ($services as $service) {
9958
            $name = $service->name;
9959
 
9960
            // hide/show link
9961
            if ($service->enabled) {
9962
                $displayname = "<span>$name</span>";
9963
            } else {
9964
                $displayname = "<span class=\"dimmed_text\">$name</span>";
9965
            }
9966
 
9967
            // delete link
9968
            $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9969
 
9970
            $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9971
 
9972
            if ($service->restrictedusers) {
9973
                $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9974
            } else {
9975
                $users = get_string('allusers', 'webservice');
9976
            }
9977
 
9978
            $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9979
 
9980
            // add a row to the table
9981
            $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9982
        }
9983
        // add new custom service option
9984
        $return .= html_writer::table($table);
9985
 
9986
        $return .= '<br />';
9987
        // add a token to the table
9988
        $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9989
 
9990
        return highlight($query, $return);
9991
    }
9992
}
9993
 
9994
/**
9995
 * Special class for overview of external services
9996
 *
9997
 * @author Jerome Mouneyrac
9998
 */
9999
class admin_setting_webservicesoverview extends admin_setting {
10000
 
10001
    /**
10002
     * Calls parent::__construct with specific arguments
10003
     */
10004
    public function __construct() {
10005
        $this->nosave = true;
10006
        parent::__construct('webservicesoverviewui',
10007
                        get_string('webservicesoverview', 'webservice'), '', '');
10008
    }
10009
 
10010
    /**
10011
     * Always returns true, does nothing
10012
     *
10013
     * @return true
10014
     */
10015
    public function get_setting() {
10016
        return true;
10017
    }
10018
 
10019
    /**
10020
     * Always returns true, does nothing
10021
     *
10022
     * @return true
10023
     */
10024
    public function get_defaultsetting() {
10025
        return true;
10026
    }
10027
 
10028
    /**
10029
     * Always returns '', does not write anything
10030
     *
10031
     * @return string Always returns ''
10032
     */
10033
    public function write_setting($data) {
10034
        // do not write any setting
10035
        return '';
10036
    }
10037
 
10038
    /**
10039
     * Builds the XHTML to display the control
10040
     *
10041
     * @param string $data Unused
10042
     * @param string $query
10043
     * @return string
10044
     */
10045
    public function output_html($data, $query='') {
10046
        global $CFG, $OUTPUT;
10047
 
10048
        $return = "";
10049
        $brtag = html_writer::empty_tag('br');
10050
 
10051
        /// One system controlling Moodle with Token
10052
        $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
10053
        $table = new html_table();
10054
        $table->head = array(get_string('step', 'webservice'), get_string('status'),
10055
            get_string('description'));
10056
        $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10057
        $table->id = 'onesystemcontrol';
10058
        $table->attributes['class'] = 'admintable wsoverview generaltable';
10059
        $table->data = array();
10060
 
10061
        $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
10062
                . $brtag . $brtag;
10063
 
10064
        /// 1. Enable Web Services
10065
        $row = array();
10066
        $url = new moodle_url("/admin/search.php?query=enablewebservices");
10067
        $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10068
                        array('href' => $url));
10069
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
10070
        if ($CFG->enablewebservices) {
10071
            $status = get_string('yes');
10072
        }
10073
        $row[1] = $status;
10074
        $row[2] = get_string('enablewsdescription', 'webservice');
10075
        $table->data[] = $row;
10076
 
10077
        /// 2. Enable protocols
10078
        $row = array();
10079
        $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10080
        $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10081
                        array('href' => $url));
10082
        $status = html_writer::tag('span', get_string('none'), array('class' => 'badge bg-danger text-white'));
10083
        //retrieve activated protocol
10084
        $active_protocols = empty($CFG->webserviceprotocols) ?
10085
                array() : explode(',', $CFG->webserviceprotocols);
10086
        if (!empty($active_protocols)) {
10087
            $status = "";
10088
            foreach ($active_protocols as $protocol) {
10089
                $status .= $protocol . $brtag;
10090
            }
10091
        }
10092
        $row[1] = $status;
10093
        $row[2] = get_string('enableprotocolsdescription', 'webservice');
10094
        $table->data[] = $row;
10095
 
10096
        /// 3. Create user account
10097
        $row = array();
10098
        $url = new moodle_url("/user/editadvanced.php?id=-1");
10099
        $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
10100
                        array('href' => $url));
10101
        $row[1] = "";
10102
        $row[2] = get_string('createuserdescription', 'webservice');
10103
        $table->data[] = $row;
10104
 
10105
        /// 4. Add capability to users
10106
        $row = array();
10107
        $url = new moodle_url("/admin/roles/check.php?contextid=1");
10108
        $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
10109
                        array('href' => $url));
10110
        $row[1] = "";
10111
        $row[2] = get_string('checkusercapabilitydescription', 'webservice');
10112
        $table->data[] = $row;
10113
 
10114
        /// 5. Select a web service
10115
        $row = array();
10116
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10117
        $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10118
                        array('href' => $url));
10119
        $row[1] = "";
10120
        $row[2] = get_string('createservicedescription', 'webservice');
10121
        $table->data[] = $row;
10122
 
10123
        /// 6. Add functions
10124
        $row = array();
10125
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10126
        $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10127
                        array('href' => $url));
10128
        $row[1] = "";
10129
        $row[2] = get_string('addfunctionsdescription', 'webservice');
10130
        $table->data[] = $row;
10131
 
10132
        /// 7. Add the specific user
10133
        $row = array();
10134
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10135
        $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
10136
                        array('href' => $url));
10137
        $row[1] = "";
10138
        $row[2] = get_string('selectspecificuserdescription', 'webservice');
10139
        $table->data[] = $row;
10140
 
10141
        /// 8. Create token for the specific user
10142
        $row = array();
10143
        $url = new moodle_url('/admin/webservice/tokens.php', ['action' => 'create']);
10144
        $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
10145
                        array('href' => $url));
10146
        $row[1] = "";
10147
        $row[2] = get_string('createtokenforuserdescription', 'webservice');
10148
        $table->data[] = $row;
10149
 
10150
        /// 9. Enable the documentation
10151
        $row = array();
10152
        $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
10153
        $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
10154
                        array('href' => $url));
10155
        $status = '<span class="warning">' . get_string('no') . '</span>';
10156
        if ($CFG->enablewsdocumentation) {
10157
            $status = get_string('yes');
10158
        }
10159
        $row[1] = $status;
10160
        $row[2] = get_string('enabledocumentationdescription', 'webservice');
10161
        $table->data[] = $row;
10162
 
10163
        /// 10. Test the service
10164
        $row = array();
10165
        $url = new moodle_url("/admin/webservice/testclient.php");
10166
        $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10167
                        array('href' => $url));
10168
        $row[1] = "";
10169
        $row[2] = get_string('testwithtestclientdescription', 'webservice');
10170
        $table->data[] = $row;
10171
 
10172
        $return .= html_writer::table($table);
10173
 
10174
        /// Users as clients with token
10175
        $return .= $brtag . $brtag . $brtag;
10176
        $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
10177
        $table = new html_table();
10178
        $table->head = array(get_string('step', 'webservice'), get_string('status'),
10179
            get_string('description'));
10180
        $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10181
        $table->id = 'userasclients';
10182
        $table->attributes['class'] = 'admintable wsoverview generaltable';
10183
        $table->data = array();
10184
 
10185
        $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
10186
                $brtag . $brtag;
10187
 
10188
        /// 1. Enable Web Services
10189
        $row = array();
10190
        $url = new moodle_url("/admin/search.php?query=enablewebservices");
10191
        $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10192
                        array('href' => $url));
10193
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
10194
        if ($CFG->enablewebservices) {
10195
            $status = get_string('yes');
10196
        }
10197
        $row[1] = $status;
10198
        $row[2] = get_string('enablewsdescription', 'webservice');
10199
        $table->data[] = $row;
10200
 
10201
        /// 2. Enable protocols
10202
        $row = array();
10203
        $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10204
        $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10205
                        array('href' => $url));
10206
        $status = html_writer::tag('span', get_string('none'), array('class' => 'badge bg-danger text-white'));
10207
        //retrieve activated protocol
10208
        $active_protocols = empty($CFG->webserviceprotocols) ?
10209
                array() : explode(',', $CFG->webserviceprotocols);
10210
        if (!empty($active_protocols)) {
10211
            $status = "";
10212
            foreach ($active_protocols as $protocol) {
10213
                $status .= $protocol . $brtag;
10214
            }
10215
        }
10216
        $row[1] = $status;
10217
        $row[2] = get_string('enableprotocolsdescription', 'webservice');
10218
        $table->data[] = $row;
10219
 
10220
 
10221
        /// 3. Select a web service
10222
        $row = array();
10223
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10224
        $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10225
                        array('href' => $url));
10226
        $row[1] = "";
10227
        $row[2] = get_string('createserviceforusersdescription', 'webservice');
10228
        $table->data[] = $row;
10229
 
10230
        /// 4. Add functions
10231
        $row = array();
10232
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10233
        $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10234
                        array('href' => $url));
10235
        $row[1] = "";
10236
        $row[2] = get_string('addfunctionsdescription', 'webservice');
10237
        $table->data[] = $row;
10238
 
10239
        /// 5. Add capability to users
10240
        $row = array();
10241
        $url = new moodle_url("/admin/roles/check.php?contextid=1");
10242
        $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
10243
                        array('href' => $url));
10244
        $row[1] = "";
10245
        $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
10246
        $table->data[] = $row;
10247
 
10248
        /// 6. Test the service
10249
        $row = array();
10250
        $url = new moodle_url("/admin/webservice/testclient.php");
10251
        $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10252
                        array('href' => $url));
10253
        $row[1] = "";
10254
        $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
10255
        $table->data[] = $row;
10256
 
10257
        $return .= html_writer::table($table);
10258
 
10259
        return highlight($query, $return);
10260
    }
10261
 
10262
}
10263
 
10264
 
10265
/**
10266
 * Special class for web service protocol administration.
10267
 *
10268
 * @author Petr Skoda (skodak)
10269
 */
10270
class admin_setting_managewebserviceprotocols extends admin_setting {
10271
 
10272
    /**
10273
     * Calls parent::__construct with specific arguments
10274
     */
10275
    public function __construct() {
10276
        $this->nosave = true;
10277
        parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
10278
    }
10279
 
10280
    /**
10281
     * Always returns true, does nothing
10282
     *
10283
     * @return true
10284
     */
10285
    public function get_setting() {
10286
        return true;
10287
    }
10288
 
10289
    /**
10290
     * Always returns true, does nothing
10291
     *
10292
     * @return true
10293
     */
10294
    public function get_defaultsetting() {
10295
        return true;
10296
    }
10297
 
10298
    /**
10299
     * Always returns '', does not write anything
10300
     *
10301
     * @return string Always returns ''
10302
     */
10303
    public function write_setting($data) {
10304
    // do not write any setting
10305
        return '';
10306
    }
10307
 
10308
    /**
10309
     * Checks if $query is one of the available webservices
10310
     *
10311
     * @param string $query The string to search for
10312
     * @return bool Returns true if found, false if not
10313
     */
10314
    public function is_related($query) {
10315
        if (parent::is_related($query)) {
10316
            return true;
10317
        }
10318
 
10319
        $protocols = core_component::get_plugin_list('webservice');
10320
        foreach ($protocols as $protocol=>$location) {
10321
            if (strpos($protocol, $query) !== false) {
10322
                return true;
10323
            }
10324
            $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
10325
            if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
10326
                return true;
10327
            }
10328
        }
10329
        return false;
10330
    }
10331
 
10332
    /**
10333
     * Builds the XHTML to display the control
10334
     *
10335
     * @param string $data Unused
10336
     * @param string $query
10337
     * @return string
10338
     */
10339
    public function output_html($data, $query='') {
10340
        global $CFG, $OUTPUT;
10341
 
10342
        // display strings
10343
        $stradministration = get_string('administration');
10344
        $strsettings = get_string('settings');
10345
        $stredit = get_string('edit');
10346
        $strprotocol = get_string('protocol', 'webservice');
10347
        $strenable = get_string('enable');
10348
        $strdisable = get_string('disable');
10349
        $strversion = get_string('version');
10350
 
10351
        $protocols_available = core_component::get_plugin_list('webservice');
10352
        $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10353
        ksort($protocols_available);
10354
 
10355
        foreach ($activeprotocols as $key => $protocol) {
10356
            if (empty($protocols_available[$protocol])) {
10357
                unset($activeprotocols[$key]);
10358
            }
10359
        }
10360
 
10361
        $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10362
        if (in_array('xmlrpc', $activeprotocols)) {
10363
            $notify = new \core\output\notification(get_string('xmlrpcwebserviceenabled', 'admin'),
10364
                \core\output\notification::NOTIFY_WARNING);
10365
            $return .= $OUTPUT->render($notify);
10366
        }
10367
        $return .= $OUTPUT->box_start('generalbox webservicesui');
10368
 
10369
        $table = new html_table();
10370
        $table->head  = array($strprotocol, $strversion, $strenable, $strsettings);
10371
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10372
        $table->id = 'webserviceprotocols';
10373
        $table->attributes['class'] = 'admintable generaltable';
10374
        $table->data  = array();
10375
 
10376
        // iterate through auth plugins and add to the display table
10377
        $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10378
        foreach ($protocols_available as $protocol => $location) {
10379
            $name = get_string('pluginname', 'webservice_'.$protocol);
10380
 
10381
            $plugin = new stdClass();
10382
            if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10383
                include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10384
            }
10385
            $version = isset($plugin->version) ? $plugin->version : '';
10386
 
10387
            // hide/show link
10388
            if (in_array($protocol, $activeprotocols)) {
10389
                $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10390
                $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10391
                $displayname = "<span>$name</span>";
10392
            } else {
10393
                $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10394
                $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10395
                $displayname = "<span class=\"dimmed_text\">$name</span>";
10396
            }
10397
 
10398
            // settings link
10399
            if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10400
                $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10401
            } else {
10402
                $settings = '';
10403
            }
10404
 
10405
            // add a row to the table
10406
            $table->data[] = array($displayname, $version, $hideshow, $settings);
10407
        }
10408
        $return .= html_writer::table($table);
10409
        $return .= get_string('configwebserviceplugins', 'webservice');
10410
        $return .= $OUTPUT->box_end();
10411
 
10412
        return highlight($query, $return);
10413
    }
10414
}
10415
 
10416
/**
10417
 * Colour picker
10418
 *
10419
 * @copyright 2010 Sam Hemelryk
10420
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10421
 */
10422
class admin_setting_configcolourpicker extends admin_setting {
10423
 
10424
    /**
10425
     * Information for previewing the colour
10426
     *
10427
     * @var array|null
10428
     */
10429
    protected $previewconfig = null;
10430
 
10431
    /**
10432
     * Use default when empty.
10433
     */
10434
    protected $usedefaultwhenempty = true;
10435
 
10436
    /**
10437
     *
10438
     * @param string $name
10439
     * @param string $visiblename
10440
     * @param string $description
10441
     * @param string $defaultsetting
10442
     * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10443
     */
1441 ariadna 10444
    public function __construct($name, $visiblename, $description, $defaultsetting, ?array $previewconfig = null,
1 efrain 10445
            $usedefaultwhenempty = true) {
10446
        $this->previewconfig = $previewconfig;
10447
        $this->usedefaultwhenempty = $usedefaultwhenempty;
10448
        parent::__construct($name, $visiblename, $description, $defaultsetting);
10449
        $this->set_force_ltr(true);
10450
    }
10451
 
10452
    /**
10453
     * Return the setting
10454
     *
10455
     * @return mixed returns config if successful else null
10456
     */
10457
    public function get_setting() {
10458
        return $this->config_read($this->name);
10459
    }
10460
 
10461
    /**
10462
     * Saves the setting
10463
     *
10464
     * @param string $data
10465
     * @return string error message or empty string on success
10466
     */
10467
    public function write_setting($data) {
10468
        $data = $this->validate($data);
10469
        if ($data === false) {
10470
            return  get_string('validateerror', 'admin');
10471
        }
10472
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10473
    }
10474
 
10475
    /**
10476
     * Validates the colour that was entered by the user
10477
     *
10478
     * @param string $data
10479
     * @return string|false
10480
     */
10481
    protected function validate($data) {
10482
        /**
10483
         * List of valid HTML colour names
10484
         *
10485
         * @var array
10486
         */
10487
         $colornames = array(
10488
            'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10489
            'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10490
            'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10491
            'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10492
            'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10493
            'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10494
            'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10495
            'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10496
            'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10497
            'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10498
            'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10499
            'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10500
            'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10501
            'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10502
            'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10503
            'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10504
            'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10505
            'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10506
            'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10507
            'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10508
            'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10509
            'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10510
            'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10511
            'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10512
            'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10513
            'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10514
            'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10515
            'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10516
            'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10517
            'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10518
            'whitesmoke', 'yellow', 'yellowgreen'
10519
        );
10520
 
10521
        if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10522
            if (strpos($data, '#')!==0) {
10523
                $data = '#'.$data;
10524
            }
10525
            return $data;
10526
        } else if (in_array(strtolower($data), $colornames)) {
10527
            return $data;
10528
        } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10529
            return $data;
10530
        } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10531
            return $data;
10532
        } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10533
            return $data;
10534
        } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10535
            return $data;
10536
        } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10537
            return $data;
10538
        } else if (empty($data)) {
10539
            if ($this->usedefaultwhenempty){
10540
                return $this->defaultsetting;
10541
            } else {
10542
                return '';
10543
            }
10544
        } else {
10545
            return false;
10546
        }
10547
    }
10548
 
10549
    /**
10550
     * Generates the HTML for the setting
10551
     *
10552
     * @global moodle_page $PAGE
10553
     * @global core_renderer $OUTPUT
10554
     * @param string $data
10555
     * @param string $query
10556
     */
10557
    public function output_html($data, $query = '') {
10558
        global $PAGE, $OUTPUT;
10559
 
10560
        $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10561
        $context = (object) [
10562
            'id' => $this->get_id(),
10563
            'name' => $this->get_full_name(),
10564
            'value' => $data,
10565
            'icon' => $icon->export_for_template($OUTPUT),
10566
            'haspreviewconfig' => !empty($this->previewconfig),
10567
            'forceltr' => $this->get_force_ltr(),
10568
            'readonly' => $this->is_readonly(),
10569
        ];
10570
 
10571
        $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10572
        $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10573
 
10574
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10575
            $this->get_defaultsetting(), $query);
10576
    }
10577
 
10578
}
10579
 
10580
 
10581
/**
10582
 * Class used for uploading of one file into file storage,
10583
 * the file name is stored in config table.
10584
 *
10585
 * Please note you need to implement your own '_pluginfile' callback function,
10586
 * this setting only stores the file, it does not deal with file serving.
10587
 *
10588
 * @copyright 2013 Petr Skoda {@link http://skodak.org}
10589
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10590
 */
10591
class admin_setting_configstoredfile extends admin_setting {
10592
    /** @var array file area options - should be one file only */
10593
    protected $options;
10594
    /** @var string name of the file area */
10595
    protected $filearea;
10596
    /** @var int intemid */
10597
    protected $itemid;
10598
    /** @var string used for detection of changes */
10599
    protected $oldhashes;
10600
 
10601
    /**
10602
     * Create new stored file setting.
10603
     *
10604
     * @param string $name low level setting name
10605
     * @param string $visiblename human readable setting name
10606
     * @param string $description description of setting
10607
     * @param mixed $filearea file area for file storage
10608
     * @param int $itemid itemid for file storage
10609
     * @param array $options file area options
10610
     */
1441 ariadna 10611
    public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, ?array $options = null) {
1 efrain 10612
        parent::__construct($name, $visiblename, $description, '');
10613
        $this->filearea = $filearea;
10614
        $this->itemid   = $itemid;
10615
        $this->options  = (array)$options;
10616
        $this->customcontrol = true;
10617
    }
10618
 
10619
    /**
10620
     * Applies defaults and returns all options.
10621
     * @return array
10622
     */
10623
    protected function get_options() {
10624
        global $CFG;
10625
 
10626
        require_once("$CFG->libdir/filelib.php");
10627
        require_once("$CFG->dirroot/repository/lib.php");
10628
        $defaults = array(
10629
            'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10630
            'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10631
            'context' => context_system::instance());
10632
        foreach($this->options as $k => $v) {
10633
            $defaults[$k] = $v;
10634
        }
10635
 
10636
        return $defaults;
10637
    }
10638
 
10639
    public function get_setting() {
10640
        return $this->config_read($this->name);
10641
    }
10642
 
10643
    public function write_setting($data) {
10644
        global $USER;
10645
 
10646
        // Let's not deal with validation here, this is for admins only.
10647
        $current = $this->get_setting();
10648
        if (empty($data) && ($current === null || $current === '')) {
10649
            // This will be the case when applying default settings (installation).
10650
            return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10651
        } else if (!is_number($data)) {
10652
            // Draft item id is expected here!
10653
            return get_string('errorsetting', 'admin');
10654
        }
10655
 
10656
        $options = $this->get_options();
10657
        $fs = get_file_storage();
10658
        $component = is_null($this->plugin) ? 'core' : $this->plugin;
10659
 
10660
        $this->oldhashes = null;
10661
        if ($current) {
10662
            $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10663
            if ($file = $fs->get_file_by_hash($hash)) {
10664
                $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10665
            }
10666
            unset($file);
10667
        }
10668
 
10669
        if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10670
            // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10671
            // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10672
            // with an error because the draft area does not exist, as he did not use it.
10673
            $usercontext = context_user::instance($USER->id);
10674
            if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10675
                return get_string('errorsetting', 'admin');
10676
            }
10677
        }
10678
 
10679
        file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10680
        $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10681
 
10682
        $filepath = '';
10683
        if ($files) {
10684
            /** @var stored_file $file */
10685
            $file = reset($files);
10686
            $filepath = $file->get_filepath().$file->get_filename();
10687
        }
10688
 
10689
        return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10690
    }
10691
 
10692
    public function post_write_settings($original) {
10693
        $options = $this->get_options();
10694
        $fs = get_file_storage();
10695
        $component = is_null($this->plugin) ? 'core' : $this->plugin;
10696
 
10697
        $current = $this->get_setting();
10698
        $newhashes = null;
10699
        if ($current) {
10700
            $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10701
            if ($file = $fs->get_file_by_hash($hash)) {
10702
                $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10703
            }
10704
            unset($file);
10705
        }
10706
 
10707
        if ($this->oldhashes === $newhashes) {
10708
            $this->oldhashes = null;
10709
            return false;
10710
        }
10711
        $this->oldhashes = null;
10712
 
10713
        $callbackfunction = $this->updatedcallback;
10714
        if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10715
            $callbackfunction($this->get_full_name());
10716
        }
10717
        return true;
10718
    }
10719
 
10720
    public function output_html($data, $query = '') {
10721
        global $CFG;
10722
 
10723
        $options = $this->get_options();
10724
        $id = $this->get_id();
10725
        $elname = $this->get_full_name();
10726
        $draftitemid = file_get_submitted_draft_itemid($elname);
10727
        $component = is_null($this->plugin) ? 'core' : $this->plugin;
10728
        file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10729
 
10730
        // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10731
        require_once("$CFG->dirroot/lib/form/filemanager.php");
10732
 
10733
        $fmoptions = new stdClass();
10734
        $fmoptions->mainfile       = $options['mainfile'];
10735
        $fmoptions->maxbytes       = $options['maxbytes'];
10736
        $fmoptions->maxfiles       = $options['maxfiles'];
10737
        $fmoptions->subdirs        = $options['subdirs'];
10738
        $fmoptions->accepted_types = $options['accepted_types'];
10739
        $fmoptions->return_types   = $options['return_types'];
10740
        $fmoptions->context        = $options['context'];
10741
        $fmoptions->areamaxbytes   = $options['areamaxbytes'];
10742
 
10743
        $fm = new MoodleQuickForm_filemanager($elname, $this->visiblename, ['id' => $id], $fmoptions);
10744
        $fm->setValue($draftitemid);
10745
 
10746
        return format_admin_setting($this, $this->visiblename,
10747
            '<div class="form-filemanager" data-fieldtype="filemanager">' . $fm->toHtml() . '</div>',
10748
            $this->description, true, '', '', $query);
10749
    }
10750
}
10751
 
10752
 
10753
/**
10754
 * Administration interface for user specified regular expressions for device detection.
10755
 *
10756
 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10757
 * @todo Final deprecation on Moodle 4.7 MDL-79052
10758
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10759
 */
10760
class admin_setting_devicedetectregex extends admin_setting {
10761
 
10762
    /**
10763
     * Calls parent::__construct with specific args
10764
     *
10765
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10766
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10767
     * @param string $name
10768
     * @param string $visiblename
10769
     * @param string $description
10770
     * @param mixed $defaultsetting
10771
     */
10772
    public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10773
        debugging(
10774
            __FUNCTION__ . '() is deprecated.' .
10775
                'All functions associated with devicedetectregex theme setting are being removed.',
10776
            DEBUG_DEVELOPER
10777
        );
10778
        global $CFG;
10779
        parent::__construct($name, $visiblename, $description, $defaultsetting);
10780
    }
10781
 
10782
    /**
10783
     * Return the current setting(s)
10784
     *
10785
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10786
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10787
     * @return ?array Current settings array
10788
     */
10789
    public function get_setting() {
10790
        debugging(
10791
            __FUNCTION__ . '() is deprecated.' .
10792
                'All functions associated with devicedetectregex theme setting are being removed.',
10793
            DEBUG_DEVELOPER
10794
        );
10795
        global $CFG;
10796
 
10797
        $config = $this->config_read($this->name);
10798
        if (is_null($config)) {
10799
            return null;
10800
        }
10801
 
10802
        return $this->prepare_form_data($config);
10803
    }
10804
 
10805
    /**
10806
     * Save selected settings
10807
     *
10808
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10809
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10810
     * @param array $data Array of settings to save
10811
     * @return string error message or empty string on success
10812
     */
10813
    public function write_setting($data) {
10814
        debugging(
10815
            __FUNCTION__ . '() is deprecated.' .
10816
                'All functions associated with devicedetectregex theme setting are being removed.',
10817
            DEBUG_DEVELOPER
10818
        );
10819
        if (empty($data)) {
10820
            $data = array();
10821
        }
10822
 
10823
        if ($this->config_write($this->name, $this->process_form_data($data))) {
10824
            return ''; // success
10825
        } else {
10826
            return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10827
        }
10828
    }
10829
 
10830
    /**
10831
     * Return XHTML field(s) for regexes
10832
     *
10833
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10834
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10835
     * @param array $data Array of options to set in HTML
10836
     * @return string XHTML string for the fields and wrapping div(s)
10837
     */
10838
    public function output_html($data, $query='') {
10839
        debugging(
10840
            __FUNCTION__ . '() is deprecated.' .
10841
                'All functions associated with devicedetectregex theme setting are being removed.',
10842
            DEBUG_DEVELOPER
10843
        );
10844
        global $OUTPUT;
10845
 
10846
        $context = (object) [
10847
            'expressions' => [],
10848
            'name' => $this->get_full_name()
10849
        ];
10850
 
10851
        if (empty($data)) {
10852
            $looplimit = 1;
10853
        } else {
10854
            $looplimit = (count($data)/2)+1;
10855
        }
10856
 
10857
        for ($i=0; $i<$looplimit; $i++) {
10858
 
10859
            $expressionname = 'expression'.$i;
10860
 
10861
            if (!empty($data[$expressionname])){
10862
                $expression = $data[$expressionname];
10863
            } else {
10864
                $expression = '';
10865
            }
10866
 
10867
            $valuename = 'value'.$i;
10868
 
10869
            if (!empty($data[$valuename])){
10870
                $value = $data[$valuename];
10871
            } else {
10872
                $value= '';
10873
            }
10874
 
10875
            $context->expressions[] = [
10876
                'index' => $i,
10877
                'expression' => $expression,
10878
                'value' => $value
10879
            ];
10880
        }
10881
 
10882
        $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10883
 
10884
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10885
    }
10886
 
10887
    /**
10888
     * Converts the string of regexes
10889
     *
10890
     * @see self::process_form_data()
10891
     * @param $regexes string of regexes
10892
     * @return array of form fields and their values
10893
     */
10894
    protected function prepare_form_data($regexes) {
10895
 
10896
        $regexes = json_decode($regexes);
10897
 
10898
        $form = array();
10899
 
10900
        $i = 0;
10901
 
10902
        foreach ($regexes as $value => $regex) {
10903
            $expressionname  = 'expression'.$i;
10904
            $valuename = 'value'.$i;
10905
 
10906
            $form[$expressionname] = $regex;
10907
            $form[$valuename] = $value;
10908
            $i++;
10909
        }
10910
 
10911
        return $form;
10912
    }
10913
 
10914
    /**
10915
     * Converts the data from admin settings form into a string of regexes
10916
     *
10917
     * @see self::prepare_form_data()
10918
     * @param array $data array of admin form fields and values
10919
     * @return false|string of regexes
10920
     */
10921
    protected function process_form_data(array $form) {
10922
 
10923
        $count = count($form); // number of form field values
10924
 
10925
        if ($count % 2) {
10926
            // we must get five fields per expression
10927
            return false;
10928
        }
10929
 
10930
        $regexes = array();
10931
        for ($i = 0; $i < $count / 2; $i++) {
10932
            $expressionname  = "expression".$i;
10933
            $valuename       = "value".$i;
10934
 
10935
            $expression = trim($form['expression'.$i]);
10936
            $value      = trim($form['value'.$i]);
10937
 
10938
            if (empty($expression)){
10939
                continue;
10940
            }
10941
 
10942
            $regexes[$value] = $expression;
10943
        }
10944
 
10945
        $regexes = json_encode($regexes);
10946
 
10947
        return $regexes;
10948
    }
10949
 
10950
}
10951
 
10952
/**
10953
 * Multiselect for current modules
10954
 *
10955
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10956
 */
10957
class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10958
    private $excludesystem;
10959
 
10960
    /**
10961
     * Calls parent::__construct - note array $choices is not required
10962
     *
10963
     * @param string $name setting name
10964
     * @param string $visiblename localised setting name
10965
     * @param string $description setting description
10966
     * @param array $defaultsetting a plain array of default module ids
10967
     * @param bool $excludesystem If true, excludes modules with 'system' archetype
10968
     */
10969
    public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10970
            $excludesystem = true) {
10971
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10972
        $this->excludesystem = $excludesystem;
10973
    }
10974
 
10975
    /**
10976
     * Loads an array of current module choices
10977
     *
10978
     * @return bool always return true
10979
     */
10980
    public function load_choices() {
10981
        if (is_array($this->choices)) {
10982
            return true;
10983
        }
10984
        $this->choices = array();
10985
 
10986
        global $CFG, $DB;
10987
        $records = $DB->get_records('modules', array('visible'=>1), 'name');
10988
        foreach ($records as $record) {
10989
            // Exclude modules if the code doesn't exist
10990
            if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
10991
                // Also exclude system modules (if specified)
10992
                if (!($this->excludesystem &&
10993
                        plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
10994
                        MOD_ARCHETYPE_SYSTEM)) {
10995
                    $this->choices[$record->id] = $record->name;
10996
                }
10997
            }
10998
        }
10999
        return true;
11000
    }
11001
}
11002
 
11003
/**
11004
 * Admin setting to show if a php extension is enabled or not.
11005
 *
11006
 * @copyright 2013 Damyon Wiese
11007
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11008
 */
11009
class admin_setting_php_extension_enabled extends admin_setting {
11010
 
11011
    /** @var string The name of the extension to check for */
11012
    private $extension;
11013
 
11014
    /**
11015
     * Calls parent::__construct with specific arguments
11016
     */
11017
    public function __construct($name, $visiblename, $description, $extension) {
11018
        $this->extension = $extension;
11019
        $this->nosave = true;
11020
        parent::__construct($name, $visiblename, $description, '');
11021
    }
11022
 
11023
    /**
11024
     * Always returns true, does nothing
11025
     *
11026
     * @return true
11027
     */
11028
    public function get_setting() {
11029
        return true;
11030
    }
11031
 
11032
    /**
11033
     * Always returns true, does nothing
11034
     *
11035
     * @return true
11036
     */
11037
    public function get_defaultsetting() {
11038
        return true;
11039
    }
11040
 
11041
    /**
11042
     * Always returns '', does not write anything
11043
     *
11044
     * @return string Always returns ''
11045
     */
11046
    public function write_setting($data) {
11047
        // Do not write any setting.
11048
        return '';
11049
    }
11050
 
11051
    /**
11052
     * Outputs the html for this setting.
11053
     * @return string Returns an XHTML string
11054
     */
11055
    public function output_html($data, $query='') {
11056
        global $OUTPUT;
11057
 
11058
        $o = '';
11059
        if (!extension_loaded($this->extension)) {
11060
            $warning = $OUTPUT->pix_icon('i/warning', '') . ' ' . $this->description;
11061
 
11062
            $o .= format_admin_setting($this, $this->visiblename, $warning);
11063
        }
11064
        return $o;
11065
    }
11066
}
11067
 
11068
/**
11069
 * Server timezone setting.
11070
 *
11071
 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
11072
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11073
 * @author    Petr Skoda <petr.skoda@totaralms.com>
11074
 */
11075
class admin_setting_servertimezone extends admin_setting_configselect {
11076
    /**
11077
     * Constructor.
11078
     */
11079
    public function __construct() {
11080
        $default = core_date::get_default_php_timezone();
11081
        if ($default === 'UTC') {
11082
            // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
11083
            $default = 'Europe/London';
11084
        }
11085
 
11086
        parent::__construct('timezone',
11087
            new lang_string('timezone', 'core_admin'),
11088
            new lang_string('configtimezone', 'core_admin'), $default, null);
11089
    }
11090
 
11091
    /**
11092
     * Lazy load timezone options.
11093
     * @return bool true if loaded, false if error
11094
     */
11095
    public function load_choices() {
11096
        global $CFG;
11097
        if (is_array($this->choices)) {
11098
            return true;
11099
        }
11100
 
11101
        $current = isset($CFG->timezone) ? $CFG->timezone : null;
11102
        $this->choices = core_date::get_list_of_timezones($current, false);
11103
        if ($current == 99) {
11104
            // Do not show 99 unless it is current value, we want to get rid of it over time.
11105
            $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
11106
                core_date::get_default_php_timezone());
11107
        }
11108
 
11109
        return true;
11110
    }
11111
}
11112
 
11113
/**
11114
 * Forced user timezone setting.
11115
 *
11116
 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
11117
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11118
 * @author    Petr Skoda <petr.skoda@totaralms.com>
11119
 */
11120
class admin_setting_forcetimezone extends admin_setting_configselect {
11121
    /**
11122
     * Constructor.
11123
     */
11124
    public function __construct() {
11125
        parent::__construct('forcetimezone',
11126
            new lang_string('forcetimezone', 'core_admin'),
11127
            new lang_string('helpforcetimezone', 'core_admin'), '99', null);
11128
    }
11129
 
11130
    /**
11131
     * Lazy load timezone options.
11132
     * @return bool true if loaded, false if error
11133
     */
11134
    public function load_choices() {
11135
        global $CFG;
11136
        if (is_array($this->choices)) {
11137
            return true;
11138
        }
11139
 
11140
        $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
11141
        $this->choices = core_date::get_list_of_timezones($current, true);
11142
        $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
11143
 
11144
        return true;
11145
    }
11146
}
11147
 
11148
 
11149
/**
11150
 * Search setup steps info.
11151
 *
11152
 * @package core
11153
 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
11154
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11155
 */
11156
class admin_setting_searchsetupinfo extends admin_setting {
11157
 
11158
    /**
11159
     * Calls parent::__construct with specific arguments
11160
     */
11161
    public function __construct() {
11162
        $this->nosave = true;
11163
        parent::__construct('searchsetupinfo', '', '', '');
11164
    }
11165
 
11166
    /**
11167
     * Always returns true, does nothing
11168
     *
11169
     * @return true
11170
     */
11171
    public function get_setting() {
11172
        return true;
11173
    }
11174
 
11175
    /**
11176
     * Always returns true, does nothing
11177
     *
11178
     * @return true
11179
     */
11180
    public function get_defaultsetting() {
11181
        return true;
11182
    }
11183
 
11184
    /**
11185
     * Always returns '', does not write anything
11186
     *
11187
     * @param array $data
11188
     * @return string Always returns ''
11189
     */
11190
    public function write_setting($data) {
11191
        // Do not write any setting.
11192
        return '';
11193
    }
11194
 
11195
    /**
11196
     * Builds the HTML to display the control
11197
     *
11198
     * @param string $data Unused
11199
     * @param string $query
11200
     * @return string
11201
     */
11202
    public function output_html($data, $query='') {
11203
        global $CFG, $OUTPUT, $ADMIN;
11204
 
11205
        $return = '';
11206
        $brtag = html_writer::empty_tag('br');
11207
 
11208
        $searchareas = \core_search\manager::get_search_areas_list();
11209
        $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
11210
        $anyindexed = false;
11211
        foreach ($searchareas as $areaid => $searcharea) {
11212
            list($componentname, $varname) = $searcharea->get_config_var_name();
11213
            if (get_config($componentname, $varname . '_indexingstart')) {
11214
                $anyindexed = true;
11215
                break;
11216
            }
11217
        }
11218
 
11219
        $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
11220
 
11221
        $table = new html_table();
11222
        $table->head = array(get_string('step', 'search'), get_string('status'));
11223
        $table->colclasses = array('leftalign step', 'leftalign status');
11224
        $table->id = 'searchsetup';
11225
        $table->attributes['class'] = 'admintable generaltable';
11226
        $table->data = array();
11227
 
11228
        $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
11229
 
11230
        // Select a search engine.
11231
        $row = array();
11232
        $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
11233
        $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
11234
                        array('href' => $url));
11235
 
11236
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11237
        if (!empty($CFG->searchengine)) {
11238
            $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
11239
                array('class' => 'badge bg-success text-white'));
11240
 
11241
        }
11242
        $row[1] = $status;
11243
        $table->data[] = $row;
11244
 
11245
        // Available areas.
11246
        $row = array();
11247
        $url = new moodle_url('/admin/searchareas.php');
11248
        $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
11249
                        array('href' => $url));
11250
 
11251
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11252
        if ($anyenabled) {
11253
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11254
 
11255
        }
11256
        $row[1] = $status;
11257
        $table->data[] = $row;
11258
 
11259
        // Setup search engine.
11260
        $row = array();
11261
        if (empty($CFG->searchengine)) {
11262
            $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
11263
            $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11264
        } else {
11265
            if ($ADMIN->locate('search' . $CFG->searchengine)) {
11266
                $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
11267
                $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
11268
            } else {
11269
                $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
11270
            }
11271
 
11272
            // Check the engine status.
11273
            $searchengine = \core_search\manager::search_engine_instance();
11274
            try {
11275
                $serverstatus = $searchengine->is_server_ready();
11276
            } catch (\moodle_exception $e) {
11277
                $serverstatus = $e->getMessage();
11278
            }
11279
            if ($serverstatus === true) {
11280
                $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11281
            } else {
11282
                $status = html_writer::tag('span', $serverstatus, array('class' => 'badge bg-danger text-white'));
11283
            }
11284
            $row[1] = $status;
11285
        }
11286
        $table->data[] = $row;
11287
 
11288
        // Indexed data.
11289
        $row = array();
11290
        $url = new moodle_url('/admin/searchareas.php');
11291
        $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11292
        if ($anyindexed) {
11293
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11294
        } else {
11295
            $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11296
        }
11297
        $row[1] = $status;
11298
        $table->data[] = $row;
11299
 
11300
        // Enable global search.
11301
        $row = array();
11302
        $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11303
        $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11304
                        array('href' => $url));
11305
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11306
        if (\core_search\manager::is_global_search_enabled()) {
11307
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11308
        }
11309
        $row[1] = $status;
11310
        $table->data[] = $row;
11311
 
11312
        // Replace front page search.
11313
        $row = array();
11314
        $url = new moodle_url("/admin/search.php?query=searchincludeallcourses");
11315
        $row[0] = '6. ' . html_writer::tag('a', get_string('replacefrontsearch', 'admin'),
11316
                                           array('href' => $url));
11317
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11318
        if (\core_search\manager::can_replace_course_search()) {
11319
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11320
        }
11321
        $row[1] = $status;
11322
        $table->data[] = $row;
11323
 
11324
        $return .= html_writer::table($table);
11325
 
11326
        return highlight($query, $return);
11327
    }
11328
 
11329
}
11330
 
11331
/**
11332
 * Used to validate the contents of SCSS code and ensuring they are parsable.
11333
 *
11334
 * It does not attempt to detect undefined SCSS variables because it is designed
11335
 * to be used without knowledge of other config/scss included.
11336
 *
11337
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11338
 * @copyright 2016 Dan Poltawski <dan@moodle.com>
11339
 */
11340
class admin_setting_scsscode extends admin_setting_configtextarea {
11341
 
11342
    /**
11343
     * Validate the contents of the SCSS to ensure its parsable. Does not
11344
     * attempt to detect undefined scss variables.
11345
     *
11346
     * @param string $data The scss code from text field.
11347
     * @return mixed bool true for success or string:error on failure.
11348
     */
11349
    public function validate($data) {
11350
        if (empty($data)) {
11351
            return true;
11352
        }
11353
 
11354
        $scss = new core_scss();
11355
        try {
11356
            $scss->compile($data);
11357
        } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
11358
            return get_string('scssinvalid', 'admin', $e->getMessage());
11359
        } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
11360
            // Silently ignore this - it could be a scss variable defined from somewhere
11361
            // else which we are not examining here.
11362
            return true;
11363
        }
11364
 
11365
        return true;
11366
    }
11367
}
11368
 
11369
 
11370
/**
11371
 * Administration setting to define a list of file types.
11372
 *
11373
 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
11374
 * @copyright 2017 David Mudrák <david@moodle.com>
11375
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11376
 */
11377
class admin_setting_filetypes extends admin_setting_configtext {
11378
 
11379
    /** @var array Allow selection from these file types only. */
11380
    protected $onlytypes = [];
11381
 
11382
    /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
11383
    protected $allowall = true;
11384
 
11385
    /** @var core_form\filetypes_util instance to use as a helper. */
11386
    protected $util = null;
11387
 
11388
    /**
11389
     * Constructor.
11390
     *
11391
     * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
11392
     * @param string $visiblename Localised label of the setting
11393
     * @param string $description Localised description of the setting
11394
     * @param string $defaultsetting Default setting value.
11395
     * @param array $options Setting widget options, an array with optional keys:
11396
     *   'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
11397
     *   'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
11398
     */
11399
    public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
11400
 
11401
        parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
11402
 
11403
        if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
11404
            $this->onlytypes = $options['onlytypes'];
11405
        }
11406
 
11407
        if (!$this->onlytypes && array_key_exists('allowall', $options)) {
11408
            $this->allowall = (bool)$options['allowall'];
11409
        }
11410
 
11411
        $this->util = new \core_form\filetypes_util();
11412
    }
11413
 
11414
    /**
11415
     * Normalize the user's input and write it to the database as comma separated list.
11416
     *
11417
     * Comma separated list as a text representation of the array was chosen to
11418
     * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
11419
     *
11420
     * @param string $data Value submitted by the admin.
11421
     * @return string Epty string if all good, error message otherwise.
11422
     */
11423
    public function write_setting($data) {
11424
        return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
11425
    }
11426
 
11427
    /**
11428
     * Validate data before storage
11429
     *
11430
     * @param string $data The setting values provided by the admin
11431
     * @return bool|string True if ok, the string if error found
11432
     */
11433
    public function validate($data) {
11434
        $parentcheck = parent::validate($data);
11435
        if ($parentcheck !== true) {
11436
            return $parentcheck;
11437
        }
11438
 
11439
        // Check for unknown file types.
11440
        if ($unknown = $this->util->get_unknown_file_types($data)) {
11441
            return get_string('filetypesunknown', 'core_form', implode(', ', $unknown));
11442
        }
11443
 
11444
        // Check for disallowed file types.
11445
        if ($notlisted = $this->util->get_not_listed($data, $this->onlytypes)) {
11446
            return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted));
11447
        }
11448
 
11449
        return true;
11450
    }
11451
 
11452
    /**
11453
     * Return an HTML string for the setting element.
11454
     *
11455
     * @param string $data The current setting value
11456
     * @param string $query Admin search query to be highlighted
11457
     * @return string HTML to be displayed
11458
     */
11459
    public function output_html($data, $query='') {
11460
        global $OUTPUT, $PAGE;
11461
 
11462
        $default = $this->get_defaultsetting();
11463
        $context = (object) [
11464
            'id' => $this->get_id(),
11465
            'name' => $this->get_full_name(),
11466
            'value' => $data,
11467
            'descriptions' => $this->util->describe_file_types($data),
11468
        ];
11469
        $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11470
 
11471
        $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11472
            $this->get_id(),
11473
            $this->visiblename->out(),
11474
            $this->onlytypes,
11475
            $this->allowall,
11476
        ]);
11477
 
11478
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11479
    }
11480
 
11481
    /**
11482
     * Should the values be always displayed in LTR mode?
11483
     *
11484
     * We always return true here because these values are not RTL compatible.
11485
     *
11486
     * @return bool True because these values are not RTL compatible.
11487
     */
11488
    public function get_force_ltr() {
11489
        return true;
11490
    }
11491
}
11492
 
11493
/**
11494
 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11495
 *
11496
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11497
 * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11498
 */
11499
class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11500
 
11501
    /**
11502
     * Constructor.
11503
     *
11504
     * @param string $name
11505
     * @param string $visiblename
11506
     * @param string $description
11507
     * @param mixed $defaultsetting string or array
11508
     * @param mixed $paramtype
11509
     * @param string $cols
11510
     * @param string $rows
11511
     */
11512
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11513
                                $cols = '60', $rows = '8') {
11514
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11515
        // Pre-set force LTR to false.
11516
        $this->set_force_ltr(false);
11517
    }
11518
 
11519
    /**
11520
     * Validate the content and format of the age of digital consent map to ensure it is parsable.
11521
     *
11522
     * @param string $data The age of digital consent map from text field.
11523
     * @return mixed bool true for success or string:error on failure.
11524
     */
11525
    public function validate($data) {
11526
        if (empty($data)) {
11527
            return true;
11528
        }
11529
 
11530
        try {
11531
            \core_auth\digital_consent::parse_age_digital_consent_map($data);
11532
        } catch (\moodle_exception $e) {
11533
            return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11534
        }
11535
 
11536
        return true;
11537
    }
11538
}
11539
 
11540
/**
11541
 * Selection of plugins that can work as site policy handlers
11542
 *
11543
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11544
 * @copyright 2018 Marina Glancy
11545
 */
11546
class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11547
 
11548
    /**
11549
     * Constructor
11550
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11551
     *        for ones in config_plugins.
11552
     * @param string $visiblename localised
11553
     * @param string $description long localised info
11554
     * @param string $defaultsetting
11555
     */
11556
    public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11557
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11558
    }
11559
 
11560
    /**
11561
     * Lazy-load the available choices for the select box
11562
     */
11563
    public function load_choices() {
11564
        if (during_initial_install()) {
11565
            return false;
11566
        }
11567
        if (is_array($this->choices)) {
11568
            return true;
11569
        }
11570
 
11571
        $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11572
        $manager = new \core_privacy\local\sitepolicy\manager();
11573
        $plugins = $manager->get_all_handlers();
11574
        foreach ($plugins as $pname => $unused) {
11575
            $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11576
                ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11577
        }
11578
 
11579
        return true;
11580
    }
11581
}
11582
 
11583
/**
11584
 * Used to validate theme presets code and ensuring they compile well.
11585
 *
11586
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11587
 * @copyright 2019 Bas Brands <bas@moodle.com>
11588
 */
11589
class admin_setting_configthemepreset extends admin_setting_configselect {
11590
 
11591
    /** @var string The name of the theme to check for */
11592
    private $themename;
11593
 
11594
    /**
11595
     * Constructor
11596
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11597
     * or 'myplugin/mysetting' for ones in config_plugins.
11598
     * @param string $visiblename localised
11599
     * @param string $description long localised info
11600
     * @param string|int $defaultsetting
11601
     * @param array $choices array of $value=>$label for each selection
11602
     * @param string $themename name of theme to check presets for.
11603
     */
11604
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11605
        $this->themename = $themename;
11606
        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11607
    }
11608
 
11609
    /**
11610
     * Write settings if validated
11611
     *
11612
     * @param string $data
11613
     * @return string
11614
     */
11615
    public function write_setting($data) {
11616
        $validated = $this->validate($data);
11617
        if ($validated !== true) {
11618
            return $validated;
11619
        }
11620
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11621
    }
11622
 
11623
    /**
11624
     * Validate the preset file to ensure its parsable.
11625
     *
11626
     * @param string $data The preset file chosen.
11627
     * @return mixed bool true for success or string:error on failure.
11628
     */
11629
    public function validate($data) {
11630
 
11631
        if (in_array($data, ['default.scss', 'plain.scss'])) {
11632
            return true;
11633
        }
11634
 
11635
        $fs = get_file_storage();
11636
        $theme = theme_config::load($this->themename);
11637
        $context = context_system::instance();
11638
 
11639
        // If the preset has not changed there is no need to validate it.
11640
        if ($theme->settings->preset == $data) {
11641
            return true;
11642
        }
11643
 
11644
        if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11645
            // This operation uses a lot of resources.
11646
            raise_memory_limit(MEMORY_EXTRA);
11647
            core_php_time_limit::raise(300);
11648
 
11649
            // TODO: MDL-62757 When changing anything in this method please do not forget to check
11650
            // if the get_css_content_from_scss() method in class theme_config needs updating too.
11651
 
11652
            $compiler = new core_scss();
11653
            $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11654
            $compiler->append_raw_scss($presetfile->get_content());
11655
            if ($scssproperties = $theme->get_scss_property()) {
11656
                $compiler->setImportPaths($scssproperties[0]);
11657
            }
11658
            $compiler->append_raw_scss($theme->get_extra_scss_code());
11659
 
11660
            try {
11661
                $compiler->to_css();
11662
            } catch (Exception $e) {
11663
                return get_string('invalidthemepreset', 'admin', $e->getMessage());
11664
            }
11665
 
11666
            // Try to save memory.
11667
            $compiler = null;
11668
            unset($compiler);
11669
        }
11670
 
11671
        return true;
11672
    }
11673
}
11674
 
11675
/**
11676
 * Selection of plugins that can work as H5P libraries handlers
11677
 *
11678
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11679
 * @copyright 2020 Sara Arjona <sara@moodle.com>
11680
 */
11681
class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11682
 
11683
    /**
11684
     * Constructor
11685
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11686
     *        for ones in config_plugins.
11687
     * @param string $visiblename localised
11688
     * @param string $description long localised info
11689
     * @param string $defaultsetting
11690
     */
11691
    public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11692
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11693
    }
11694
 
11695
    /**
11696
     * Lazy-load the available choices for the select box
11697
     */
11698
    public function load_choices() {
11699
        if (during_initial_install()) {
11700
            return false;
11701
        }
11702
        if (is_array($this->choices)) {
11703
            return true;
11704
        }
11705
 
11706
        $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11707
        foreach ($this->choices as $name => $class) {
11708
            $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11709
                ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11710
        }
11711
 
11712
        return true;
11713
    }
11714
}
11715
 
11716
/**
11717
 * Displays the result of a check via ajax.
11718
 *
11719
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11720
 * @author Matthew Hilton <matthewhilton@catalyst-au.net>
11721
 * @copyright Catalyst IT, 2023
11722
 */
11723
class admin_setting_check extends admin_setting {
11724
 
11725
    /** @var \core\check\check $check the check to use **/
11726
    private $check;
11727
 
11728
    /** @var bool $includedetails if the details of result are included. **/
11729
    private $includedetails;
11730
 
11731
    /**
11732
     * Creates check setting.
11733
     *
11734
     * @param string $name name of setting
11735
     * @param \core\check\check $check The check linked to this setting.
11736
     * @param bool $includedetails if the details of the result are included
11737
     */
11738
    public function __construct(string $name, \core\check\check $check, bool $includedetails = false) {
11739
        $this->check = $check;
11740
        $this->includedetails = $includedetails;
11741
        $heading = $check->get_name();
11742
 
11743
        parent::__construct($name, $heading, '', '');
11744
    }
11745
 
11746
    /**
11747
     * Returns the check linked to this setting.
11748
     *
11749
     * @return \core\check\check
11750
     */
11751
    public function get_check() {
11752
        return $this->check;
11753
    }
11754
 
11755
    /**
11756
     * Returns setting (unused)
11757
     *
11758
     * @return true
11759
     */
11760
    public function get_setting() {
11761
        return true;
11762
    }
11763
 
11764
    /**
11765
     * Writes the setting (unused)
11766
     *
11767
     * @param mixed $data
11768
     */
11769
    public function write_setting($data) {
11770
        return '';
11771
    }
11772
 
11773
    /**
11774
     * Outputs the admin setting HTML to be rendered.
11775
     *
11776
     * @param mixed $data
11777
     * @param string $query
11778
     * @return string html
11779
     */
11780
    public function output_html($data, $query = '') {
11781
        global $PAGE, $OUTPUT;
11782
 
11783
        $domref = uniqid($this->check->get_ref());
11784
 
11785
        // The actual result is obtained via ajax,
11786
        // since its likely somewhat slow to obtain.
11787
        $context = [
11788
            'domselector' => '[data-check-reference="' . $domref . '"]',
11789
            'admintreeid' => $this->get_id(),
11790
            'settingname' => $this->name,
11791
            'includedetails' => $this->includedetails,
11792
        ];
11793
        $PAGE->requires->js_call_amd('core/check/check_result', 'getAndRender', $context);
11794
 
11795
        // Render a generic loading icon while waiting for ajax.
11796
        $loadingstr = get_string('checkloading', '', $this->check->get_name());
11797
        $loadingicon = $OUTPUT->pix_icon('i/loading', $loadingstr);
11798
 
11799
        // Wrap it in a notification so we reduce style changes when loading is finished.
11800
        $output = $OUTPUT->notification($loadingicon . $loadingstr, \core\output\notification::NOTIFY_INFO, false);
11801
 
11802
        // Add the action link.
1441 ariadna 11803
        if ($actionlink = $this->check->get_action_link()) {
11804
            $output .= $OUTPUT->render($actionlink);
11805
        }
1 efrain 11806
 
11807
        // Wrap in a div with a reference. The JS getAndRender will replace this with the response from the webservice.
11808
        $statusdiv = \html_writer::div($output, '', ['data-check-reference' => $domref]);
11809
 
11810
        return format_admin_setting($this, $this->visiblename, '', $statusdiv);
11811
    }
11812
}
11813
 
1441 ariadna 11814
 
11815
/**
11816
 * Show the save changes button.
11817
 */
11818
class admin_setting_savebutton extends admin_setting {
11819
    /**
11820
     * Constructor.
11821
     *
11822
     * @param string $name unique ascii name.
11823
     * @param string $visiblename localised name.
11824
     * @param string $description localised long description.
11825
     * @param mixed $defaultsetting string or array depending on implementation.
11826
     */
11827
    public function __construct(string $name, string $visiblename = "", string $description = "", $defaultsetting = "") {
11828
        $this->nosave = true;
11829
        parent::__construct($name, $visiblename, $description, $defaultsetting);
11830
    }
11831
 
11832
    /**
11833
     * Always returns true, does nothing.
11834
     *
11835
     * @return bool Always return true.
11836
     */
11837
    public function get_setting(): bool {
11838
        return true;
11839
    }
11840
 
11841
    /**
11842
     * Always returns '', does not write anything.
11843
     *
11844
     * @param mixed $data string or array, must not be NULL.
11845
     * @return string Always returns ''.
11846
     */
11847
    public function write_setting($data): string {
11848
        return '';
11849
    }
11850
 
11851
    /**
11852
     * Return part of form with setting.
11853
     *
11854
     * This function should always be overwritten.
11855
     *
11856
     * @param mixed $data array or string depending on setting.
11857
     * @param string $query
11858
     * @return string
11859
     */
11860
    public function output_html($data, $query = ''): string {
11861
        global $OUTPUT;
11862
        return $OUTPUT->render_from_template('core_admin/setting_savebutton', []);
11863
    }
11864
}