Proyectos de Subversion Moodle

Rev

| 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);
142
 
143
        $subpluginsfile = "{$base}/db/subplugins.json";
144
        if (file_exists($subpluginsfile)) {
145
            $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
146
        } else if (file_exists("{$base}/db/subplugins.php")) {
147
            debugging('Use of subplugins.php has been deprecated. ' .
148
                    'Please update your plugin to provide a subplugins.json file instead.',
149
                    DEBUG_DEVELOPER);
150
            $subplugins = [];
151
            include("{$base}/db/subplugins.php");
152
        }
153
 
154
        if (!empty($subplugins)) {
155
            foreach (array_keys($subplugins) as $subplugintype) {
156
                $instances = core_component::get_plugin_list($subplugintype);
157
                foreach ($instances as $subpluginname => $notusedpluginpath) {
158
                    uninstall_plugin($subplugintype, $subpluginname);
159
                }
160
            }
161
        }
162
    }
163
 
164
    $component = $type . '_' . $name;  // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
165
 
166
    if ($type === 'mod') {
167
        $pluginname = $name;  // eg. 'forum'
168
        if (get_string_manager()->string_exists('modulename', $component)) {
169
            $strpluginname = get_string('modulename', $component);
170
        } else {
171
            $strpluginname = $component;
172
        }
173
 
174
    } else {
175
        $pluginname = $component;
176
        if (get_string_manager()->string_exists('pluginname', $component)) {
177
            $strpluginname = get_string('pluginname', $component);
178
        } else {
179
            $strpluginname = $component;
180
        }
181
    }
182
 
183
    echo $OUTPUT->heading($pluginname);
184
 
185
    // Delete all tag areas, collections and instances associated with this plugin.
186
    core_tag_area::uninstall($component);
187
 
188
    // Custom plugin uninstall.
189
    $plugindirectory = core_component::get_plugin_directory($type, $name);
190
    $uninstalllib = $plugindirectory . '/db/uninstall.php';
191
    if (file_exists($uninstalllib)) {
192
        require_once($uninstalllib);
193
        $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall';    // eg. 'xmldb_workshop_uninstall()'
194
        if (function_exists($uninstallfunction)) {
195
            // Do not verify result, let plugin complain if necessary.
196
            $uninstallfunction();
197
        }
198
    }
199
 
200
    // Specific plugin type cleanup.
201
    $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
202
    if ($plugininfo) {
203
        $plugininfo->uninstall_cleanup();
204
        core_plugin_manager::reset_caches();
205
    }
206
    $plugininfo = null;
207
 
208
    // Perform clean-up task common for all the plugin/subplugin types.
209
 
210
    // Delete the web service functions and pre-built services.
211
    \core_external\util::delete_service_descriptions($component);
212
 
213
    // delete calendar events
214
    $DB->delete_records('event', array('modulename' => $pluginname));
215
    $DB->delete_records('event', ['component' => $component]);
216
 
217
    // Delete scheduled tasks.
218
    $DB->delete_records('task_adhoc', ['component' => $component]);
219
    $DB->delete_records('task_scheduled', array('component' => $component));
220
 
221
    // Delete Inbound Message datakeys.
222
    $DB->delete_records_select('messageinbound_datakeys',
223
            'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
224
 
225
    // Delete Inbound Message handlers.
226
    $DB->delete_records('messageinbound_handlers', array('component' => $component));
227
 
228
    // delete all the logs
229
    $DB->delete_records('log', array('module' => $pluginname));
230
 
231
    // delete log_display information
232
    $DB->delete_records('log_display', array('component' => $component));
233
 
234
    // delete the module configuration records
235
    unset_all_config_for_plugin($component);
236
    if ($type === 'mod') {
237
        unset_all_config_for_plugin($pluginname);
238
    }
239
 
240
    // Wipe any xAPI state information.
241
    if (core_xapi\handler::supports_xapi($component)) {
242
        core_xapi\api::remove_states_from_component($component);
243
    }
244
 
245
    // delete message provider
246
    message_provider_uninstall($component);
247
 
248
    // delete the plugin tables
249
    $xmldbfilepath = $plugindirectory . '/db/install.xml';
250
    drop_plugin_tables($component, $xmldbfilepath, false);
251
    if ($type === 'mod' or $type === 'block') {
252
        // non-frankenstyle table prefixes
253
        drop_plugin_tables($name, $xmldbfilepath, false);
254
    }
255
 
256
    // delete the capabilities that were defined by this module
257
    capabilities_cleanup($component);
258
 
259
    // Delete all remaining files in the filepool owned by the component.
260
    $fs = get_file_storage();
261
    $fs->delete_component_files($component);
262
 
263
    // Finally purge all caches.
264
    purge_all_caches();
265
 
266
    // Invalidate the hash used for upgrade detections.
267
    set_config('allversionshash', '');
268
 
269
    echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
270
}
271
 
272
/**
273
 * Returns the version of installed component
274
 *
275
 * @param string $component component name
276
 * @param string $source either 'disk' or 'installed' - where to get the version information from
277
 * @return string|bool version number or false if the component is not found
278
 */
279
function get_component_version($component, $source='installed') {
280
    global $CFG, $DB;
281
 
282
    list($type, $name) = core_component::normalize_component($component);
283
 
284
    // moodle core or a core subsystem
285
    if ($type === 'core') {
286
        if ($source === 'installed') {
287
            if (empty($CFG->version)) {
288
                return false;
289
            } else {
290
                return $CFG->version;
291
            }
292
        } else {
293
            if (!is_readable($CFG->dirroot.'/version.php')) {
294
                return false;
295
            } else {
296
                $version = null; //initialize variable for IDEs
297
                include($CFG->dirroot.'/version.php');
298
                return $version;
299
            }
300
        }
301
    }
302
 
303
    // activity module
304
    if ($type === 'mod') {
305
        if ($source === 'installed') {
306
            if ($CFG->version < 2013092001.02) {
307
                return $DB->get_field('modules', 'version', array('name'=>$name));
308
            } else {
309
                return get_config('mod_'.$name, 'version');
310
            }
311
 
312
        } else {
313
            $mods = core_component::get_plugin_list('mod');
314
            if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
315
                return false;
316
            } else {
317
                $plugin = new stdClass();
318
                $plugin->version = null;
319
                $module = $plugin;
320
                include($mods[$name].'/version.php');
321
                return $plugin->version;
322
            }
323
        }
324
    }
325
 
326
    // block
327
    if ($type === 'block') {
328
        if ($source === 'installed') {
329
            if ($CFG->version < 2013092001.02) {
330
                return $DB->get_field('block', 'version', array('name'=>$name));
331
            } else {
332
                return get_config('block_'.$name, 'version');
333
            }
334
        } else {
335
            $blocks = core_component::get_plugin_list('block');
336
            if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
337
                return false;
338
            } else {
339
                $plugin = new stdclass();
340
                include($blocks[$name].'/version.php');
341
                return $plugin->version;
342
            }
343
        }
344
    }
345
 
346
    // all other plugin types
347
    if ($source === 'installed') {
348
        return get_config($type.'_'.$name, 'version');
349
    } else {
350
        $plugins = core_component::get_plugin_list($type);
351
        if (empty($plugins[$name])) {
352
            return false;
353
        } else {
354
            $plugin = new stdclass();
355
            include($plugins[$name].'/version.php');
356
            return $plugin->version;
357
        }
358
    }
359
}
360
 
361
/**
362
 * Delete all plugin tables
363
 *
364
 * @param string $name Name of plugin, used as table prefix
365
 * @param string $file Path to install.xml file
366
 * @param bool $feedback defaults to true
367
 * @return bool Always returns true
368
 */
369
function drop_plugin_tables($name, $file, $feedback=true) {
370
    global $CFG, $DB;
371
 
372
    // first try normal delete
373
    if (file_exists($file)) {
374
        $DB->get_manager()->delete_tables_from_xmldb_file($file);
375
        return true;
376
    }
377
 
378
    // then try to find all tables that start with name and are not in any xml file
379
    $used_tables = get_used_table_names();
380
 
381
    $tables = $DB->get_tables();
382
 
383
    /// Iterate over, fixing id fields as necessary
384
    foreach ($tables as $table) {
385
        if (in_array($table, $used_tables)) {
386
            continue;
387
        }
388
 
389
        if (strpos($table, $name) !== 0) {
390
            continue;
391
        }
392
 
393
        // found orphan table --> delete it
394
        if ($DB->get_manager()->table_exists($table)) {
395
            $xmldb_table = new xmldb_table($table);
396
            $DB->get_manager()->drop_table($xmldb_table);
397
        }
398
    }
399
 
400
    return true;
401
}
402
 
403
/**
404
 * Returns names of all known tables == tables that moodle knows about.
405
 *
406
 * @return array Array of lowercase table names
407
 */
408
function get_used_table_names() {
409
    $table_names = array();
410
    $dbdirs = get_db_directories();
411
 
412
    foreach ($dbdirs as $dbdir) {
413
        $file = $dbdir.'/install.xml';
414
 
415
        $xmldb_file = new xmldb_file($file);
416
 
417
        if (!$xmldb_file->fileExists()) {
418
            continue;
419
        }
420
 
421
        $loaded    = $xmldb_file->loadXMLStructure();
422
        $structure = $xmldb_file->getStructure();
423
 
424
        if ($loaded and $tables = $structure->getTables()) {
425
            foreach($tables as $table) {
426
                $table_names[] = strtolower($table->getName());
427
            }
428
        }
429
    }
430
 
431
    return $table_names;
432
}
433
 
434
/**
435
 * Returns list of all directories where we expect install.xml files
436
 * @return array Array of paths
437
 */
438
function get_db_directories() {
439
    global $CFG;
440
 
441
    $dbdirs = array();
442
 
443
    /// First, the main one (lib/db)
444
    $dbdirs[] = $CFG->libdir.'/db';
445
 
446
    /// Then, all the ones defined by core_component::get_plugin_types()
447
    $plugintypes = core_component::get_plugin_types();
448
    foreach ($plugintypes as $plugintype => $pluginbasedir) {
449
        if ($plugins = core_component::get_plugin_list($plugintype)) {
450
            foreach ($plugins as $plugin => $plugindir) {
451
                $dbdirs[] = $plugindir.'/db';
452
            }
453
        }
454
    }
455
 
456
    return $dbdirs;
457
}
458
 
459
/**
460
 * Try to obtain or release the cron lock.
461
 * @param string  $name  name of lock
462
 * @param int  $until timestamp when this lock considered stale, null means remove lock unconditionally
463
 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
464
 * @return bool true if lock obtained
465
 */
466
function set_cron_lock($name, $until, $ignorecurrent=false) {
467
    global $DB;
468
    if (empty($name)) {
469
        debugging("Tried to get a cron lock for a null fieldname");
470
        return false;
471
    }
472
 
473
    // remove lock by force == remove from config table
474
    if (is_null($until)) {
475
        set_config($name, null);
476
        return true;
477
    }
478
 
479
    if (!$ignorecurrent) {
480
        // read value from db - other processes might have changed it
481
        $value = $DB->get_field('config', 'value', array('name'=>$name));
482
 
483
        if ($value and $value > time()) {
484
            //lock active
485
            return false;
486
        }
487
    }
488
 
489
    set_config($name, $until);
490
    return true;
491
}
492
 
493
/**
494
 * Test if and critical warnings are present
495
 * @return bool
496
 */
497
function admin_critical_warnings_present() {
498
    global $SESSION;
499
 
500
    if (!has_capability('moodle/site:config', context_system::instance())) {
501
        return 0;
502
    }
503
 
504
    if (!isset($SESSION->admin_critical_warning)) {
505
        $SESSION->admin_critical_warning = 0;
506
        if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
507
            $SESSION->admin_critical_warning = 1;
508
        }
509
    }
510
 
511
    return $SESSION->admin_critical_warning;
512
}
513
 
514
/**
515
 * Detects if float supports at least 10 decimal digits
516
 *
517
 * Detects if float supports at least 10 decimal digits
518
 * and also if float-->string conversion works as expected.
519
 *
520
 * @return bool true if problem found
521
 */
522
function is_float_problem() {
523
    $num1 = 2009010200.01;
524
    $num2 = 2009010200.02;
525
 
526
    return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
527
}
528
 
529
/**
530
 * Try to verify that dataroot is not accessible from web.
531
 *
532
 * Try to verify that dataroot is not accessible from web.
533
 * It is not 100% correct but might help to reduce number of vulnerable sites.
534
 * Protection from httpd.conf and .htaccess is not detected properly.
535
 *
536
 * @uses INSECURE_DATAROOT_WARNING
537
 * @uses INSECURE_DATAROOT_ERROR
538
 * @param bool $fetchtest try to test public access by fetching file, default false
539
 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
540
 */
541
function is_dataroot_insecure($fetchtest=false) {
542
    global $CFG;
543
 
544
    $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
545
 
546
    $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
547
    $rp = strrev(trim($rp, '/'));
548
    $rp = explode('/', $rp);
549
    foreach($rp as $r) {
550
        if (strpos($siteroot, '/'.$r.'/') === 0) {
551
            $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
552
        } else {
553
            break; // probably alias root
554
        }
555
    }
556
 
557
    $siteroot = strrev($siteroot);
558
    $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
559
 
560
    if (strpos($dataroot, $siteroot) !== 0) {
561
        return false;
562
    }
563
 
564
    if (!$fetchtest) {
565
        return INSECURE_DATAROOT_WARNING;
566
    }
567
 
568
    // now try all methods to fetch a test file using http protocol
569
 
570
    $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
571
    preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
572
    $httpdocroot = $matches[1];
573
    $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
574
    make_upload_directory('diag');
575
    $testfile = $CFG->dataroot.'/diag/public.txt';
576
    if (!file_exists($testfile)) {
577
        file_put_contents($testfile, 'test file, do not delete');
578
        @chmod($testfile, $CFG->filepermissions);
579
    }
580
    $teststr = trim(file_get_contents($testfile));
581
    if (empty($teststr)) {
582
    // hmm, strange
583
        return INSECURE_DATAROOT_WARNING;
584
    }
585
 
586
    $testurl = $datarooturl.'/diag/public.txt';
587
    if (extension_loaded('curl') and
588
        !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
589
        !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
590
        ($ch = @curl_init($testurl)) !== false) {
591
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
592
        curl_setopt($ch, CURLOPT_HEADER, false);
593
        $data = curl_exec($ch);
594
        if (!curl_errno($ch)) {
595
            $data = trim($data);
596
            if ($data === $teststr) {
597
                curl_close($ch);
598
                return INSECURE_DATAROOT_ERROR;
599
            }
600
        }
601
        curl_close($ch);
602
    }
603
 
604
    if ($data = @file_get_contents($testurl)) {
605
        $data = trim($data);
606
        if ($data === $teststr) {
607
            return INSECURE_DATAROOT_ERROR;
608
        }
609
    }
610
 
611
    preg_match('|https?://([^/]+)|i', $testurl, $matches);
612
    $sitename = $matches[1];
613
    $error = 0;
614
    if ($fp = @fsockopen($sitename, 80, $error)) {
615
        preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
616
        $localurl = $matches[1];
617
        $out = "GET $localurl HTTP/1.1\r\n";
618
        $out .= "Host: $sitename\r\n";
619
        $out .= "Connection: Close\r\n\r\n";
620
        fwrite($fp, $out);
621
        $data = '';
622
        $incoming = false;
623
        while (!feof($fp)) {
624
            if ($incoming) {
625
                $data .= fgets($fp, 1024);
626
            } else if (@fgets($fp, 1024) === "\r\n") {
627
                    $incoming = true;
628
                }
629
        }
630
        fclose($fp);
631
        $data = trim($data);
632
        if ($data === $teststr) {
633
            return INSECURE_DATAROOT_ERROR;
634
        }
635
    }
636
 
637
    return INSECURE_DATAROOT_WARNING;
638
}
639
 
640
/**
641
 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
642
 */
643
function enable_cli_maintenance_mode() {
644
    global $CFG, $SITE;
645
 
646
    if (file_exists("$CFG->dataroot/climaintenance.html")) {
647
        unlink("$CFG->dataroot/climaintenance.html");
648
    }
649
 
650
    if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
651
        $data = $CFG->maintenance_message;
652
        $data = bootstrap_renderer::early_error_content($data, null, null, null);
653
        $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
654
 
655
    } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
656
        $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
657
 
658
    } else {
659
        $data = get_string('sitemaintenance', 'admin');
660
        $data = bootstrap_renderer::early_error_content($data, null, null, null);
661
        $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin',
662
            format_string($SITE->fullname, true, ['context' => context_system::instance()])), $data);
663
    }
664
 
665
    file_put_contents("$CFG->dataroot/climaintenance.html", $data);
666
    chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
667
}
668
 
669
/// CLASS DEFINITIONS /////////////////////////////////////////////////////////
670
 
671
 
672
/**
673
 * Interface for anything appearing in the admin tree
674
 *
675
 * The interface that is implemented by anything that appears in the admin tree
676
 * block. It forces inheriting classes to define a method for checking user permissions
677
 * and methods for finding something in the admin tree.
678
 *
679
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
680
 */
681
interface part_of_admin_tree {
682
 
683
/**
684
 * Finds a named part_of_admin_tree.
685
 *
686
 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
687
 * and not parentable_part_of_admin_tree, then this function should only check if
688
 * $this->name matches $name. If it does, it should return a reference to $this,
689
 * otherwise, it should return a reference to NULL.
690
 *
691
 * If a class inherits parentable_part_of_admin_tree, this method should be called
692
 * recursively on all child objects (assuming, of course, the parent object's name
693
 * doesn't match the search criterion).
694
 *
695
 * @param string $name The internal name of the part_of_admin_tree we're searching for.
696
 * @return mixed An object reference or a NULL reference.
697
 */
698
    public function locate($name);
699
 
700
    /**
701
     * Removes named part_of_admin_tree.
702
     *
703
     * @param string $name The internal name of the part_of_admin_tree we want to remove.
704
     * @return bool success.
705
     */
706
    public function prune($name);
707
 
708
    /**
709
     * Search using query
710
     * @param string $query
711
     * @return mixed array-object structure of found settings and pages
712
     */
713
    public function search($query);
714
 
715
    /**
716
     * Verifies current user's access to this part_of_admin_tree.
717
     *
718
     * Used to check if the current user has access to this part of the admin tree or
719
     * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
720
     * then this method is usually just a call to has_capability() in the site context.
721
     *
722
     * If a class inherits parentable_part_of_admin_tree, this method should return the
723
     * logical OR of the return of check_access() on all child objects.
724
     *
725
     * @return bool True if the user has access, false if she doesn't.
726
     */
727
    public function check_access();
728
 
729
    /**
730
     * Mostly useful for removing of some parts of the tree in admin tree block.
731
     *
732
     * @return bool True is hidden from normal list view
733
     */
734
    public function is_hidden();
735
 
736
    /**
737
     * Show we display Save button at the page bottom?
738
     * @return bool
739
     */
740
    public function show_save();
741
}
742
 
743
 
744
/**
745
 * Interface implemented by any part_of_admin_tree that has children.
746
 *
747
 * The interface implemented by any part_of_admin_tree that can be a parent
748
 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
749
 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
750
 * include an add method for adding other part_of_admin_tree objects as children.
751
 *
752
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
753
 */
754
interface parentable_part_of_admin_tree extends part_of_admin_tree {
755
 
756
/**
757
 * Adds a part_of_admin_tree object to the admin tree.
758
 *
759
 * Used to add a part_of_admin_tree object to this object or a child of this
760
 * object. $something should only be added if $destinationname matches
761
 * $this->name. If it doesn't, add should be called on child objects that are
762
 * also parentable_part_of_admin_tree's.
763
 *
764
 * $something should be appended as the last child in the $destinationname. If the
765
 * $beforesibling is specified, $something should be prepended to it. If the given
766
 * sibling is not found, $something should be appended to the end of $destinationname
767
 * and a developer debugging message should be displayed.
768
 *
769
 * @param string $destinationname The internal name of the new parent for $something.
770
 * @param part_of_admin_tree $something The object to be added.
771
 * @return bool True on success, false on failure.
772
 */
773
    public function add($destinationname, $something, $beforesibling = null);
774
 
775
}
776
 
777
 
778
/**
779
 * The object used to represent folders (a.k.a. categories) in the admin tree block.
780
 *
781
 * Each admin_category object contains a number of part_of_admin_tree objects.
782
 *
783
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
784
 */
785
class admin_category implements parentable_part_of_admin_tree, linkable_settings_page {
786
 
787
    /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
788
    protected $children;
789
    /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
790
    public $name;
791
    /** @var string The displayed name for this category. Usually obtained through get_string() */
792
    public $visiblename;
793
    /** @var bool Should this category be hidden in admin tree block? */
794
    public $hidden;
795
    /** @var mixed Either a string or an array or strings */
796
    public $path;
797
    /** @var mixed Either a string or an array or strings */
798
    public $visiblepath;
799
 
800
    /** @var array fast lookup category cache, all categories of one tree point to one cache */
801
    protected $category_cache;
802
 
803
    /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
804
    protected $sort = false;
805
    /** @var bool If set to true children will be sorted in ascending order. */
806
    protected $sortasc = true;
807
    /** @var bool If set to true sub categories and pages will be split and then sorted.. */
808
    protected $sortsplit = true;
809
    /** @var bool $sorted True if the children have been sorted and don't need resorting */
810
    protected $sorted = false;
811
 
812
    /**
813
     * Constructor for an empty admin category
814
     *
815
     * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
816
     * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
817
     * @param bool $hidden hide category in admin tree block, defaults to false
818
     */
819
    public function __construct($name, $visiblename, $hidden=false) {
820
        $this->children    = array();
821
        $this->name        = $name;
822
        $this->visiblename = $visiblename;
823
        $this->hidden      = $hidden;
824
    }
825
 
826
    /**
827
     * Get the URL to view this settings page.
828
     *
829
     * @return moodle_url
830
     */
831
    public function get_settings_page_url(): moodle_url {
832
        return new moodle_url(
833
            '/admin/category.php',
834
            [
835
                'category' => $this->name,
836
            ]
837
        );
838
    }
839
 
840
    /**
841
     * Returns a reference to the part_of_admin_tree object with internal name $name.
842
     *
843
     * @param string $name The internal name of the object we want.
844
     * @param bool $findpath initialize path and visiblepath arrays
845
     * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
846
     *                  defaults to false
847
     */
848
    public function locate($name, $findpath=false) {
849
        if (!isset($this->category_cache[$this->name])) {
850
            // somebody much have purged the cache
851
            $this->category_cache[$this->name] = $this;
852
        }
853
 
854
        if ($this->name == $name) {
855
            if ($findpath) {
856
                $this->visiblepath[] = $this->visiblename;
857
                $this->path[]        = $this->name;
858
            }
859
            return $this;
860
        }
861
 
862
        // quick category lookup
863
        if (!$findpath and isset($this->category_cache[$name])) {
864
            return $this->category_cache[$name];
865
        }
866
 
867
        $return = NULL;
868
        foreach($this->children as $childid=>$unused) {
869
            if ($return = $this->children[$childid]->locate($name, $findpath)) {
870
                break;
871
            }
872
        }
873
 
874
        if (!is_null($return) and $findpath) {
875
            $return->visiblepath[] = $this->visiblename;
876
            $return->path[]        = $this->name;
877
        }
878
 
879
        return $return;
880
    }
881
 
882
    /**
883
     * Search using query
884
     *
885
     * @param string query
886
     * @return mixed array-object structure of found settings and pages
887
     */
888
    public function search($query) {
889
        $result = array();
890
        foreach ($this->get_children() as $child) {
891
            $subsearch = $child->search($query);
892
            if (!is_array($subsearch)) {
893
                debugging('Incorrect search result from '.$child->name);
894
                continue;
895
            }
896
            $result = array_merge($result, $subsearch);
897
        }
898
        return $result;
899
    }
900
 
901
    /**
902
     * Removes part_of_admin_tree object with internal name $name.
903
     *
904
     * @param string $name The internal name of the object we want to remove.
905
     * @return bool success
906
     */
907
    public function prune($name) {
908
 
909
        if ($this->name == $name) {
910
            return false;  //can not remove itself
911
        }
912
 
913
        foreach($this->children as $precedence => $child) {
914
            if ($child->name == $name) {
915
                // clear cache and delete self
916
                while($this->category_cache) {
917
                    // delete the cache, but keep the original array address
918
                    array_pop($this->category_cache);
919
                }
920
                unset($this->children[$precedence]);
921
                return true;
922
            } else if ($this->children[$precedence]->prune($name)) {
923
                return true;
924
            }
925
        }
926
        return false;
927
    }
928
 
929
    /**
930
     * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
931
     *
932
     * By default the new part of the tree is appended as the last child of the parent. You
933
     * can specify a sibling node that the new part should be prepended to. If the given
934
     * sibling is not found, the part is appended to the end (as it would be by default) and
935
     * a developer debugging message is displayed.
936
     *
937
     * @throws coding_exception if the $beforesibling is empty string or is not string at all.
938
     * @param string $destinationame The internal name of the immediate parent that we want for $something.
939
     * @param mixed $something A part_of_admin_tree or setting instance to be added.
940
     * @param string $beforesibling The name of the parent's child the $something should be prepended to.
941
     * @return bool True if successfully added, false if $something can not be added.
942
     */
943
    public function add($parentname, $something, $beforesibling = null) {
944
        global $CFG;
945
 
946
        $parent = $this->locate($parentname);
947
        if (is_null($parent)) {
948
            debugging('parent does not exist!');
949
            return false;
950
        }
951
 
952
        if ($something instanceof part_of_admin_tree) {
953
            if (!($parent instanceof parentable_part_of_admin_tree)) {
954
                debugging('error - parts of tree can be inserted only into parentable parts');
955
                return false;
956
            }
957
            if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
958
                // The name of the node is already used, simply warn the developer that this should not happen.
959
                // It is intentional to check for the debug level before performing the check.
960
                debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
961
            }
962
            if (is_null($beforesibling)) {
963
                // Append $something as the parent's last child.
964
                $parent->children[] = $something;
965
            } else {
966
                if (!is_string($beforesibling) or trim($beforesibling) === '') {
967
                    throw new coding_exception('Unexpected value of the beforesibling parameter');
968
                }
969
                // Try to find the position of the sibling.
970
                $siblingposition = null;
971
                foreach ($parent->children as $childposition => $child) {
972
                    if ($child->name === $beforesibling) {
973
                        $siblingposition = $childposition;
974
                        break;
975
                    }
976
                }
977
                if (is_null($siblingposition)) {
978
                    debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
979
                    $parent->children[] = $something;
980
                } else {
981
                    $parent->children = array_merge(
982
                        array_slice($parent->children, 0, $siblingposition),
983
                        array($something),
984
                        array_slice($parent->children, $siblingposition)
985
                    );
986
                }
987
            }
988
            if ($something instanceof admin_category) {
989
                if (isset($this->category_cache[$something->name])) {
990
                    debugging('Duplicate admin category name: '.$something->name);
991
                } else {
992
                    $this->category_cache[$something->name] = $something;
993
                    $something->category_cache =& $this->category_cache;
994
                    foreach ($something->children as $child) {
995
                        // just in case somebody already added subcategories
996
                        if ($child instanceof admin_category) {
997
                            if (isset($this->category_cache[$child->name])) {
998
                                debugging('Duplicate admin category name: '.$child->name);
999
                            } else {
1000
                                $this->category_cache[$child->name] = $child;
1001
                                $child->category_cache =& $this->category_cache;
1002
                            }
1003
                        }
1004
                    }
1005
                }
1006
            }
1007
            return true;
1008
 
1009
        } else {
1010
            debugging('error - can not add this element');
1011
            return false;
1012
        }
1013
 
1014
    }
1015
 
1016
    /**
1017
     * Checks if the user has access to anything in this category.
1018
     *
1019
     * @return bool True if the user has access to at least one child in this category, false otherwise.
1020
     */
1021
    public function check_access() {
1022
        foreach ($this->children as $child) {
1023
            if ($child->check_access()) {
1024
                return true;
1025
            }
1026
        }
1027
        return false;
1028
    }
1029
 
1030
    /**
1031
     * Is this category hidden in admin tree block?
1032
     *
1033
     * @return bool True if hidden
1034
     */
1035
    public function is_hidden() {
1036
        return $this->hidden;
1037
    }
1038
 
1039
    /**
1040
     * Show we display Save button at the page bottom?
1041
     * @return bool
1042
     */
1043
    public function show_save() {
1044
        foreach ($this->children as $child) {
1045
            if ($child->show_save()) {
1046
                return true;
1047
            }
1048
        }
1049
        return false;
1050
    }
1051
 
1052
    /**
1053
     * Sets sorting on this category.
1054
     *
1055
     * Please note this function doesn't actually do the sorting.
1056
     * It can be called anytime.
1057
     * Sorting occurs when the user calls get_children.
1058
     * Code using the children array directly won't see the sorted results.
1059
     *
1060
     * @param bool $sort If set to true children will be sorted, if false they won't be.
1061
     * @param bool $asc If true sorting will be ascending, otherwise descending.
1062
     * @param bool $split If true we sort pages and sub categories separately.
1063
     */
1064
    public function set_sorting($sort, $asc = true, $split = true) {
1065
        $this->sort = (bool)$sort;
1066
        $this->sortasc = (bool)$asc;
1067
        $this->sortsplit = (bool)$split;
1068
    }
1069
 
1070
    /**
1071
     * Returns the children associated with this category.
1072
     *
1073
     * @return part_of_admin_tree[]
1074
     */
1075
    public function get_children() {
1076
        // If we should sort and it hasn't already been sorted.
1077
        if ($this->sort && !$this->sorted) {
1078
            if ($this->sortsplit) {
1079
                $categories = array();
1080
                $pages = array();
1081
                foreach ($this->children as $child) {
1082
                    if ($child instanceof admin_category) {
1083
                        $categories[] = $child;
1084
                    } else {
1085
                        $pages[] = $child;
1086
                    }
1087
                }
1088
                core_collator::asort_objects_by_property($categories, 'visiblename');
1089
                core_collator::asort_objects_by_property($pages, 'visiblename');
1090
                if (!$this->sortasc) {
1091
                    $categories = array_reverse($categories);
1092
                    $pages = array_reverse($pages);
1093
                }
1094
                $this->children = array_merge($pages, $categories);
1095
            } else {
1096
                core_collator::asort_objects_by_property($this->children, 'visiblename');
1097
                if (!$this->sortasc) {
1098
                    $this->children = array_reverse($this->children);
1099
                }
1100
            }
1101
            $this->sorted = true;
1102
        }
1103
        return $this->children;
1104
    }
1105
 
1106
    /**
1107
     * Magically gets a property from this object.
1108
     *
1109
     * @param $property
1110
     * @return part_of_admin_tree[]
1111
     * @throws coding_exception
1112
     */
1113
    public function __get($property) {
1114
        if ($property === 'children') {
1115
            return $this->get_children();
1116
        }
1117
        throw new coding_exception('Invalid property requested.');
1118
    }
1119
 
1120
    /**
1121
     * Magically sets a property against this object.
1122
     *
1123
     * @param string $property
1124
     * @param mixed $value
1125
     * @throws coding_exception
1126
     */
1127
    public function __set($property, $value) {
1128
        if ($property === 'children') {
1129
            $this->sorted = false;
1130
            $this->children = $value;
1131
        } else {
1132
            throw new coding_exception('Invalid property requested.');
1133
        }
1134
    }
1135
 
1136
    /**
1137
     * Checks if an inaccessible property is set.
1138
     *
1139
     * @param string $property
1140
     * @return bool
1141
     * @throws coding_exception
1142
     */
1143
    public function __isset($property) {
1144
        if ($property === 'children') {
1145
            return isset($this->children);
1146
        }
1147
        throw new coding_exception('Invalid property requested.');
1148
    }
1149
}
1150
 
1151
 
1152
/**
1153
 * Root of admin settings tree, does not have any parent.
1154
 *
1155
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1156
 */
1157
class admin_root extends admin_category {
1158
/** @var array List of errors */
1159
    public $errors;
1160
    /** @var string search query */
1161
    public $search;
1162
    /** @var bool full tree flag - true means all settings required, false only pages required */
1163
    public $fulltree;
1164
    /** @var bool flag indicating loaded tree */
1165
    public $loaded;
1166
    /** @var mixed site custom defaults overriding defaults in settings files*/
1167
    public $custom_defaults;
1168
 
1169
    /**
1170
     * @param bool $fulltree true means all settings required,
1171
     *                            false only pages required
1172
     */
1173
    public function __construct($fulltree) {
1174
        global $CFG;
1175
 
1176
        parent::__construct('root', get_string('administration'), false);
1177
        $this->errors   = array();
1178
        $this->search   = '';
1179
        $this->fulltree = $fulltree;
1180
        $this->loaded   = false;
1181
 
1182
        $this->category_cache = array();
1183
 
1184
        // load custom defaults if found
1185
        $this->custom_defaults = null;
1186
        $defaultsfile = "$CFG->dirroot/local/defaults.php";
1187
        if (is_readable($defaultsfile)) {
1188
            $defaults = array();
1189
            include($defaultsfile);
1190
            if (is_array($defaults) and count($defaults)) {
1191
                $this->custom_defaults = $defaults;
1192
            }
1193
        }
1194
    }
1195
 
1196
    /**
1197
     * Empties children array, and sets loaded to false
1198
     *
1199
     * @param bool $requirefulltree
1200
     */
1201
    public function purge_children($requirefulltree) {
1202
        $this->children = array();
1203
        $this->fulltree = ($requirefulltree || $this->fulltree);
1204
        $this->loaded   = false;
1205
        //break circular dependencies - this helps PHP 5.2
1206
        while($this->category_cache) {
1207
            array_pop($this->category_cache);
1208
        }
1209
        $this->category_cache = array();
1210
    }
1211
}
1212
 
1213
 
1214
/**
1215
 * Links external PHP pages into the admin tree.
1216
 *
1217
 * See detailed usage example at the top of this document (adminlib.php)
1218
 *
1219
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1220
 */
1221
class admin_externalpage implements part_of_admin_tree, linkable_settings_page {
1222
 
1223
    /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1224
    public $name;
1225
 
1226
    /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1227
    public $visiblename;
1228
 
1229
    /** @var string The external URL that we should link to when someone requests this external page. */
1230
    public $url;
1231
 
1232
    /** @var array The role capability/permission a user must have to access this external page. */
1233
    public $req_capability;
1234
 
1235
    /** @var object The context in which capability/permission should be checked, default is site context. */
1236
    public $context;
1237
 
1238
    /** @var bool hidden in admin tree block. */
1239
    public $hidden;
1240
 
1241
    /** @var mixed either string or array of string */
1242
    public $path;
1243
 
1244
    /** @var array list of visible names of page parents */
1245
    public $visiblepath;
1246
 
1247
    /**
1248
     * Constructor for adding an external page into the admin tree.
1249
     *
1250
     * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1251
     * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1252
     * @param string $url The external URL that we should link to when someone requests this external page.
1253
     * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1254
     * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1255
     * @param stdClass $context The context the page relates to. Not sure what happens
1256
     *      if you specify something other than system or front page. Defaults to system.
1257
     */
1258
    public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1259
        $this->name        = $name;
1260
        $this->visiblename = $visiblename;
1261
        $this->url         = $url;
1262
        if (is_array($req_capability)) {
1263
            $this->req_capability = $req_capability;
1264
        } else {
1265
            $this->req_capability = array($req_capability);
1266
        }
1267
        $this->hidden = $hidden;
1268
        $this->context = $context;
1269
    }
1270
 
1271
    /**
1272
     * Get the URL to view this settings page.
1273
     *
1274
     * @return moodle_url
1275
     */
1276
    public function get_settings_page_url(): moodle_url {
1277
        return new moodle_url($this->url);
1278
    }
1279
 
1280
    /**
1281
     * Returns a reference to the part_of_admin_tree object with internal name $name.
1282
     *
1283
     * @param string $name The internal name of the object we want.
1284
     * @param bool $findpath defaults to false
1285
     * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1286
     */
1287
    public function locate($name, $findpath=false) {
1288
        if ($this->name == $name) {
1289
            if ($findpath) {
1290
                $this->visiblepath = array($this->visiblename);
1291
                $this->path        = array($this->name);
1292
            }
1293
            return $this;
1294
        } else {
1295
            $return = NULL;
1296
            return $return;
1297
        }
1298
    }
1299
 
1300
    /**
1301
     * This function always returns false, required function by interface
1302
     *
1303
     * @param string $name
1304
     * @return false
1305
     */
1306
    public function prune($name) {
1307
        return false;
1308
    }
1309
 
1310
    /**
1311
     * Search using query
1312
     *
1313
     * @param string $query
1314
     * @return mixed array-object structure of found settings and pages
1315
     */
1316
    public function search($query) {
1317
        $found = false;
1318
        if (strpos(strtolower($this->name), $query) !== false) {
1319
            $found = true;
1320
        } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1321
                $found = true;
1322
            }
1323
        if ($found) {
1324
            $result = new stdClass();
1325
            $result->page     = $this;
1326
            $result->settings = array();
1327
            return array($this->name => $result);
1328
        } else {
1329
            return array();
1330
        }
1331
    }
1332
 
1333
    /**
1334
     * Determines if the current user has access to this external page based on $this->req_capability.
1335
     *
1336
     * @return bool True if user has access, false otherwise.
1337
     */
1338
    public function check_access() {
1339
        global $CFG;
1340
        $context = empty($this->context) ? context_system::instance() : $this->context;
1341
        foreach($this->req_capability as $cap) {
1342
            if (has_capability($cap, $context)) {
1343
                return true;
1344
            }
1345
        }
1346
        return false;
1347
    }
1348
 
1349
    /**
1350
     * Is this external page hidden in admin tree block?
1351
     *
1352
     * @return bool True if hidden
1353
     */
1354
    public function is_hidden() {
1355
        return $this->hidden;
1356
    }
1357
 
1358
    /**
1359
     * Show we display Save button at the page bottom?
1360
     * @return bool
1361
     */
1362
    public function show_save() {
1363
        return false;
1364
    }
1365
}
1366
 
1367
/**
1368
 * Used to store details of the dependency between two settings elements.
1369
 *
1370
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1371
 * @copyright 2017 Davo Smith, Synergy Learning
1372
 */
1373
class admin_settingdependency {
1374
    /** @var string the name of the setting to be shown/hidden */
1375
    public $settingname;
1376
    /** @var string the setting this is dependent on */
1377
    public $dependenton;
1378
    /** @var string the condition to show/hide the element */
1379
    public $condition;
1380
    /** @var string the value to compare against */
1381
    public $value;
1382
 
1383
    /** @var string[] list of valid conditions */
1384
    private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1385
 
1386
    /**
1387
     * admin_settingdependency constructor.
1388
     * @param string $settingname
1389
     * @param string $dependenton
1390
     * @param string $condition
1391
     * @param string $value
1392
     * @throws \coding_exception
1393
     */
1394
    public function __construct($settingname, $dependenton, $condition, $value) {
1395
        $this->settingname = $this->parse_name($settingname);
1396
        $this->dependenton = $this->parse_name($dependenton);
1397
        $this->condition = $condition;
1398
        $this->value = $value;
1399
 
1400
        if (!in_array($this->condition, self::$validconditions)) {
1401
            throw new coding_exception("Invalid condition '$condition'");
1402
        }
1403
    }
1404
 
1405
    /**
1406
     * Convert the setting name into the form field name.
1407
     * @param string $name
1408
     * @return string
1409
     */
1410
    private function parse_name($name) {
1411
        $bits = explode('/', $name);
1412
        $name = array_pop($bits);
1413
        $plugin = '';
1414
        if ($bits) {
1415
            $plugin = array_pop($bits);
1416
            if ($plugin === 'moodle') {
1417
                $plugin = '';
1418
            }
1419
        }
1420
        return 's_'.$plugin.'_'.$name;
1421
    }
1422
 
1423
    /**
1424
     * Gather together all the dependencies in a format suitable for initialising javascript
1425
     * @param admin_settingdependency[] $dependencies
1426
     * @return array
1427
     */
1428
    public static function prepare_for_javascript($dependencies) {
1429
        $result = [];
1430
        foreach ($dependencies as $d) {
1431
            if (!isset($result[$d->dependenton])) {
1432
                $result[$d->dependenton] = [];
1433
            }
1434
            if (!isset($result[$d->dependenton][$d->condition])) {
1435
                $result[$d->dependenton][$d->condition] = [];
1436
            }
1437
            if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1438
                $result[$d->dependenton][$d->condition][$d->value] = [];
1439
            }
1440
            $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1441
        }
1442
        return $result;
1443
    }
1444
}
1445
 
1446
/**
1447
 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1448
 *
1449
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1450
 */
1451
class admin_settingpage implements part_of_admin_tree, linkable_settings_page {
1452
 
1453
    /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1454
    public $name;
1455
 
1456
    /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1457
    public $visiblename;
1458
 
1459
    /** @var mixed An array of admin_setting objects that are part of this setting page. */
1460
    public $settings;
1461
 
1462
    /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1463
    protected $dependencies = [];
1464
 
1465
    /** @var array The role capability/permission a user must have to access this external page. */
1466
    public $req_capability;
1467
 
1468
    /** @var object The context in which capability/permission should be checked, default is site context. */
1469
    public $context;
1470
 
1471
    /** @var bool hidden in admin tree block. */
1472
    public $hidden;
1473
 
1474
    /** @var mixed string of paths or array of strings of paths */
1475
    public $path;
1476
 
1477
    /** @var array list of visible names of page parents */
1478
    public $visiblepath;
1479
 
1480
    /**
1481
     * see admin_settingpage for details of this function
1482
     *
1483
     * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1484
     * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1485
     * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1486
     * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1487
     * @param stdClass $context The context the page relates to. Not sure what happens
1488
     *      if you specify something other than system or front page. Defaults to system.
1489
     */
1490
    public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1491
        $this->settings    = new stdClass();
1492
        $this->name        = $name;
1493
        $this->visiblename = $visiblename;
1494
        if (is_array($req_capability)) {
1495
            $this->req_capability = $req_capability;
1496
        } else {
1497
            $this->req_capability = array($req_capability);
1498
        }
1499
        $this->hidden      = $hidden;
1500
        $this->context     = $context;
1501
    }
1502
 
1503
    /**
1504
     * Get the URL to view this page.
1505
     *
1506
     * @return moodle_url
1507
     */
1508
    public function get_settings_page_url(): moodle_url {
1509
        return new moodle_url(
1510
            '/admin/settings.php',
1511
            [
1512
                'section' => $this->name,
1513
            ]
1514
        );
1515
    }
1516
 
1517
    /**
1518
     * see admin_category
1519
     *
1520
     * @param string $name
1521
     * @param bool $findpath
1522
     * @return mixed Object (this) if name ==  this->name, else returns null
1523
     */
1524
    public function locate($name, $findpath=false) {
1525
        if ($this->name == $name) {
1526
            if ($findpath) {
1527
                $this->visiblepath = array($this->visiblename);
1528
                $this->path        = array($this->name);
1529
            }
1530
            return $this;
1531
        } else {
1532
            $return = NULL;
1533
            return $return;
1534
        }
1535
    }
1536
 
1537
    /**
1538
     * Search string in settings page.
1539
     *
1540
     * @param string $query
1541
     * @return array
1542
     */
1543
    public function search($query) {
1544
        $found = array();
1545
 
1546
        foreach ($this->settings as $setting) {
1547
            if ($setting->is_related($query)) {
1548
                $found[] = $setting;
1549
            }
1550
        }
1551
 
1552
        if ($found) {
1553
            $result = new stdClass();
1554
            $result->page     = $this;
1555
            $result->settings = $found;
1556
            return array($this->name => $result);
1557
        }
1558
 
1559
        $found = false;
1560
        if (strpos(strtolower($this->name), $query) !== false) {
1561
            $found = true;
1562
        } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1563
                $found = true;
1564
            }
1565
        if ($found) {
1566
            $result = new stdClass();
1567
            $result->page     = $this;
1568
            $result->settings = array();
1569
            return array($this->name => $result);
1570
        } else {
1571
            return array();
1572
        }
1573
    }
1574
 
1575
    /**
1576
     * This function always returns false, required by interface
1577
     *
1578
     * @param string $name
1579
     * @return bool Always false
1580
     */
1581
    public function prune($name) {
1582
        return false;
1583
    }
1584
 
1585
    /**
1586
     * adds an admin_setting to this admin_settingpage
1587
     *
1588
     * 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
1589
     * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1590
     *
1591
     * @param object $setting is the admin_setting object you want to add
1592
     * @return bool true if successful, false if not
1593
     */
1594
    public function add($setting) {
1595
        if (!($setting instanceof admin_setting)) {
1596
            debugging('error - not a setting instance');
1597
            return false;
1598
        }
1599
 
1600
        $name = $setting->name;
1601
        if ($setting->plugin) {
1602
            $name = $setting->plugin . $name;
1603
        }
1604
        $this->settings->{$name} = $setting;
1605
        return true;
1606
    }
1607
 
1608
    /**
1609
     * Hide the named setting if the specified condition is matched.
1610
     *
1611
     * @param string $settingname
1612
     * @param string $dependenton
1613
     * @param string $condition
1614
     * @param string $value
1615
     */
1616
    public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1617
        $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1618
 
1619
        // Reformat the dependency name to the plugin | name format used in the display.
1620
        $dependenton = str_replace('/', ' | ', $dependenton);
1621
 
1622
        // Let the setting know, so it can be displayed underneath.
1623
        $findname = str_replace('/', '', $settingname);
1624
        foreach ($this->settings as $name => $setting) {
1625
            if ($name === $findname) {
1626
                $setting->add_dependent_on($dependenton);
1627
            }
1628
        }
1629
    }
1630
 
1631
    /**
1632
     * see admin_externalpage
1633
     *
1634
     * @return bool Returns true for yes false for no
1635
     */
1636
    public function check_access() {
1637
        global $CFG;
1638
        $context = empty($this->context) ? context_system::instance() : $this->context;
1639
        foreach($this->req_capability as $cap) {
1640
            if (has_capability($cap, $context)) {
1641
                return true;
1642
            }
1643
        }
1644
        return false;
1645
    }
1646
 
1647
    /**
1648
     * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1649
     * @return string Returns an XHTML string
1650
     */
1651
    public function output_html() {
1652
        $adminroot = admin_get_root();
1653
        $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1654
        foreach($this->settings as $setting) {
1655
            $fullname = $setting->get_full_name();
1656
            if (array_key_exists($fullname, $adminroot->errors)) {
1657
                $data = $adminroot->errors[$fullname]->data;
1658
            } else {
1659
                $data = $setting->get_setting();
1660
                // do not use defaults if settings not available - upgrade settings handles the defaults!
1661
            }
1662
            $return .= $setting->output_html($data);
1663
        }
1664
        $return .= '</fieldset>';
1665
        return $return;
1666
    }
1667
 
1668
    /**
1669
     * Is this settings page hidden in admin tree block?
1670
     *
1671
     * @return bool True if hidden
1672
     */
1673
    public function is_hidden() {
1674
        return $this->hidden;
1675
    }
1676
 
1677
    /**
1678
     * Show we display Save button at the page bottom?
1679
     * @return bool
1680
     */
1681
    public function show_save() {
1682
        foreach($this->settings as $setting) {
1683
            if (empty($setting->nosave)) {
1684
                return true;
1685
            }
1686
        }
1687
        return false;
1688
    }
1689
 
1690
    /**
1691
     * Should any of the settings on this page be shown / hidden based on conditions?
1692
     * @return bool
1693
     */
1694
    public function has_dependencies() {
1695
        return (bool)$this->dependencies;
1696
    }
1697
 
1698
    /**
1699
     * Format the setting show/hide conditions ready to initialise the page javascript
1700
     * @return array
1701
     */
1702
    public function get_dependencies_for_javascript() {
1703
        if (!$this->has_dependencies()) {
1704
            return [];
1705
        }
1706
        return admin_settingdependency::prepare_for_javascript($this->dependencies);
1707
    }
1708
}
1709
 
1710
 
1711
/**
1712
 * Admin settings class. Only exists on setting pages.
1713
 * Read & write happens at this level; no authentication.
1714
 *
1715
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1716
 */
1717
abstract class admin_setting {
1718
    /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1719
    public $name;
1720
    /** @var lang_string|string localised name */
1721
    public $visiblename;
1722
    /** @var string localised long description in Markdown format */
1723
    public $description;
1724
    /** @var mixed Can be string or array of string */
1725
    public $defaultsetting;
1726
    /** @var ?callable */
1727
    public $updatedcallback;
1728
    /** @var mixed can be String or Null.  Null means main config table */
1729
    public $plugin; // null means main config table
1730
    /** @var bool true indicates this setting does not actually save anything, just information */
1731
    public $nosave = false;
1732
    /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1733
    public $affectsmodinfo = false;
1734
    /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1735
    private $flags = array();
1736
    /** @var bool Whether this field must be forced LTR. */
1737
    private $forceltr = null;
1738
    /** @var array list of other settings that may cause this setting to be hidden */
1739
    private $dependenton = [];
1740
    /** @var bool Whether this setting uses a custom form control */
1741
    protected $customcontrol = false;
1742
    /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
1743
    public $paramtype;
1744
 
1745
    /**
1746
     * Constructor
1747
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1748
     *                     or 'myplugin/mysetting' for ones in config_plugins.
1749
     * @param string $visiblename localised name
1750
     * @param string $description localised long description
1751
     * @param mixed $defaultsetting string or array depending on implementation
1752
     */
1753
    public function __construct($name, $visiblename, $description, $defaultsetting) {
1754
        $this->parse_setting_name($name);
1755
        $this->visiblename    = $visiblename;
1756
        $this->description    = $description;
1757
        $this->defaultsetting = $defaultsetting;
1758
    }
1759
 
1760
    /**
1761
     * Generic function to add a flag to this admin setting.
1762
     *
1763
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1764
     * @param bool $default - The default for the flag
1765
     * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1766
     * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1767
     */
1768
    protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1769
        if (empty($this->flags[$shortname])) {
1770
            $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1771
        } else {
1772
            $this->flags[$shortname]->set_options($enabled, $default);
1773
        }
1774
    }
1775
 
1776
    /**
1777
     * Set the enabled 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_enabled_flag_options($enabled, $default) {
1783
        $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1784
    }
1785
 
1786
    /**
1787
     * Set the advanced options flag on this admin setting.
1788
     *
1789
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1790
     * @param bool $default - The default for the flag
1791
     */
1792
    public function set_advanced_flag_options($enabled, $default) {
1793
        $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1794
    }
1795
 
1796
 
1797
    /**
1798
     * Set the locked 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_locked_flag_options($enabled, $default) {
1804
        $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1805
    }
1806
 
1807
    /**
1808
     * Set the required options flag on this admin setting.
1809
     *
1810
     * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1811
     * @param bool $default - The default for the flag.
1812
     */
1813
    public function set_required_flag_options($enabled, $default) {
1814
        $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1815
    }
1816
 
1817
    /**
1818
     * Is this option forced in config.php?
1819
     *
1820
     * @return bool
1821
     */
1822
    public function is_readonly(): bool {
1823
        global $CFG;
1824
 
1825
        if (empty($this->plugin)) {
1826
            if ($this->is_forceable() && array_key_exists($this->name, $CFG->config_php_settings)) {
1827
                return true;
1828
            }
1829
        } else {
1830
            if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
1831
                and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
1832
                return true;
1833
            }
1834
        }
1835
        return false;
1836
    }
1837
 
1838
    /**
1839
     * Get the currently saved value for a setting flag
1840
     *
1841
     * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1842
     * @return bool
1843
     */
1844
    public function get_setting_flag_value(admin_setting_flag $flag) {
1845
        $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1846
        if (!isset($value)) {
1847
            $value = $flag->get_default();
1848
        }
1849
 
1850
        return !empty($value);
1851
    }
1852
 
1853
    /**
1854
     * Get the list of defaults for the flags on this setting.
1855
     *
1856
     * @param array of strings describing the defaults for this setting. This is appended to by this function.
1857
     */
1858
    public function get_setting_flag_defaults(& $defaults) {
1859
        foreach ($this->flags as $flag) {
1860
            if ($flag->is_enabled() && $flag->get_default()) {
1861
                $defaults[] = $flag->get_displayname();
1862
            }
1863
        }
1864
    }
1865
 
1866
    /**
1867
     * Output the input fields for the advanced and locked flags on this setting.
1868
     *
1869
     * @param bool $adv - The current value of the advanced flag.
1870
     * @param bool $locked - The current value of the locked flag.
1871
     * @return string $output - The html for the flags.
1872
     */
1873
    public function output_setting_flags() {
1874
        $output = '';
1875
 
1876
        foreach ($this->flags as $flag) {
1877
            if ($flag->is_enabled()) {
1878
                $output .= $flag->output_setting_flag($this);
1879
            }
1880
        }
1881
 
1882
        if (!empty($output)) {
1883
            return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1884
        }
1885
        return $output;
1886
    }
1887
 
1888
    /**
1889
     * Write the values of the flags for this admin setting.
1890
     *
1891
     * @param ?array $data - The data submitted from the form or null to set the default value for new installs.
1892
     * @return bool - true if successful.
1893
     */
1894
    public function write_setting_flags($data) {
1895
        $result = true;
1896
        foreach ($this->flags as $flag) {
1897
            $result = $result && $flag->write_setting_flag($this, $data);
1898
        }
1899
        return $result;
1900
    }
1901
 
1902
    /**
1903
     * Set up $this->name and potentially $this->plugin
1904
     *
1905
     * Set up $this->name and possibly $this->plugin based on whether $name looks
1906
     * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1907
     * on the names, that is, output a developer debug warning if the name
1908
     * contains anything other than [a-zA-Z0-9_]+.
1909
     *
1910
     * @param string $name the setting name passed in to the constructor.
1911
     */
1912
    private function parse_setting_name($name) {
1913
        $bits = explode('/', $name);
1914
        if (count($bits) > 2) {
1915
            throw new moodle_exception('invalidadminsettingname', '', '', $name);
1916
        }
1917
        $this->name = array_pop($bits);
1918
        if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1919
            throw new moodle_exception('invalidadminsettingname', '', '', $name);
1920
        }
1921
        if (!empty($bits)) {
1922
            $this->plugin = array_pop($bits);
1923
            if ($this->plugin === 'moodle') {
1924
                $this->plugin = null;
1925
            } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1926
                    throw new moodle_exception('invalidadminsettingname', '', '', $name);
1927
                }
1928
        }
1929
    }
1930
 
1931
    /**
1932
     * Returns the fullname prefixed by the plugin
1933
     * @return string
1934
     */
1935
    public function get_full_name() {
1936
        return 's_'.$this->plugin.'_'.$this->name;
1937
    }
1938
 
1939
    /**
1940
     * Returns the ID string based on plugin and name
1941
     * @return string
1942
     */
1943
    public function get_id() {
1944
        return 'id_s_'.$this->plugin.'_'.$this->name;
1945
    }
1946
 
1947
    /**
1948
     * @param bool $affectsmodinfo If true, changes to this setting will
1949
     *   cause the course cache to be rebuilt
1950
     */
1951
    public function set_affects_modinfo($affectsmodinfo) {
1952
        $this->affectsmodinfo = $affectsmodinfo;
1953
    }
1954
 
1955
    /**
1956
     * Returns the config if possible
1957
     *
1958
     * @return mixed returns config if successful else null
1959
     */
1960
    public function config_read($name) {
1961
        global $CFG;
1962
        if (!empty($this->plugin)) {
1963
            $value = get_config($this->plugin, $name);
1964
            return $value === false ? NULL : $value;
1965
 
1966
        } else {
1967
            if (isset($CFG->$name)) {
1968
                return $CFG->$name;
1969
            } else {
1970
                return NULL;
1971
            }
1972
        }
1973
    }
1974
 
1975
    /**
1976
     * Used to set a config pair and log change
1977
     *
1978
     * @param string $name
1979
     * @param mixed $value Gets converted to string if not null
1980
     * @return bool Write setting to config table
1981
     */
1982
    public function config_write($name, $value) {
1983
        global $DB, $USER, $CFG;
1984
 
1985
        if ($this->nosave) {
1986
            return true;
1987
        }
1988
 
1989
        // make sure it is a real change
1990
        $oldvalue = get_config($this->plugin, $name);
1991
        $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1992
        $value = is_null($value) ? null : (string)$value;
1993
 
1994
        if ($oldvalue === $value) {
1995
            return true;
1996
        }
1997
 
1998
        // store change
1999
        set_config($name, $value, $this->plugin);
2000
 
2001
        // Some admin settings affect course modinfo
2002
        if ($this->affectsmodinfo) {
2003
            // Clear course cache for all courses
2004
            rebuild_course_cache(0, true);
2005
        }
2006
 
2007
        $this->add_to_config_log($name, $oldvalue, $value);
2008
 
2009
        return true; // BC only
2010
    }
2011
 
2012
    /**
2013
     * Log config changes if necessary.
2014
     * @param string $name
2015
     * @param string $oldvalue
2016
     * @param string $value
2017
     */
2018
    protected function add_to_config_log($name, $oldvalue, $value) {
2019
        add_to_config_log($name, $oldvalue, $value, $this->plugin);
2020
    }
2021
 
2022
    /**
2023
     * Returns current value of this setting
2024
     * @return mixed array or string depending on instance, NULL means not set yet
2025
     */
2026
    abstract public function get_setting();
2027
 
2028
    /**
2029
     * Returns default setting if exists
2030
     * @return mixed array or string depending on instance; NULL means no default, user must supply
2031
     */
2032
    public function get_defaultsetting() {
2033
        $adminroot =  admin_get_root(false, false);
2034
        if (!empty($adminroot->custom_defaults)) {
2035
            $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
2036
            if (isset($adminroot->custom_defaults[$plugin])) {
2037
                if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
2038
                    return $adminroot->custom_defaults[$plugin][$this->name];
2039
                }
2040
            }
2041
        }
2042
        return $this->defaultsetting;
2043
    }
2044
 
2045
    /**
2046
     * Store new setting
2047
     *
2048
     * @param mixed $data string or array, must not be NULL
2049
     * @return string empty string if ok, string error message otherwise
2050
     */
2051
    abstract public function write_setting($data);
2052
 
2053
    /**
2054
     * Return part of form with setting
2055
     * This function should always be overwritten
2056
     *
2057
     * @param mixed $data array or string depending on setting
2058
     * @param string $query
2059
     * @return string
2060
     */
2061
    public function output_html($data, $query='') {
2062
    // should be overridden
2063
        return;
2064
    }
2065
 
2066
    /**
2067
     * Function called if setting updated - cleanup, cache reset, etc.
2068
     * @param callable $functionname Sets the function name
2069
     * @return void
2070
     */
2071
    public function set_updatedcallback($functionname) {
2072
        $this->updatedcallback = $functionname;
2073
    }
2074
 
2075
    /**
2076
     * Execute postupdatecallback if necessary.
2077
     * @param mixed $original original value before write_setting()
2078
     * @return bool true if changed, false if not.
2079
     */
2080
    public function post_write_settings($original) {
2081
        // Comparison must work for arrays too.
2082
        if (serialize($original) === serialize($this->get_setting())) {
2083
            return false;
2084
        }
2085
 
2086
        $callbackfunction = $this->updatedcallback;
2087
        if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2088
            $callbackfunction($this->get_full_name());
2089
        }
2090
        return true;
2091
    }
2092
 
2093
    /**
2094
     * Is setting related to query text - used when searching
2095
     * @param string $query
2096
     * @return bool
2097
     */
2098
    public function is_related($query) {
2099
        if (strpos(strtolower($this->name), $query) !== false) {
2100
            return true;
2101
        }
2102
        if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2103
            return true;
2104
        }
2105
        if (strpos(core_text::strtolower($this->description), $query) !== false) {
2106
            return true;
2107
        }
2108
        $current = $this->get_setting();
2109
        if (!is_null($current)) {
2110
            if (is_string($current)) {
2111
                if (strpos(core_text::strtolower($current), $query) !== false) {
2112
                    return true;
2113
                }
2114
            }
2115
        }
2116
        $default = $this->get_defaultsetting();
2117
        if (!is_null($default)) {
2118
            if (is_string($default)) {
2119
                if (strpos(core_text::strtolower($default), $query) !== false) {
2120
                    return true;
2121
                }
2122
            }
2123
        }
2124
        return false;
2125
    }
2126
 
2127
    /**
2128
     * Get whether this should be displayed in LTR mode.
2129
     *
2130
     * @return bool|null
2131
     */
2132
    public function get_force_ltr() {
2133
        return $this->forceltr;
2134
    }
2135
 
2136
    /**
2137
     * Set whether to force LTR or not.
2138
     *
2139
     * @param bool $value True when forced, false when not force, null when unknown.
2140
     */
2141
    public function set_force_ltr($value) {
2142
        $this->forceltr = $value;
2143
    }
2144
 
2145
    /**
2146
     * Add a setting to the list of those that could cause this one to be hidden
2147
     * @param string $dependenton
2148
     */
2149
    public function add_dependent_on($dependenton) {
2150
        $this->dependenton[] = $dependenton;
2151
    }
2152
 
2153
    /**
2154
     * Get a list of the settings that could cause this one to be hidden.
2155
     * @return array
2156
     */
2157
    public function get_dependent_on() {
2158
        return $this->dependenton;
2159
    }
2160
 
2161
    /**
2162
     * Whether this setting uses a custom form control.
2163
     * This function is especially useful to decide if we should render a label element for this setting or not.
2164
     *
2165
     * @return bool
2166
     */
2167
    public function has_custom_form_control(): bool {
2168
        return $this->customcontrol;
2169
    }
2170
 
2171
    /**
2172
     * Whether the setting can be overridden in config.php.
2173
     *
2174
     * Returning true will allow the setting to be defined and overridden in config.php.
2175
     * Returning false will prevent the config setting from being overridden even when it gets defined in config.php.
2176
     *
2177
     * @return bool
2178
     */
2179
    public function is_forceable(): bool {
2180
        return true;
2181
    }
2182
}
2183
 
2184
/**
2185
 * An additional option that can be applied to an admin setting.
2186
 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2187
 *
2188
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2189
 */
2190
class admin_setting_flag {
2191
    /** @var bool Flag to indicate if this option can be toggled for this setting */
2192
    private $enabled = false;
2193
    /** @var bool Flag to indicate if this option defaults to true or false */
2194
    private $default = false;
2195
    /** @var string Short string used to create setting name - e.g. 'adv' */
2196
    private $shortname = '';
2197
    /** @var string String used as the label for this flag */
2198
    private $displayname = '';
2199
    /** @var bool Checkbox for this flag is displayed in admin page */
2200
    const ENABLED = true;
2201
    /** @var bool Checkbox for this flag is not displayed in admin page */
2202
    const DISABLED = false;
2203
 
2204
    /**
2205
     * Constructor
2206
     *
2207
     * @param bool $enabled Can this option can be toggled.
2208
     *                      Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2209
     * @param bool $default The default checked state for this setting option.
2210
     * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2211
     * @param string $displayname The displayname of this flag. Used as a label for the flag.
2212
     */
2213
    public function __construct($enabled, $default, $shortname, $displayname) {
2214
        $this->shortname = $shortname;
2215
        $this->displayname = $displayname;
2216
        $this->set_options($enabled, $default);
2217
    }
2218
 
2219
    /**
2220
     * Update the values of this setting options class
2221
     *
2222
     * @param bool $enabled Can this option can be toggled.
2223
     *                      Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2224
     * @param bool $default The default checked state for this setting option.
2225
     */
2226
    public function set_options($enabled, $default) {
2227
        $this->enabled = $enabled;
2228
        $this->default = $default;
2229
    }
2230
 
2231
    /**
2232
     * Should this option appear in the interface and be toggleable?
2233
     *
2234
     * @return bool Is it enabled?
2235
     */
2236
    public function is_enabled() {
2237
        return $this->enabled;
2238
    }
2239
 
2240
    /**
2241
     * Should this option be checked by default?
2242
     *
2243
     * @return bool Is it on by default?
2244
     */
2245
    public function get_default() {
2246
        return $this->default;
2247
    }
2248
 
2249
    /**
2250
     * Return the short name for this flag. e.g. 'adv' or 'locked'
2251
     *
2252
     * @return string
2253
     */
2254
    public function get_shortname() {
2255
        return $this->shortname;
2256
    }
2257
 
2258
    /**
2259
     * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2260
     *
2261
     * @return string
2262
     */
2263
    public function get_displayname() {
2264
        return $this->displayname;
2265
    }
2266
 
2267
    /**
2268
     * Save the submitted data for this flag - or set it to the default if $data is null.
2269
     *
2270
     * @param admin_setting $setting - The admin setting for this flag
2271
     * @param array $data - The data submitted from the form or null to set the default value for new installs.
2272
     * @return bool
2273
     */
2274
    public function write_setting_flag(admin_setting $setting, $data) {
2275
        $result = true;
2276
        if ($this->is_enabled()) {
2277
            if (!isset($data)) {
2278
                $value = $this->get_default();
2279
            } else {
2280
                $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2281
            }
2282
            $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2283
        }
2284
 
2285
        return $result;
2286
 
2287
    }
2288
 
2289
    /**
2290
     * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2291
     *
2292
     * @param admin_setting $setting - The admin setting for this flag
2293
     * @return string - The html for the checkbox.
2294
     */
2295
    public function output_setting_flag(admin_setting $setting) {
2296
        global $OUTPUT;
2297
 
2298
        $value = $setting->get_setting_flag_value($this);
2299
 
2300
        $context = new stdClass();
2301
        $context->id = $setting->get_id() . '_' . $this->get_shortname();
2302
        $context->name = $setting->get_full_name() .  '_' . $this->get_shortname();
2303
        $context->value = 1;
2304
        $context->checked = $value ? true : false;
2305
        $context->label = $this->get_displayname();
2306
 
2307
        return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2308
    }
2309
}
2310
 
2311
 
2312
/**
2313
 * No setting - just heading and text.
2314
 *
2315
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2316
 */
2317
class admin_setting_heading extends admin_setting {
2318
 
2319
    /**
2320
     * not a setting, just text
2321
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2322
     * @param string $heading heading
2323
     * @param string $information text in box
2324
     */
2325
    public function __construct($name, $heading, $information) {
2326
        $this->nosave = true;
2327
        parent::__construct($name, $heading, $information, '');
2328
    }
2329
 
2330
    /**
2331
     * Always returns true
2332
     * @return bool Always returns true
2333
     */
2334
    public function get_setting() {
2335
        return true;
2336
    }
2337
 
2338
    /**
2339
     * Always returns true
2340
     * @return bool Always returns true
2341
     */
2342
    public function get_defaultsetting() {
2343
        return true;
2344
    }
2345
 
2346
    /**
2347
     * Never write settings
2348
     * @return string Always returns an empty string
2349
     */
2350
    public function write_setting($data) {
2351
    // do not write any setting
2352
        return '';
2353
    }
2354
 
2355
    /**
2356
     * Returns an HTML string
2357
     * @return string Returns an HTML string
2358
     */
2359
    public function output_html($data, $query='') {
2360
        global $OUTPUT;
2361
        $context = new stdClass();
2362
        $context->title = $this->visiblename;
2363
        $context->description = $this->description;
2364
        $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2365
        return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2366
    }
2367
}
2368
 
2369
/**
2370
 * No setting - just name and description in same row.
2371
 *
2372
 * @copyright 2018 onwards Amaia Anabitarte
2373
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2374
 */
2375
class admin_setting_description extends admin_setting {
2376
 
2377
    /**
2378
     * Not a setting, just text
2379
     *
2380
     * @param string $name
2381
     * @param string $visiblename
2382
     * @param string $description
2383
     */
2384
    public function __construct($name, $visiblename, $description) {
2385
        $this->nosave = true;
2386
        parent::__construct($name, $visiblename, $description, '');
2387
    }
2388
 
2389
    /**
2390
     * Always returns true
2391
     *
2392
     * @return bool Always returns true
2393
     */
2394
    public function get_setting() {
2395
        return true;
2396
    }
2397
 
2398
    /**
2399
     * Always returns true
2400
     *
2401
     * @return bool Always returns true
2402
     */
2403
    public function get_defaultsetting() {
2404
        return true;
2405
    }
2406
 
2407
    /**
2408
     * Never write settings
2409
     *
2410
     * @param mixed $data Gets converted to str for comparison against yes value
2411
     * @return string Always returns an empty string
2412
     */
2413
    public function write_setting($data) {
2414
        // Do not write any setting.
2415
        return '';
2416
    }
2417
 
2418
    /**
2419
     * Returns an HTML string
2420
     *
2421
     * @param string $data
2422
     * @param string $query
2423
     * @return string Returns an HTML string
2424
     */
2425
    public function output_html($data, $query='') {
2426
        global $OUTPUT;
2427
 
2428
        $context = new stdClass();
2429
        $context->title = $this->visiblename;
2430
        $context->description = $this->description;
2431
 
2432
        return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2433
    }
2434
}
2435
 
2436
 
2437
 
2438
/**
2439
 * The most flexible setting, the user enters text.
2440
 *
2441
 * This type of field should be used for config settings which are using
2442
 * English words and are not localised (passwords, database name, list of values, ...).
2443
 *
2444
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2445
 */
2446
class admin_setting_configtext extends admin_setting {
2447
 
2448
    /** @var int default field size */
2449
    public $size;
2450
    /** @var array List of arbitrary data attributes */
2451
    protected $datavalues = [];
2452
 
2453
    /**
2454
     * Config text constructor
2455
     *
2456
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2457
     * @param string $visiblename localised
2458
     * @param string $description long localised info
2459
     * @param string $defaultsetting
2460
     * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2461
     * @param int $size default field size
2462
     */
2463
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2464
        $this->paramtype = $paramtype;
2465
        if (!is_null($size)) {
2466
            $this->size  = $size;
2467
        } else {
2468
            $this->size  = ($paramtype === PARAM_INT) ? 5 : 30;
2469
        }
2470
        parent::__construct($name, $visiblename, $description, $defaultsetting);
2471
    }
2472
 
2473
    /**
2474
     * Get whether this should be displayed in LTR mode.
2475
     *
2476
     * Try to guess from the PARAM type unless specifically set.
2477
     */
2478
    public function get_force_ltr() {
2479
        $forceltr = parent::get_force_ltr();
2480
        if ($forceltr === null) {
2481
            return !is_rtl_compatible($this->paramtype);
2482
        }
2483
        return $forceltr;
2484
    }
2485
 
2486
    /**
2487
     * Return the setting
2488
     *
2489
     * @return mixed returns config if successful else null
2490
     */
2491
    public function get_setting() {
2492
        return $this->config_read($this->name);
2493
    }
2494
 
2495
    public function write_setting($data) {
2496
        if ($this->paramtype === PARAM_INT and $data === '') {
2497
        // do not complain if '' used instead of 0
2498
            $data = 0;
2499
        }
2500
        // $data is a string
2501
        $validated = $this->validate($data);
2502
        if ($validated !== true) {
2503
            return $validated;
2504
        }
2505
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2506
    }
2507
 
2508
    /**
2509
     * Validate data before storage
2510
     * @param string data
2511
     * @return mixed true if ok string if error found
2512
     */
2513
    public function validate($data) {
2514
        // allow paramtype to be a custom regex if it is the form of /pattern/
2515
        if (preg_match('#^/.*/$#', $this->paramtype)) {
2516
            if (preg_match($this->paramtype, $data)) {
2517
                return true;
2518
            } else {
2519
                return get_string('validateerror', 'admin');
2520
            }
2521
 
2522
        } else if ($this->paramtype === PARAM_RAW) {
2523
            return true;
2524
 
2525
        } else {
2526
            $cleaned = clean_param($data, $this->paramtype);
2527
            if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2528
                return true;
2529
            } else {
2530
                return get_string('validateerror', 'admin');
2531
            }
2532
        }
2533
    }
2534
 
2535
    /**
2536
     * Set arbitrary data attributes for template.
2537
     *
2538
     * @param string $key Attribute key for template.
2539
     * @param string $value Attribute value for template.
2540
     */
2541
    public function set_data_attribute(string $key, string $value): void {
2542
        $this->datavalues[] = [
2543
            'key' => $key,
2544
            'value' => $value,
2545
        ];
2546
    }
2547
 
2548
    /**
2549
     * Return an XHTML string for the setting
2550
     * @return string Returns an XHTML string
2551
     */
2552
    public function output_html($data, $query = '') {
2553
        global $OUTPUT;
2554
 
2555
        $default = $this->get_defaultsetting();
2556
        $context = (object) [
2557
            'size' => $this->size,
2558
            'id' => $this->get_id(),
2559
            'name' => $this->get_full_name(),
2560
            'value' => $data,
2561
            'forceltr' => $this->get_force_ltr(),
2562
            'readonly' => $this->is_readonly(),
2563
            'data' => $this->datavalues,
2564
            'maxcharacter' => array_key_exists('validation-max-length', $this->datavalues),
2565
        ];
2566
        $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2567
 
2568
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2569
    }
2570
}
2571
 
2572
/**
2573
 * Text input with a maximum length constraint.
2574
 *
2575
 * @copyright 2015 onwards Ankit Agarwal
2576
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2577
 */
2578
class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2579
 
2580
    /** @var int maximum number of chars allowed. */
2581
    protected $maxlength;
2582
 
2583
    /**
2584
     * Config text constructor
2585
     *
2586
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2587
     *                     or 'myplugin/mysetting' for ones in config_plugins.
2588
     * @param string $visiblename localised
2589
     * @param string $description long localised info
2590
     * @param string $defaultsetting
2591
     * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2592
     * @param int $size default field size
2593
     * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2594
     */
2595
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2596
                                $size=null, $maxlength = 0) {
2597
        $this->maxlength = $maxlength;
2598
        $this->set_data_attribute('validation-max-length', $maxlength);
2599
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2600
    }
2601
 
2602
    /**
2603
     * Validate data before storage
2604
     *
2605
     * @param string $data data
2606
     * @return mixed true if ok string if error found
2607
     */
2608
    public function validate($data) {
2609
        $parentvalidation = parent::validate($data);
2610
        if ($parentvalidation === true) {
2611
            if ($this->maxlength > 0) {
2612
                // Max length check.
2613
                $length = core_text::strlen($data);
2614
                if ($length > $this->maxlength) {
2615
                    return get_string('maximumchars', 'moodle',  $this->maxlength);
2616
                }
2617
                return true;
2618
            } else {
2619
                return true; // No max length check needed.
2620
            }
2621
        } else {
2622
            return $parentvalidation;
2623
        }
2624
    }
2625
 
2626
    /**
2627
     * Return an XHTML string for the setting.
2628
     *
2629
     * @param string $data data.
2630
     * @param string $query query statement.
2631
     * @return string Returns an XHTML string
2632
     */
2633
    public function output_html($data, $query = ''): string {
2634
        global $PAGE;
2635
        $PAGE->requires->js_call_amd('core_form/configtext_maxlength', 'init');
2636
 
2637
        return parent::output_html($data, $query);
2638
    }
2639
}
2640
 
2641
/**
2642
 * General text area without html editor.
2643
 *
2644
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2645
 */
2646
class admin_setting_configtextarea extends admin_setting_configtext {
2647
    private $rows;
2648
    private $cols;
2649
 
2650
    /**
2651
     * @param string $name
2652
     * @param string $visiblename
2653
     * @param string $description
2654
     * @param mixed $defaultsetting string or array
2655
     * @param mixed $paramtype
2656
     * @param string $cols The number of columns to make the editor
2657
     * @param string $rows The number of rows to make the editor
2658
     */
2659
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2660
        $this->rows = $rows;
2661
        $this->cols = $cols;
2662
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2663
    }
2664
 
2665
    /**
2666
     * Returns an XHTML string for the editor
2667
     *
2668
     * @param string $data
2669
     * @param string $query
2670
     * @return string XHTML string for the editor
2671
     */
2672
    public function output_html($data, $query='') {
2673
        global $OUTPUT;
2674
 
2675
        $default = $this->get_defaultsetting();
2676
        $defaultinfo = $default;
2677
        if (!is_null($default) and $default !== '') {
2678
            $defaultinfo = "\n".$default;
2679
        }
2680
 
2681
        $context = (object) [
2682
            'cols' => $this->cols,
2683
            'rows' => $this->rows,
2684
            'id' => $this->get_id(),
2685
            'name' => $this->get_full_name(),
2686
            'value' => $data,
2687
            'forceltr' => $this->get_force_ltr(),
2688
            'readonly' => $this->is_readonly(),
2689
        ];
2690
        $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2691
 
2692
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2693
    }
2694
}
2695
 
2696
/**
2697
 * General text area with html editor.
2698
 */
2699
class admin_setting_confightmleditor extends admin_setting_configtextarea {
2700
 
2701
    /**
2702
     * @param string $name
2703
     * @param string $visiblename
2704
     * @param string $description
2705
     * @param mixed $defaultsetting string or array
2706
     * @param mixed $paramtype
2707
     */
2708
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2709
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2710
        $this->set_force_ltr(false);
2711
        editors_head_setup();
2712
    }
2713
 
2714
    /**
2715
     * Returns an XHTML string for the editor
2716
     *
2717
     * @param string $data
2718
     * @param string $query
2719
     * @return string XHTML string for the editor
2720
     */
2721
    public function output_html($data, $query='') {
2722
        $editor = editors_get_preferred_editor(FORMAT_HTML);
2723
        $editor->set_text($data);
2724
        $editor->use_editor($this->get_id(), array('noclean'=>true));
2725
        return parent::output_html($data, $query);
2726
    }
2727
 
2728
    /**
2729
     * Checks if data has empty html.
2730
     *
2731
     * @param string $data
2732
     * @return string Empty when no errors.
2733
     */
2734
    public function write_setting($data) {
2735
        if (trim(html_to_text($data)) === '') {
2736
            $data = '';
2737
        }
2738
        return parent::write_setting($data);
2739
    }
2740
}
2741
 
2742
 
2743
/**
2744
 * Password field, allows unmasking of password
2745
 *
2746
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2747
 */
2748
class admin_setting_configpasswordunmask extends admin_setting_configtext {
2749
 
2750
    /**
2751
     * Constructor
2752
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2753
     * @param string $visiblename localised
2754
     * @param string $description long localised info
2755
     * @param string $defaultsetting default password
2756
     */
2757
    public function __construct($name, $visiblename, $description, $defaultsetting) {
2758
        parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2759
    }
2760
 
2761
    /**
2762
     * Log config changes if necessary.
2763
     * @param string $name
2764
     * @param string $oldvalue
2765
     * @param string $value
2766
     */
2767
    protected function add_to_config_log($name, $oldvalue, $value) {
2768
        if ($value !== '') {
2769
            $value = '********';
2770
        }
2771
        if ($oldvalue !== '' and $oldvalue !== null) {
2772
            $oldvalue = '********';
2773
        }
2774
        parent::add_to_config_log($name, $oldvalue, $value);
2775
    }
2776
 
2777
    /**
2778
     * Returns HTML for the field.
2779
     *
2780
     * @param   string  $data       Value for the field
2781
     * @param   string  $query      Passed as final argument for format_admin_setting
2782
     * @return  string              Rendered HTML
2783
     */
2784
    public function output_html($data, $query='') {
2785
        global $OUTPUT;
2786
 
2787
        $context = (object) [
2788
            'id' => $this->get_id(),
2789
            'name' => $this->get_full_name(),
2790
            'size' => $this->size,
2791
            'value' => $this->is_readonly() ? null : $data,
2792
            'forceltr' => $this->get_force_ltr(),
2793
            'readonly' => $this->is_readonly(),
2794
        ];
2795
        $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2796
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2797
    }
2798
}
2799
 
2800
/**
2801
 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2802
 *
2803
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2804
 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2805
 */
2806
class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2807
 
2808
    /**
2809
     * Constructor
2810
     *
2811
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2812
     * @param string $visiblename localised
2813
     * @param string $description long localised info
2814
     * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2815
     */
2816
    public function __construct($name, $visiblename, $description, $defaultsetting) {
2817
        parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2818
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2819
    }
2820
}
2821
 
2822
/**
2823
 * Admin setting class for encrypted values using secure encryption.
2824
 *
2825
 * @copyright 2019 The Open University
2826
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2827
 */
2828
class admin_setting_encryptedpassword extends admin_setting {
2829
 
2830
    /**
2831
     * Constructor. Same as parent except that the default value is always an empty string.
2832
     *
2833
     * @param string $name Internal name used in config table
2834
     * @param string $visiblename Name shown on form
2835
     * @param string $description Description that appears below field
2836
     */
2837
    public function __construct(string $name, string $visiblename, string $description) {
2838
        parent::__construct($name, $visiblename, $description, '');
2839
    }
2840
 
2841
    public function get_setting() {
2842
        return $this->config_read($this->name);
2843
    }
2844
 
2845
    public function write_setting($data) {
2846
        $data = trim($data);
2847
        if ($data === '') {
2848
            // Value can really be set to nothing.
2849
            $savedata = '';
2850
        } else {
2851
            // Encrypt value before saving it.
2852
            $savedata = \core\encryption::encrypt($data);
2853
        }
2854
        return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
2855
    }
2856
 
2857
    public function output_html($data, $query='') {
2858
        global $OUTPUT;
2859
 
2860
        $default = $this->get_defaultsetting();
2861
        $context = (object) [
2862
            'id' => $this->get_id(),
2863
            'name' => $this->get_full_name(),
2864
            'set' => $data !== '',
2865
            'novalue' => $this->get_setting() === null
2866
        ];
2867
        $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
2868
 
2869
        return format_admin_setting($this, $this->visiblename, $element, $this->description,
2870
                true, '', $default, $query);
2871
    }
2872
}
2873
 
2874
/**
2875
 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2876
 * Note: Only advanced makes sense right now - locked does not.
2877
 *
2878
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2879
 */
2880
class admin_setting_configempty extends admin_setting_configtext {
2881
 
2882
    /**
2883
     * @param string $name
2884
     * @param string $visiblename
2885
     * @param string $description
2886
     */
2887
    public function __construct($name, $visiblename, $description) {
2888
        parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2889
    }
2890
 
2891
    /**
2892
     * Returns an XHTML string for the hidden field
2893
     *
2894
     * @param string $data
2895
     * @param string $query
2896
     * @return string XHTML string for the editor
2897
     */
2898
    public function output_html($data, $query='') {
2899
        global $OUTPUT;
2900
 
2901
        $context = (object) [
2902
            'id' => $this->get_id(),
2903
            'name' => $this->get_full_name()
2904
        ];
2905
        $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2906
 
2907
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2908
    }
2909
}
2910
 
2911
 
2912
/**
2913
 * Path to directory
2914
 *
2915
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2916
 */
2917
class admin_setting_configfile extends admin_setting_configtext {
2918
    /**
2919
     * Constructor
2920
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2921
     * @param string $visiblename localised
2922
     * @param string $description long localised info
2923
     * @param string $defaultdirectory default directory location
2924
     */
2925
    public function __construct($name, $visiblename, $description, $defaultdirectory) {
2926
        parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2927
    }
2928
 
2929
    /**
2930
     * Returns XHTML for the field
2931
     *
2932
     * Returns XHTML for the field and also checks whether the file
2933
     * specified in $data exists using file_exists()
2934
     *
2935
     * @param string $data File name and path to use in value attr
2936
     * @param string $query
2937
     * @return string XHTML field
2938
     */
2939
    public function output_html($data, $query='') {
2940
        global $CFG, $OUTPUT;
2941
 
2942
        $default = $this->get_defaultsetting();
2943
        $context = (object) [
2944
            'id' => $this->get_id(),
2945
            'name' => $this->get_full_name(),
2946
            'size' => $this->size,
2947
            'value' => $data,
2948
            'showvalidity' => !empty($data),
2949
            'valid' => $data && file_exists($data),
2950
            'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
2951
            'forceltr' => $this->get_force_ltr(),
2952
        ];
2953
 
2954
        if ($context->readonly) {
2955
            $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2956
        }
2957
 
2958
        $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2959
 
2960
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2961
    }
2962
 
2963
    /**
2964
     * Checks if execpatch has been disabled in config.php
2965
     */
2966
    public function write_setting($data) {
2967
        global $CFG;
2968
        if (!empty($CFG->preventexecpath)) {
2969
            if ($this->get_setting() === null) {
2970
                // Use default during installation.
2971
                $data = $this->get_defaultsetting();
2972
                if ($data === null) {
2973
                    $data = '';
2974
                }
2975
            } else {
2976
                return '';
2977
            }
2978
        }
2979
        return parent::write_setting($data);
2980
    }
2981
 
2982
}
2983
 
2984
 
2985
/**
2986
 * Path to executable file
2987
 *
2988
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2989
 */
2990
class admin_setting_configexecutable extends admin_setting_configfile {
2991
 
2992
    /**
2993
     * Returns an XHTML field
2994
     *
2995
     * @param string $data This is the value for the field
2996
     * @param string $query
2997
     * @return string XHTML field
2998
     */
2999
    public function output_html($data, $query='') {
3000
        global $CFG, $OUTPUT;
3001
        $default = $this->get_defaultsetting();
3002
        require_once("$CFG->libdir/filelib.php");
3003
 
3004
        $context = (object) [
3005
            'id' => $this->get_id(),
3006
            'name' => $this->get_full_name(),
3007
            'size' => $this->size,
3008
            'value' => $data,
3009
            'showvalidity' => !empty($data),
3010
            'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
3011
            'readonly' => !empty($CFG->preventexecpath),
3012
            'forceltr' => $this->get_force_ltr()
3013
        ];
3014
 
3015
        if (!empty($CFG->preventexecpath)) {
3016
            $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
3017
        }
3018
 
3019
        $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
3020
 
3021
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
3022
    }
3023
}
3024
 
3025
 
3026
/**
3027
 * Path to directory
3028
 *
3029
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3030
 */
3031
class admin_setting_configdirectory extends admin_setting_configfile {
3032
 
3033
    /**
3034
     * Returns an XHTML field
3035
     *
3036
     * @param string $data This is the value for the field
3037
     * @param string $query
3038
     * @return string XHTML
3039
     */
3040
    public function output_html($data, $query='') {
3041
        global $CFG, $OUTPUT;
3042
        $default = $this->get_defaultsetting();
3043
 
3044
        $context = (object) [
3045
            'id' => $this->get_id(),
3046
            'name' => $this->get_full_name(),
3047
            'size' => $this->size,
3048
            'value' => $data,
3049
            'showvalidity' => !empty($data),
3050
            'valid' => $data && file_exists($data) && is_dir($data),
3051
            'readonly' => !empty($CFG->preventexecpath),
3052
            'forceltr' => $this->get_force_ltr()
3053
        ];
3054
 
3055
        if (!empty($CFG->preventexecpath)) {
3056
            $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
3057
        }
3058
 
3059
        $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
3060
 
3061
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
3062
    }
3063
}
3064
 
3065
 
3066
/**
3067
 * Checkbox
3068
 *
3069
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3070
 */
3071
class admin_setting_configcheckbox extends admin_setting {
3072
    /** @var string Value used when checked */
3073
    public $yes;
3074
    /** @var string Value used when not checked */
3075
    public $no;
3076
 
3077
    /**
3078
     * Constructor
3079
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3080
     * @param string $visiblename localised
3081
     * @param string $description long localised info
3082
     * @param string $defaultsetting
3083
     * @param string $yes value used when checked
3084
     * @param string $no value used when not checked
3085
     */
3086
    public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
3087
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3088
        $this->yes = (string)$yes;
3089
        $this->no  = (string)$no;
3090
    }
3091
 
3092
    /**
3093
     * Retrieves the current setting using the objects name
3094
     *
3095
     * @return string
3096
     */
3097
    public function get_setting() {
3098
        return $this->config_read($this->name);
3099
    }
3100
 
3101
    /**
3102
     * Sets the value for the setting
3103
     *
3104
     * Sets the value for the setting to either the yes or no values
3105
     * of the object by comparing $data to yes
3106
     *
3107
     * @param mixed $data Gets converted to str for comparison against yes value
3108
     * @return string empty string or error
3109
     */
3110
    public function write_setting($data) {
3111
        if ((string)$data === $this->yes) { // convert to strings before comparison
3112
            $data = $this->yes;
3113
        } else {
3114
            $data = $this->no;
3115
        }
3116
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3117
    }
3118
 
3119
    /**
3120
     * Returns an XHTML checkbox field
3121
     *
3122
     * @param string $data If $data matches yes then checkbox is checked
3123
     * @param string $query
3124
     * @return string XHTML field
3125
     */
3126
    public function output_html($data, $query='') {
3127
        global $OUTPUT;
3128
 
3129
        $context = (object) [
3130
            'id' => $this->get_id(),
3131
            'name' => $this->get_full_name(),
3132
            'no' => $this->no,
3133
            'value' => $this->yes,
3134
            'checked' => (string) $data === $this->yes,
3135
            'readonly' => $this->is_readonly(),
3136
        ];
3137
 
3138
        $default = $this->get_defaultsetting();
3139
        if (!is_null($default)) {
3140
            if ((string)$default === $this->yes) {
3141
                $defaultinfo = get_string('checkboxyes', 'admin');
3142
            } else {
3143
                $defaultinfo = get_string('checkboxno', 'admin');
3144
            }
3145
        } else {
3146
            $defaultinfo = NULL;
3147
        }
3148
 
3149
        $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
3150
 
3151
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3152
    }
3153
}
3154
 
3155
 
3156
/**
3157
 * Multiple checkboxes, each represents different value, stored in csv format
3158
 *
3159
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3160
 */
3161
class admin_setting_configmulticheckbox extends admin_setting {
3162
    /** @var callable|null Loader function for choices */
3163
    protected $choiceloader = null;
3164
 
3165
    /** @var array Array of choices value=>label. */
3166
    public $choices;
3167
 
3168
    /**
3169
     * Constructor: uses parent::__construct
3170
     *
3171
     * The $choices parameter may be either an array of $value => $label format,
3172
     * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and
3173
     * returns an array in that format.
3174
     *
3175
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3176
     * @param string $visiblename localised
3177
     * @param string $description long localised info
3178
     * @param ?array $defaultsetting array of selected
3179
     * @param array|callable|null $choices array of $value => $label for each checkbox, or a callback
3180
     */
3181
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3182
        if (is_array($choices)) {
3183
            $this->choices = $choices;
3184
        }
3185
        if (is_callable($choices)) {
3186
            $this->choiceloader = $choices;
3187
        }
3188
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3189
    }
3190
 
3191
    /**
3192
     * This function may be used in ancestors for lazy loading of choices
3193
     *
3194
     * Override this method if loading of choices is expensive, such
3195
     * as when it requires multiple db requests.
3196
     *
3197
     * @return bool true if loaded, false if error
3198
     */
3199
    public function load_choices() {
3200
        if ($this->choiceloader) {
3201
            if (!is_array($this->choices)) {
3202
                $this->choices = call_user_func($this->choiceloader);
3203
            }
3204
        }
3205
        return true;
3206
    }
3207
 
3208
    /**
3209
     * Is setting related to query text - used when searching
3210
     *
3211
     * @param string $query
3212
     * @return bool true on related, false on not or failure
3213
     */
3214
    public function is_related($query) {
3215
        if (!$this->load_choices() or empty($this->choices)) {
3216
            return false;
3217
        }
3218
        if (parent::is_related($query)) {
3219
            return true;
3220
        }
3221
 
3222
        foreach ($this->choices as $desc) {
3223
            if (strpos(core_text::strtolower($desc), $query) !== false) {
3224
                return true;
3225
            }
3226
        }
3227
        return false;
3228
    }
3229
 
3230
    /**
3231
     * Returns the current setting if it is set
3232
     *
3233
     * @return mixed null if null, else an array
3234
     */
3235
    public function get_setting() {
3236
        $result = $this->config_read($this->name);
3237
 
3238
        if (is_null($result)) {
3239
            return NULL;
3240
        }
3241
        if ($result === '') {
3242
            return array();
3243
        }
3244
        $enabled = explode(',', $result);
3245
        $setting = array();
3246
        foreach ($enabled as $option) {
3247
            $setting[$option] = 1;
3248
        }
3249
        return $setting;
3250
    }
3251
 
3252
    /**
3253
     * Saves the setting(s) provided in $data
3254
     *
3255
     * @param array $data An array of data, if not array returns empty str
3256
     * @return mixed empty string on useless data or bool true=success, false=failed
3257
     */
3258
    public function write_setting($data) {
3259
        if (!is_array($data)) {
3260
            return ''; // ignore it
3261
        }
3262
        if (!$this->load_choices() or empty($this->choices)) {
3263
            return '';
3264
        }
3265
        unset($data['xxxxx']);
3266
        $result = array();
3267
        foreach ($data as $key => $value) {
3268
            if ($value and array_key_exists($key, $this->choices)) {
3269
                $result[] = $key;
3270
            }
3271
        }
3272
        return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3273
    }
3274
 
3275
    /**
3276
     * Returns XHTML field(s) as required by choices
3277
     *
3278
     * Relies on data being an array should data ever be another valid vartype with
3279
     * acceptable value this may cause a warning/error
3280
     * if (!is_array($data)) would fix the problem
3281
     *
3282
     * @todo Add vartype handling to ensure $data is an array
3283
     *
3284
     * @param array $data An array of checked values
3285
     * @param string $query
3286
     * @return string XHTML field
3287
     */
3288
    public function output_html($data, $query='') {
3289
        global $OUTPUT;
3290
 
3291
        if (!$this->load_choices() or empty($this->choices)) {
3292
            return '';
3293
        }
3294
 
3295
        $default = $this->get_defaultsetting();
3296
        if (is_null($default)) {
3297
            $default = array();
3298
        }
3299
        if (is_null($data)) {
3300
            $data = array();
3301
        }
3302
 
3303
        $context = (object) [
3304
            'id' => $this->get_id(),
3305
            'name' => $this->get_full_name(),
3306
            'readonly' => $this->is_readonly(),
3307
        ];
3308
 
3309
        $options = array();
3310
        $defaults = array();
3311
        foreach ($this->choices as $key => $description) {
3312
            if (!empty($default[$key])) {
3313
                $defaults[] = $description;
3314
            }
3315
 
3316
            $options[] = [
3317
                'key' => $key,
3318
                'checked' => !empty($data[$key]),
3319
                'label' => highlightfast($query, $description)
3320
            ];
3321
        }
3322
 
3323
        if (is_null($default)) {
3324
            $defaultinfo = null;
3325
        } else if (!empty($defaults)) {
3326
            $defaultinfo = implode(', ', $defaults);
3327
        } else {
3328
            $defaultinfo = get_string('none');
3329
        }
3330
 
3331
        $context->options = $options;
3332
        $context->hasoptions = !empty($options);
3333
 
3334
        $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3335
 
3336
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3337
 
3338
    }
3339
}
3340
 
3341
 
3342
/**
3343
 * Multiple checkboxes 2, value stored as string 00101011
3344
 *
3345
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3346
 */
3347
class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3348
 
3349
    /**
3350
     * Returns the setting if set
3351
     *
3352
     * @return mixed null if not set, else an array of set settings
3353
     */
3354
    public function get_setting() {
3355
        $result = $this->config_read($this->name);
3356
        if (is_null($result)) {
3357
            return NULL;
3358
        }
3359
        if (!$this->load_choices()) {
3360
            return NULL;
3361
        }
3362
        $result = str_pad($result, count($this->choices), '0');
3363
        $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3364
        $setting = array();
3365
        foreach ($this->choices as $key=>$unused) {
3366
            $value = array_shift($result);
3367
            if ($value) {
3368
                $setting[$key] = 1;
3369
            }
3370
        }
3371
        return $setting;
3372
    }
3373
 
3374
    /**
3375
     * Save setting(s) provided in $data param
3376
     *
3377
     * @param array $data An array of settings to save
3378
     * @return mixed empty string for bad data or bool true=>success, false=>error
3379
     */
3380
    public function write_setting($data) {
3381
        if (!is_array($data)) {
3382
            return ''; // ignore it
3383
        }
3384
        if (!$this->load_choices() or empty($this->choices)) {
3385
            return '';
3386
        }
3387
        $result = '';
3388
        foreach ($this->choices as $key=>$unused) {
3389
            if (!empty($data[$key])) {
3390
                $result .= '1';
3391
            } else {
3392
                $result .= '0';
3393
            }
3394
        }
3395
        return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3396
    }
3397
}
3398
 
3399
 
3400
/**
3401
 * Select one value from list
3402
 *
3403
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3404
 */
3405
class admin_setting_configselect extends admin_setting {
3406
    /** @var array Array of choices value=>label */
3407
    public $choices;
3408
    /** @var array Array of choices grouped using optgroups */
3409
    public $optgroups;
3410
    /** @var callable|null Loader function for choices */
3411
    protected $choiceloader = null;
3412
    /** @var callable|null Validation function */
3413
    protected $validatefunction = null;
3414
 
3415
    /**
3416
     * Constructor.
3417
     *
3418
     * If you want to lazy-load the choices, pass a callback function that returns a choice
3419
     * array for the $choices parameter.
3420
     *
3421
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3422
     * @param string $visiblename localised
3423
     * @param string $description long localised info
3424
     * @param string|int|array $defaultsetting
3425
     * @param array|callable|null $choices array of $value=>$label for each selection, or callback
3426
     */
3427
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3428
        // Look for optgroup and single options.
3429
        if (is_array($choices)) {
3430
            $this->choices = [];
3431
            foreach ($choices as $key => $val) {
3432
                if (is_array($val)) {
3433
                    $this->optgroups[$key] = $val;
3434
                    $this->choices = array_merge($this->choices, $val);
3435
                } else {
3436
                    $this->choices[$key] = $val;
3437
                }
3438
            }
3439
        }
3440
        if (is_callable($choices)) {
3441
            $this->choiceloader = $choices;
3442
        }
3443
 
3444
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3445
    }
3446
 
3447
    /**
3448
     * Sets a validate function.
3449
     *
3450
     * The callback will be passed one parameter, the new setting value, and should return either
3451
     * an empty string '' if the value is OK, or an error message if not.
3452
     *
3453
     * @param callable|null $validatefunction Validate function or null to clear
3454
     * @since Moodle 3.10
3455
     */
3456
    public function set_validate_function(?callable $validatefunction = null) {
3457
        $this->validatefunction = $validatefunction;
3458
    }
3459
 
3460
    /**
3461
     * This function may be used in ancestors for lazy loading of choices
3462
     *
3463
     * Override this method if loading of choices is expensive, such
3464
     * as when it requires multiple db requests.
3465
     *
3466
     * @return bool true if loaded, false if error
3467
     */
3468
    public function load_choices() {
3469
        if ($this->choiceloader) {
3470
            if (!is_array($this->choices)) {
3471
                $this->choices = call_user_func($this->choiceloader);
3472
            }
3473
            return true;
3474
        }
3475
        return true;
3476
    }
3477
 
3478
    /**
3479
     * Check if this is $query is related to a choice
3480
     *
3481
     * @param string $query
3482
     * @return bool true if related, false if not
3483
     */
3484
    public function is_related($query) {
3485
        if (parent::is_related($query)) {
3486
            return true;
3487
        }
3488
        if (!$this->load_choices()) {
3489
            return false;
3490
        }
3491
        foreach ($this->choices as $key=>$value) {
3492
            if (strpos(core_text::strtolower($key), $query) !== false) {
3493
                return true;
3494
            }
3495
            if (strpos(core_text::strtolower($value), $query) !== false) {
3496
                return true;
3497
            }
3498
        }
3499
        return false;
3500
    }
3501
 
3502
    /**
3503
     * Return the setting
3504
     *
3505
     * @return mixed returns config if successful else null
3506
     */
3507
    public function get_setting() {
3508
        return $this->config_read($this->name);
3509
    }
3510
 
3511
    /**
3512
     * Save a setting
3513
     *
3514
     * @param string $data
3515
     * @return string empty of error string
3516
     */
3517
    public function write_setting($data) {
3518
        if (!$this->load_choices() or empty($this->choices)) {
3519
            return '';
3520
        }
3521
        if (!array_key_exists($data, $this->choices)) {
3522
            return ''; // ignore it
3523
        }
3524
 
3525
        // Validate the new setting.
3526
        $error = $this->validate_setting($data);
3527
        if ($error) {
3528
            return $error;
3529
        }
3530
 
3531
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3532
    }
3533
 
3534
    /**
3535
     * Validate the setting. This uses the callback function if provided; subclasses could override
3536
     * to carry out validation directly in the class.
3537
     *
3538
     * @param string $data New value being set
3539
     * @return string Empty string if valid, or error message text
3540
     * @since Moodle 3.10
3541
     */
3542
    protected function validate_setting(string $data): string {
3543
        // If validation function is specified, call it now.
3544
        if ($this->validatefunction) {
3545
            return call_user_func($this->validatefunction, $data);
3546
        } else {
3547
            return '';
3548
        }
3549
    }
3550
 
3551
    /**
3552
     * Returns XHTML select field
3553
     *
3554
     * Ensure the options are loaded, and generate the XHTML for the select
3555
     * element and any warning message. Separating this out from output_html
3556
     * makes it easier to subclass this class.
3557
     *
3558
     * @param string $data the option to show as selected.
3559
     * @param string $current the currently selected option in the database, null if none.
3560
     * @param string $default the default selected option.
3561
     * @return array the HTML for the select element, and a warning message.
3562
     * @deprecated since Moodle 3.2
3563
     */
3564
    public function output_select_html($data, $current, $default, $extraname = '') {
3565
        debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3566
    }
3567
 
3568
    /**
3569
     * Returns XHTML select field and wrapping div(s)
3570
     *
3571
     * @see output_select_html()
3572
     *
3573
     * @param string $data the option to show as selected
3574
     * @param string $query
3575
     * @return string XHTML field and wrapping div
3576
     */
3577
    public function output_html($data, $query='') {
3578
        global $OUTPUT;
3579
 
3580
        $default = $this->get_defaultsetting();
3581
        $current = $this->get_setting();
3582
 
3583
        if (!$this->load_choices() || empty($this->choices)) {
3584
            return '';
3585
        }
3586
 
3587
        $context = (object) [
3588
            'id' => $this->get_id(),
3589
            'name' => $this->get_full_name(),
3590
        ];
3591
 
3592
        if (!is_null($default) && array_key_exists($default, $this->choices)) {
3593
            $defaultinfo = $this->choices[$default];
3594
        } else {
3595
            $defaultinfo = NULL;
3596
        }
3597
 
3598
        // Warnings.
3599
        $warning = '';
3600
        if ($current === null) {
3601
            // First run.
3602
        } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3603
            // No warning.
3604
        } else if (!array_key_exists($current, $this->choices)) {
3605
            $warning = get_string('warningcurrentsetting', 'admin', $current);
3606
            if (!is_null($default) && $data == $current) {
3607
                $data = $default; // Use default instead of first value when showing the form.
3608
            }
3609
        }
3610
 
3611
        $options = [];
3612
        $template = 'core_admin/setting_configselect';
3613
 
3614
        if (!empty($this->optgroups)) {
3615
            $optgroups = [];
3616
            foreach ($this->optgroups as $label => $choices) {
3617
                $optgroup = array('label' => $label, 'options' => []);
3618
                foreach ($choices as $value => $name) {
3619
                    $optgroup['options'][] = [
3620
                        'value' => $value,
3621
                        'name' => $name,
3622
                        'selected' => (string) $value == $data
3623
                    ];
3624
                    unset($this->choices[$value]);
3625
                }
3626
                $optgroups[] = $optgroup;
3627
            }
3628
            $context->options = $options;
3629
            $context->optgroups = $optgroups;
3630
            $template = 'core_admin/setting_configselect_optgroup';
3631
        }
3632
 
3633
        foreach ($this->choices as $value => $name) {
3634
            $options[] = [
3635
                'value' => $value,
3636
                'name' => $name,
3637
                'selected' => (string) $value == $data
3638
            ];
3639
        }
3640
        $context->options = $options;
3641
        $context->readonly = $this->is_readonly();
3642
 
3643
        $element = $OUTPUT->render_from_template($template, $context);
3644
 
3645
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3646
    }
3647
}
3648
 
3649
/**
3650
 * Select multiple items from list
3651
 *
3652
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3653
 */
3654
class admin_setting_configmultiselect extends admin_setting_configselect {
3655
    /**
3656
     * Constructor
3657
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3658
     * @param string $visiblename localised
3659
     * @param string $description long localised info
3660
     * @param array $defaultsetting array of selected items
3661
     * @param ?array $choices array of $value=>$label for each list item
3662
     */
3663
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3664
        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3665
    }
3666
 
3667
    /**
3668
     * Returns the select setting(s)
3669
     *
3670
     * @return mixed null or array. Null if no settings else array of setting(s)
3671
     */
3672
    public function get_setting() {
3673
        $result = $this->config_read($this->name);
3674
        if (is_null($result)) {
3675
            return NULL;
3676
        }
3677
        if ($result === '') {
3678
            return array();
3679
        }
3680
        return explode(',', $result);
3681
    }
3682
 
3683
    /**
3684
     * Saves setting(s) provided through $data
3685
     *
3686
     * Potential bug in the works should anyone call with this function
3687
     * using a vartype that is not an array
3688
     *
3689
     * @param array $data
3690
     */
3691
    public function write_setting($data) {
3692
        if (!is_array($data)) {
3693
            return ''; //ignore it
3694
        }
3695
        if (!$this->load_choices() or empty($this->choices)) {
3696
            return '';
3697
        }
3698
 
3699
        unset($data['xxxxx']);
3700
 
3701
        $save = array();
3702
        foreach ($data as $value) {
3703
            if (!array_key_exists($value, $this->choices)) {
3704
                continue; // ignore it
3705
            }
3706
            $save[] = $value;
3707
        }
3708
 
3709
        return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3710
    }
3711
 
3712
    /**
3713
     * Is setting related to query text - used when searching
3714
     *
3715
     * @param string $query
3716
     * @return bool true if related, false if not
3717
     */
3718
    public function is_related($query) {
3719
        if (!$this->load_choices() or empty($this->choices)) {
3720
            return false;
3721
        }
3722
        if (parent::is_related($query)) {
3723
            return true;
3724
        }
3725
 
3726
        foreach ($this->choices as $desc) {
3727
            if (strpos(core_text::strtolower($desc), $query) !== false) {
3728
                return true;
3729
            }
3730
        }
3731
        return false;
3732
    }
3733
 
3734
    /**
3735
     * Returns XHTML multi-select field
3736
     *
3737
     * @todo Add vartype handling to ensure $data is an array
3738
     * @param array $data Array of values to select by default
3739
     * @param string $query
3740
     * @return string XHTML multi-select field
3741
     */
3742
    public function output_html($data, $query='') {
3743
        global $OUTPUT;
3744
 
3745
        if (!$this->load_choices() or empty($this->choices)) {
3746
            return '';
3747
        }
3748
 
3749
        $default = $this->get_defaultsetting();
3750
        if (is_null($default)) {
3751
            $default = array();
3752
        }
3753
        if (is_null($data)) {
3754
            $data = array();
3755
        }
3756
 
3757
        $context = (object) [
3758
            'id' => $this->get_id(),
3759
            'name' => $this->get_full_name(),
3760
            'size' => min(10, count($this->choices))
3761
        ];
3762
 
3763
        $defaults = [];
3764
        $options = [];
3765
        $template = 'core_admin/setting_configmultiselect';
3766
 
3767
        if (!empty($this->optgroups)) {
3768
            $optgroups = [];
3769
            foreach ($this->optgroups as $label => $choices) {
3770
                $optgroup = array('label' => $label, 'options' => []);
3771
                foreach ($choices as $value => $name) {
3772
                    if (in_array($value, $default)) {
3773
                        $defaults[] = $name;
3774
                    }
3775
                    $optgroup['options'][] = [
3776
                        'value' => $value,
3777
                        'name' => $name,
3778
                        'selected' => in_array($value, $data)
3779
                    ];
3780
                    unset($this->choices[$value]);
3781
                }
3782
                $optgroups[] = $optgroup;
3783
            }
3784
            $context->optgroups = $optgroups;
3785
            $template = 'core_admin/setting_configmultiselect_optgroup';
3786
        }
3787
 
3788
        foreach ($this->choices as $value => $name) {
3789
            if (in_array($value, $default)) {
3790
                $defaults[] = $name;
3791
            }
3792
            $options[] = [
3793
                'value' => $value,
3794
                'name' => $name,
3795
                'selected' => in_array($value, $data)
3796
            ];
3797
        }
3798
        $context->options = $options;
3799
        $context->readonly = $this->is_readonly();
3800
 
3801
        if (is_null($default)) {
3802
            $defaultinfo = NULL;
3803
        } if (!empty($defaults)) {
3804
            $defaultinfo = implode(', ', $defaults);
3805
        } else {
3806
            $defaultinfo = get_string('none');
3807
        }
3808
 
3809
        $element = $OUTPUT->render_from_template($template, $context);
3810
 
3811
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3812
    }
3813
}
3814
 
3815
/**
3816
 * Time selector
3817
 *
3818
 * This is a liiitle bit messy. we're using two selects, but we're returning
3819
 * them as an array named after $name (so we only use $name2 internally for the setting)
3820
 *
3821
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3822
 */
3823
class admin_setting_configtime extends admin_setting {
3824
    /** @var string Used for setting second select (minutes) */
3825
    public $name2;
3826
 
3827
    /**
3828
     * Constructor
3829
     * @param string $hoursname setting for hours
3830
     * @param string $minutesname setting for hours
3831
     * @param string $visiblename localised
3832
     * @param string $description long localised info
3833
     * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3834
     */
3835
    public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3836
        $this->name2 = $minutesname;
3837
        parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3838
    }
3839
 
3840
    /**
3841
     * Get the selected time
3842
     *
3843
     * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3844
     */
3845
    public function get_setting() {
3846
        $result1 = $this->config_read($this->name);
3847
        $result2 = $this->config_read($this->name2);
3848
        if (is_null($result1) or is_null($result2)) {
3849
            return NULL;
3850
        }
3851
 
3852
        return array('h' => $result1, 'm' => $result2);
3853
    }
3854
 
3855
    /**
3856
     * Store the time (hours and minutes)
3857
     *
3858
     * @param array $data Must be form 'h'=>xx, 'm'=>xx
3859
     * @return string error message or empty string on success
3860
     */
3861
    public function write_setting($data) {
3862
        if (!is_array($data)) {
3863
            return '';
3864
        }
3865
 
3866
        $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3867
        return ($result ? '' : get_string('errorsetting', 'admin'));
3868
    }
3869
 
3870
    /**
3871
     * Returns XHTML time select fields
3872
     *
3873
     * @param array $data Must be form 'h'=>xx, 'm'=>xx
3874
     * @param string $query
3875
     * @return string XHTML time select fields and wrapping div(s)
3876
     */
3877
    public function output_html($data, $query='') {
3878
        global $OUTPUT;
3879
 
3880
        $default = $this->get_defaultsetting();
3881
        if (is_array($default)) {
3882
            $defaultinfo = $default['h'].':'.$default['m'];
3883
        } else {
3884
            $defaultinfo = NULL;
3885
        }
3886
 
3887
        $context = (object) [
3888
            'id' => $this->get_id(),
3889
            'name' => $this->get_full_name(),
3890
            'readonly' => $this->is_readonly(),
3891
            'hours' => array_map(function($i) use ($data) {
3892
                return [
3893
                    'value' => $i,
3894
                    'name' => $i,
3895
                    'selected' => $i == $data['h']
3896
                ];
3897
            }, range(0, 23)),
3898
            'minutes' => array_map(function($i) use ($data) {
3899
                return [
3900
                    'value' => $i,
3901
                    'name' => $i,
3902
                    'selected' => $i == $data['m']
3903
                ];
3904
            }, range(0, 59, 5))
3905
        ];
3906
 
3907
        $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3908
 
3909
        return format_admin_setting($this, $this->visiblename, $element, $this->description,
3910
            $this->get_id() . 'h', '', $defaultinfo, $query);
3911
    }
3912
 
3913
}
3914
 
3915
 
3916
/**
3917
 * Seconds duration setting.
3918
 *
3919
 * @copyright 2012 Petr Skoda (http://skodak.org)
3920
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3921
 */
3922
class admin_setting_configduration extends admin_setting {
3923
 
3924
    /** @var int default duration unit */
3925
    protected $defaultunit;
3926
    /** @var callable|null Validation function */
3927
    protected $validatefunction = null;
3928
 
3929
    /** @var int The minimum allowed value */
3930
    protected int $minduration = 0;
3931
 
3932
    /** @var null|int The maximum allowed value */
3933
    protected null|int $maxduration = null;
3934
 
3935
    /**
3936
     * Constructor
3937
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3938
     *                     or 'myplugin/mysetting' for ones in config_plugins.
3939
     * @param string $visiblename localised name
3940
     * @param string $description localised long description
3941
     * @param mixed $defaultsetting string or array depending on implementation
3942
     * @param int $defaultunit - day, week, etc. (in seconds)
3943
     */
3944
    public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3945
        if (is_number($defaultsetting)) {
3946
            $defaultsetting = self::parse_seconds($defaultsetting);
3947
        }
3948
        $units = self::get_units();
3949
        if (isset($units[$defaultunit])) {
3950
            $this->defaultunit = $defaultunit;
3951
        } else {
3952
            $this->defaultunit = 86400;
3953
        }
3954
        parent::__construct($name, $visiblename, $description, $defaultsetting);
3955
    }
3956
 
3957
    /**
3958
     * Set the minimum allowed value.
3959
     * This must be at least 0.
3960
     *
3961
     * @param int $duration
3962
     */
3963
    public function set_min_duration(int $duration): void {
3964
        if ($duration < 0) {
3965
            throw new coding_exception('The minimum duration must be at least 0.');
3966
        }
3967
 
3968
        $this->minduration = $duration;
3969
    }
3970
 
3971
    /**
3972
     * Set the maximum allowed value.
3973
     *
3974
     * A value of null will disable the maximum duration value.
3975
     *
3976
     * @param int|null $duration
3977
     */
3978
    public function set_max_duration(?int $duration): void {
3979
        $this->maxduration = $duration;
3980
    }
3981
 
3982
    /**
3983
     * Sets a validate function.
3984
     *
3985
     * The callback will be passed one parameter, the new setting value, and should return either
3986
     * an empty string '' if the value is OK, or an error message if not.
3987
     *
3988
     * @param callable|null $validatefunction Validate function or null to clear
3989
     * @since Moodle 3.10
3990
     */
3991
    public function set_validate_function(?callable $validatefunction = null) {
3992
        $this->validatefunction = $validatefunction;
3993
    }
3994
 
3995
    /**
3996
     * Validate the setting. This uses the callback function if provided; subclasses could override
3997
     * to carry out validation directly in the class.
3998
     *
3999
     * @param int $data New value being set
4000
     * @return string Empty string if valid, or error message text
4001
     * @since Moodle 3.10
4002
     */
4003
    protected function validate_setting(int $data): string {
4004
        if ($data < $this->minduration) {
4005
            return get_string(
4006
                'configduration_low',
4007
                'admin',
4008
                self::get_duration_text($this->minduration, get_string('numseconds', 'core', 0))
4009
            );
4010
        }
4011
 
4012
        if ($this->maxduration && $data > $this->maxduration) {
4013
            return get_string('configduration_high', 'admin', self::get_duration_text($this->maxduration));
4014
        }
4015
 
4016
        // If validation function is specified, call it now.
4017
        if ($this->validatefunction) {
4018
            return call_user_func($this->validatefunction, $data);
4019
        }
4020
        return '';
4021
    }
4022
 
4023
    /**
4024
     * Returns selectable units.
4025
     * @static
4026
     * @return array
4027
     */
4028
    protected static function get_units() {
4029
        return array(
4030
            604800 => get_string('weeks'),
4031
            86400 => get_string('days'),
4032
            3600 => get_string('hours'),
4033
            60 => get_string('minutes'),
4034
            1 => get_string('seconds'),
4035
        );
4036
    }
4037
 
4038
    /**
4039
     * Converts seconds to some more user friendly string.
4040
     * @static
4041
     * @param int $seconds
4042
     * @param null|string The value to use when the duration is empty. If not specified, a "None" value is used.
4043
     * @return string
4044
     */
4045
    protected static function get_duration_text(int $seconds, ?string $emptyvalue = null): string {
4046
        if (empty($seconds)) {
4047
            if ($emptyvalue !== null) {
4048
                return $emptyvalue;
4049
            }
4050
            return get_string('none');
4051
        }
4052
        $data = self::parse_seconds($seconds);
4053
        switch ($data['u']) {
4054
            case (60*60*24*7):
4055
                return get_string('numweeks', '', $data['v']);
4056
            case (60*60*24):
4057
                return get_string('numdays', '', $data['v']);
4058
            case (60*60):
4059
                return get_string('numhours', '', $data['v']);
4060
            case (60):
4061
                return get_string('numminutes', '', $data['v']);
4062
            default:
4063
                return get_string('numseconds', '', $data['v']*$data['u']);
4064
        }
4065
    }
4066
 
4067
    /**
4068
     * Finds suitable units for given duration.
4069
     * @static
4070
     * @param int $seconds
4071
     * @return array
4072
     */
4073
    protected static function parse_seconds($seconds) {
4074
        foreach (self::get_units() as $unit => $unused) {
4075
            if ($seconds % $unit === 0) {
4076
                return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
4077
            }
4078
        }
4079
        return array('v'=>(int)$seconds, 'u'=>1);
4080
    }
4081
 
4082
    /**
4083
     * Get the selected duration as array.
4084
     *
4085
     * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
4086
     */
4087
    public function get_setting() {
4088
        $seconds = $this->config_read($this->name);
4089
        if (is_null($seconds)) {
4090
            return null;
4091
        }
4092
 
4093
        return self::parse_seconds($seconds);
4094
    }
4095
 
4096
    /**
4097
     * Store the duration as seconds.
4098
     *
4099
     * @param array $data Must be form 'h'=>xx, 'm'=>xx
4100
     * @return string error message or empty string on success
4101
     */
4102
    public function write_setting($data) {
4103
        if (!is_array($data)) {
4104
            return '';
4105
        }
4106
 
4107
        $unit = (int)$data['u'];
4108
        $value = (int)$data['v'];
4109
        $seconds = $value * $unit;
4110
 
4111
        // Validate the new setting.
4112
        $error = $this->validate_setting($seconds);
4113
        if ($error) {
4114
            return $error;
4115
        }
4116
 
4117
        $result = $this->config_write($this->name, $seconds);
4118
        return ($result ? '' : get_string('errorsetting', 'admin'));
4119
    }
4120
 
4121
    /**
4122
     * Returns duration text+select fields.
4123
     *
4124
     * @param array $data Must be form 'v'=>xx, 'u'=>xx
4125
     * @param string $query
4126
     * @return string duration text+select fields and wrapping div(s)
4127
     */
4128
    public function output_html($data, $query='') {
4129
        global $OUTPUT;
4130
 
4131
        $default = $this->get_defaultsetting();
4132
        if (is_number($default)) {
4133
            $defaultinfo = self::get_duration_text($default);
4134
        } else if (is_array($default)) {
4135
            $defaultinfo = self::get_duration_text($default['v']*$default['u']);
4136
        } else {
4137
            $defaultinfo = null;
4138
        }
4139
 
4140
        $inputid = $this->get_id() . 'v';
4141
        $units = array_filter(self::get_units(), function($unit): bool {
4142
            if (!$this->maxduration) {
4143
                // No duration limit. All units are valid.
4144
                return true;
4145
            }
4146
 
4147
            return $unit <= $this->maxduration;
4148
        }, ARRAY_FILTER_USE_KEY);
4149
 
4150
        $defaultunit = $this->defaultunit;
4151
 
4152
        $context = (object) [
4153
            'id' => $this->get_id(),
4154
            'name' => $this->get_full_name(),
4155
            'value' => $data['v'] ?? '',
4156
            'readonly' => $this->is_readonly(),
4157
            'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
4158
                return [
4159
                    'value' => $unit,
4160
                    'name' => $units[$unit],
4161
                    'selected' => isset($data) && (($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u'])
4162
                ];
4163
            }, array_keys($units))
4164
        ];
4165
 
4166
        $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
4167
 
4168
        return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
4169
    }
4170
}
4171
 
4172
 
4173
/**
4174
 * Seconds duration setting with an advanced checkbox, that controls a additional
4175
 * $name.'_adv' setting.
4176
 *
4177
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4178
 * @copyright 2014 The Open University
4179
 */
4180
class admin_setting_configduration_with_advanced extends admin_setting_configduration {
4181
    /**
4182
     * Constructor
4183
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
4184
     *                     or 'myplugin/mysetting' for ones in config_plugins.
4185
     * @param string $visiblename localised name
4186
     * @param string $description localised long description
4187
     * @param array  $defaultsetting array of int value, and bool whether it is
4188
     *                     is advanced by default.
4189
     * @param int $defaultunit - day, week, etc. (in seconds)
4190
     */
4191
    public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
4192
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
4193
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4194
    }
4195
}
4196
 
4197
 
4198
/**
4199
 * Used to validate a textarea used for ip addresses
4200
 *
4201
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4202
 * @copyright 2011 Petr Skoda (http://skodak.org)
4203
 */
4204
class admin_setting_configiplist extends admin_setting_configtextarea {
4205
 
4206
    /**
4207
     * Validate the contents of the textarea as IP addresses
4208
     *
4209
     * Used to validate a new line separated list of IP addresses collected from
4210
     * a textarea control
4211
     *
4212
     * @param string $data A list of IP Addresses separated by new lines
4213
     * @return mixed bool true for success or string:error on failure
4214
     */
4215
    public function validate($data) {
4216
        if(!empty($data)) {
4217
            $lines = explode("\n", $data);
4218
        } else {
4219
            return true;
4220
        }
4221
        $result = true;
4222
        $badips = array();
4223
        foreach ($lines as $line) {
4224
            $tokens = explode('#', $line);
4225
            $ip = trim($tokens[0]);
4226
            if (empty($ip)) {
4227
                continue;
4228
            }
4229
            if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
4230
                preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
4231
                preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
4232
            } else {
4233
                $result = false;
4234
                $badips[] = $ip;
4235
            }
4236
        }
4237
        if($result) {
4238
            return true;
4239
        } else {
4240
            return get_string('validateiperror', 'admin', join(', ', $badips));
4241
        }
4242
    }
4243
}
4244
 
4245
/**
4246
 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
4247
 *
4248
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4249
 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4250
 */
4251
class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
4252
 
4253
    /**
4254
     * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
4255
     * Used to validate a new line separated list of entries collected from a textarea control.
4256
     *
4257
     * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
4258
     * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
4259
     * via the get_setting() method, which has been overriden.
4260
     *
4261
     * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
4262
     * @return mixed bool true for success or string:error on failure
4263
     */
4264
    public function validate($data) {
4265
        if (empty($data)) {
4266
            return true;
4267
        }
4268
        $entries = explode("\n", $data);
4269
        $badentries = [];
4270
 
4271
        foreach ($entries as $key => $entry) {
4272
            $entry = trim($entry);
4273
            if (empty($entry)) {
4274
                return get_string('validateemptylineerror', 'admin');
4275
            }
4276
 
4277
            // Validate each string entry against the supported formats.
4278
            if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
4279
                    || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
4280
                    || \core\ip_utils::is_domain_matching_pattern($entry)) {
4281
                continue;
4282
            }
4283
 
4284
            // Otherwise, the entry is invalid.
4285
            $badentries[] = $entry;
4286
        }
4287
 
4288
        if ($badentries) {
4289
            return get_string('validateerrorlist', 'admin', join(', ', $badentries));
4290
        }
4291
        return true;
4292
    }
4293
 
4294
    /**
4295
     * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
4296
     *
4297
     * @param string $data the setting data, as sent from the web form.
4298
     * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
4299
     */
4300
    protected function ace_encode($data) {
4301
        if (empty($data)) {
4302
            return $data;
4303
        }
4304
        $entries = explode("\n", $data);
4305
        foreach ($entries as $key => $entry) {
4306
            $entry = trim($entry);
4307
            // This regex matches any string that has non-ascii character.
4308
            if (preg_match('/[^\x00-\x7f]/', $entry)) {
4309
                // If we can convert the unicode string to an idn, do so.
4310
                // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
4311
                $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4312
                $entries[$key] = $val ? $val : $entry;
4313
            }
4314
        }
4315
        return implode("\n", $entries);
4316
    }
4317
 
4318
    /**
4319
     * Decode any ascii-encoded domain names back to their utf-8 representation for display.
4320
     *
4321
     * @param string $data the setting data, as found in the database.
4322
     * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
4323
     */
4324
    protected function ace_decode($data) {
4325
        $entries = explode("\n", $data);
4326
        foreach ($entries as $key => $entry) {
4327
            $entry = trim($entry);
4328
            if (strpos($entry, 'xn--') !== false) {
4329
                $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4330
            }
4331
        }
4332
        return implode("\n", $entries);
4333
    }
4334
 
4335
    /**
4336
     * Override, providing utf8-decoding for ascii-encoded IDN strings.
4337
     *
4338
     * @return mixed returns punycode-converted setting string if successful, else null.
4339
     */
4340
    public function get_setting() {
4341
        // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4342
        $data = $this->config_read($this->name);
4343
        if (function_exists('idn_to_utf8') && !is_null($data)) {
4344
            $data = $this->ace_decode($data);
4345
        }
4346
        return $data;
4347
    }
4348
 
4349
    /**
4350
     * Override, providing ascii-encoding for utf8 (native) IDN strings.
4351
     *
4352
     * @param string $data
4353
     * @return string
4354
     */
4355
    public function write_setting($data) {
4356
        if ($this->paramtype === PARAM_INT and $data === '') {
4357
            // Do not complain if '' used instead of 0.
4358
            $data = 0;
4359
        }
4360
 
4361
        // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4362
        if (function_exists('idn_to_ascii')) {
4363
            $data = $this->ace_encode($data);
4364
        }
4365
 
4366
        $validated = $this->validate($data);
4367
        if ($validated !== true) {
4368
            return $validated;
4369
        }
4370
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4371
    }
4372
}
4373
 
4374
/**
4375
 * Used to validate a textarea used for port numbers.
4376
 *
4377
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4378
 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4379
 */
4380
class admin_setting_configportlist extends admin_setting_configtextarea {
4381
 
4382
    /**
4383
     * Validate the contents of the textarea as port numbers.
4384
     * Used to validate a new line separated list of ports collected from a textarea control.
4385
     *
4386
     * @param string $data A list of ports separated by new lines
4387
     * @return mixed bool true for success or string:error on failure
4388
     */
4389
    public function validate($data) {
4390
        if (empty($data)) {
4391
            return true;
4392
        }
4393
        $ports = explode("\n", $data);
4394
        $badentries = [];
4395
        foreach ($ports as $port) {
4396
            $port = trim($port);
4397
            if (empty($port)) {
4398
                return get_string('validateemptylineerror', 'admin');
4399
            }
4400
 
4401
            // Is the string a valid integer number?
4402
            if (strval(intval($port)) !== $port || intval($port) <= 0) {
4403
                $badentries[] = $port;
4404
            }
4405
        }
4406
        if ($badentries) {
4407
            return get_string('validateerrorlist', 'admin', $badentries);
4408
        }
4409
        return true;
4410
    }
4411
}
4412
 
4413
 
4414
/**
4415
 * An admin setting for selecting one or more users who have a capability
4416
 * in the system context
4417
 *
4418
 * An admin setting for selecting one or more users, who have a particular capability
4419
 * in the system context. Warning, make sure the list will never be too long. There is
4420
 * no paging or searching of this list.
4421
 *
4422
 * To correctly get a list of users from this config setting, you need to call the
4423
 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4424
 *
4425
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4426
 */
4427
class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4428
    /** @var string The capabilities name */
4429
    protected $capability;
4430
    /** @var int include admin users too */
4431
    protected $includeadmins;
4432
 
4433
    /**
4434
     * Constructor.
4435
     *
4436
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4437
     * @param string $visiblename localised name
4438
     * @param string $description localised long description
4439
     * @param array $defaultsetting array of usernames
4440
     * @param string $capability string capability name.
4441
     * @param bool $includeadmins include administrators
4442
     */
4443
    function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4444
        $this->capability    = $capability;
4445
        $this->includeadmins = $includeadmins;
4446
        parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4447
    }
4448
 
4449
    /**
4450
     * Load all of the uses who have the capability into choice array
4451
     *
4452
     * @return bool Always returns true
4453
     */
4454
    function load_choices() {
4455
        if (is_array($this->choices)) {
4456
            return true;
4457
        }
4458
        list($sort, $sortparams) = users_order_by_sql('u');
4459
        if (!empty($sortparams)) {
4460
            throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4461
                    'This is unexpected, and a problem because there is no way to pass these ' .
4462
                    'parameters to get_users_by_capability. See MDL-34657.');
4463
        }
4464
        $userfieldsapi = \core_user\fields::for_name();
4465
        $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
4466
        $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4467
        $this->choices = array(
4468
            '$@NONE@$' => get_string('nobody'),
4469
            '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4470
        );
4471
        if ($this->includeadmins) {
4472
            $admins = get_admins();
4473
            foreach ($admins as $user) {
4474
                $this->choices[$user->id] = fullname($user);
4475
            }
4476
        }
4477
        if (is_array($users)) {
4478
            foreach ($users as $user) {
4479
                $this->choices[$user->id] = fullname($user);
4480
            }
4481
        }
4482
        return true;
4483
    }
4484
 
4485
    /**
4486
     * Returns the default setting for class
4487
     *
4488
     * @return mixed Array, or string. Empty string if no default
4489
     */
4490
    public function get_defaultsetting() {
4491
        $this->load_choices();
4492
        $defaultsetting = parent::get_defaultsetting();
4493
        if (empty($defaultsetting)) {
4494
            return array('$@NONE@$');
4495
        } else if (array_key_exists($defaultsetting, $this->choices)) {
4496
                return $defaultsetting;
4497
            } else {
4498
                return '';
4499
            }
4500
    }
4501
 
4502
    /**
4503
     * Returns the current setting
4504
     *
4505
     * @return mixed array or string
4506
     */
4507
    public function get_setting() {
4508
        $result = parent::get_setting();
4509
        if ($result === null) {
4510
            // this is necessary for settings upgrade
4511
            return null;
4512
        }
4513
        if (empty($result)) {
4514
            $result = array('$@NONE@$');
4515
        }
4516
        return $result;
4517
    }
4518
 
4519
    /**
4520
     * Save the chosen setting provided as $data
4521
     *
4522
     * @param array $data
4523
     * @return mixed string or array
4524
     */
4525
    public function write_setting($data) {
4526
    // If all is selected, remove any explicit options.
4527
        if (in_array('$@ALL@$', $data)) {
4528
            $data = array('$@ALL@$');
4529
        }
4530
        // None never needs to be written to the DB.
4531
        if (in_array('$@NONE@$', $data)) {
4532
            unset($data[array_search('$@NONE@$', $data)]);
4533
        }
4534
        return parent::write_setting($data);
4535
    }
4536
}
4537
 
4538
 
4539
/**
4540
 * Special checkbox for calendar - resets SESSION vars.
4541
 *
4542
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4543
 */
4544
class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4545
    /**
4546
     * Calls the parent::__construct with default values
4547
     *
4548
     * name =>  calendar_adminseesall
4549
     * visiblename => get_string('adminseesall', 'admin')
4550
     * description => get_string('helpadminseesall', 'admin')
4551
     * defaultsetting => 0
4552
     */
4553
    public function __construct() {
4554
        parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4555
            get_string('helpadminseesall', 'admin'), '0');
4556
    }
4557
 
4558
    /**
4559
     * Stores the setting passed in $data
4560
     *
4561
     * @param mixed gets converted to string for comparison
4562
     * @return string empty string or error message
4563
     */
4564
    public function write_setting($data) {
4565
        global $SESSION;
4566
        return parent::write_setting($data);
4567
    }
4568
}
4569
 
4570
/**
4571
 * Special select for settings that are altered in setup.php and can not be altered on the fly
4572
 *
4573
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4574
 */
4575
class admin_setting_special_selectsetup extends admin_setting_configselect {
4576
    /**
4577
     * Reads the setting directly from the database
4578
     *
4579
     * @return mixed
4580
     */
4581
    public function get_setting() {
4582
    // read directly from db!
4583
        return get_config(NULL, $this->name);
4584
    }
4585
 
4586
    /**
4587
     * Save the setting passed in $data
4588
     *
4589
     * @param string $data The setting to save
4590
     * @return string empty or error message
4591
     */
4592
    public function write_setting($data) {
4593
        global $CFG;
4594
        // do not change active CFG setting!
4595
        $current = $CFG->{$this->name};
4596
        $result = parent::write_setting($data);
4597
        $CFG->{$this->name} = $current;
4598
        return $result;
4599
    }
4600
}
4601
 
4602
 
4603
/**
4604
 * Special select for frontpage - stores data in course table
4605
 *
4606
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4607
 */
4608
class admin_setting_sitesetselect extends admin_setting_configselect {
4609
    /**
4610
     * Returns the site name for the selected site
4611
     *
4612
     * @see get_site()
4613
     * @return string The site name of the selected site
4614
     */
4615
    public function get_setting() {
4616
        $site = course_get_format(get_site())->get_course();
4617
        return $site->{$this->name};
4618
    }
4619
 
4620
    /**
4621
     * Updates the database and save the setting
4622
     *
4623
     * @param string data
4624
     * @return string empty or error message
4625
     */
4626
    public function write_setting($data) {
4627
        global $DB, $SITE, $COURSE;
4628
        if (!in_array($data, array_keys($this->choices))) {
4629
            return get_string('errorsetting', 'admin');
4630
        }
4631
        $record = new stdClass();
4632
        $record->id           = SITEID;
4633
        $temp                 = $this->name;
4634
        $record->$temp        = $data;
4635
        $record->timemodified = time();
4636
 
4637
        course_get_format($SITE)->update_course_format_options($record);
4638
        $DB->update_record('course', $record);
4639
 
4640
        // Reset caches.
4641
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4642
        if ($SITE->id == $COURSE->id) {
4643
            $COURSE = $SITE;
4644
        }
4645
        core_courseformat\base::reset_course_cache($SITE->id);
4646
 
4647
        return '';
4648
 
4649
    }
4650
 
4651
    /**
4652
     * admin_setting_sitesetselect is not meant to be overridden in config.php.
4653
     *
4654
     * @return bool
4655
     */
4656
    public function is_forceable(): bool {
4657
        return false;
4658
    }
4659
}
4660
 
4661
 
4662
/**
4663
 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4664
 * block to hidden.
4665
 *
4666
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4667
 */
4668
class admin_setting_bloglevel extends admin_setting_configselect {
4669
    /**
4670
     * Updates the database and save the setting
4671
     *
4672
     * @param string data
4673
     * @return string empty or error message
4674
     */
4675
    public function write_setting($data) {
4676
        global $DB, $CFG;
4677
        if ($data == 0) {
4678
            $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4679
            foreach ($blogblocks as $block) {
4680
                $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4681
            }
4682
        } else {
4683
            // reenable all blocks only when switching from disabled blogs
4684
            if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4685
                $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4686
                foreach ($blogblocks as $block) {
4687
                    $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4688
                }
4689
            }
4690
        }
4691
        return parent::write_setting($data);
4692
    }
4693
}
4694
 
4695
 
4696
/**
4697
 * Special select - lists on the frontpage - hacky
4698
 *
4699
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4700
 */
4701
class admin_setting_courselist_frontpage extends admin_setting {
4702
 
4703
    /** @var array Array of choices value=>label. */
4704
    public $choices;
4705
 
4706
    /**
4707
     * Construct override, requires one param
4708
     *
4709
     * @param bool $loggedin Is the user logged in
4710
     */
4711
    public function __construct($loggedin) {
4712
        global $CFG;
4713
        require_once($CFG->dirroot.'/course/lib.php');
4714
        $name        = 'frontpage'.($loggedin ? 'loggedin' : '');
4715
        $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4716
        $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4717
        $defaults    = array(FRONTPAGEALLCOURSELIST);
4718
        parent::__construct($name, $visiblename, $description, $defaults);
4719
    }
4720
 
4721
    /**
4722
     * Loads the choices available
4723
     *
4724
     * @return bool always returns true
4725
     */
4726
    public function load_choices() {
4727
        if (is_array($this->choices)) {
4728
            return true;
4729
        }
4730
        $this->choices = array(FRONTPAGENEWS          => get_string('frontpagenews'),
4731
            FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4732
            FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4733
            FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4734
            FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4735
            FRONTPAGECOURSESEARCH  => get_string('frontpagecoursesearch'),
4736
            'none'                 => get_string('none'));
4737
        if ($this->name === 'frontpage') {
4738
            unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4739
        }
4740
        return true;
4741
    }
4742
 
4743
    /**
4744
     * Returns the selected settings
4745
     *
4746
     * @param mixed array or setting or null
4747
     */
4748
    public function get_setting() {
4749
        $result = $this->config_read($this->name);
4750
        if (is_null($result)) {
4751
            return NULL;
4752
        }
4753
        if ($result === '') {
4754
            return array();
4755
        }
4756
        return explode(',', $result);
4757
    }
4758
 
4759
    /**
4760
     * Save the selected options
4761
     *
4762
     * @param array $data
4763
     * @return mixed empty string (data is not an array) or bool true=success false=failure
4764
     */
4765
    public function write_setting($data) {
4766
        if (!is_array($data)) {
4767
            return '';
4768
        }
4769
        $this->load_choices();
4770
        $save = array();
4771
        foreach($data as $datum) {
4772
            if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4773
                continue;
4774
            }
4775
            $save[$datum] = $datum; // no duplicates
4776
        }
4777
        return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4778
    }
4779
 
4780
    /**
4781
     * Return XHTML select field and wrapping div
4782
     *
4783
     * @todo Add vartype handling to make sure $data is an array
4784
     * @param array $data Array of elements to select by default
4785
     * @return string XHTML select field and wrapping div
4786
     */
4787
    public function output_html($data, $query='') {
4788
        global $OUTPUT;
4789
 
4790
        $this->load_choices();
4791
        $currentsetting = array();
4792
        foreach ($data as $key) {
4793
            if ($key != 'none' and array_key_exists($key, $this->choices)) {
4794
                $currentsetting[] = $key; // already selected first
4795
            }
4796
        }
4797
 
4798
        $context = (object) [
4799
            'id' => $this->get_id(),
4800
            'name' => $this->get_full_name(),
4801
        ];
4802
 
4803
        $options = $this->choices;
4804
        $selects = [];
4805
        for ($i = 0; $i < count($this->choices) - 1; $i++) {
4806
            if (!array_key_exists($i, $currentsetting)) {
4807
                $currentsetting[$i] = 'none';
4808
            }
4809
            $selects[] = [
4810
                'key' => $i,
4811
                'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4812
                    return [
4813
                        'name' => $options[$option],
4814
                        'value' => $option,
4815
                        'selected' => $currentsetting[$i] == $option
4816
                    ];
4817
                }, array_keys($options))
4818
            ];
4819
        }
4820
        $context->selects = $selects;
4821
 
4822
        $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4823
 
4824
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4825
    }
4826
}
4827
 
4828
 
4829
/**
4830
 * Special checkbox for frontpage - stores data in course table
4831
 *
4832
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4833
 */
4834
class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4835
    /**
4836
     * Returns the current sites name
4837
     *
4838
     * @return string
4839
     */
4840
    public function get_setting() {
4841
        $site = course_get_format(get_site())->get_course();
4842
        return $site->{$this->name};
4843
    }
4844
 
4845
    /**
4846
     * Save the selected setting
4847
     *
4848
     * @param string $data The selected site
4849
     * @return string empty string or error message
4850
     */
4851
    public function write_setting($data) {
4852
        global $DB, $SITE, $COURSE;
4853
        $record = new stdClass();
4854
        $record->id            = $SITE->id;
4855
        $record->{$this->name} = ($data == '1' ? 1 : 0);
4856
        $record->timemodified  = time();
4857
 
4858
        course_get_format($SITE)->update_course_format_options($record);
4859
        $DB->update_record('course', $record);
4860
 
4861
        // Reset caches.
4862
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4863
        if ($SITE->id == $COURSE->id) {
4864
            $COURSE = $SITE;
4865
        }
4866
        core_courseformat\base::reset_course_cache($SITE->id);
4867
 
4868
        return '';
4869
    }
4870
 
4871
    /**
4872
     * admin_setting_sitesetcheckbox is not meant to be overridden in config.php.
4873
     *
4874
     * @return bool
4875
     */
4876
    public function is_forceable(): bool {
4877
        return false;
4878
    }
4879
}
4880
 
4881
/**
4882
 * Special text for frontpage - stores data in course table.
4883
 * Empty string means not set here. Manual setting is required.
4884
 *
4885
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4886
 */
4887
class admin_setting_sitesettext extends admin_setting_configtext {
4888
 
4889
    /**
4890
     * Constructor.
4891
     */
4892
    public function __construct() {
4893
        call_user_func_array([parent::class, '__construct'], func_get_args());
4894
        $this->set_force_ltr(false);
4895
    }
4896
 
4897
    /**
4898
     * Return the current setting
4899
     *
4900
     * @return mixed string or null
4901
     */
4902
    public function get_setting() {
4903
        $site = course_get_format(get_site())->get_course();
4904
        return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4905
    }
4906
 
4907
    /**
4908
     * Validate the selected data
4909
     *
4910
     * @param string $data The selected value to validate
4911
     * @return mixed true or message string
4912
     */
4913
    public function validate($data) {
4914
        global $DB, $SITE;
4915
        $cleaned = clean_param($data, PARAM_TEXT);
4916
        if ($cleaned === '') {
4917
            return get_string('required');
4918
        }
4919
        if ($this->name ==='shortname' &&
4920
                $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4921
            return get_string('shortnametaken', 'error', $data);
4922
        }
4923
        if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4924
            return true;
4925
        } else {
4926
            return get_string('validateerror', 'admin');
4927
        }
4928
    }
4929
 
4930
    /**
4931
     * Save the selected setting
4932
     *
4933
     * @param string $data The selected value
4934
     * @return string empty or error message
4935
     */
4936
    public function write_setting($data) {
4937
        global $DB, $SITE, $COURSE;
4938
        $data = trim($data);
4939
        $validated = $this->validate($data);
4940
        if ($validated !== true) {
4941
            return $validated;
4942
        }
4943
 
4944
        $record = new stdClass();
4945
        $record->id            = $SITE->id;
4946
        $record->{$this->name} = $data;
4947
        $record->timemodified  = time();
4948
 
4949
        course_get_format($SITE)->update_course_format_options($record);
4950
        $DB->update_record('course', $record);
4951
 
4952
        // Reset caches.
4953
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4954
        if ($SITE->id == $COURSE->id) {
4955
            $COURSE = $SITE;
4956
        }
4957
        core_courseformat\base::reset_course_cache($SITE->id);
4958
 
4959
        return '';
4960
    }
4961
 
4962
    /**
4963
     * admin_setting_sitesettext is not meant to be overridden in config.php.
4964
     *
4965
     * @return bool
4966
     */
4967
    public function is_forceable(): bool {
4968
        return false;
4969
    }
4970
}
4971
 
4972
 
4973
/**
4974
 * This type of field should be used for mandatory config settings.
4975
 *
4976
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4977
 */
4978
class admin_setting_requiredtext extends admin_setting_configtext {
4979
 
4980
    /**
4981
     * Validate data before storage.
4982
     *
4983
     * @param string $data The string to be validated.
4984
     * @return bool|string true for success or error string if invalid.
4985
     */
4986
    public function validate($data) {
4987
        $cleaned = clean_param($data, PARAM_TEXT);
4988
        if ($cleaned === '') {
4989
            return get_string('required');
4990
        }
4991
 
4992
        return parent::validate($data);
4993
    }
4994
}
4995
 
4996
/**
4997
 * This type of field should be used for mandatory config settings where setting password is required.
4998
 *
4999
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5000
 */
5001
class admin_setting_requiredpasswordunmask extends admin_setting_configpasswordunmask {
5002
 
5003
    /**
5004
     * Validate data before storage.
5005
     *
5006
     * @param string $data The string to be validated.
5007
     * @return bool|string true for success or error string if invalid.
5008
     */
5009
    public function validate($data) {
5010
        $cleaned = clean_param($data, PARAM_TEXT);
5011
        if ($cleaned === '') {
5012
            return get_string('required');
5013
        }
5014
 
5015
        return parent::validate($data);
5016
    }
5017
}
5018
 
5019
/**
5020
 * Special text editor for site description.
5021
 *
5022
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5023
 */
5024
class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
5025
 
5026
    /**
5027
     * Calls parent::__construct with specific arguments
5028
     */
5029
    public function __construct() {
5030
        parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
5031
            PARAM_RAW, 60, 15);
5032
    }
5033
 
5034
    /**
5035
     * Return the current setting
5036
     * @return string The current setting
5037
     */
5038
    public function get_setting() {
5039
        $site = course_get_format(get_site())->get_course();
5040
        return $site->{$this->name};
5041
    }
5042
 
5043
    /**
5044
     * Save the new setting
5045
     *
5046
     * @param string $data The new value to save
5047
     * @return string empty or error message
5048
     */
5049
    public function write_setting($data) {
5050
        global $DB, $SITE, $COURSE;
5051
        $record = new stdClass();
5052
        $record->id            = $SITE->id;
5053
        $record->{$this->name} = $data;
5054
        $record->timemodified  = time();
5055
 
5056
        course_get_format($SITE)->update_course_format_options($record);
5057
        $DB->update_record('course', $record);
5058
 
5059
        // Reset caches.
5060
        $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
5061
        if ($SITE->id == $COURSE->id) {
5062
            $COURSE = $SITE;
5063
        }
5064
        core_courseformat\base::reset_course_cache($SITE->id);
5065
 
5066
        return '';
5067
    }
5068
 
5069
    /**
5070
     * admin_setting_special_frontpagedesc is not meant to be overridden in config.php.
5071
     *
5072
     * @return bool
5073
     */
5074
    public function is_forceable(): bool {
5075
        return false;
5076
    }
5077
}
5078
 
5079
 
5080
/**
5081
 * Administration interface for emoticon_manager settings.
5082
 *
5083
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5084
 */
5085
class admin_setting_emoticons extends admin_setting {
5086
 
5087
    /**
5088
     * Calls parent::__construct with specific args
5089
     */
5090
    public function __construct() {
5091
        global $CFG;
5092
 
5093
        $manager = get_emoticon_manager();
5094
        $defaults = $this->prepare_form_data($manager->default_emoticons());
5095
        parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
5096
    }
5097
 
5098
    /**
5099
     * Return the current setting(s)
5100
     *
5101
     * @return ?array Current settings array
5102
     */
5103
    public function get_setting() {
5104
        global $CFG;
5105
 
5106
        $manager = get_emoticon_manager();
5107
 
5108
        $config = $this->config_read($this->name);
5109
        if (is_null($config)) {
5110
            return null;
5111
        }
5112
 
5113
        $config = $manager->decode_stored_config($config);
5114
        if (is_null($config)) {
5115
            return null;
5116
        }
5117
 
5118
        return $this->prepare_form_data($config);
5119
    }
5120
 
5121
    /**
5122
     * Save selected settings
5123
     *
5124
     * @param array $data Array of settings to save
5125
     * @return string error message or empty string on success
5126
     */
5127
    public function write_setting($data) {
5128
 
5129
        $manager = get_emoticon_manager();
5130
        $emoticons = $this->process_form_data($data);
5131
 
5132
        if ($emoticons === false) {
5133
            return false;
5134
        }
5135
 
5136
        if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
5137
            return ''; // success
5138
        } else {
5139
            return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
5140
        }
5141
    }
5142
 
5143
    /**
5144
     * Return XHTML field(s) for options
5145
     *
5146
     * @param array $data Array of options to set in HTML
5147
     * @return string XHTML string for the fields and wrapping div(s)
5148
     */
5149
    public function output_html($data, $query='') {
5150
        global $OUTPUT;
5151
 
5152
        $context = (object) [
5153
            'name' => $this->get_full_name(),
5154
            'emoticons' => [],
5155
            'forceltr' => true,
5156
        ];
5157
 
5158
        $i = 0;
5159
        foreach ($data as $field => $value) {
5160
 
5161
            // When $i == 0: text.
5162
            // When $i == 1: imagename.
5163
            // When $i == 2: imagecomponent.
5164
            // When $i == 3: altidentifier.
5165
            // When $i == 4: altcomponent.
5166
            $fields[$i] = (object) [
5167
                'field' => $field,
5168
                'value' => $value,
5169
                'index' => $i
5170
            ];
5171
            $i++;
5172
 
5173
            if ($i > 4) {
5174
                $icon = null;
5175
                if (!empty($fields[1]->value)) {
5176
                    if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
5177
                        $alt = get_string($fields[3]->value, $fields[4]->value);
5178
                    } else {
5179
                        $alt = $fields[0]->value;
5180
                    }
5181
                    $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
5182
                }
5183
                $context->emoticons[] = [
5184
                    'fields' => $fields,
5185
                    'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
5186
                ];
5187
                $fields = [];
5188
                $i = 0;
5189
            }
5190
        }
5191
 
5192
        $context->reseturl = new moodle_url('/admin/resetemoticons.php');
5193
        $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
5194
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5195
    }
5196
 
5197
    /**
5198
     * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
5199
     *
5200
     * @see self::process_form_data()
5201
     * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
5202
     * @return array of form fields and their values
5203
     */
5204
    protected function prepare_form_data(array $emoticons) {
5205
 
5206
        $form = array();
5207
        $i = 0;
5208
        foreach ($emoticons as $emoticon) {
5209
            $form['text'.$i]            = $emoticon->text;
5210
            $form['imagename'.$i]       = $emoticon->imagename;
5211
            $form['imagecomponent'.$i]  = $emoticon->imagecomponent;
5212
            $form['altidentifier'.$i]   = $emoticon->altidentifier;
5213
            $form['altcomponent'.$i]    = $emoticon->altcomponent;
5214
            $i++;
5215
        }
5216
        // add one more blank field set for new object
5217
        $form['text'.$i]            = '';
5218
        $form['imagename'.$i]       = '';
5219
        $form['imagecomponent'.$i]  = '';
5220
        $form['altidentifier'.$i]   = '';
5221
        $form['altcomponent'.$i]    = '';
5222
 
5223
        return $form;
5224
    }
5225
 
5226
    /**
5227
     * Converts the data from admin settings form into an array of emoticon objects
5228
     *
5229
     * @see self::prepare_form_data()
5230
     * @param array $data array of admin form fields and values
5231
     * @return false|array of emoticon objects
5232
     */
5233
    protected function process_form_data(array $form) {
5234
 
5235
        $count = count($form); // number of form field values
5236
 
5237
        if ($count % 5) {
5238
            // we must get five fields per emoticon object
5239
            return false;
5240
        }
5241
 
5242
        $emoticons = array();
5243
        for ($i = 0; $i < $count / 5; $i++) {
5244
            $emoticon                   = new stdClass();
5245
            $emoticon->text             = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
5246
            $emoticon->imagename        = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
5247
            $emoticon->imagecomponent   = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
5248
            $emoticon->altidentifier    = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
5249
            $emoticon->altcomponent     = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
5250
 
5251
            if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
5252
                // prevent from breaking http://url.addresses by accident
5253
                $emoticon->text = '';
5254
            }
5255
 
5256
            if (strlen($emoticon->text) < 2) {
5257
                // do not allow single character emoticons
5258
                $emoticon->text = '';
5259
            }
5260
 
5261
            if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
5262
                // emoticon text must contain some non-alphanumeric character to prevent
5263
                // breaking HTML tags
5264
                $emoticon->text = '';
5265
            }
5266
 
5267
            if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
5268
                $emoticons[] = $emoticon;
5269
            }
5270
        }
5271
        return $emoticons;
5272
    }
5273
 
5274
}
5275
 
5276
 
5277
/**
5278
 * Special setting for limiting of the list of available languages.
5279
 *
5280
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5281
 */
5282
class admin_setting_langlist extends admin_setting_configtext {
5283
    /**
5284
     * Calls parent::__construct with specific arguments
5285
     */
5286
    public function __construct() {
5287
        parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
5288
    }
5289
 
5290
    /**
5291
     * Validate that each language identifier exists on the site
5292
     *
5293
     * @param string $data
5294
     * @return bool|string True if validation successful, otherwise error string
5295
     */
5296
    public function validate($data) {
5297
        $parentcheck = parent::validate($data);
5298
        if ($parentcheck !== true) {
5299
            return $parentcheck;
5300
        }
5301
 
5302
        if ($data === '') {
5303
            return true;
5304
        }
5305
 
5306
        // Normalize language identifiers.
5307
        $langcodes = array_map('trim', explode(',', $data));
5308
        foreach ($langcodes as $langcode) {
5309
            // If the langcode contains optional alias, split it out.
5310
            [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
5311
 
5312
            if (!get_string_manager()->translation_exists($langcode)) {
5313
                return get_string('invalidlanguagecode', 'error', $langcode);
5314
            }
5315
        }
5316
 
5317
        return true;
5318
    }
5319
 
5320
    /**
5321
     * Save the new setting
5322
     *
5323
     * @param string $data The new setting
5324
     * @return string error message or empty string on success
5325
     */
5326
    public function write_setting($data) {
5327
        $return = parent::write_setting($data);
5328
        get_string_manager()->reset_caches();
5329
        return $return;
5330
    }
5331
}
5332
 
5333
 
5334
/**
5335
 * Allows to specify comma separated list of known country codes.
5336
 *
5337
 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
5338
 * known codes.
5339
 *
5340
 * @package     core
5341
 * @category    admin
5342
 * @copyright   2020 David Mudrák <david@moodle.com>
5343
 * @license     https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5344
 */
5345
class admin_setting_countrycodes extends admin_setting_configtext {
5346
 
5347
    /**
5348
     * Construct the instance of the setting.
5349
     *
5350
     * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
5351
     * @param lang_string|string $visiblename Language string with the field label text.
5352
     * @param lang_string|string $description Language string with the field description text.
5353
     * @param string $defaultsetting Default value of the setting.
5354
     * @param int $size Input text field size.
5355
     */
5356
    public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
5357
        parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
5358
    }
5359
 
5360
    /**
5361
     * Validate the setting value before storing it.
5362
     *
5363
     * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
5364
     * a comma separated list of such words.
5365
     *
5366
     * @param string $data Value inserted into the setting field.
5367
     * @return bool|string True if the value is OK, error string otherwise.
5368
     */
5369
    public function validate($data) {
5370
 
5371
        $parentcheck = parent::validate($data);
5372
 
5373
        if ($parentcheck !== true) {
5374
            return $parentcheck;
5375
        }
5376
 
5377
        if ($data === '') {
5378
            return true;
5379
        }
5380
 
5381
        $allcountries = get_string_manager()->get_list_of_countries(true);
5382
 
5383
        foreach (explode(',', $data) as $code) {
5384
            if (!isset($allcountries[$code])) {
5385
                return get_string('invalidcountrycode', 'core_error', $code);
5386
            }
5387
        }
5388
 
5389
        return true;
5390
    }
5391
}
5392
 
5393
 
5394
/**
5395
 * Selection of one of the recognised countries using the list
5396
 * returned by {@link get_list_of_countries()}.
5397
 *
5398
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5399
 */
5400
class admin_settings_country_select extends admin_setting_configselect {
5401
    protected $includeall;
5402
    public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
5403
        $this->includeall = $includeall;
5404
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
5405
    }
5406
 
5407
    /**
5408
     * Lazy-load the available choices for the select box
5409
     */
5410
    public function load_choices() {
5411
        global $CFG;
5412
        if (is_array($this->choices)) {
5413
            return true;
5414
        }
5415
        $this->choices = array_merge(
5416
                array('0' => get_string('choosedots')),
5417
                get_string_manager()->get_list_of_countries($this->includeall));
5418
        return true;
5419
    }
5420
}
5421
 
5422
 
5423
/**
5424
 * admin_setting_configselect for the default number of sections in a course,
5425
 * simply so we can lazy-load the choices.
5426
 *
5427
 * @copyright 2011 The Open University
5428
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5429
 */
5430
class admin_settings_num_course_sections extends admin_setting_configselect {
5431
    public function __construct($name, $visiblename, $description, $defaultsetting) {
5432
        parent::__construct($name, $visiblename, $description, $defaultsetting, array());
5433
    }
5434
 
5435
    /** Lazy-load the available choices for the select box */
5436
    public function load_choices() {
5437
        $max = get_config('moodlecourse', 'maxsections');
5438
        if (!isset($max) || !is_numeric($max)) {
5439
            $max = 52;
5440
        }
5441
        for ($i = 0; $i <= $max; $i++) {
5442
            $this->choices[$i] = "$i";
5443
        }
5444
        return true;
5445
    }
5446
}
5447
 
5448
 
5449
/**
5450
 * Course category selection
5451
 *
5452
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5453
 */
5454
class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete {
5455
    /**
5456
     * Calls parent::__construct with specific arguments
5457
     */
5458
    public function __construct($name, $visiblename, $description, $defaultsetting = 1) {
5459
        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null);
5460
    }
5461
 
5462
    /**
5463
     * Load the available choices for the select box
5464
     *
5465
     * @return bool
5466
     */
5467
    public function load_choices() {
5468
        if (is_array($this->choices)) {
5469
            return true;
5470
        }
5471
        $this->choices = core_course_category::make_categories_list('', 0, ' / ');
5472
        return true;
5473
    }
5474
}
5475
 
5476
 
5477
/**
5478
 * Special control for selecting days to backup
5479
 *
5480
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5481
 */
5482
class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
5483
    /**
5484
     * Calls parent::__construct with specific arguments
5485
     */
5486
    public function __construct() {
5487
        parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
5488
        $this->plugin = 'backup';
5489
    }
5490
 
5491
    /**
5492
     * Load the available choices for the select box
5493
     *
5494
     * @return bool Always returns true
5495
     */
5496
    public function load_choices() {
5497
        if (is_array($this->choices)) {
5498
            return true;
5499
        }
5500
        $this->choices = array();
5501
        $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5502
        foreach ($days as $day) {
5503
            $this->choices[$day] = get_string($day, 'calendar');
5504
        }
5505
        return true;
5506
    }
5507
}
5508
 
5509
/**
5510
 * Special setting for backup auto destination.
5511
 *
5512
 * @package    core
5513
 * @subpackage admin
5514
 * @copyright  2014 Frédéric Massart - FMCorz.net
5515
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5516
 */
5517
class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5518
 
5519
    /**
5520
     * Calls parent::__construct with specific arguments.
5521
     */
5522
    public function __construct() {
5523
        parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5524
    }
5525
 
5526
    /**
5527
     * Check if the directory must be set, depending on backup/backup_auto_storage.
5528
     *
5529
     * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5530
     * there will be conflicts if this validation happens before the other one.
5531
     *
5532
     * @param string $data Form data.
5533
     * @return string Empty when no errors.
5534
     */
5535
    public function write_setting($data) {
5536
        $storage = (int) get_config('backup', 'backup_auto_storage');
5537
        if ($storage !== 0) {
5538
            if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5539
                // The directory must exist and be writable.
5540
                return get_string('backuperrorinvaliddestination');
5541
            }
5542
        }
5543
        return parent::write_setting($data);
5544
    }
5545
}
5546
 
5547
 
5548
/**
5549
 * Special debug setting
5550
 *
5551
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5552
 */
5553
class admin_setting_special_debug extends admin_setting_configselect {
5554
    /**
5555
     * Calls parent::__construct with specific arguments
5556
     */
5557
    public function __construct() {
5558
        parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5559
    }
5560
 
5561
    /**
5562
     * Load the available choices for the select box
5563
     *
5564
     * @return bool
5565
     */
5566
    public function load_choices() {
5567
        if (is_array($this->choices)) {
5568
            return true;
5569
        }
5570
        $this->choices = array(DEBUG_NONE      => get_string('debugnone', 'admin'),
5571
            DEBUG_MINIMAL   => get_string('debugminimal', 'admin'),
5572
            DEBUG_NORMAL    => get_string('debugnormal', 'admin'),
5573
            DEBUG_ALL       => get_string('debugall', 'admin'),
5574
            DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5575
        return true;
5576
    }
5577
}
5578
 
5579
 
5580
/**
5581
 * Special admin control
5582
 *
5583
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5584
 */
5585
class admin_setting_special_calendar_weekend extends admin_setting {
5586
    /**
5587
     * Calls parent::__construct with specific arguments
5588
     */
5589
    public function __construct() {
5590
        $name = 'calendar_weekend';
5591
        $visiblename = get_string('calendar_weekend', 'admin');
5592
        $description = get_string('helpweekenddays', 'admin');
5593
        $default = array ('0', '6'); // Saturdays and Sundays
5594
        parent::__construct($name, $visiblename, $description, $default);
5595
    }
5596
 
5597
    /**
5598
     * Gets the current settings as an array
5599
     *
5600
     * @return mixed Null if none, else array of settings
5601
     */
5602
    public function get_setting() {
5603
        $result = $this->config_read($this->name);
5604
        if (is_null($result)) {
5605
            return NULL;
5606
        }
5607
        if ($result === '') {
5608
            return array();
5609
        }
5610
        $settings = array();
5611
        for ($i=0; $i<7; $i++) {
5612
            if ($result & (1 << $i)) {
5613
                $settings[] = $i;
5614
            }
5615
        }
5616
        return $settings;
5617
    }
5618
 
5619
    /**
5620
     * Save the new settings
5621
     *
5622
     * @param array $data Array of new settings
5623
     * @return string error message or empty string on success
5624
     */
5625
    public function write_setting($data) {
5626
        if (!is_array($data)) {
5627
            return '';
5628
        }
5629
        unset($data['xxxxx']);
5630
        $result = 0;
5631
        foreach($data as $index) {
5632
            $result |= 1 << $index;
5633
        }
5634
        return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5635
    }
5636
 
5637
    /**
5638
     * Return XHTML to display the control
5639
     *
5640
     * @param array $data array of selected days
5641
     * @param string $query
5642
     * @return string XHTML for display (field + wrapping div(s)
5643
     */
5644
    public function output_html($data, $query='') {
5645
        global $OUTPUT;
5646
 
5647
        // The order matters very much because of the implied numeric keys.
5648
        $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5649
        $context = (object) [
5650
            'name' => $this->get_full_name(),
5651
            'id' => $this->get_id(),
5652
            'days' => array_map(function($index) use ($days, $data) {
5653
                return [
5654
                    'index' => $index,
5655
                    'label' => get_string($days[$index], 'calendar'),
5656
                    'checked' => in_array($index, $data)
5657
                ];
5658
            }, array_keys($days))
5659
        ];
5660
 
5661
        $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5662
 
5663
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5664
 
5665
    }
5666
}
5667
 
5668
 
5669
/**
5670
 * Admin setting that allows a user to pick a behaviour.
5671
 *
5672
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5673
 */
5674
class admin_setting_question_behaviour extends admin_setting_configselect {
5675
    /**
5676
     * @param string $name name of config variable
5677
     * @param string $visiblename display name
5678
     * @param string $description description
5679
     * @param string $default default.
5680
     */
5681
    public function __construct($name, $visiblename, $description, $default) {
5682
        parent::__construct($name, $visiblename, $description, $default, null);
5683
    }
5684
 
5685
    /**
5686
     * Load list of behaviours as choices
5687
     * @return bool true => success, false => error.
5688
     */
5689
    public function load_choices() {
5690
        global $CFG;
5691
        require_once($CFG->dirroot . '/question/engine/lib.php');
5692
        $this->choices = question_engine::get_behaviour_options('');
5693
        return true;
5694
    }
5695
}
5696
 
5697
 
5698
/**
5699
 * Admin setting that allows a user to pick appropriate roles for something.
5700
 *
5701
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5702
 */
5703
class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5704
    /** @var array Array of capabilities which identify roles */
5705
    private $types;
5706
 
5707
    /**
5708
     * @param string $name Name of config variable
5709
     * @param string $visiblename Display name
5710
     * @param string $description Description
5711
     * @param array $types Array of archetypes which identify
5712
     *              roles that will be enabled by default.
5713
     */
5714
    public function __construct($name, $visiblename, $description, $types) {
5715
        parent::__construct($name, $visiblename, $description, NULL, NULL);
5716
        $this->types = $types;
5717
    }
5718
 
5719
    /**
5720
     * Load roles as choices
5721
     *
5722
     * @return bool true=>success, false=>error
5723
     */
5724
    public function load_choices() {
5725
        global $CFG, $DB;
5726
        if (during_initial_install()) {
5727
            return false;
5728
        }
5729
        if (is_array($this->choices)) {
5730
            return true;
5731
        }
5732
        if ($roles = get_all_roles()) {
5733
            $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5734
            return true;
5735
        } else {
5736
            return false;
5737
        }
5738
    }
5739
 
5740
    /**
5741
     * Return the default setting for this control
5742
     *
5743
     * @return ?array Array of default settings
5744
     */
5745
    public function get_defaultsetting() {
5746
        global $CFG;
5747
 
5748
        if (during_initial_install()) {
5749
            return null;
5750
        }
5751
        $result = array();
5752
        foreach($this->types as $archetype) {
5753
            if ($caproles = get_archetype_roles($archetype)) {
5754
                foreach ($caproles as $caprole) {
5755
                    $result[$caprole->id] = 1;
5756
                }
5757
            }
5758
        }
5759
        return $result;
5760
    }
5761
}
5762
 
5763
 
5764
/**
5765
 * Admin setting that is a list of installed filter plugins.
5766
 *
5767
 * @copyright 2015 The Open University
5768
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5769
 */
5770
class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5771
 
5772
    /**
5773
     * Constructor
5774
     *
5775
     * @param string $name unique ascii name, either 'mysetting' for settings
5776
     *      that in config, or 'myplugin/mysetting' for ones in config_plugins.
5777
     * @param string $visiblename localised name
5778
     * @param string $description localised long description
5779
     * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5780
     */
5781
    public function __construct($name, $visiblename, $description, $default) {
5782
        if (empty($default)) {
5783
            $default = array();
5784
        }
5785
        $this->load_choices();
5786
        foreach ($default as $plugin) {
5787
            if (!isset($this->choices[$plugin])) {
5788
                unset($default[$plugin]);
5789
            }
5790
        }
5791
        parent::__construct($name, $visiblename, $description, $default, null);
5792
    }
5793
 
5794
    public function load_choices() {
5795
        if (is_array($this->choices)) {
5796
            return true;
5797
        }
5798
        $this->choices = array();
5799
 
5800
        foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5801
            $this->choices[$plugin] = filter_get_name($plugin);
5802
        }
5803
        return true;
5804
    }
5805
}
5806
 
5807
 
5808
/**
5809
 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5810
 *
5811
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5812
 */
5813
class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5814
    /**
5815
     * Constructor
5816
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5817
     * @param string $visiblename localised
5818
     * @param string $description long localised info
5819
     * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5820
     * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5821
     * @param int $size default field size
5822
     */
5823
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5824
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5825
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5826
    }
5827
}
5828
 
5829
 
5830
/**
5831
 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5832
 *
5833
 * @copyright 2009 Petr Skoda (http://skodak.org)
5834
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5835
 */
5836
class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5837
 
5838
    /**
5839
     * Constructor
5840
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5841
     * @param string $visiblename localised
5842
     * @param string $description long localised info
5843
     * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5844
     * @param string $yes value used when checked
5845
     * @param string $no value used when not checked
5846
     */
5847
    public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5848
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5849
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5850
    }
5851
 
5852
}
5853
 
5854
 
5855
/**
5856
 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5857
 *
5858
 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5859
 *
5860
 * @copyright 2010 Sam Hemelryk
5861
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5862
 */
5863
class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5864
    /**
5865
     * Constructor
5866
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5867
     * @param string $visiblename localised
5868
     * @param string $description long localised info
5869
     * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5870
     * @param string $yes value used when checked
5871
     * @param string $no value used when not checked
5872
     */
5873
    public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5874
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5875
        $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5876
    }
5877
 
5878
}
5879
 
5880
/**
5881
 * Autocomplete as you type form element.
5882
 *
5883
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5884
 */
5885
class admin_setting_configselect_autocomplete extends admin_setting_configselect {
5886
    /** @var boolean $tags Should we allow typing new entries to the field? */
5887
    protected $tags = false;
5888
    /** @var string $ajax Name of an AMD module to send/process ajax requests. */
5889
    protected $ajax = '';
5890
    /** @var string $placeholder Placeholder text for an empty list. */
5891
    protected $placeholder = '';
5892
    /** @var bool $casesensitive Whether the search has to be case-sensitive. */
5893
    protected $casesensitive = false;
5894
    /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */
5895
    protected $showsuggestions = true;
5896
    /** @var string $noselectionstring String that is shown when there are no selections. */
5897
    protected $noselectionstring = '';
5898
 
5899
    /**
5900
     * Returns XHTML select field and wrapping div(s)
5901
     *
5902
     * @see output_select_html()
5903
     *
5904
     * @param string $data the option to show as selected
5905
     * @param string $query
5906
     * @return string XHTML field and wrapping div
5907
     */
5908
    public function output_html($data, $query='') {
5909
        global $PAGE;
5910
 
5911
        $html = parent::output_html($data, $query);
5912
 
5913
        if ($html === '') {
5914
            return $html;
5915
        }
5916
 
5917
        $this->placeholder = get_string('search');
5918
 
5919
        $params = array('#' . $this->get_id(), $this->tags, $this->ajax,
5920
            $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring);
5921
 
5922
        // Load autocomplete wrapper for select2 library.
5923
        $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params);
5924
 
5925
        return $html;
5926
    }
5927
}
5928
 
5929
/**
5930
 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5931
 *
5932
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5933
 */
5934
class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5935
    /**
5936
     * Calls parent::__construct with specific arguments
5937
     */
5938
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5939
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5940
        $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5941
    }
5942
 
5943
}
5944
 
5945
/**
5946
 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5947
 *
5948
 * @copyright 2017 Marina Glancy
5949
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5950
 */
5951
class admin_setting_configselect_with_lock extends admin_setting_configselect {
5952
    /**
5953
     * Constructor
5954
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5955
     *     or 'myplugin/mysetting' for ones in config_plugins.
5956
     * @param string $visiblename localised
5957
     * @param string $description long localised info
5958
     * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5959
     * @param array $choices array of $value=>$label for each selection
5960
     */
5961
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5962
        parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5963
        $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5964
    }
5965
}
5966
 
5967
 
5968
/**
5969
 * Graded roles in gradebook
5970
 *
5971
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5972
 */
5973
class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5974
    /**
5975
     * Calls parent::__construct with specific arguments
5976
     */
5977
    public function __construct() {
5978
        parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5979
            get_string('configgradebookroles', 'admin'),
5980
            array('student'));
5981
    }
5982
}
5983
 
5984
 
5985
/**
5986
 *
5987
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5988
 */
5989
class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5990
    /**
5991
     * Saves the new settings passed in $data
5992
     *
5993
     * @param string $data
5994
     * @return mixed string or Array
5995
     */
5996
    public function write_setting($data) {
5997
        global $CFG, $DB;
5998
 
5999
        $oldvalue  = $this->config_read($this->name);
6000
        $return    = parent::write_setting($data);
6001
        $newvalue  = $this->config_read($this->name);
6002
 
6003
        if ($oldvalue !== $newvalue) {
6004
        // force full regrading
6005
            $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
6006
        }
6007
 
6008
        return $return;
6009
    }
6010
}
6011
 
6012
 
6013
/**
6014
 * Which roles to show on course description page
6015
 *
6016
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6017
 */
6018
class admin_setting_special_coursecontact extends admin_setting_pickroles {
6019
    /**
6020
     * Calls parent::__construct with specific arguments
6021
     */
6022
    public function __construct() {
6023
        parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
6024
            get_string('coursecontact_desc', 'admin'),
6025
            array('editingteacher'));
6026
        $this->set_updatedcallback(function (){
6027
            cache::make('core', 'coursecontacts')->purge();
6028
        });
6029
    }
6030
}
6031
 
6032
 
6033
/**
6034
 *
6035
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6036
 */
6037
class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
6038
    /**
6039
     * Calls parent::__construct with specific arguments
6040
     */
6041
    public function __construct() {
6042
        parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
6043
            get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
6044
    }
6045
 
6046
    /**
6047
     * Old syntax of class constructor. Deprecated in PHP7.
6048
     *
6049
     * @deprecated since Moodle 3.1
6050
     */
6051
    public function admin_setting_special_gradelimiting() {
6052
        debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
6053
        self::__construct();
6054
    }
6055
 
6056
    /**
6057
     * Force site regrading
6058
     */
6059
    function regrade_all() {
6060
        global $CFG;
6061
        require_once("$CFG->libdir/gradelib.php");
6062
        grade_force_site_regrading();
6063
    }
6064
 
6065
    /**
6066
     * Saves the new settings
6067
     *
6068
     * @param mixed $data
6069
     * @return string empty string or error message
6070
     */
6071
    function write_setting($data) {
6072
        $previous = $this->get_setting();
6073
 
6074
        if ($previous === null) {
6075
            if ($data) {
6076
                $this->regrade_all();
6077
            }
6078
        } else {
6079
            if ($data != $previous) {
6080
                $this->regrade_all();
6081
            }
6082
        }
6083
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6084
    }
6085
 
6086
}
6087
 
6088
/**
6089
 * Special setting for $CFG->grade_minmaxtouse.
6090
 *
6091
 * @package    core
6092
 * @copyright  2015 Frédéric Massart - FMCorz.net
6093
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6094
 */
6095
class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
6096
 
6097
    /**
6098
     * Constructor.
6099
     */
6100
    public function __construct() {
6101
        parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
6102
            new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
6103
            array(
6104
                GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
6105
                GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
6106
            )
6107
        );
6108
    }
6109
 
6110
    /**
6111
     * Saves the new setting.
6112
     *
6113
     * @param mixed $data
6114
     * @return string empty string or error message
6115
     */
6116
    function write_setting($data) {
6117
        global $CFG;
6118
 
6119
        $previous = $this->get_setting();
6120
        $result = parent::write_setting($data);
6121
 
6122
        // If saved and the value has changed.
6123
        if (empty($result) && $previous != $data) {
6124
            require_once($CFG->libdir . '/gradelib.php');
6125
            grade_force_site_regrading();
6126
        }
6127
 
6128
        return $result;
6129
    }
6130
 
6131
}
6132
 
6133
 
6134
/**
6135
 * Primary grade export plugin - has state tracking.
6136
 *
6137
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6138
 */
6139
class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
6140
    /**
6141
     * Calls parent::__construct with specific arguments
6142
     */
6143
    public function __construct() {
6144
        parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
6145
            get_string('configgradeexport', 'admin'), array(), NULL);
6146
    }
6147
 
6148
    /**
6149
     * Load the available choices for the multicheckbox
6150
     *
6151
     * @return bool always returns true
6152
     */
6153
    public function load_choices() {
6154
        if (is_array($this->choices)) {
6155
            return true;
6156
        }
6157
        $this->choices = array();
6158
 
6159
        if ($plugins = core_component::get_plugin_list('gradeexport')) {
6160
            foreach($plugins as $plugin => $unused) {
6161
                $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
6162
            }
6163
        }
6164
        return true;
6165
    }
6166
}
6167
 
6168
/**
6169
 * A setting for the default grade export plugin.
6170
 *
6171
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6172
 */
6173
class admin_setting_special_gradeexportdefault extends admin_setting_configselect {
6174
    /**
6175
     * Calls parent::__construct with specific arguments
6176
     */
6177
    public function __construct() {
6178
        parent::__construct('gradeexport_default', get_string('gradeexportdefault', 'admin'),
6179
                get_string('configgradeexportdefault', 'admin'), null, null);
6180
    }
6181
 
6182
    /**
6183
     * Returns the default option
6184
     *
6185
     * @return string default option
6186
     */
6187
    public function get_defaultsetting() {
6188
        $this->load_choices();
6189
        $defaultsetting = parent::get_defaultsetting();
6190
        if (array_key_exists($defaultsetting, $this->choices)) {
6191
            return $defaultsetting;
6192
        } else {
6193
            return array_key_first($this->choices);
6194
        }
6195
    }
6196
 
6197
    /**
6198
     * Load the available choices for the configselect
6199
     *
6200
     * @return bool always returns true
6201
     */
6202
    public function load_choices() {
6203
        if (is_array($this->choices)) {
6204
            return true;
6205
        }
6206
        $this->choices = [];
6207
 
6208
        if ($plugins = core_component::get_plugin_list('gradeexport')) {
6209
            foreach ($plugins as $plugin => $unused) {
6210
                $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
6211
            }
6212
        }
6213
        return true;
6214
    }
6215
}
6216
 
6217
/**
6218
 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
6219
 *
6220
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6221
 */
6222
class admin_setting_special_gradepointdefault extends admin_setting_configtext {
6223
    /**
6224
     * Config gradepointmax constructor
6225
     *
6226
     * @param string $name Overidden by "gradepointmax"
6227
     * @param string $visiblename Overridden by "gradepointmax" language string.
6228
     * @param string $description Overridden by "gradepointmax_help" language string.
6229
     * @param string $defaultsetting Not used, overridden by 100.
6230
     * @param mixed $paramtype Overridden by PARAM_INT.
6231
     * @param int $size Overridden by 5.
6232
     */
6233
    public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
6234
        $name = 'gradepointdefault';
6235
        $visiblename = get_string('gradepointdefault', 'grades');
6236
        $description = get_string('gradepointdefault_help', 'grades');
6237
        $defaultsetting = 100;
6238
        $paramtype = PARAM_INT;
6239
        $size = 5;
6240
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6241
    }
6242
 
6243
    /**
6244
     * Validate data before storage
6245
     * @param string $data The submitted data
6246
     * @return bool|string true if ok, string if error found
6247
     */
6248
    public function validate($data) {
6249
        global $CFG;
6250
        if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
6251
            return true;
6252
        } else {
6253
            return get_string('gradepointdefault_validateerror', 'grades');
6254
        }
6255
    }
6256
}
6257
 
6258
 
6259
/**
6260
 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
6261
 *
6262
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6263
 */
6264
class admin_setting_special_gradepointmax extends admin_setting_configtext {
6265
 
6266
    /**
6267
     * Config gradepointmax constructor
6268
     *
6269
     * @param string $name Overidden by "gradepointmax"
6270
     * @param string $visiblename Overridden by "gradepointmax" language string.
6271
     * @param string $description Overridden by "gradepointmax_help" language string.
6272
     * @param string $defaultsetting Not used, overridden by 100.
6273
     * @param mixed $paramtype Overridden by PARAM_INT.
6274
     * @param int $size Overridden by 5.
6275
     */
6276
    public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
6277
        $name = 'gradepointmax';
6278
        $visiblename = get_string('gradepointmax', 'grades');
6279
        $description = get_string('gradepointmax_help', 'grades');
6280
        $defaultsetting = 100;
6281
        $paramtype = PARAM_INT;
6282
        $size = 5;
6283
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6284
    }
6285
 
6286
    /**
6287
     * Save the selected setting
6288
     *
6289
     * @param string $data The selected site
6290
     * @return string empty string or error message
6291
     */
6292
    public function write_setting($data) {
6293
        if ($data === '') {
6294
            $data = (int)$this->defaultsetting;
6295
        } else {
6296
            $data = $data;
6297
        }
6298
        return parent::write_setting($data);
6299
    }
6300
 
6301
    /**
6302
     * Validate data before storage
6303
     * @param string $data The submitted data
6304
     * @return bool|string true if ok, string if error found
6305
     */
6306
    public function validate($data) {
6307
        if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
6308
            return true;
6309
        } else {
6310
            return get_string('gradepointmax_validateerror', 'grades');
6311
        }
6312
    }
6313
 
6314
    /**
6315
     * Return an XHTML string for the setting
6316
     * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6317
     * @param string $query search query to be highlighted
6318
     * @return string XHTML to display control
6319
     */
6320
    public function output_html($data, $query = '') {
6321
        global $OUTPUT;
6322
 
6323
        $default = $this->get_defaultsetting();
6324
        $context = (object) [
6325
            'size' => $this->size,
6326
            'id' => $this->get_id(),
6327
            'name' => $this->get_full_name(),
6328
            'value' => $data,
6329
            'attributes' => [
6330
                'maxlength' => 5
6331
            ],
6332
            'forceltr' => $this->get_force_ltr()
6333
        ];
6334
        $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
6335
 
6336
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
6337
    }
6338
}
6339
 
6340
 
6341
/**
6342
 * Grade category settings
6343
 *
6344
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6345
 */
6346
class admin_setting_gradecat_combo extends admin_setting {
6347
 
6348
    /** @var array Array of choices value=>label. */
6349
    public $choices;
6350
 
6351
    /**
6352
     * Sets choices and calls parent::__construct with passed arguments
6353
     * @param string $name
6354
     * @param string $visiblename
6355
     * @param string $description
6356
     * @param mixed $defaultsetting string or array depending on implementation
6357
     * @param array $choices An array of choices for the control
6358
     */
6359
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
6360
        $this->choices = $choices;
6361
        parent::__construct($name, $visiblename, $description, $defaultsetting);
6362
    }
6363
 
6364
    /**
6365
     * Return the current setting(s) array
6366
     *
6367
     * @return ?array Array of value=>xx, forced=>xx
6368
     */
6369
    public function get_setting() {
6370
        global $CFG;
6371
 
6372
        $value = $this->config_read($this->name);
6373
        $flag  = $this->config_read($this->name.'_flag');
6374
 
6375
        if (is_null($value) or is_null($flag)) {
6376
            return NULL;
6377
        }
6378
 
6379
        // Bitwise operation is still required, in cases where unused 'advanced' flag is still set.
6380
        $flag   = (int)$flag;
6381
        $forced = (bool)(1 & $flag); // First bit.
6382
 
6383
        return array('value' => $value, 'forced' => $forced);
6384
    }
6385
 
6386
    /**
6387
     * Save the new settings passed in $data
6388
     *
6389
     * @todo Add vartype handling to ensure $data is array
6390
     * @param array $data Associative array of value=>xx, forced=>xx
6391
     * @return string empty or error message
6392
     */
6393
    public function write_setting($data) {
6394
        global $CFG;
6395
 
6396
        $value = $data['value'];
6397
        $forced = empty($data['forced']) ? 0 : 1;
6398
 
6399
        if (!in_array($value, array_keys($this->choices))) {
6400
            return 'Error setting ';
6401
        }
6402
 
6403
        $oldvalue = $this->config_read($this->name);
6404
        $oldflag = (int)$this->config_read($this->name.'_flag');
6405
        $oldforced = (1 & $oldflag); // first bit
6406
 
6407
        $result1 = $this->config_write($this->name, $value);
6408
        $result2 = $this->config_write($this->name.'_flag', $forced);
6409
 
6410
        // force regrade if needed
6411
        if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
6412
            require_once($CFG->libdir.'/gradelib.php');
6413
            grade_category::updated_forced_settings();
6414
        }
6415
 
6416
        if ($result1 and $result2) {
6417
            return '';
6418
        } else {
6419
            return get_string('errorsetting', 'admin');
6420
        }
6421
    }
6422
 
6423
    /**
6424
     * Return XHTML to display the field and wrapping div
6425
     *
6426
     * @todo Add vartype handling to ensure $data is array
6427
     * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6428
     * @param string $query
6429
     * @return string XHTML to display control
6430
     */
6431
    public function output_html($data, $query='') {
6432
        global $OUTPUT;
6433
 
6434
        $value  = $data['value'];
6435
 
6436
        $default = $this->get_defaultsetting();
6437
        if (!is_null($default)) {
6438
            $defaultinfo = array();
6439
            if (isset($this->choices[$default['value']])) {
6440
                $defaultinfo[] = $this->choices[$default['value']];
6441
            }
6442
            if (!empty($default['forced'])) {
6443
                $defaultinfo[] = get_string('force');
6444
            }
6445
            $defaultinfo = implode(', ', $defaultinfo);
6446
 
6447
        } else {
6448
            $defaultinfo = NULL;
6449
        }
6450
 
6451
        $options = $this->choices;
6452
        $context = (object) [
6453
            'id' => $this->get_id(),
6454
            'name' => $this->get_full_name(),
6455
            'forced' => !empty($data['forced']),
6456
            'options' => array_map(function($option) use ($options, $value) {
6457
                return [
6458
                    'value' => $option,
6459
                    'name' => $options[$option],
6460
                    'selected' => $option == $value
6461
                ];
6462
            }, array_keys($options)),
6463
        ];
6464
 
6465
        $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
6466
 
6467
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
6468
    }
6469
}
6470
 
6471
 
6472
/**
6473
 * Selection of grade report in user profiles
6474
 *
6475
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6476
 */
6477
class admin_setting_grade_profilereport extends admin_setting_configselect {
6478
    /**
6479
     * Calls parent::__construct with specific arguments
6480
     */
6481
    public function __construct() {
6482
        parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
6483
    }
6484
 
6485
    /**
6486
     * Loads an array of choices for the configselect control
6487
     *
6488
     * @return bool always return true
6489
     */
6490
    public function load_choices() {
6491
        if (is_array($this->choices)) {
6492
            return true;
6493
        }
6494
        $this->choices = array();
6495
 
6496
        global $CFG;
6497
        require_once($CFG->libdir.'/gradelib.php');
6498
 
6499
        foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6500
            if (file_exists($plugindir.'/lib.php')) {
6501
                require_once($plugindir.'/lib.php');
6502
                $functionname = 'grade_report_'.$plugin.'_profilereport';
6503
                if (function_exists($functionname)) {
6504
                    $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
6505
                }
6506
            }
6507
        }
6508
        return true;
6509
    }
6510
}
6511
 
6512
/**
6513
 * Provides a selection of grade reports to be used for "grades".
6514
 *
6515
 * @copyright 2015 Adrian Greeve <adrian@moodle.com>
6516
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6517
 */
6518
class admin_setting_my_grades_report extends admin_setting_configselect {
6519
 
6520
    /**
6521
     * Calls parent::__construct with specific arguments.
6522
     */
6523
    public function __construct() {
6524
        parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
6525
                new lang_string('mygrades_desc', 'grades'), 'overview', null);
6526
    }
6527
 
6528
    /**
6529
     * Loads an array of choices for the configselect control.
6530
     *
6531
     * @return bool always returns true.
6532
     */
6533
    public function load_choices() {
6534
        global $CFG; // Remove this line and behold the horror of behat test failures!
6535
        $this->choices = array();
6536
        foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6537
            if (file_exists($plugindir . '/lib.php')) {
6538
                require_once($plugindir . '/lib.php');
6539
                // Check to see if the class exists. Check the correct plugin convention first.
6540
                if (class_exists('gradereport_' . $plugin)) {
6541
                    $classname = 'gradereport_' . $plugin;
6542
                } else if (class_exists('grade_report_' . $plugin)) {
6543
                    // We are using the old plugin naming convention.
6544
                    $classname = 'grade_report_' . $plugin;
6545
                } else {
6546
                    continue;
6547
                }
6548
                if ($classname::supports_mygrades()) {
6549
                    $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
6550
                }
6551
            }
6552
        }
6553
        // Add an option to specify an external url.
6554
        $this->choices['external'] = get_string('externalurl', 'grades');
6555
        return true;
6556
    }
6557
}
6558
 
6559
/**
6560
 * Special class for register auth selection
6561
 *
6562
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6563
 */
6564
class admin_setting_special_registerauth extends admin_setting_configselect {
6565
    /**
6566
     * Calls parent::__construct with specific arguments
6567
     */
6568
    public function __construct() {
6569
        parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
6570
    }
6571
 
6572
    /**
6573
     * Returns the default option
6574
     *
6575
     * @return string empty or default option
6576
     */
6577
    public function get_defaultsetting() {
6578
        $this->load_choices();
6579
        $defaultsetting = parent::get_defaultsetting();
6580
        if (array_key_exists($defaultsetting, $this->choices)) {
6581
            return $defaultsetting;
6582
        } else {
6583
            return '';
6584
        }
6585
    }
6586
 
6587
    /**
6588
     * Loads the possible choices for the array
6589
     *
6590
     * @return bool always returns true
6591
     */
6592
    public function load_choices() {
6593
        global $CFG;
6594
 
6595
        if (is_array($this->choices)) {
6596
            return true;
6597
        }
6598
        $this->choices = array();
6599
        $this->choices[''] = get_string('disable');
6600
 
6601
        $authsenabled = get_enabled_auth_plugins();
6602
 
6603
        foreach ($authsenabled as $auth) {
6604
            $authplugin = get_auth_plugin($auth);
6605
            if (!$authplugin->can_signup()) {
6606
                continue;
6607
            }
6608
            // Get the auth title (from core or own auth lang files)
6609
            $authtitle = $authplugin->get_title();
6610
            $this->choices[$auth] = $authtitle;
6611
        }
6612
        return true;
6613
    }
6614
}
6615
 
6616
 
6617
/**
6618
 * General plugins manager
6619
 */
6620
class admin_page_pluginsoverview extends admin_externalpage {
6621
 
6622
    /**
6623
     * Sets basic information about the external page
6624
     */
6625
    public function __construct() {
6626
        global $CFG;
6627
        parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6628
            "$CFG->wwwroot/$CFG->admin/plugins.php");
6629
    }
6630
}
6631
 
6632
/**
6633
 * Module manage page
6634
 *
6635
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6636
 */
6637
class admin_page_managemods extends admin_externalpage {
6638
    /**
6639
     * Calls parent::__construct with specific arguments
6640
     */
6641
    public function __construct() {
6642
        global $CFG;
6643
        parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6644
    }
6645
 
6646
    /**
6647
     * Try to find the specified module
6648
     *
6649
     * @param string $query The module to search for
6650
     * @return array
6651
     */
6652
    public function search($query) {
6653
        global $CFG, $DB;
6654
        if ($result = parent::search($query)) {
6655
            return $result;
6656
        }
6657
 
6658
        $found = false;
6659
        if ($modules = $DB->get_records('modules')) {
6660
            foreach ($modules as $module) {
6661
                if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6662
                    continue;
6663
                }
6664
                if (strpos($module->name, $query) !== false) {
6665
                    $found = true;
6666
                    break;
6667
                }
6668
                $strmodulename = get_string('modulename', $module->name);
6669
                if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6670
                    $found = true;
6671
                    break;
6672
                }
6673
            }
6674
        }
6675
        if ($found) {
6676
            $result = new stdClass();
6677
            $result->page     = $this;
6678
            $result->settings = array();
6679
            return array($this->name => $result);
6680
        } else {
6681
            return array();
6682
        }
6683
    }
6684
}
6685
 
6686
 
6687
/**
6688
 * Special class for enrol plugins management.
6689
 *
6690
 * @copyright 2010 Petr Skoda {@link http://skodak.org}
6691
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6692
 */
6693
class admin_setting_manageenrols extends admin_setting {
6694
    /**
6695
     * Calls parent::__construct with specific arguments
6696
     */
6697
    public function __construct() {
6698
        $this->nosave = true;
6699
        parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6700
    }
6701
 
6702
    /**
6703
     * Always returns true, does nothing
6704
     *
6705
     * @return true
6706
     */
6707
    public function get_setting() {
6708
        return true;
6709
    }
6710
 
6711
    /**
6712
     * Always returns true, does nothing
6713
     *
6714
     * @return true
6715
     */
6716
    public function get_defaultsetting() {
6717
        return true;
6718
    }
6719
 
6720
    /**
6721
     * Always returns '', does not write anything
6722
     *
6723
     * @return string Always returns ''
6724
     */
6725
    public function write_setting($data) {
6726
    // do not write any setting
6727
        return '';
6728
    }
6729
 
6730
    /**
6731
     * Checks if $query is one of the available enrol plugins
6732
     *
6733
     * @param string $query The string to search for
6734
     * @return bool Returns true if found, false if not
6735
     */
6736
    public function is_related($query) {
6737
        if (parent::is_related($query)) {
6738
            return true;
6739
        }
6740
 
6741
        $query = core_text::strtolower($query);
6742
        $enrols = enrol_get_plugins(false);
6743
        foreach ($enrols as $name=>$enrol) {
6744
            $localised = get_string('pluginname', 'enrol_'.$name);
6745
            if (strpos(core_text::strtolower($name), $query) !== false) {
6746
                return true;
6747
            }
6748
            if (strpos(core_text::strtolower($localised), $query) !== false) {
6749
                return true;
6750
            }
6751
        }
6752
        return false;
6753
    }
6754
 
6755
    /**
6756
     * Builds the XHTML to display the control
6757
     *
6758
     * @param string $data Unused
6759
     * @param string $query
6760
     * @return string
6761
     */
6762
    public function output_html($data, $query='') {
6763
        global $CFG, $OUTPUT, $DB, $PAGE;
6764
 
6765
        // Display strings.
6766
        $strup        = get_string('up');
6767
        $strdown      = get_string('down');
6768
        $strsettings  = get_string('settings');
6769
        $strenable    = get_string('enable');
6770
        $strdisable   = get_string('disable');
6771
        $struninstall = get_string('uninstallplugin', 'core_admin');
6772
        $strusage     = get_string('enrolusage', 'enrol');
6773
        $strversion   = get_string('version');
6774
        $strtest      = get_string('testsettings', 'core_enrol');
6775
 
6776
        $pluginmanager = core_plugin_manager::instance();
6777
 
6778
        $enrols_available = enrol_get_plugins(false);
6779
        $active_enrols    = enrol_get_plugins(true);
6780
 
6781
        $allenrols = array();
6782
        foreach ($active_enrols as $key=>$enrol) {
6783
            $allenrols[$key] = true;
6784
        }
6785
        foreach ($enrols_available as $key=>$enrol) {
6786
            $allenrols[$key] = true;
6787
        }
6788
        // Now find all borked plugins and at least allow then to uninstall.
6789
        $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6790
        foreach ($condidates as $candidate) {
6791
            if (empty($allenrols[$candidate])) {
6792
                $allenrols[$candidate] = true;
6793
            }
6794
        }
6795
 
6796
        $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6797
        $return .= $OUTPUT->box_start('generalbox enrolsui');
6798
 
6799
        $table = new html_table();
6800
        $table->head  = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6801
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6802
        $table->id = 'courseenrolmentplugins';
6803
        $table->attributes['class'] = 'admintable generaltable';
6804
        $table->data  = array();
6805
 
6806
        // Iterate through enrol plugins and add to the display table.
6807
        $updowncount = 1;
6808
        $enrolcount = count($active_enrols);
6809
        $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6810
        $printed = array();
6811
        foreach($allenrols as $enrol => $unused) {
6812
            $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6813
            $version = get_config('enrol_'.$enrol, 'version');
6814
            if ($version === false) {
6815
                $version = '';
6816
            }
6817
 
6818
            if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6819
                $name = get_string('pluginname', 'enrol_'.$enrol);
6820
            } else {
6821
                $name = $enrol;
6822
            }
6823
            // Usage.
6824
            $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6825
            $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6826
            $usage = "$ci / $cp";
6827
 
6828
            // Hide/show links.
6829
            $class = '';
6830
            if (isset($active_enrols[$enrol])) {
6831
                $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6832
                $hideshow = "<a href=\"$aurl\">";
6833
                $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6834
                $enabled = true;
6835
                $displayname = $name;
6836
            } else if (isset($enrols_available[$enrol])) {
6837
                $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6838
                $hideshow = "<a href=\"$aurl\">";
6839
                $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6840
                $enabled = false;
6841
                $displayname = $name;
6842
                $class = 'dimmed_text';
6843
            } else {
6844
                $hideshow = '';
6845
                $enabled = false;
6846
                $displayname = '<span class="notifyproblem">'.$name.'</span>';
6847
            }
6848
            if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6849
                $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6850
            } else {
6851
                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6852
            }
6853
 
6854
            // Up/down link (only if enrol is enabled).
6855
            $updown = '';
6856
            if ($enabled) {
6857
                if ($updowncount > 1) {
6858
                    $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6859
                    $updown .= "<a href=\"$aurl\">";
6860
                    $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6861
                } else {
6862
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
6863
                }
6864
                if ($updowncount < $enrolcount) {
6865
                    $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6866
                    $updown .= "<a href=\"$aurl\">";
6867
                    $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6868
                } else {
6869
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
6870
                }
6871
                ++$updowncount;
6872
            }
6873
 
6874
            // Add settings link.
6875
            if (!$version) {
6876
                $settings = '';
6877
            } else if ($surl = $plugininfo->get_settings_url()) {
6878
                $settings = html_writer::link($surl, $strsettings);
6879
            } else {
6880
                $settings = '';
6881
            }
6882
 
6883
            // Add uninstall info.
6884
            $uninstall = '';
6885
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6886
                $uninstall = html_writer::link($uninstallurl, $struninstall);
6887
            }
6888
 
6889
            $test = '';
6890
            if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6891
                $testsettingsurl = new moodle_url('/enrol/test_settings.php', ['enrol' => $enrol]);
6892
                $test = html_writer::link($testsettingsurl, $strtest);
6893
            }
6894
 
6895
            // Add a row to the table.
6896
            $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6897
            if ($class) {
6898
                $row->attributes['class'] = $class;
6899
            }
6900
            $table->data[] = $row;
6901
 
6902
            $printed[$enrol] = true;
6903
        }
6904
 
6905
        $return .= html_writer::table($table);
6906
        $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6907
        $return .= $OUTPUT->box_end();
6908
        return highlight($query, $return);
6909
    }
6910
}
6911
 
6912
 
6913
/**
6914
 * Blocks manage page
6915
 *
6916
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6917
 */
6918
class admin_page_manageblocks extends admin_externalpage {
6919
    /**
6920
     * Calls parent::__construct with specific arguments
6921
     */
6922
    public function __construct() {
6923
        global $CFG;
6924
        parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6925
    }
6926
 
6927
    /**
6928
     * Search for a specific block
6929
     *
6930
     * @param string $query The string to search for
6931
     * @return array
6932
     */
6933
    public function search($query) {
6934
        global $CFG, $DB;
6935
        if ($result = parent::search($query)) {
6936
            return $result;
6937
        }
6938
 
6939
        $found = false;
6940
        if ($blocks = $DB->get_records('block')) {
6941
            foreach ($blocks as $block) {
6942
                if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6943
                    continue;
6944
                }
6945
                if (strpos($block->name, $query) !== false) {
6946
                    $found = true;
6947
                    break;
6948
                }
6949
                $strblockname = get_string('pluginname', 'block_'.$block->name);
6950
                if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6951
                    $found = true;
6952
                    break;
6953
                }
6954
            }
6955
        }
6956
        if ($found) {
6957
            $result = new stdClass();
6958
            $result->page     = $this;
6959
            $result->settings = array();
6960
            return array($this->name => $result);
6961
        } else {
6962
            return array();
6963
        }
6964
    }
6965
}
6966
 
6967
/**
6968
 * Message outputs configuration
6969
 *
6970
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6971
 */
6972
class admin_page_managemessageoutputs extends admin_externalpage {
6973
    /**
6974
     * Calls parent::__construct with specific arguments
6975
     */
6976
    public function __construct() {
6977
        global $CFG;
6978
        parent::__construct('managemessageoutputs',
6979
            get_string('defaultmessageoutputs', 'message'),
6980
            new moodle_url('/admin/message.php')
6981
        );
6982
    }
6983
 
6984
    /**
6985
     * Search for a specific message processor
6986
     *
6987
     * @param string $query The string to search for
6988
     * @return array
6989
     */
6990
    public function search($query) {
6991
        global $CFG, $DB;
6992
        if ($result = parent::search($query)) {
6993
            return $result;
6994
        }
6995
 
6996
        $found = false;
6997
        if ($processors = get_message_processors()) {
6998
            foreach ($processors as $processor) {
6999
                if (!$processor->available) {
7000
                    continue;
7001
                }
7002
                if (strpos($processor->name, $query) !== false) {
7003
                    $found = true;
7004
                    break;
7005
                }
7006
                $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
7007
                if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
7008
                    $found = true;
7009
                    break;
7010
                }
7011
            }
7012
        }
7013
        if ($found) {
7014
            $result = new stdClass();
7015
            $result->page     = $this;
7016
            $result->settings = array();
7017
            return array($this->name => $result);
7018
        } else {
7019
            return array();
7020
        }
7021
    }
7022
}
7023
 
7024
/**
7025
 * Manage question behaviours page
7026
 *
7027
 * @copyright  2011 The Open University
7028
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7029
 */
7030
class admin_page_manageqbehaviours extends admin_externalpage {
7031
    /**
7032
     * Constructor
7033
     */
7034
    public function __construct() {
7035
        global $CFG;
7036
        parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
7037
                new moodle_url('/admin/qbehaviours.php'));
7038
    }
7039
 
7040
    /**
7041
     * Search question behaviours for the specified string
7042
     *
7043
     * @param string $query The string to search for in question behaviours
7044
     * @return array
7045
     */
7046
    public function search($query) {
7047
        global $CFG;
7048
        if ($result = parent::search($query)) {
7049
            return $result;
7050
        }
7051
 
7052
        $found = false;
7053
        require_once($CFG->dirroot . '/question/engine/lib.php');
7054
        foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
7055
            if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
7056
                    $query) !== false) {
7057
                $found = true;
7058
                break;
7059
            }
7060
        }
7061
        if ($found) {
7062
            $result = new stdClass();
7063
            $result->page     = $this;
7064
            $result->settings = array();
7065
            return array($this->name => $result);
7066
        } else {
7067
            return array();
7068
        }
7069
    }
7070
}
7071
 
7072
 
7073
/**
7074
 * Question type manage page
7075
 *
7076
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7077
 */
7078
class admin_page_manageqtypes extends admin_externalpage {
7079
    /**
7080
     * Calls parent::__construct with specific arguments
7081
     */
7082
    public function __construct() {
7083
        global $CFG;
7084
        parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
7085
                new moodle_url('/admin/qtypes.php'));
7086
    }
7087
 
7088
    /**
7089
     * Search question types for the specified string
7090
     *
7091
     * @param string $query The string to search for in question types
7092
     * @return array
7093
     */
7094
    public function search($query) {
7095
        global $CFG;
7096
        if ($result = parent::search($query)) {
7097
            return $result;
7098
        }
7099
 
7100
        $found = false;
7101
        require_once($CFG->dirroot . '/question/engine/bank.php');
7102
        foreach (question_bank::get_all_qtypes() as $qtype) {
7103
            if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
7104
                $found = true;
7105
                break;
7106
            }
7107
        }
7108
        if ($found) {
7109
            $result = new stdClass();
7110
            $result->page     = $this;
7111
            $result->settings = array();
7112
            return array($this->name => $result);
7113
        } else {
7114
            return array();
7115
        }
7116
    }
7117
}
7118
 
7119
 
7120
class admin_page_manageportfolios extends admin_externalpage {
7121
    /**
7122
     * Calls parent::__construct with specific arguments
7123
     */
7124
    public function __construct() {
7125
        global $CFG;
7126
        parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
7127
                "$CFG->wwwroot/$CFG->admin/portfolio.php");
7128
    }
7129
 
7130
    /**
7131
     * Searches page for the specified string.
7132
     * @param string $query The string to search for
7133
     * @return array
7134
     */
7135
    public function search($query) {
7136
        global $CFG;
7137
        if ($result = parent::search($query)) {
7138
            return $result;
7139
        }
7140
 
7141
        $found = false;
7142
        $portfolios = core_component::get_plugin_list('portfolio');
7143
        foreach ($portfolios as $p => $dir) {
7144
            if (strpos($p, $query) !== false) {
7145
                $found = true;
7146
                break;
7147
            }
7148
        }
7149
        if (!$found) {
7150
            foreach (portfolio_instances(false, false) as $instance) {
7151
                $title = $instance->get('name');
7152
                if (strpos(core_text::strtolower($title), $query) !== false) {
7153
                    $found = true;
7154
                    break;
7155
                }
7156
            }
7157
        }
7158
 
7159
        if ($found) {
7160
            $result = new stdClass();
7161
            $result->page     = $this;
7162
            $result->settings = array();
7163
            return array($this->name => $result);
7164
        } else {
7165
            return array();
7166
        }
7167
    }
7168
}
7169
 
7170
 
7171
class admin_page_managerepositories extends admin_externalpage {
7172
    /**
7173
     * Calls parent::__construct with specific arguments
7174
     */
7175
    public function __construct() {
7176
        global $CFG;
7177
        parent::__construct('managerepositories', get_string('manage',
7178
                'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
7179
    }
7180
 
7181
    /**
7182
     * Searches page for the specified string.
7183
     * @param string $query The string to search for
7184
     * @return array
7185
     */
7186
    public function search($query) {
7187
        global $CFG;
7188
        if ($result = parent::search($query)) {
7189
            return $result;
7190
        }
7191
 
7192
        $found = false;
7193
        $repositories= core_component::get_plugin_list('repository');
7194
        foreach ($repositories as $p => $dir) {
7195
            if (strpos($p, $query) !== false) {
7196
                $found = true;
7197
                break;
7198
            }
7199
        }
7200
        if (!$found) {
7201
            foreach (repository::get_types() as $instance) {
7202
                $title = $instance->get_typename();
7203
                if (strpos(core_text::strtolower($title), $query) !== false) {
7204
                    $found = true;
7205
                    break;
7206
                }
7207
            }
7208
        }
7209
 
7210
        if ($found) {
7211
            $result = new stdClass();
7212
            $result->page     = $this;
7213
            $result->settings = array();
7214
            return array($this->name => $result);
7215
        } else {
7216
            return array();
7217
        }
7218
    }
7219
}
7220
 
7221
 
7222
/**
7223
 * Special class for authentication administration.
7224
 *
7225
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7226
 */
7227
class admin_setting_manageauths extends admin_setting {
7228
    /**
7229
     * Calls parent::__construct with specific arguments
7230
     */
7231
    public function __construct() {
7232
        $this->nosave = true;
7233
        parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
7234
    }
7235
 
7236
    /**
7237
     * Always returns true
7238
     *
7239
     * @return true
7240
     */
7241
    public function get_setting() {
7242
        return true;
7243
    }
7244
 
7245
    /**
7246
     * Always returns true
7247
     *
7248
     * @return true
7249
     */
7250
    public function get_defaultsetting() {
7251
        return true;
7252
    }
7253
 
7254
    /**
7255
     * Always returns '' and doesn't write anything
7256
     *
7257
     * @return string Always returns ''
7258
     */
7259
    public function write_setting($data) {
7260
    // do not write any setting
7261
        return '';
7262
    }
7263
 
7264
    /**
7265
     * Search to find if Query is related to auth plugin
7266
     *
7267
     * @param string $query The string to search for
7268
     * @return bool true for related false for not
7269
     */
7270
    public function is_related($query) {
7271
        if (parent::is_related($query)) {
7272
            return true;
7273
        }
7274
 
7275
        $authsavailable = core_component::get_plugin_list('auth');
7276
        foreach ($authsavailable as $auth => $dir) {
7277
            if (strpos($auth, $query) !== false) {
7278
                return true;
7279
            }
7280
            $authplugin = get_auth_plugin($auth);
7281
            $authtitle = $authplugin->get_title();
7282
            if (strpos(core_text::strtolower($authtitle), $query) !== false) {
7283
                return true;
7284
            }
7285
        }
7286
        return false;
7287
    }
7288
 
7289
    /**
7290
     * Return XHTML to display control
7291
     *
7292
     * @param mixed $data Unused
7293
     * @param string $query
7294
     * @return string highlight
7295
     */
7296
    public function output_html($data, $query='') {
7297
        global $CFG, $OUTPUT, $DB;
7298
 
7299
        // display strings
7300
        $txt = get_strings(array('authenticationplugins', 'users', 'administration',
7301
            'settings', 'edit', 'name', 'enable', 'disable',
7302
            'up', 'down', 'none', 'users'));
7303
        $txt->updown = "$txt->up/$txt->down";
7304
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7305
        $txt->testsettings = get_string('testsettings', 'core_auth');
7306
 
7307
        $authsavailable = core_component::get_plugin_list('auth');
7308
        get_enabled_auth_plugins(true); // fix the list of enabled auths
7309
        if (empty($CFG->auth)) {
7310
            $authsenabled = array();
7311
        } else {
7312
            $authsenabled = explode(',', $CFG->auth);
7313
        }
7314
 
7315
        // construct the display array, with enabled auth plugins at the top, in order
7316
        $displayauths = array();
7317
        $registrationauths = array();
7318
        $registrationauths[''] = $txt->disable;
7319
        $authplugins = array();
7320
        foreach ($authsenabled as $auth) {
7321
            $authplugin = get_auth_plugin($auth);
7322
            $authplugins[$auth] = $authplugin;
7323
            /// Get the auth title (from core or own auth lang files)
7324
            $authtitle = $authplugin->get_title();
7325
            /// Apply titles
7326
            $displayauths[$auth] = $authtitle;
7327
            if ($authplugin->can_signup()) {
7328
                $registrationauths[$auth] = $authtitle;
7329
            }
7330
        }
7331
 
7332
        foreach ($authsavailable as $auth => $dir) {
7333
            if (array_key_exists($auth, $displayauths)) {
7334
                continue; //already in the list
7335
            }
7336
            $authplugin = get_auth_plugin($auth);
7337
            $authplugins[$auth] = $authplugin;
7338
            /// Get the auth title (from core or own auth lang files)
7339
            $authtitle = $authplugin->get_title();
7340
            /// Apply titles
7341
            $displayauths[$auth] = $authtitle;
7342
            if ($authplugin->can_signup()) {
7343
                $registrationauths[$auth] = $authtitle;
7344
            }
7345
        }
7346
 
7347
        $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
7348
        $return .= $OUTPUT->box_start('generalbox authsui');
7349
 
7350
        $table = new html_table();
7351
        $table->head  = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
7352
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7353
        $table->data  = array();
7354
        $table->attributes['class'] = 'admintable generaltable';
7355
        $table->id = 'manageauthtable';
7356
 
7357
        //add always enabled plugins first
7358
        $displayname = $displayauths['manual'];
7359
        $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
7360
        $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
7361
        $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
7362
        $displayname = $displayauths['nologin'];
7363
        $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
7364
        $table->data[] = array($displayname, $usercount, '', '', '', '', '');
7365
 
7366
 
7367
        // iterate through auth plugins and add to the display table
7368
        $updowncount = 1;
7369
        $authcount = count($authsenabled);
7370
        $url = "auth.php?sesskey=" . sesskey();
7371
        foreach ($displayauths as $auth => $name) {
7372
            if ($auth == 'manual' or $auth == 'nologin') {
7373
                continue;
7374
            }
7375
            $class = '';
7376
            // hide/show link
7377
            if (in_array($auth, $authsenabled)) {
7378
                $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
7379
                $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7380
                $enabled = true;
7381
                $displayname = $name;
7382
            }
7383
            else {
7384
                $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
7385
                $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7386
                $enabled = false;
7387
                $displayname = $name;
7388
                $class = 'dimmed_text';
7389
            }
7390
 
7391
            $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
7392
 
7393
            // up/down link (only if auth is enabled)
7394
            $updown = '';
7395
            if ($enabled) {
7396
                if ($updowncount > 1) {
7397
                    $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
7398
                    $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7399
                }
7400
                else {
7401
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
7402
                }
7403
                if ($updowncount < $authcount) {
7404
                    $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
7405
                    $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7406
                }
7407
                else {
7408
                    $updown .= $OUTPUT->spacer() . '&nbsp;';
7409
                }
7410
                ++ $updowncount;
7411
            }
7412
 
7413
            // settings link
7414
            if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
7415
                $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
7416
            } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
7417
                throw new \coding_exception('config.html is no longer supported, please use settings.php instead.');
7418
            } else {
7419
                $settings = '';
7420
            }
7421
 
7422
            // Uninstall link.
7423
            $uninstall = '';
7424
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
7425
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7426
            }
7427
 
7428
            $test = '';
7429
            if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
7430
                $testurl = new moodle_url('/auth/test_settings.php', ['auth' => $auth]);
7431
                $test = html_writer::link($testurl, $txt->testsettings);
7432
            }
7433
 
7434
            // Add a row to the table.
7435
            $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
7436
            if ($class) {
7437
                $row->attributes['class'] = $class;
7438
            }
7439
            $table->data[] = $row;
7440
        }
7441
        $return .= html_writer::table($table);
7442
        $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
7443
        $return .= $OUTPUT->box_end();
7444
        return highlight($query, $return);
7445
    }
7446
}
7447
 
7448
/**
7449
 * Special class for antiviruses administration.
7450
 *
7451
 * @copyright  2015 Ruslan Kabalin, Lancaster University.
7452
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7453
 */
7454
class admin_setting_manageantiviruses extends admin_setting {
7455
    /**
7456
     * Calls parent::__construct with specific arguments
7457
     */
7458
    public function __construct() {
7459
        $this->nosave = true;
7460
        parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
7461
    }
7462
 
7463
    /**
7464
     * Always returns true, does nothing
7465
     *
7466
     * @return true
7467
     */
7468
    public function get_setting() {
7469
        return true;
7470
    }
7471
 
7472
    /**
7473
     * Always returns true, does nothing
7474
     *
7475
     * @return true
7476
     */
7477
    public function get_defaultsetting() {
7478
        return true;
7479
    }
7480
 
7481
    /**
7482
     * Always returns '', does not write anything
7483
     *
7484
     * @param string $data Unused
7485
     * @return string Always returns ''
7486
     */
7487
    public function write_setting($data) {
7488
        // Do not write any setting.
7489
        return '';
7490
    }
7491
 
7492
    /**
7493
     * Checks if $query is one of the available editors
7494
     *
7495
     * @param string $query The string to search for
7496
     * @return bool Returns true if found, false if not
7497
     */
7498
    public function is_related($query) {
7499
        if (parent::is_related($query)) {
7500
            return true;
7501
        }
7502
 
7503
        $antivirusesavailable = \core\antivirus\manager::get_available();
7504
        foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
7505
            if (strpos($antivirus, $query) !== false) {
7506
                return true;
7507
            }
7508
            if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
7509
                return true;
7510
            }
7511
        }
7512
        return false;
7513
    }
7514
 
7515
    /**
7516
     * Builds the XHTML to display the control
7517
     *
7518
     * @param string $data Unused
7519
     * @param string $query
7520
     * @return string
7521
     */
7522
    public function output_html($data, $query='') {
7523
        global $CFG, $OUTPUT;
7524
 
7525
        // Display strings.
7526
        $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7527
            'up', 'down', 'none'));
7528
        $struninstall = get_string('uninstallplugin', 'core_admin');
7529
 
7530
        $txt->updown = "$txt->up/$txt->down";
7531
 
7532
        $antivirusesavailable = \core\antivirus\manager::get_available();
7533
        $activeantiviruses = explode(',', $CFG->antiviruses);
7534
 
7535
        $activeantiviruses = array_reverse($activeantiviruses);
7536
        foreach ($activeantiviruses as $key => $antivirus) {
7537
            if (empty($antivirusesavailable[$antivirus])) {
7538
                unset($activeantiviruses[$key]);
7539
            } else {
7540
                $name = $antivirusesavailable[$antivirus];
7541
                unset($antivirusesavailable[$antivirus]);
7542
                $antivirusesavailable[$antivirus] = $name;
7543
            }
7544
        }
7545
        $antivirusesavailable = array_reverse($antivirusesavailable, true);
7546
        $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
7547
        $return .= $OUTPUT->box_start('generalbox antivirusesui');
7548
 
7549
        $table = new html_table();
7550
        $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7551
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7552
        $table->id = 'antivirusmanagement';
7553
        $table->attributes['class'] = 'admintable generaltable';
7554
        $table->data  = array();
7555
 
7556
        // Iterate through auth plugins and add to the display table.
7557
        $updowncount = 1;
7558
        $antiviruscount = count($activeantiviruses);
7559
        $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
7560
        foreach ($antivirusesavailable as $antivirus => $name) {
7561
            // Hide/show link.
7562
            $class = '';
7563
            if (in_array($antivirus, $activeantiviruses)) {
7564
                $hideshowurl = $baseurl;
7565
                $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
7566
                $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
7567
                $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7568
                $enabled = true;
7569
                $displayname = $name;
7570
            } else {
7571
                $hideshowurl = $baseurl;
7572
                $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
7573
                $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
7574
                $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7575
                $enabled = false;
7576
                $displayname = $name;
7577
                $class = 'dimmed_text';
7578
            }
7579
 
7580
            // Up/down link.
7581
            $updown = '';
7582
            if ($enabled) {
7583
                if ($updowncount > 1) {
7584
                    $updownurl = $baseurl;
7585
                    $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
7586
                    $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
7587
                    $updown = html_writer::link($updownurl, $updownimg);
7588
                } else {
7589
                    $updownimg = $OUTPUT->spacer();
7590
                }
7591
                if ($updowncount < $antiviruscount) {
7592
                    $updownurl = $baseurl;
7593
                    $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
7594
                    $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
7595
                    $updown = html_writer::link($updownurl, $updownimg);
7596
                } else {
7597
                    $updownimg = $OUTPUT->spacer();
7598
                }
7599
                ++ $updowncount;
7600
            }
7601
 
7602
            // Settings link.
7603
            if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
7604
                $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
7605
                $settings = html_writer::link($eurl, $txt->settings);
7606
            } else {
7607
                $settings = '';
7608
            }
7609
 
7610
            $uninstall = '';
7611
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
7612
                $uninstall = html_writer::link($uninstallurl, $struninstall);
7613
            }
7614
 
7615
            // Add a row to the table.
7616
            $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7617
            if ($class) {
7618
                $row->attributes['class'] = $class;
7619
            }
7620
            $table->data[] = $row;
7621
        }
7622
        $return .= html_writer::table($table);
7623
        $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
7624
        $return .= $OUTPUT->box_end();
7625
        return highlight($query, $return);
7626
    }
7627
}
7628
 
7629
/**
7630
 * Course formats manager. Allows to enable/disable formats and jump to settings
7631
 */
7632
class admin_setting_manageformats extends admin_setting {
7633
 
7634
    /**
7635
     * Calls parent::__construct with specific arguments
7636
     */
7637
    public function __construct() {
7638
        $this->nosave = true;
7639
        parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7640
    }
7641
 
7642
    /**
7643
     * Always returns true
7644
     *
7645
     * @return true
7646
     */
7647
    public function get_setting() {
7648
        return true;
7649
    }
7650
 
7651
    /**
7652
     * Always returns true
7653
     *
7654
     * @return true
7655
     */
7656
    public function get_defaultsetting() {
7657
        return true;
7658
    }
7659
 
7660
    /**
7661
     * Always returns '' and doesn't write anything
7662
     *
7663
     * @param mixed $data string or array, must not be NULL
7664
     * @return string Always returns ''
7665
     */
7666
    public function write_setting($data) {
7667
        // do not write any setting
7668
        return '';
7669
    }
7670
 
7671
    /**
7672
     * Search to find if Query is related to format plugin
7673
     *
7674
     * @param string $query The string to search for
7675
     * @return bool true for related false for not
7676
     */
7677
    public function is_related($query) {
7678
        if (parent::is_related($query)) {
7679
            return true;
7680
        }
7681
        $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7682
        foreach ($formats as $format) {
7683
            if (strpos($format->component, $query) !== false ||
7684
                    strpos(core_text::strtolower($format->displayname), $query) !== false) {
7685
                return true;
7686
            }
7687
        }
7688
        return false;
7689
    }
7690
 
7691
    /**
7692
     * Return XHTML to display control
7693
     *
7694
     * @param mixed $data Unused
7695
     * @param string $query
7696
     * @return string highlight
7697
     */
7698
    public function output_html($data, $query='') {
7699
        global $CFG, $OUTPUT;
7700
        $return = '';
7701
        $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7702
        $return .= $OUTPUT->box_start('generalbox formatsui');
7703
 
7704
        $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7705
 
7706
        // display strings
7707
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7708
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7709
        $txt->updown = "$txt->up/$txt->down";
7710
 
7711
        $table = new html_table();
7712
        $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7713
        $table->align = array('left', 'center', 'center', 'center', 'center');
7714
        $table->attributes['class'] = 'manageformattable generaltable admintable';
7715
        $table->data  = array();
7716
 
7717
        $cnt = 0;
7718
        $defaultformat = get_config('moodlecourse', 'format');
7719
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7720
        foreach ($formats as $format) {
7721
            $url = new moodle_url('/admin/courseformats.php',
7722
                    array('sesskey' => sesskey(), 'format' => $format->name));
7723
            $isdefault = '';
7724
            $class = '';
7725
            if ($format->is_enabled()) {
7726
                $strformatname = $format->displayname;
7727
                if ($defaultformat === $format->name) {
7728
                    $hideshow = $txt->default;
7729
                } else {
7730
                    $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7731
                            $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7732
                }
7733
            } else {
7734
                $strformatname = $format->displayname;
7735
                $class = 'dimmed_text';
7736
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7737
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7738
            }
7739
            $updown = '';
7740
            if ($cnt) {
7741
                $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7742
                    $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7743
            } else {
7744
                $updown .= $spacer;
7745
            }
7746
            if ($cnt < count($formats) - 1) {
7747
                $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7748
                    $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7749
            } else {
7750
                $updown .= $spacer;
7751
            }
7752
            $cnt++;
7753
            $settings = '';
7754
            if ($format->get_settings_url()) {
7755
                $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7756
            }
7757
            $uninstall = '';
7758
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7759
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7760
            }
7761
            $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7762
            if ($class) {
7763
                $row->attributes['class'] = $class;
7764
            }
7765
            $table->data[] = $row;
7766
        }
7767
        $return .= html_writer::table($table);
7768
        $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7769
        $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7770
        $return .= $OUTPUT->box_end();
7771
        return highlight($query, $return);
7772
    }
7773
}
7774
 
7775
/**
7776
 * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7777
 *
7778
 * @package    core
7779
 * @copyright  2018 Toni Barbera
7780
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7781
 */
7782
class admin_setting_managecustomfields extends admin_setting {
7783
 
7784
    /**
7785
     * Calls parent::__construct with specific arguments
7786
     */
7787
    public function __construct() {
7788
        $this->nosave = true;
7789
        parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7790
    }
7791
 
7792
    /**
7793
     * Always returns true
7794
     *
7795
     * @return true
7796
     */
7797
    public function get_setting() {
7798
        return true;
7799
    }
7800
 
7801
    /**
7802
     * Always returns true
7803
     *
7804
     * @return true
7805
     */
7806
    public function get_defaultsetting() {
7807
        return true;
7808
    }
7809
 
7810
    /**
7811
     * Always returns '' and doesn't write anything
7812
     *
7813
     * @param mixed $data string or array, must not be NULL
7814
     * @return string Always returns ''
7815
     */
7816
    public function write_setting($data) {
7817
        // Do not write any setting.
7818
        return '';
7819
    }
7820
 
7821
    /**
7822
     * Search to find if Query is related to format plugin
7823
     *
7824
     * @param string $query The string to search for
7825
     * @return bool true for related false for not
7826
     */
7827
    public function is_related($query) {
7828
        if (parent::is_related($query)) {
7829
            return true;
7830
        }
7831
        $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7832
        foreach ($formats as $format) {
7833
            if (strpos($format->component, $query) !== false ||
7834
                    strpos(core_text::strtolower($format->displayname), $query) !== false) {
7835
                return true;
7836
            }
7837
        }
7838
        return false;
7839
    }
7840
 
7841
    /**
7842
     * Return XHTML to display control
7843
     *
7844
     * @param mixed $data Unused
7845
     * @param string $query
7846
     * @return string highlight
7847
     */
7848
    public function output_html($data, $query='') {
7849
        global $CFG, $OUTPUT;
7850
        $return = '';
7851
        $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7852
        $return .= $OUTPUT->box_start('generalbox customfieldsui');
7853
 
7854
        $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7855
 
7856
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7857
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7858
        $txt->updown = "$txt->up/$txt->down";
7859
 
7860
        $table = new html_table();
7861
        $table->head  = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7862
        $table->align = array('left', 'center', 'center', 'center');
7863
        $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7864
        $table->data  = array();
7865
 
7866
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7867
        foreach ($fields as $field) {
7868
            $url = new moodle_url('/admin/customfields.php',
7869
                    array('sesskey' => sesskey(), 'field' => $field->name));
7870
 
7871
            if ($field->is_enabled()) {
7872
                $strfieldname = $field->displayname;
7873
                $class = '';
7874
                $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7875
                        $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7876
            } else {
7877
                $strfieldname = $field->displayname;
7878
                $class = 'dimmed_text';
7879
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7880
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7881
            }
7882
            $settings = '';
7883
            if ($field->get_settings_url()) {
7884
                $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7885
            }
7886
            $uninstall = '';
7887
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7888
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7889
            }
7890
            $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7891
            $row->attributes['class'] = $class;
7892
            $table->data[] = $row;
7893
        }
7894
        $return .= html_writer::table($table);
7895
        $return .= $OUTPUT->box_end();
7896
        return highlight($query, $return);
7897
    }
7898
}
7899
 
7900
/**
7901
 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7902
 *
7903
 * @copyright  2016 Brendan Heywood (brendan@catalyst-au.net)
7904
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7905
 */
7906
class admin_setting_managedataformats extends admin_setting {
7907
 
7908
    /**
7909
     * Calls parent::__construct with specific arguments
7910
     */
7911
    public function __construct() {
7912
        $this->nosave = true;
7913
        parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7914
    }
7915
 
7916
    /**
7917
     * Always returns true
7918
     *
7919
     * @return true
7920
     */
7921
    public function get_setting() {
7922
        return true;
7923
    }
7924
 
7925
    /**
7926
     * Always returns true
7927
     *
7928
     * @return true
7929
     */
7930
    public function get_defaultsetting() {
7931
        return true;
7932
    }
7933
 
7934
    /**
7935
     * Always returns '' and doesn't write anything
7936
     *
7937
     * @param mixed $data string or array, must not be NULL
7938
     * @return string Always returns ''
7939
     */
7940
    public function write_setting($data) {
7941
        // Do not write any setting.
7942
        return '';
7943
    }
7944
 
7945
    /**
7946
     * Search to find if Query is related to format plugin
7947
     *
7948
     * @param string $query The string to search for
7949
     * @return bool true for related false for not
7950
     */
7951
    public function is_related($query) {
7952
        if (parent::is_related($query)) {
7953
            return true;
7954
        }
7955
        $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7956
        foreach ($formats as $format) {
7957
            if (strpos($format->component, $query) !== false ||
7958
                    strpos(core_text::strtolower($format->displayname), $query) !== false) {
7959
                return true;
7960
            }
7961
        }
7962
        return false;
7963
    }
7964
 
7965
    /**
7966
     * Return XHTML to display control
7967
     *
7968
     * @param mixed $data Unused
7969
     * @param string $query
7970
     * @return string highlight
7971
     */
7972
    public function output_html($data, $query='') {
7973
        global $CFG, $OUTPUT;
7974
        $return = '';
7975
 
7976
        $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7977
 
7978
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7979
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7980
        $txt->updown = "$txt->up/$txt->down";
7981
 
7982
        $table = new html_table();
7983
        $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7984
        $table->align = array('left', 'center', 'center', 'center', 'center');
7985
        $table->attributes['class'] = 'manageformattable generaltable admintable';
7986
        $table->data  = array();
7987
 
7988
        $cnt = 0;
7989
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7990
        $totalenabled = 0;
7991
        foreach ($formats as $format) {
7992
            if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7993
                $totalenabled++;
7994
            }
7995
        }
7996
        foreach ($formats as $format) {
7997
            $status = $format->get_status();
7998
            $url = new moodle_url('/admin/dataformats.php',
7999
                    array('sesskey' => sesskey(), 'name' => $format->name));
8000
 
8001
            $class = '';
8002
            if ($format->is_enabled()) {
8003
                $strformatname = $format->displayname;
8004
                if ($totalenabled == 1&& $format->is_enabled()) {
8005
                    $hideshow = '';
8006
                } else {
8007
                    $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8008
                        $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8009
                }
8010
            } else {
8011
                $class = 'dimmed_text';
8012
                $strformatname = $format->displayname;
8013
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8014
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8015
            }
8016
 
8017
            $updown = '';
8018
            if ($cnt) {
8019
                $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8020
                    $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8021
            } else {
8022
                $updown .= $spacer;
8023
            }
8024
            if ($cnt < count($formats) - 1) {
8025
                $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8026
                    $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8027
            } else {
8028
                $updown .= $spacer;
8029
            }
8030
 
8031
            $uninstall = '';
8032
            if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8033
                $uninstall = get_string('status_missing', 'core_plugin');
8034
            } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8035
                $uninstall = get_string('status_new', 'core_plugin');
8036
            } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
8037
                if ($totalenabled != 1 || !$format->is_enabled()) {
8038
                    $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8039
                }
8040
            }
8041
 
8042
            $settings = '';
8043
            if ($format->get_settings_url()) {
8044
                $settings = html_writer::link($format->get_settings_url(), $txt->settings);
8045
            }
8046
 
8047
            $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
8048
            if ($class) {
8049
                $row->attributes['class'] = $class;
8050
            }
8051
            $table->data[] = $row;
8052
            $cnt++;
8053
        }
8054
        $return .= html_writer::table($table);
8055
        return highlight($query, $return);
8056
    }
8057
}
8058
 
8059
/**
8060
 * Special class for filter administration.
8061
 *
8062
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8063
 */
8064
class admin_page_managefilters extends admin_externalpage {
8065
    /**
8066
     * Calls parent::__construct with specific arguments
8067
     */
8068
    public function __construct() {
8069
        global $CFG;
8070
        parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
8071
    }
8072
 
8073
    /**
8074
     * Searches all installed filters for specified filter
8075
     *
8076
     * @param string $query The filter(string) to search for
8077
     * @param string $query
8078
     */
8079
    public function search($query) {
8080
        global $CFG;
8081
        if ($result = parent::search($query)) {
8082
            return $result;
8083
        }
8084
 
8085
        $found = false;
8086
        $filternames = filter_get_all_installed();
8087
        foreach ($filternames as $path => $strfiltername) {
8088
            if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
8089
                $found = true;
8090
                break;
8091
            }
8092
            if (strpos($path, $query) !== false) {
8093
                $found = true;
8094
                break;
8095
            }
8096
        }
8097
 
8098
        if ($found) {
8099
            $result = new stdClass;
8100
            $result->page = $this;
8101
            $result->settings = array();
8102
            return array($this->name => $result);
8103
        } else {
8104
            return array();
8105
        }
8106
    }
8107
}
8108
 
8109
/**
8110
 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8111
 * Requires a get_rank method on the plugininfo class for sorting.
8112
 *
8113
 * @copyright 2017 Damyon Wiese
8114
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8115
 */
8116
abstract class admin_setting_manage_plugins extends admin_setting {
8117
 
8118
    /**
8119
     * Get the admin settings section name (just a unique string)
8120
     *
8121
     * @return string
8122
     */
8123
    public function get_section_name() {
8124
        return 'manage' . $this->get_plugin_type() . 'plugins';
8125
    }
8126
 
8127
    /**
8128
     * Get the admin settings section title (use get_string).
8129
     *
8130
     * @return string
8131
     */
8132
    abstract public function get_section_title();
8133
 
8134
    /**
8135
     * Get the type of plugin to manage.
8136
     *
8137
     * @return string
8138
     */
8139
    abstract public function get_plugin_type();
8140
 
8141
    /**
8142
     * Get the name of the second column.
8143
     *
8144
     * @return string
8145
     */
8146
    public function get_info_column_name() {
8147
        return '';
8148
    }
8149
 
8150
    /**
8151
     * Get the type of plugin to manage.
8152
     *
8153
     * @param \core\plugininfo\base $plugininfo The plugin info class.
8154
     * @return string
8155
     */
8156
    abstract public function get_info_column($plugininfo);
8157
 
8158
    /**
8159
     * Calls parent::__construct with specific arguments
8160
     */
8161
    public function __construct() {
8162
        $this->nosave = true;
8163
        parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
8164
    }
8165
 
8166
    /**
8167
     * Always returns true, does nothing
8168
     *
8169
     * @return true
8170
     */
8171
    public function get_setting() {
8172
        return true;
8173
    }
8174
 
8175
    /**
8176
     * Always returns true, does nothing
8177
     *
8178
     * @return true
8179
     */
8180
    public function get_defaultsetting() {
8181
        return true;
8182
    }
8183
 
8184
    /**
8185
     * Always returns '', does not write anything
8186
     *
8187
     * @param mixed $data
8188
     * @return string Always returns ''
8189
     */
8190
    public function write_setting($data) {
8191
        // Do not write any setting.
8192
        return '';
8193
    }
8194
 
8195
    /**
8196
     * Checks if $query is one of the available plugins of this type
8197
     *
8198
     * @param string $query The string to search for
8199
     * @return bool Returns true if found, false if not
8200
     */
8201
    public function is_related($query) {
8202
        if (parent::is_related($query)) {
8203
            return true;
8204
        }
8205
 
8206
        $query = core_text::strtolower($query);
8207
        $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
8208
        foreach ($plugins as $name => $plugin) {
8209
            $localised = $plugin->displayname;
8210
            if (strpos(core_text::strtolower($name), $query) !== false) {
8211
                return true;
8212
            }
8213
            if (strpos(core_text::strtolower($localised), $query) !== false) {
8214
                return true;
8215
            }
8216
        }
8217
        return false;
8218
    }
8219
 
8220
    /**
8221
     * The URL for the management page for this plugintype.
8222
     *
8223
     * @return moodle_url
8224
     */
8225
    protected function get_manage_url() {
8226
        return new moodle_url('/admin/updatesetting.php');
8227
    }
8228
 
8229
    /**
8230
     * Builds the HTML to display the control.
8231
     *
8232
     * @param string $data Unused
8233
     * @param string $query
8234
     * @return string
8235
     */
8236
    public function output_html($data, $query = '') {
8237
        global $CFG, $OUTPUT, $DB, $PAGE;
8238
 
8239
        $context = (object) [
8240
            'manageurl' => new moodle_url($this->get_manage_url(), [
8241
                    'type' => $this->get_plugin_type(),
8242
                    'sesskey' => sesskey(),
8243
                ]),
8244
            'infocolumnname' => $this->get_info_column_name(),
8245
            'plugins' => [],
8246
        ];
8247
 
8248
        $pluginmanager = core_plugin_manager::instance();
8249
        $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
8250
        $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
8251
        $plugins = array_merge($enabled, $allplugins);
8252
        foreach ($plugins as $key => $plugin) {
8253
            $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
8254
 
8255
            $pluginkey = (object) [
8256
                'plugin' => $plugin->displayname,
8257
                'enabled' => $plugin->is_enabled(),
8258
                'togglelink' => '',
8259
                'moveuplink' => '',
8260
                'movedownlink' => '',
8261
                'settingslink' => $plugin->get_settings_url(),
8262
                'uninstalllink' => '',
8263
                'info' => '',
8264
            ];
8265
 
8266
            // Enable/Disable link.
8267
            $togglelink = new moodle_url($pluginlink);
8268
            if ($plugin->is_enabled()) {
8269
                $toggletarget = false;
8270
                $togglelink->param('action', 'disable');
8271
 
8272
                if (count($context->plugins)) {
8273
                    // This is not the first plugin.
8274
                    $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
8275
                }
8276
 
8277
                if (count($enabled) > count($context->plugins) + 1) {
8278
                    // This is not the last plugin.
8279
                    $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
8280
                }
8281
 
8282
                $pluginkey->info = $this->get_info_column($plugin);
8283
            } else {
8284
                $toggletarget = true;
8285
                $togglelink->param('action', 'enable');
8286
            }
8287
 
8288
            $pluginkey->toggletarget = $toggletarget;
8289
            $pluginkey->togglelink = $togglelink;
8290
 
8291
            $frankenstyle = $plugin->type . '_' . $plugin->name;
8292
            if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
8293
                // This plugin supports uninstallation.
8294
                $pluginkey->uninstalllink = $uninstalllink;
8295
            }
8296
 
8297
            if (!empty($this->get_info_column_name())) {
8298
                // This plugintype has an info column.
8299
                $pluginkey->info = $this->get_info_column($plugin);
8300
            }
8301
 
8302
            $context->plugins[] = $pluginkey;
8303
        }
8304
 
8305
        $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8306
        return highlight($query, $str);
8307
    }
8308
}
8309
 
8310
/**
8311
 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8312
 * Requires a get_rank method on the plugininfo class for sorting.
8313
 *
8314
 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8315
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8316
 */
8317
class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8318
    public function get_section_title() {
8319
        return get_string('type_fileconverter_plural', 'plugin');
8320
    }
8321
 
8322
    public function get_plugin_type() {
8323
        return 'fileconverter';
8324
    }
8325
 
8326
    public function get_info_column_name() {
8327
        return get_string('supportedconversions', 'plugin');
8328
    }
8329
 
8330
    public function get_info_column($plugininfo) {
8331
        return $plugininfo->get_supported_conversions();
8332
    }
8333
}
8334
 
8335
/**
8336
 * Special class for media player plugins management.
8337
 *
8338
 * @copyright 2016 Marina Glancy
8339
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8340
 */
8341
class admin_setting_managemediaplayers extends admin_setting {
8342
    /**
8343
     * Calls parent::__construct with specific arguments
8344
     */
8345
    public function __construct() {
8346
        $this->nosave = true;
8347
        parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8348
    }
8349
 
8350
    /**
8351
     * Always returns true, does nothing
8352
     *
8353
     * @return true
8354
     */
8355
    public function get_setting() {
8356
        return true;
8357
    }
8358
 
8359
    /**
8360
     * Always returns true, does nothing
8361
     *
8362
     * @return true
8363
     */
8364
    public function get_defaultsetting() {
8365
        return true;
8366
    }
8367
 
8368
    /**
8369
     * Always returns '', does not write anything
8370
     *
8371
     * @param mixed $data
8372
     * @return string Always returns ''
8373
     */
8374
    public function write_setting($data) {
8375
        // Do not write any setting.
8376
        return '';
8377
    }
8378
 
8379
    /**
8380
     * Checks if $query is one of the available enrol plugins
8381
     *
8382
     * @param string $query The string to search for
8383
     * @return bool Returns true if found, false if not
8384
     */
8385
    public function is_related($query) {
8386
        if (parent::is_related($query)) {
8387
            return true;
8388
        }
8389
 
8390
        $query = core_text::strtolower($query);
8391
        $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8392
        foreach ($plugins as $name => $plugin) {
8393
            $localised = $plugin->displayname;
8394
            if (strpos(core_text::strtolower($name), $query) !== false) {
8395
                return true;
8396
            }
8397
            if (strpos(core_text::strtolower($localised), $query) !== false) {
8398
                return true;
8399
            }
8400
        }
8401
        return false;
8402
    }
8403
 
8404
    /**
8405
     * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8406
     * @return \core\plugininfo\media[]
8407
     */
8408
    protected function get_sorted_plugins() {
8409
        $pluginmanager = core_plugin_manager::instance();
8410
 
8411
        $plugins = $pluginmanager->get_plugins_of_type('media');
8412
        $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8413
 
8414
        // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8415
        \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8416
 
8417
        $order = array_values($enabledplugins);
8418
        $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8419
 
8420
        $sortedplugins = array();
8421
        foreach ($order as $name) {
8422
            $sortedplugins[$name] = $plugins[$name];
8423
        }
8424
 
8425
        return $sortedplugins;
8426
    }
8427
 
8428
    /**
8429
     * Builds the XHTML to display the control
8430
     *
8431
     * @param string $data Unused
8432
     * @param string $query
8433
     * @return string
8434
     */
8435
    public function output_html($data, $query='') {
8436
        global $CFG, $OUTPUT, $DB, $PAGE;
8437
 
8438
        // Display strings.
8439
        $strup        = get_string('up');
8440
        $strdown      = get_string('down');
8441
        $strsettings  = get_string('settings');
8442
        $strenable    = get_string('enable');
8443
        $strdisable   = get_string('disable');
8444
        $struninstall = get_string('uninstallplugin', 'core_admin');
8445
        $strversion   = get_string('version');
8446
        $strname      = get_string('name');
8447
        $strsupports  = get_string('supports', 'core_media');
8448
 
8449
        $pluginmanager = core_plugin_manager::instance();
8450
 
8451
        $plugins = $this->get_sorted_plugins();
8452
        $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8453
 
8454
        $return = $OUTPUT->box_start('generalbox mediaplayersui');
8455
 
8456
        $table = new html_table();
8457
        $table->head  = array($strname, $strsupports, $strversion,
8458
            $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8459
        $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8460
            'centeralign', 'centeralign', 'centeralign', 'centeralign');
8461
        $table->id = 'mediaplayerplugins';
8462
        $table->attributes['class'] = 'admintable generaltable';
8463
        $table->data  = array();
8464
 
8465
        // Iterate through media plugins and add to the display table.
8466
        $updowncount = 1;
8467
        $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8468
        $printed = array();
8469
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8470
 
8471
        $usedextensions = [];
8472
        foreach ($plugins as $name => $plugin) {
8473
            $url->param('media', $name);
8474
            /** @var \core\plugininfo\media $plugininfo */
8475
            $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8476
            $version = $plugininfo->versiondb;
8477
            $supports = $plugininfo->supports($usedextensions);
8478
 
8479
            // Hide/show links.
8480
            $class = '';
8481
            if (!$plugininfo->is_installed_and_upgraded()) {
8482
                $hideshow = '';
8483
                $enabled = false;
8484
                $displayname = '<span class="notifyproblem">'.$name.'</span>';
8485
            } else {
8486
                $enabled = $plugininfo->is_enabled();
8487
                if ($enabled) {
8488
                    $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8489
                        $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8490
                } else {
8491
                    $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8492
                        $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8493
                    $class = 'dimmed_text';
8494
                }
8495
                $displayname = $plugin->displayname;
8496
                if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8497
                    $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8498
                }
8499
            }
8500
            if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8501
                $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8502
            } else {
8503
                $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8504
            }
8505
 
8506
            // Up/down link (only if enrol is enabled).
8507
            $updown = '';
8508
            if ($enabled) {
8509
                if ($updowncount > 1) {
8510
                    $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8511
                        $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8512
                } else {
8513
                    $updown = $spacer;
8514
                }
8515
                if ($updowncount < count($enabledplugins)) {
8516
                    $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8517
                        $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8518
                } else {
8519
                    $updown .= $spacer;
8520
                }
8521
                ++$updowncount;
8522
            }
8523
 
8524
            $uninstall = '';
8525
            $status = $plugininfo->get_status();
8526
            if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8527
                $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8528
            }
8529
            if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8530
                $uninstall = get_string('status_new', 'core_plugin');
8531
            } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8532
                $uninstall .= html_writer::link($uninstallurl, $struninstall);
8533
            }
8534
 
8535
            $settings = '';
8536
            if ($plugininfo->get_settings_url()) {
8537
                $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8538
            }
8539
 
8540
            // Add a row to the table.
8541
            $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8542
            if ($class) {
8543
                $row->attributes['class'] = $class;
8544
            }
8545
            $table->data[] = $row;
8546
 
8547
            $printed[$name] = true;
8548
        }
8549
 
8550
        $return .= html_writer::table($table);
8551
        $return .= $OUTPUT->box_end();
8552
        return highlight($query, $return);
8553
    }
8554
}
8555
 
8556
 
8557
/**
8558
 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8559
 *
8560
 * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
8561
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8562
 */
8563
class admin_setting_managecontentbankcontenttypes extends admin_setting {
8564
 
8565
    /**
8566
     * Calls parent::__construct with specific arguments
8567
     */
8568
    public function __construct() {
8569
        $this->nosave = true;
8570
        parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8571
    }
8572
 
8573
    /**
8574
     * Always returns true
8575
     *
8576
     * @return true
8577
     */
8578
    public function get_setting() {
8579
        return true;
8580
    }
8581
 
8582
    /**
8583
     * Always returns true
8584
     *
8585
     * @return true
8586
     */
8587
    public function get_defaultsetting() {
8588
        return true;
8589
    }
8590
 
8591
    /**
8592
     * Always returns '' and doesn't write anything
8593
     *
8594
     * @param mixed $data string or array, must not be NULL
8595
     * @return string Always returns ''
8596
     */
8597
    public function write_setting($data) {
8598
        // Do not write any setting.
8599
        return '';
8600
    }
8601
 
8602
    /**
8603
     * Search to find if Query is related to content bank plugin
8604
     *
8605
     * @param string $query The string to search for
8606
     * @return bool true for related false for not
8607
     */
8608
    public function is_related($query) {
8609
        if (parent::is_related($query)) {
8610
            return true;
8611
        }
8612
        $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8613
        foreach ($types as $type) {
8614
            if (strpos($type->component, $query) !== false ||
8615
                strpos(core_text::strtolower($type->displayname), $query) !== false) {
8616
                return true;
8617
            }
8618
        }
8619
        return false;
8620
    }
8621
 
8622
    /**
8623
     * Return XHTML to display control
8624
     *
8625
     * @param mixed $data Unused
8626
     * @param string $query
8627
     * @return string highlight
8628
     */
8629
    public function output_html($data, $query='') {
8630
        global $CFG, $OUTPUT;
8631
        $return = '';
8632
 
8633
        $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8634
        $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8635
        $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8636
 
8637
        $table = new html_table();
8638
        $table->head  = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8639
        $table->align = array('left', 'center', 'center', 'center', 'center');
8640
        $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8641
        $table->data  = array();
8642
        $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8643
 
8644
        $totalenabled = 0;
8645
        $count = 0;
8646
        foreach ($types as $type) {
8647
            if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8648
                $totalenabled++;
8649
            }
8650
        }
8651
 
8652
        foreach ($types as $type) {
8653
            $url = new moodle_url('/admin/contentbank.php',
8654
                array('sesskey' => sesskey(), 'name' => $type->name));
8655
 
8656
            $class = '';
8657
            $strtypename = $type->displayname;
8658
            if ($type->is_enabled()) {
8659
                $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8660
                    $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8661
            } else {
8662
                $class = 'dimmed_text';
8663
                $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8664
                    $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8665
            }
8666
 
8667
            $updown = '';
8668
            if ($count) {
8669
                $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8670
                        $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8671
            } else {
8672
                $updown .= $spacer;
8673
            }
8674
            if ($count < count($types) - 1) {
8675
                $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8676
                        $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8677
            } else {
8678
                $updown .= $spacer;
8679
            }
8680
 
8681
            $settings = '';
8682
            if ($type->get_settings_url()) {
8683
                $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8684
            }
8685
 
8686
            $uninstall = '';
8687
            if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8688
                $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8689
            }
8690
 
8691
            $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8692
            if ($class) {
8693
                $row->attributes['class'] = $class;
8694
            }
8695
            $table->data[] = $row;
8696
            $count++;
8697
        }
8698
        $return .= html_writer::table($table);
8699
        return highlight($query, $return);
8700
    }
8701
}
8702
 
8703
/**
8704
 * Initialise admin page - this function does require login and permission
8705
 * checks specified in page definition.
8706
 *
8707
 * This function must be called on each admin page before other code.
8708
 *
8709
 * @global moodle_page $PAGE
8710
 *
8711
 * @param string $section name of page
8712
 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8713
 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8714
 *      added to the turn blocks editing on/off form, so this page reloads correctly.
8715
 * @param string $actualurl if the actual page being viewed is not the normal one for this
8716
 *      page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8717
 * @param array $options Additional options that can be specified for page setup.
8718
 *      pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8719
 *      nosearch - Do not display search bar
8720
 */
8721
function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
8722
    global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8723
 
8724
    $PAGE->set_context(null); // hack - set context to something, by default to system context
8725
 
8726
    $site = get_site();
8727
    require_login(null, false);
8728
 
8729
    if (!empty($options['pagelayout'])) {
8730
        // A specific page layout has been requested.
8731
        $PAGE->set_pagelayout($options['pagelayout']);
8732
    } else if ($section === 'upgradesettings') {
8733
        $PAGE->set_pagelayout('maintenance');
8734
    } else {
8735
        $PAGE->set_pagelayout('admin');
8736
    }
8737
 
8738
    $adminroot = admin_get_root(false, false); // settings not required for external pages
8739
    $extpage = $adminroot->locate($section, true);
8740
 
8741
    $hassiteconfig = has_capability('moodle/site:config', context_system::instance());
8742
    if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8743
        // The requested section isn't in the admin tree
8744
        // It could be because the user has inadequate capapbilities or because the section doesn't exist
8745
        if (!$hassiteconfig) {
8746
            // The requested section could depend on a different capability
8747
            // but most likely the user has inadequate capabilities
8748
            throw new \moodle_exception('accessdenied', 'admin');
8749
        } else {
8750
            throw new \moodle_exception('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8751
        }
8752
    }
8753
 
8754
    // this eliminates our need to authenticate on the actual pages
8755
    if (!$extpage->check_access()) {
8756
        throw new \moodle_exception('accessdenied', 'admin');
8757
        die;
8758
    }
8759
 
8760
    navigation_node::require_admin_tree();
8761
 
8762
    // $PAGE->set_extra_button($extrabutton); TODO
8763
 
8764
    if (!$actualurl) {
8765
        $actualurl = $extpage->url;
8766
    }
8767
 
8768
    $PAGE->set_url($actualurl, $extraurlparams);
8769
    if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8770
        $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8771
    }
8772
 
8773
    if (empty($SITE->fullname) || empty($SITE->shortname)) {
8774
        // During initial install.
8775
        $strinstallation = get_string('installation', 'install');
8776
        $strsettings = get_string('settings');
8777
        $PAGE->navbar->add($strsettings);
8778
        $PAGE->set_title($strinstallation);
8779
        $PAGE->set_heading($strinstallation);
8780
        $PAGE->set_cacheable(false);
8781
        return;
8782
    }
8783
 
8784
    // Locate the current item on the navigation and make it active when found.
8785
    $path = $extpage->path;
8786
    $node = $PAGE->settingsnav;
8787
    while ($node && count($path) > 0) {
8788
        $node = $node->get(array_pop($path));
8789
    }
8790
    if ($node) {
8791
        $node->make_active();
8792
    }
8793
 
8794
    // Normal case.
8795
    $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8796
    if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8797
        $USER->editing = $adminediting;
8798
    }
8799
 
8800
    if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
8801
        if ($PAGE->user_is_editing()) {
8802
            $caption = get_string('blockseditoff');
8803
            $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8804
        } else {
8805
            $caption = get_string('blocksediton');
8806
            $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8807
        }
8808
        $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8809
    }
8810
 
8811
    $PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $extpage->visiblepath));
8812
    $PAGE->set_heading($SITE->fullname);
8813
 
8814
    if ($hassiteconfig && empty($options['nosearch'])) {
8815
        $PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [
8816
            'action' => new moodle_url('/admin/search.php'),
8817
            'query' => $PAGE->url->get_param('query'),
8818
        ]));
8819
    }
8820
 
8821
    // prevent caching in nav block
8822
    $PAGE->navigation->clear_cache();
8823
}
8824
 
8825
/**
8826
 * Returns the reference to admin tree root
8827
 *
8828
 * @return object admin_root object
8829
 */
8830
function admin_get_root($reload=false, $requirefulltree=true) {
8831
    global $CFG, $DB, $OUTPUT, $ADMIN;
8832
 
8833
    if (is_null($ADMIN)) {
8834
    // create the admin tree!
8835
        $ADMIN = new admin_root($requirefulltree);
8836
    }
8837
 
8838
    if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8839
        $ADMIN->purge_children($requirefulltree);
8840
    }
8841
 
8842
    if (!$ADMIN->loaded) {
8843
    // we process this file first to create categories first and in correct order
8844
        require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8845
 
8846
        // now we process all other files in admin/settings to build the admin tree
8847
        foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8848
            if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8849
                continue;
8850
            }
8851
            if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8852
            // plugins are loaded last - they may insert pages anywhere
8853
                continue;
8854
            }
8855
            require($file);
8856
        }
8857
        require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8858
 
8859
        $ADMIN->loaded = true;
8860
    }
8861
 
8862
    return $ADMIN;
8863
}
8864
 
8865
/// settings utility functions
8866
 
8867
/**
8868
 * This function applies default settings recursively.
8869
 *
8870
 * Because setting the defaults of some settings can enable other settings,
8871
 * this function calls itself repeatedly (max 4 times) until no more new settings are saved.
8872
 *
8873
 * NOTE: previous "internal" parameters $admindefaultsettings, $settingsoutput were removed in Moodle 4.3.
8874
 *
8875
 * @param part_of_admin_tree|null $node NULL means apply all settings with repeated recursion
8876
 * @param bool $unconditional if true overrides all values with defaults (true for installation, false for CLI upgrade)
8877
 * @return array The names and values of the applied setting defaults
8878
 */
8879
function admin_apply_default_settings(?part_of_admin_tree $node = null, bool $unconditional = true): array {
8880
    if (is_null($node)) {
8881
        // This function relies heavily on config cache, so we need to enable in-memory caches if it
8882
        // is used during install when normal caching is disabled.
8883
        $token = new \core_cache\allow_temporary_caches(); // Value not used intentionally, see its destructor.
8884
 
8885
        core_plugin_manager::reset_caches();
8886
        $root = admin_get_root(true, true);
8887
        $saved = admin_apply_default_settings($root, $unconditional);
8888
        if (!$saved) {
8889
            return [];
8890
        }
8891
 
8892
        for ($i = 1; $i <= 3; $i++) {
8893
            core_plugin_manager::reset_caches();
8894
            $root = admin_get_root(true, true);
8895
            // No need to force defaults in repeated runs.
8896
            $moresaved = admin_apply_default_settings($root, false);
8897
            if (!$moresaved) {
8898
                // No more setting defaults to save.
8899
                return $saved;
8900
            }
8901
            $saved += $moresaved;
8902
        }
8903
 
8904
        // We should not get here unless there are some problematic settings.php files.
8905
        core_plugin_manager::reset_caches();
8906
        return $saved;
8907
    }
8908
 
8909
    // Recursive applying of defaults in admin tree.
8910
    $saved = [];
8911
    if ($node instanceof admin_category) {
8912
        foreach ($node->children as $child) {
8913
            if ($child === null) {
8914
                // This should not happen,
8915
                // this is to prevent theoretical infinite loops.
8916
                continue;
8917
            }
8918
            if ($child instanceof admin_externalpage) {
8919
                continue;
8920
            }
8921
            $saved += admin_apply_default_settings($child, $unconditional);
8922
        }
8923
 
8924
    } else if ($node instanceof admin_settingpage) {
8925
        /** @var admin_setting $setting */
8926
        foreach ((array)$node->settings as $setting) {
8927
            if ($setting->nosave) {
8928
                // Not a real setting, must be a heading or description.
8929
                continue;
8930
            }
8931
            if (!$unconditional && !is_null($setting->get_setting())) {
8932
                // Do not override existing defaults.
8933
                continue;
8934
            }
8935
            $defaultsetting = $setting->get_defaultsetting();
8936
            if (is_null($defaultsetting)) {
8937
                // No value yet - default maybe applied after admin user creation or in upgradesettings.
8938
                continue;
8939
            }
8940
            // This should be unique-enough setting name that matches administration UI.
8941
            if ($setting->plugin === null) {
8942
                $settingname = $setting->name;
8943
            } else {
8944
                $settingname = $setting->plugin . '/' . $setting->name;
8945
            }
8946
            // Set the default for this setting.
8947
            $error = $setting->write_setting($defaultsetting);
8948
            if ($error === '') {
8949
                $setting->write_setting_flags(null);
8950
                if (is_int($defaultsetting) || $defaultsetting instanceof lang_string
8951
                    || $defaultsetting instanceof moodle_url) {
8952
                    $defaultsetting = (string)$defaultsetting;
8953
                }
8954
                $saved[$settingname] = $defaultsetting;
8955
            } else {
8956
                debugging("Error applying default setting '$settingname': " . $error, DEBUG_DEVELOPER);
8957
            }
8958
        }
8959
    }
8960
 
8961
    return $saved;
8962
}
8963
 
8964
/**
8965
 * Store changed settings, this function updates the errors variable in $ADMIN
8966
 *
8967
 * @param object $formdata from form
8968
 * @return int number of changed settings
8969
 */
8970
function admin_write_settings($formdata) {
8971
    global $CFG, $SITE, $DB;
8972
 
8973
    $olddbsessions = !empty($CFG->dbsessions);
8974
    $formdata = (array)$formdata;
8975
 
8976
    $data = array();
8977
    foreach ($formdata as $fullname=>$value) {
8978
        if (strpos($fullname, 's_') !== 0) {
8979
            continue; // not a config value
8980
        }
8981
        $data[$fullname] = $value;
8982
    }
8983
 
8984
    $adminroot = admin_get_root();
8985
    $settings = admin_find_write_settings($adminroot, $data);
8986
 
8987
    $count = 0;
8988
    foreach ($settings as $fullname=>$setting) {
8989
        /** @var $setting admin_setting */
8990
        $original = $setting->get_setting();
8991
        $error = $setting->write_setting($data[$fullname]);
8992
        if ($error !== '') {
8993
            $adminroot->errors[$fullname] = new stdClass();
8994
            $adminroot->errors[$fullname]->data  = $data[$fullname];
8995
            $adminroot->errors[$fullname]->id    = $setting->get_id();
8996
            $adminroot->errors[$fullname]->error = $error;
8997
        } else {
8998
            $setting->write_setting_flags($data);
8999
        }
9000
        if ($setting->post_write_settings($original)) {
9001
            $count++;
9002
        }
9003
    }
9004
 
9005
    if ($olddbsessions != !empty($CFG->dbsessions)) {
9006
        require_logout();
9007
    }
9008
 
9009
    // Now update $SITE - just update the fields, in case other people have a
9010
    // a reference to it (e.g. $PAGE, $COURSE).
9011
    $newsite = $DB->get_record('course', array('id'=>$SITE->id));
9012
    foreach (get_object_vars($newsite) as $field => $value) {
9013
        $SITE->$field = $value;
9014
    }
9015
 
9016
    // now reload all settings - some of them might depend on the changed
9017
    admin_get_root(true);
9018
    return $count;
9019
}
9020
 
9021
/**
9022
 * Internal recursive function - finds all settings from submitted form
9023
 *
9024
 * @param object $node Instance of admin_category, or admin_settingpage
9025
 * @param array $data
9026
 * @return array
9027
 */
9028
function admin_find_write_settings($node, $data) {
9029
    $return = array();
9030
 
9031
    if (empty($data)) {
9032
        return $return;
9033
    }
9034
 
9035
    if ($node instanceof admin_category) {
9036
        if ($node->check_access()) {
9037
            $entries = array_keys($node->children);
9038
            foreach ($entries as $entry) {
9039
                $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
9040
            }
9041
        }
9042
 
9043
    } else if ($node instanceof admin_settingpage) {
9044
        if ($node->check_access()) {
9045
            foreach ($node->settings as $setting) {
9046
                $fullname = $setting->get_full_name();
9047
                if (array_key_exists($fullname, $data)) {
9048
                    $return[$fullname] = $setting;
9049
                }
9050
            }
9051
        }
9052
 
9053
    }
9054
 
9055
    return $return;
9056
}
9057
 
9058
/**
9059
 * Internal function - prints the search results
9060
 *
9061
 * @param string $query String to search for
9062
 * @return string empty or XHTML
9063
 */
9064
function admin_search_settings_html($query) {
9065
    global $CFG, $OUTPUT, $PAGE;
9066
 
9067
    if (core_text::strlen($query) < 2) {
9068
        return '';
9069
    }
9070
    $query = core_text::strtolower($query);
9071
 
9072
    $adminroot = admin_get_root();
9073
    $findings = $adminroot->search($query);
9074
    $savebutton = false;
9075
 
9076
    $tpldata = (object) [
9077
        'actionurl' => $PAGE->url->out(false),
9078
        'results' => [],
9079
        'sesskey' => sesskey(),
9080
    ];
9081
 
9082
    foreach ($findings as $found) {
9083
        $page     = $found->page;
9084
        $settings = $found->settings;
9085
        if ($page->is_hidden()) {
9086
        // hidden pages are not displayed in search results
9087
            continue;
9088
        }
9089
 
9090
        $heading = highlight($query, $page->visiblename);
9091
        $headingurl = null;
9092
        if ($page instanceof admin_externalpage) {
9093
            $headingurl = new moodle_url($page->url);
9094
        } else if ($page instanceof admin_settingpage) {
9095
            $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
9096
        } else {
9097
            continue;
9098
        }
9099
 
9100
        // Locate the page in the admin root and populate its visiblepath attribute.
9101
        $path = array();
9102
        $located = $adminroot->locate($page->name, true);
9103
        if ($located) {
9104
            foreach ($located->visiblepath as $pathitem) {
9105
                array_unshift($path, (string) $pathitem);
9106
            }
9107
        }
9108
 
9109
        $sectionsettings = [];
9110
        if (!empty($settings)) {
9111
            foreach ($settings as $setting) {
9112
                if (empty($setting->nosave)) {
9113
                    $savebutton = true;
9114
                }
9115
                $fullname = $setting->get_full_name();
9116
                if (array_key_exists($fullname, $adminroot->errors)) {
9117
                    $data = $adminroot->errors[$fullname]->data;
9118
                } else {
9119
                    $data = $setting->get_setting();
9120
                // do not use defaults if settings not available - upgradesettings handles the defaults!
9121
                }
9122
                $sectionsettings[] = $setting->output_html($data, $query);
9123
            }
9124
        }
9125
 
9126
        $tpldata->results[] = (object) [
9127
            'title' => $heading,
9128
            'path' => $path,
9129
            'url' => $headingurl->out(false),
9130
            'settings' => $sectionsettings
9131
        ];
9132
    }
9133
 
9134
    $tpldata->showsave = $savebutton;
9135
    $tpldata->hasresults = !empty($tpldata->results);
9136
 
9137
    return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
9138
}
9139
 
9140
/**
9141
 * Internal function - returns arrays of html pages with uninitialised settings
9142
 *
9143
 * @param object $node Instance of admin_category or admin_settingpage
9144
 * @return array
9145
 */
9146
function admin_output_new_settings_by_page($node) {
9147
    global $OUTPUT;
9148
    $return = array();
9149
 
9150
    if ($node instanceof admin_category) {
9151
        $entries = array_keys($node->children);
9152
        foreach ($entries as $entry) {
9153
            $return += admin_output_new_settings_by_page($node->children[$entry]);
9154
        }
9155
 
9156
    } else if ($node instanceof admin_settingpage) {
9157
            $newsettings = array();
9158
            foreach ($node->settings as $setting) {
9159
                if (is_null($setting->get_setting())) {
9160
                    $newsettings[] = $setting;
9161
                }
9162
            }
9163
            if (count($newsettings) > 0) {
9164
                $adminroot = admin_get_root();
9165
                $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
9166
                $page .= '<fieldset class="adminsettings">'."\n";
9167
                foreach ($newsettings as $setting) {
9168
                    $fullname = $setting->get_full_name();
9169
                    if (array_key_exists($fullname, $adminroot->errors)) {
9170
                        $data = $adminroot->errors[$fullname]->data;
9171
                    } else {
9172
                        $data = $setting->get_setting();
9173
                        if (is_null($data)) {
9174
                            $data = $setting->get_defaultsetting();
9175
                        }
9176
                    }
9177
                    $page .= '<div class="clearer"><!-- --></div>'."\n";
9178
                    $page .= $setting->output_html($data);
9179
                }
9180
                $page .= '</fieldset>';
9181
                $return[$node->name] = $page;
9182
            }
9183
        }
9184
 
9185
    return $return;
9186
}
9187
 
9188
/**
9189
 * Format admin settings
9190
 *
9191
 * @param object $setting
9192
 * @param string $title label element
9193
 * @param string $form form fragment, html code - not highlighted automatically
9194
 * @param string $description
9195
 * @param mixed $label link label to id, true by default or string being the label to connect it to
9196
 * @param string $warning warning text
9197
 * @param ?string $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
9198
 * @param string $query search query to be highlighted
9199
 * @return string XHTML
9200
 */
9201
function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
9202
    global $CFG, $OUTPUT;
9203
 
9204
    $context = (object) [
9205
        'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
9206
        'fullname' => $setting->get_full_name(),
9207
    ];
9208
 
9209
    // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
9210
    if ($label === true) {
9211
        $context->labelfor = $setting->get_id();
9212
    } else if ($label === false) {
9213
        $context->labelfor = '';
9214
    } else {
9215
        $context->labelfor = $label;
9216
    }
9217
 
9218
    $form .= $setting->output_setting_flags();
9219
 
9220
    $context->warning = $warning;
9221
    $context->override = '';
9222
    if (empty($setting->plugin)) {
9223
        if ($setting->is_forceable() && array_key_exists($setting->name, $CFG->config_php_settings)) {
9224
            $context->override = get_string('configoverride', 'admin');
9225
        }
9226
    } else {
9227
        if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
9228
            $context->override = get_string('configoverride', 'admin');
9229
        }
9230
    }
9231
 
9232
    $defaults = array();
9233
    if (!is_null($defaultinfo)) {
9234
        if ($defaultinfo === '') {
9235
            $defaultinfo = get_string('emptysettingvalue', 'admin');
9236
        }
9237
        $defaults[] = $defaultinfo;
9238
    }
9239
 
9240
    $context->default = null;
9241
    $setting->get_setting_flag_defaults($defaults);
9242
    if (!empty($defaults)) {
9243
        $defaultinfo = implode(', ', $defaults);
9244
        $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
9245
        $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
9246
    }
9247
 
9248
 
9249
    $context->error = '';
9250
    $adminroot = admin_get_root();
9251
    if (array_key_exists($context->fullname, $adminroot->errors)) {
9252
        $context->error = $adminroot->errors[$context->fullname]->error;
9253
    }
9254
 
9255
    if ($dependenton = $setting->get_dependent_on()) {
9256
        $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
9257
    }
9258
 
9259
    $context->id = 'admin-' . $setting->name;
9260
    $context->title = highlightfast($query, $title);
9261
    $context->name = highlightfast($query, $context->name);
9262
    $context->description = highlight($query, markdown_to_html($description));
9263
    $context->element = $form;
9264
    $context->forceltr = $setting->get_force_ltr();
9265
    $context->customcontrol = $setting->has_custom_form_control();
9266
 
9267
    return $OUTPUT->render_from_template('core_admin/setting', $context);
9268
}
9269
 
9270
/**
9271
 * Based on find_new_settings{@link ()}  in upgradesettings.php
9272
 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
9273
 *
9274
 * @param object $node Instance of admin_category, or admin_settingpage
9275
 * @return boolean true if any settings haven't been initialised, false if they all have
9276
 */
9277
function any_new_admin_settings($node) {
9278
 
9279
    if ($node instanceof admin_category) {
9280
        $entries = array_keys($node->children);
9281
        foreach ($entries as $entry) {
9282
            if (any_new_admin_settings($node->children[$entry])) {
9283
                return true;
9284
            }
9285
        }
9286
 
9287
    } else if ($node instanceof admin_settingpage) {
9288
            foreach ($node->settings as $setting) {
9289
                if ($setting->get_setting() === NULL) {
9290
                    return true;
9291
                }
9292
            }
9293
        }
9294
 
9295
    return false;
9296
}
9297
 
9298
/**
9299
 * Given a table and optionally a column name should replaces be done?
9300
 *
9301
 * @param string $table name
9302
 * @param string $column name
9303
 * @return bool success or fail
9304
 */
9305
function db_should_replace($table, $column = '', $additionalskiptables = ''): bool {
9306
 
9307
    // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing...
9308
    $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
9309
        'events_queue', 'repository_instance_config', 'block_instances', 'files'];
9310
 
9311
    // Additional skip tables.
9312
    if (!empty($additionalskiptables)) {
9313
        $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '',  $additionalskiptables)));
9314
    }
9315
 
9316
    // Don't process these.
9317
    if (in_array($table, $skiptables)) {
9318
        return false;
9319
    }
9320
 
9321
    // To be safe never replace inside a table that looks related to logging.
9322
    if (preg_match('/(^|_)logs?($|_)/', $table)) {
9323
        return false;
9324
    }
9325
 
9326
    // Do column based exclusions.
9327
    if (!empty($column)) {
9328
        // Don't touch anything that looks like a hash.
9329
        if (preg_match('/hash$/', $column)) {
9330
            return false;
9331
        }
9332
    }
9333
 
9334
    return true;
9335
}
9336
 
9337
/**
9338
 * Moved from admin/replace.php so that we can use this in cron
9339
 *
9340
 * @param string $search string to look for
9341
 * @param string $replace string to replace
9342
 * @return bool success or fail
9343
 */
9344
function db_replace($search, $replace, $additionalskiptables = '') {
9345
    global $DB, $CFG, $OUTPUT;
9346
 
9347
    // Turn off time limits, sometimes upgrades can be slow.
9348
    core_php_time_limit::raise();
9349
 
9350
    if (!$tables = $DB->get_tables() ) {    // No tables yet at all.
9351
        return false;
9352
    }
9353
    foreach ($tables as $table) {
9354
 
9355
        if (!db_should_replace($table, '', $additionalskiptables)) {
9356
            continue;
9357
        }
9358
 
9359
        if ($columns = $DB->get_columns($table)) {
9360
            $DB->set_debug(true);
9361
            foreach ($columns as $column) {
9362
                if (!db_should_replace($table, $column->name)) {
9363
                    continue;
9364
                }
9365
                $DB->replace_all_text($table, $column, $search, $replace);
9366
            }
9367
            $DB->set_debug(false);
9368
        }
9369
    }
9370
 
9371
    // delete modinfo caches
9372
    rebuild_course_cache(0, true);
9373
 
9374
    // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9375
    $blocks = core_component::get_plugin_list('block');
9376
    foreach ($blocks as $blockname=>$fullblock) {
9377
        if ($blockname === 'NEWBLOCK') {   // Someone has unzipped the template, ignore it
9378
            continue;
9379
        }
9380
 
9381
        if (!is_readable($fullblock.'/lib.php')) {
9382
            continue;
9383
        }
9384
 
9385
        $function = 'block_'.$blockname.'_global_db_replace';
9386
        include_once($fullblock.'/lib.php');
9387
        if (!function_exists($function)) {
9388
            continue;
9389
        }
9390
 
9391
        echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9392
        $function($search, $replace);
9393
        echo $OUTPUT->notification("...finished", 'notifysuccess');
9394
    }
9395
 
9396
    // Trigger an event.
9397
    $eventargs = [
9398
        'context' => context_system::instance(),
9399
        'other' => [
9400
            'search' => $search,
9401
            'replace' => $replace
9402
        ]
9403
    ];
9404
    $event = \core\event\database_text_field_content_replaced::create($eventargs);
9405
    $event->trigger();
9406
 
9407
    purge_all_caches();
9408
 
9409
    return true;
9410
}
9411
 
9412
/**
9413
 * Manage repository settings
9414
 *
9415
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9416
 */
9417
class admin_setting_managerepository extends admin_setting {
9418
/** @var string */
9419
    private $baseurl;
9420
 
9421
    /**
9422
     * calls parent::__construct with specific arguments
9423
     */
9424
    public function __construct() {
9425
        global $CFG;
9426
        parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9427
        $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9428
    }
9429
 
9430
    /**
9431
     * Always returns true, does nothing
9432
     *
9433
     * @return true
9434
     */
9435
    public function get_setting() {
9436
        return true;
9437
    }
9438
 
9439
    /**
9440
     * Always returns true does nothing
9441
     *
9442
     * @return true
9443
     */
9444
    public function get_defaultsetting() {
9445
        return true;
9446
    }
9447
 
9448
    /**
9449
     * Always returns s_managerepository
9450
     *
9451
     * @return string Always return 's_managerepository'
9452
     */
9453
    public function get_full_name() {
9454
        return 's_managerepository';
9455
    }
9456
 
9457
    /**
9458
     * Always returns '' doesn't do anything
9459
     */
9460
    public function write_setting($data) {
9461
        $url = $this->baseurl . '&amp;new=' . $data;
9462
        return '';
9463
    // TODO
9464
    // Should not use redirect and exit here
9465
    // Find a better way to do this.
9466
    // redirect($url);
9467
    // exit;
9468
    }
9469
 
9470
    /**
9471
     * Searches repository plugins for one that matches $query
9472
     *
9473
     * @param string $query The string to search for
9474
     * @return bool true if found, false if not
9475
     */
9476
    public function is_related($query) {
9477
        if (parent::is_related($query)) {
9478
            return true;
9479
        }
9480
 
9481
        $repositories= core_component::get_plugin_list('repository');
9482
        foreach ($repositories as $p => $dir) {
9483
            if (strpos($p, $query) !== false) {
9484
                return true;
9485
            }
9486
        }
9487
        foreach (repository::get_types() as $instance) {
9488
            $title = $instance->get_typename();
9489
            if (strpos(core_text::strtolower($title), $query) !== false) {
9490
                return true;
9491
            }
9492
        }
9493
        return false;
9494
    }
9495
 
9496
    /**
9497
     * Helper function that generates a moodle_url object
9498
     * relevant to the repository
9499
     */
9500
 
9501
    function repository_action_url($repository) {
9502
        return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9503
    }
9504
 
9505
    /**
9506
     * Builds XHTML to display the control
9507
     *
9508
     * @param string $data Unused
9509
     * @param string $query
9510
     * @return string XHTML
9511
     */
9512
    public function output_html($data, $query='') {
9513
        global $CFG, $USER, $OUTPUT;
9514
 
9515
        // Get strings that are used
9516
        $strshow = get_string('on', 'repository');
9517
        $strhide = get_string('off', 'repository');
9518
        $strdelete = get_string('disabled', 'repository');
9519
 
9520
        $actionchoicesforexisting = array(
9521
            'show' => $strshow,
9522
            'hide' => $strhide,
9523
            'delete' => $strdelete
9524
        );
9525
 
9526
        $actionchoicesfornew = array(
9527
            'newon' => $strshow,
9528
            'newoff' => $strhide,
9529
            'delete' => $strdelete
9530
        );
9531
 
9532
        $return = '';
9533
        $return .= $OUTPUT->box_start('generalbox');
9534
 
9535
        // Set strings that are used multiple times
9536
        $settingsstr = get_string('settings');
9537
        $disablestr = get_string('disable');
9538
 
9539
        // Table to list plug-ins
9540
        $table = new html_table();
9541
        $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9542
        $table->align = array('left', 'center', 'center', 'center', 'center');
9543
        $table->data = array();
9544
 
9545
        // Get list of used plug-ins
9546
        $repositorytypes = repository::get_types();
9547
        if (!empty($repositorytypes)) {
9548
            // Array to store plugins being used
9549
            $alreadyplugins = array();
9550
            $totalrepositorytypes = count($repositorytypes);
9551
            $updowncount = 1;
9552
            foreach ($repositorytypes as $i) {
9553
                $settings = '';
9554
                $typename = $i->get_typename();
9555
                // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9556
                $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9557
                $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9558
 
9559
                if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9560
                    // Calculate number of instances in order to display them for the Moodle administrator
9561
                    if (!empty($instanceoptionnames)) {
9562
                        $params = array();
9563
                        $params['context'] = array(context_system::instance());
9564
                        $params['onlyvisible'] = false;
9565
                        $params['type'] = $typename;
9566
                        $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9567
                        // site instances
9568
                        $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9569
                        $params['context'] = array();
9570
                        $instances = repository::static_function($typename, 'get_instances', $params);
9571
                        $courseinstances = array();
9572
                        $userinstances = array();
9573
 
9574
                        foreach ($instances as $instance) {
9575
                            $repocontext = context::instance_by_id($instance->instance->contextid);
9576
                            if ($repocontext->contextlevel == CONTEXT_COURSE) {
9577
                                $courseinstances[] = $instance;
9578
                            } else if ($repocontext->contextlevel == CONTEXT_USER) {
9579
                                $userinstances[] = $instance;
9580
                            }
9581
                        }
9582
                        // course instances
9583
                        $instancenumber = count($courseinstances);
9584
                        $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9585
 
9586
                        // user private instances
9587
                        $instancenumber =  count($userinstances);
9588
                        $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9589
                    } else {
9590
                        $admininstancenumbertext = "";
9591
                        $courseinstancenumbertext = "";
9592
                        $userinstancenumbertext = "";
9593
                    }
9594
 
9595
                    $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9596
 
9597
                    $settings .= $OUTPUT->container_start('mdl-left');
9598
                    $settings .= '<br/>';
9599
                    $settings .= $admininstancenumbertext;
9600
                    $settings .= '<br/>';
9601
                    $settings .= $courseinstancenumbertext;
9602
                    $settings .= '<br/>';
9603
                    $settings .= $userinstancenumbertext;
9604
                    $settings .= $OUTPUT->container_end();
9605
                }
9606
                // Get the current visibility
9607
                if ($i->get_visible()) {
9608
                    $currentaction = 'show';
9609
                } else {
9610
                    $currentaction = 'hide';
9611
                }
9612
 
9613
                $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9614
 
9615
                // Display up/down link
9616
                $updown = '';
9617
                // Should be done with CSS instead.
9618
                $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9619
 
9620
                if ($updowncount > 1) {
9621
                    $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9622
                    $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9623
                }
9624
                else {
9625
                    $updown .= $spacer;
9626
                }
9627
                if ($updowncount < $totalrepositorytypes) {
9628
                    $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9629
                    $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9630
                }
9631
                else {
9632
                    $updown .= $spacer;
9633
                }
9634
 
9635
                $updowncount++;
9636
 
9637
                $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9638
 
9639
                if (!in_array($typename, $alreadyplugins)) {
9640
                    $alreadyplugins[] = $typename;
9641
                }
9642
            }
9643
        }
9644
 
9645
        // Get all the plugins that exist on disk
9646
        $plugins = core_component::get_plugin_list('repository');
9647
        if (!empty($plugins)) {
9648
            foreach ($plugins as $plugin => $dir) {
9649
                // Check that it has not already been listed
9650
                if (!in_array($plugin, $alreadyplugins)) {
9651
                    $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9652
                    $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9653
                }
9654
            }
9655
        }
9656
 
9657
        $return .= html_writer::table($table);
9658
        $return .= $OUTPUT->box_end();
9659
        return highlight($query, $return);
9660
    }
9661
}
9662
 
9663
/**
9664
 * Special checkbox for enable mobile web service
9665
 * If enable then we store the service id of the mobile service into config table
9666
 * If disable then we unstore the service id from the config table
9667
 */
9668
class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9669
 
9670
    /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9671
    private $restuse;
9672
 
9673
    /**
9674
     * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9675
     *
9676
     * @return boolean
9677
     */
9678
    private function is_protocol_cap_allowed() {
9679
        global $DB, $CFG;
9680
 
9681
        // If the $this->restuse variable is not set, it needs to be set.
9682
        if (empty($this->restuse) and $this->restuse!==false) {
9683
            $params = array();
9684
            $params['permission'] = CAP_ALLOW;
9685
            $params['roleid'] = $CFG->defaultuserroleid;
9686
            $params['capability'] = 'webservice/rest:use';
9687
            $this->restuse = $DB->record_exists('role_capabilities', $params);
9688
        }
9689
 
9690
        return $this->restuse;
9691
    }
9692
 
9693
    /**
9694
     * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9695
     * @param bool $status true to allow, false to not set
9696
     */
9697
    private function set_protocol_cap($status) {
9698
        global $CFG;
9699
        if ($status and !$this->is_protocol_cap_allowed()) {
9700
            //need to allow the cap
9701
            $permission = CAP_ALLOW;
9702
            $assign = true;
9703
        } else if (!$status and $this->is_protocol_cap_allowed()){
9704
            //need to disallow the cap
9705
            $permission = CAP_INHERIT;
9706
            $assign = true;
9707
        }
9708
        if (!empty($assign)) {
9709
            $systemcontext = context_system::instance();
9710
            assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9711
        }
9712
    }
9713
 
9714
    /**
9715
     * Builds XHTML to display the control.
9716
     * The main purpose of this overloading is to display a warning when https
9717
     * is not supported by the server
9718
     * @param string $data Unused
9719
     * @param string $query
9720
     * @return string XHTML
9721
     */
9722
    public function output_html($data, $query='') {
9723
        global $OUTPUT;
9724
        $html = parent::output_html($data, $query);
9725
 
9726
        if ((string)$data === $this->yes) {
9727
            $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9728
            foreach ($notifications as $notification) {
9729
                $message = get_string($notification[0], $notification[1]);
9730
                $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9731
            }
9732
        }
9733
 
9734
        return $html;
9735
    }
9736
 
9737
    /**
9738
     * Retrieves the current setting using the objects name
9739
     *
9740
     * @return string
9741
     */
9742
    public function get_setting() {
9743
        global $CFG;
9744
 
9745
        // First check if is not set.
9746
        $result = $this->config_read($this->name);
9747
        if (is_null($result)) {
9748
            return null;
9749
        }
9750
 
9751
        // For install cli script, $CFG->defaultuserroleid is not set so return 0
9752
        // Or if web services aren't enabled this can't be,
9753
        if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9754
            return 0;
9755
        }
9756
 
9757
        require_once($CFG->dirroot . '/webservice/lib.php');
9758
        $webservicemanager = new webservice();
9759
        $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9760
        if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9761
            return $result;
9762
        } else {
9763
            return 0;
9764
        }
9765
    }
9766
 
9767
    /**
9768
     * Save the selected setting
9769
     *
9770
     * @param string $data The selected site
9771
     * @return string empty string or error message
9772
     */
9773
    public function write_setting($data) {
9774
        global $DB, $CFG;
9775
 
9776
        //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9777
        if (empty($CFG->defaultuserroleid)) {
9778
            return '';
9779
        }
9780
 
9781
        $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9782
 
9783
        require_once($CFG->dirroot . '/webservice/lib.php');
9784
        $webservicemanager = new webservice();
9785
 
9786
        $updateprotocol = false;
9787
        if ((string)$data === $this->yes) {
9788
             //code run when enable mobile web service
9789
             //enable web service systeme if necessary
9790
             set_config('enablewebservices', true);
9791
 
9792
             //enable mobile service
9793
             $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9794
             $mobileservice->enabled = 1;
9795
             $webservicemanager->update_external_service($mobileservice);
9796
 
9797
             // Enable REST server.
9798
             $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9799
 
9800
             if (!in_array('rest', $activeprotocols)) {
9801
                 $activeprotocols[] = 'rest';
9802
                 $updateprotocol = true;
9803
             }
9804
 
9805
             if ($updateprotocol) {
9806
                 set_config('webserviceprotocols', implode(',', $activeprotocols));
9807
             }
9808
 
9809
             // Allow rest:use capability for authenticated user.
9810
             $this->set_protocol_cap(true);
9811
         } else {
9812
             // Disable the mobile service.
9813
             $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9814
             $mobileservice->enabled = 0;
9815
             $webservicemanager->update_external_service($mobileservice);
9816
         }
9817
 
9818
        return (parent::write_setting($data));
9819
    }
9820
}
9821
 
9822
/**
9823
 * Special class for management of external services
9824
 *
9825
 * @author Petr Skoda (skodak)
9826
 */
9827
class admin_setting_manageexternalservices extends admin_setting {
9828
    /**
9829
     * Calls parent::__construct with specific arguments
9830
     */
9831
    public function __construct() {
9832
        $this->nosave = true;
9833
        parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9834
    }
9835
 
9836
    /**
9837
     * Always returns true, does nothing
9838
     *
9839
     * @return true
9840
     */
9841
    public function get_setting() {
9842
        return true;
9843
    }
9844
 
9845
    /**
9846
     * Always returns true, does nothing
9847
     *
9848
     * @return true
9849
     */
9850
    public function get_defaultsetting() {
9851
        return true;
9852
    }
9853
 
9854
    /**
9855
     * Always returns '', does not write anything
9856
     *
9857
     * @return string Always returns ''
9858
     */
9859
    public function write_setting($data) {
9860
    // do not write any setting
9861
        return '';
9862
    }
9863
 
9864
    /**
9865
     * Checks if $query is one of the available external services
9866
     *
9867
     * @param string $query The string to search for
9868
     * @return bool Returns true if found, false if not
9869
     */
9870
    public function is_related($query) {
9871
        global $DB;
9872
 
9873
        if (parent::is_related($query)) {
9874
            return true;
9875
        }
9876
 
9877
        $services = $DB->get_records('external_services', array(), 'id, name');
9878
        foreach ($services as $service) {
9879
            if (strpos(core_text::strtolower($service->name), $query) !== false) {
9880
                return true;
9881
            }
9882
        }
9883
        return false;
9884
    }
9885
 
9886
    /**
9887
     * Builds the XHTML to display the control
9888
     *
9889
     * @param string $data Unused
9890
     * @param string $query
9891
     * @return string
9892
     */
9893
    public function output_html($data, $query='') {
9894
        global $CFG, $OUTPUT, $DB;
9895
 
9896
        // display strings
9897
        $stradministration = get_string('administration');
9898
        $stredit = get_string('edit');
9899
        $strservice = get_string('externalservice', 'webservice');
9900
        $strdelete = get_string('delete');
9901
        $strplugin = get_string('plugin', 'admin');
9902
        $stradd = get_string('add');
9903
        $strfunctions = get_string('functions', 'webservice');
9904
        $strusers = get_string('users');
9905
        $strserviceusers = get_string('serviceusers', 'webservice');
9906
 
9907
        $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9908
        $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9909
        $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9910
 
9911
        // built in services
9912
         $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9913
         $return = "";
9914
         if (!empty($services)) {
9915
            $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9916
 
9917
 
9918
 
9919
            $table = new html_table();
9920
            $table->head  = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9921
            $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9922
            $table->id = 'builtinservices';
9923
            $table->attributes['class'] = 'admintable externalservices generaltable';
9924
            $table->data  = array();
9925
 
9926
            // iterate through auth plugins and add to the display table
9927
            foreach ($services as $service) {
9928
                $name = $service->name;
9929
 
9930
                // hide/show link
9931
                if ($service->enabled) {
9932
                    $displayname = "<span>$name</span>";
9933
                } else {
9934
                    $displayname = "<span class=\"dimmed_text\">$name</span>";
9935
                }
9936
 
9937
                $plugin = $service->component;
9938
 
9939
                $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9940
 
9941
                if ($service->restrictedusers) {
9942
                    $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9943
                } else {
9944
                    $users = get_string('allusers', 'webservice');
9945
                }
9946
 
9947
                $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9948
 
9949
                // add a row to the table
9950
                $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9951
            }
9952
            $return .= html_writer::table($table);
9953
        }
9954
 
9955
        // Custom services
9956
        $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9957
        $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9958
 
9959
        $table = new html_table();
9960
        $table->head  = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9961
        $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9962
        $table->id = 'customservices';
9963
        $table->attributes['class'] = 'admintable externalservices generaltable';
9964
        $table->data  = array();
9965
 
9966
        // iterate through auth plugins and add to the display table
9967
        foreach ($services as $service) {
9968
            $name = $service->name;
9969
 
9970
            // hide/show link
9971
            if ($service->enabled) {
9972
                $displayname = "<span>$name</span>";
9973
            } else {
9974
                $displayname = "<span class=\"dimmed_text\">$name</span>";
9975
            }
9976
 
9977
            // delete link
9978
            $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9979
 
9980
            $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9981
 
9982
            if ($service->restrictedusers) {
9983
                $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9984
            } else {
9985
                $users = get_string('allusers', 'webservice');
9986
            }
9987
 
9988
            $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9989
 
9990
            // add a row to the table
9991
            $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9992
        }
9993
        // add new custom service option
9994
        $return .= html_writer::table($table);
9995
 
9996
        $return .= '<br />';
9997
        // add a token to the table
9998
        $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9999
 
10000
        return highlight($query, $return);
10001
    }
10002
}
10003
 
10004
/**
10005
 * Special class for overview of external services
10006
 *
10007
 * @author Jerome Mouneyrac
10008
 */
10009
class admin_setting_webservicesoverview extends admin_setting {
10010
 
10011
    /**
10012
     * Calls parent::__construct with specific arguments
10013
     */
10014
    public function __construct() {
10015
        $this->nosave = true;
10016
        parent::__construct('webservicesoverviewui',
10017
                        get_string('webservicesoverview', 'webservice'), '', '');
10018
    }
10019
 
10020
    /**
10021
     * Always returns true, does nothing
10022
     *
10023
     * @return true
10024
     */
10025
    public function get_setting() {
10026
        return true;
10027
    }
10028
 
10029
    /**
10030
     * Always returns true, does nothing
10031
     *
10032
     * @return true
10033
     */
10034
    public function get_defaultsetting() {
10035
        return true;
10036
    }
10037
 
10038
    /**
10039
     * Always returns '', does not write anything
10040
     *
10041
     * @return string Always returns ''
10042
     */
10043
    public function write_setting($data) {
10044
        // do not write any setting
10045
        return '';
10046
    }
10047
 
10048
    /**
10049
     * Builds the XHTML to display the control
10050
     *
10051
     * @param string $data Unused
10052
     * @param string $query
10053
     * @return string
10054
     */
10055
    public function output_html($data, $query='') {
10056
        global $CFG, $OUTPUT;
10057
 
10058
        $return = "";
10059
        $brtag = html_writer::empty_tag('br');
10060
 
10061
        /// One system controlling Moodle with Token
10062
        $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
10063
        $table = new html_table();
10064
        $table->head = array(get_string('step', 'webservice'), get_string('status'),
10065
            get_string('description'));
10066
        $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10067
        $table->id = 'onesystemcontrol';
10068
        $table->attributes['class'] = 'admintable wsoverview generaltable';
10069
        $table->data = array();
10070
 
10071
        $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
10072
                . $brtag . $brtag;
10073
 
10074
        /// 1. Enable Web Services
10075
        $row = array();
10076
        $url = new moodle_url("/admin/search.php?query=enablewebservices");
10077
        $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10078
                        array('href' => $url));
10079
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
10080
        if ($CFG->enablewebservices) {
10081
            $status = get_string('yes');
10082
        }
10083
        $row[1] = $status;
10084
        $row[2] = get_string('enablewsdescription', 'webservice');
10085
        $table->data[] = $row;
10086
 
10087
        /// 2. Enable protocols
10088
        $row = array();
10089
        $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10090
        $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10091
                        array('href' => $url));
10092
        $status = html_writer::tag('span', get_string('none'), array('class' => 'badge bg-danger text-white'));
10093
        //retrieve activated protocol
10094
        $active_protocols = empty($CFG->webserviceprotocols) ?
10095
                array() : explode(',', $CFG->webserviceprotocols);
10096
        if (!empty($active_protocols)) {
10097
            $status = "";
10098
            foreach ($active_protocols as $protocol) {
10099
                $status .= $protocol . $brtag;
10100
            }
10101
        }
10102
        $row[1] = $status;
10103
        $row[2] = get_string('enableprotocolsdescription', 'webservice');
10104
        $table->data[] = $row;
10105
 
10106
        /// 3. Create user account
10107
        $row = array();
10108
        $url = new moodle_url("/user/editadvanced.php?id=-1");
10109
        $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
10110
                        array('href' => $url));
10111
        $row[1] = "";
10112
        $row[2] = get_string('createuserdescription', 'webservice');
10113
        $table->data[] = $row;
10114
 
10115
        /// 4. Add capability to users
10116
        $row = array();
10117
        $url = new moodle_url("/admin/roles/check.php?contextid=1");
10118
        $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
10119
                        array('href' => $url));
10120
        $row[1] = "";
10121
        $row[2] = get_string('checkusercapabilitydescription', 'webservice');
10122
        $table->data[] = $row;
10123
 
10124
        /// 5. Select a web service
10125
        $row = array();
10126
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10127
        $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10128
                        array('href' => $url));
10129
        $row[1] = "";
10130
        $row[2] = get_string('createservicedescription', 'webservice');
10131
        $table->data[] = $row;
10132
 
10133
        /// 6. Add functions
10134
        $row = array();
10135
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10136
        $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10137
                        array('href' => $url));
10138
        $row[1] = "";
10139
        $row[2] = get_string('addfunctionsdescription', 'webservice');
10140
        $table->data[] = $row;
10141
 
10142
        /// 7. Add the specific user
10143
        $row = array();
10144
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10145
        $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
10146
                        array('href' => $url));
10147
        $row[1] = "";
10148
        $row[2] = get_string('selectspecificuserdescription', 'webservice');
10149
        $table->data[] = $row;
10150
 
10151
        /// 8. Create token for the specific user
10152
        $row = array();
10153
        $url = new moodle_url('/admin/webservice/tokens.php', ['action' => 'create']);
10154
        $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
10155
                        array('href' => $url));
10156
        $row[1] = "";
10157
        $row[2] = get_string('createtokenforuserdescription', 'webservice');
10158
        $table->data[] = $row;
10159
 
10160
        /// 9. Enable the documentation
10161
        $row = array();
10162
        $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
10163
        $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
10164
                        array('href' => $url));
10165
        $status = '<span class="warning">' . get_string('no') . '</span>';
10166
        if ($CFG->enablewsdocumentation) {
10167
            $status = get_string('yes');
10168
        }
10169
        $row[1] = $status;
10170
        $row[2] = get_string('enabledocumentationdescription', 'webservice');
10171
        $table->data[] = $row;
10172
 
10173
        /// 10. Test the service
10174
        $row = array();
10175
        $url = new moodle_url("/admin/webservice/testclient.php");
10176
        $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10177
                        array('href' => $url));
10178
        $row[1] = "";
10179
        $row[2] = get_string('testwithtestclientdescription', 'webservice');
10180
        $table->data[] = $row;
10181
 
10182
        $return .= html_writer::table($table);
10183
 
10184
        /// Users as clients with token
10185
        $return .= $brtag . $brtag . $brtag;
10186
        $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
10187
        $table = new html_table();
10188
        $table->head = array(get_string('step', 'webservice'), get_string('status'),
10189
            get_string('description'));
10190
        $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10191
        $table->id = 'userasclients';
10192
        $table->attributes['class'] = 'admintable wsoverview generaltable';
10193
        $table->data = array();
10194
 
10195
        $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
10196
                $brtag . $brtag;
10197
 
10198
        /// 1. Enable Web Services
10199
        $row = array();
10200
        $url = new moodle_url("/admin/search.php?query=enablewebservices");
10201
        $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10202
                        array('href' => $url));
10203
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
10204
        if ($CFG->enablewebservices) {
10205
            $status = get_string('yes');
10206
        }
10207
        $row[1] = $status;
10208
        $row[2] = get_string('enablewsdescription', 'webservice');
10209
        $table->data[] = $row;
10210
 
10211
        /// 2. Enable protocols
10212
        $row = array();
10213
        $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10214
        $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10215
                        array('href' => $url));
10216
        $status = html_writer::tag('span', get_string('none'), array('class' => 'badge bg-danger text-white'));
10217
        //retrieve activated protocol
10218
        $active_protocols = empty($CFG->webserviceprotocols) ?
10219
                array() : explode(',', $CFG->webserviceprotocols);
10220
        if (!empty($active_protocols)) {
10221
            $status = "";
10222
            foreach ($active_protocols as $protocol) {
10223
                $status .= $protocol . $brtag;
10224
            }
10225
        }
10226
        $row[1] = $status;
10227
        $row[2] = get_string('enableprotocolsdescription', 'webservice');
10228
        $table->data[] = $row;
10229
 
10230
 
10231
        /// 3. Select a web service
10232
        $row = array();
10233
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10234
        $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10235
                        array('href' => $url));
10236
        $row[1] = "";
10237
        $row[2] = get_string('createserviceforusersdescription', 'webservice');
10238
        $table->data[] = $row;
10239
 
10240
        /// 4. Add functions
10241
        $row = array();
10242
        $url = new moodle_url("/admin/settings.php?section=externalservices");
10243
        $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10244
                        array('href' => $url));
10245
        $row[1] = "";
10246
        $row[2] = get_string('addfunctionsdescription', 'webservice');
10247
        $table->data[] = $row;
10248
 
10249
        /// 5. Add capability to users
10250
        $row = array();
10251
        $url = new moodle_url("/admin/roles/check.php?contextid=1");
10252
        $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
10253
                        array('href' => $url));
10254
        $row[1] = "";
10255
        $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
10256
        $table->data[] = $row;
10257
 
10258
        /// 6. Test the service
10259
        $row = array();
10260
        $url = new moodle_url("/admin/webservice/testclient.php");
10261
        $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10262
                        array('href' => $url));
10263
        $row[1] = "";
10264
        $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
10265
        $table->data[] = $row;
10266
 
10267
        $return .= html_writer::table($table);
10268
 
10269
        return highlight($query, $return);
10270
    }
10271
 
10272
}
10273
 
10274
 
10275
/**
10276
 * Special class for web service protocol administration.
10277
 *
10278
 * @author Petr Skoda (skodak)
10279
 */
10280
class admin_setting_managewebserviceprotocols extends admin_setting {
10281
 
10282
    /**
10283
     * Calls parent::__construct with specific arguments
10284
     */
10285
    public function __construct() {
10286
        $this->nosave = true;
10287
        parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
10288
    }
10289
 
10290
    /**
10291
     * Always returns true, does nothing
10292
     *
10293
     * @return true
10294
     */
10295
    public function get_setting() {
10296
        return true;
10297
    }
10298
 
10299
    /**
10300
     * Always returns true, does nothing
10301
     *
10302
     * @return true
10303
     */
10304
    public function get_defaultsetting() {
10305
        return true;
10306
    }
10307
 
10308
    /**
10309
     * Always returns '', does not write anything
10310
     *
10311
     * @return string Always returns ''
10312
     */
10313
    public function write_setting($data) {
10314
    // do not write any setting
10315
        return '';
10316
    }
10317
 
10318
    /**
10319
     * Checks if $query is one of the available webservices
10320
     *
10321
     * @param string $query The string to search for
10322
     * @return bool Returns true if found, false if not
10323
     */
10324
    public function is_related($query) {
10325
        if (parent::is_related($query)) {
10326
            return true;
10327
        }
10328
 
10329
        $protocols = core_component::get_plugin_list('webservice');
10330
        foreach ($protocols as $protocol=>$location) {
10331
            if (strpos($protocol, $query) !== false) {
10332
                return true;
10333
            }
10334
            $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
10335
            if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
10336
                return true;
10337
            }
10338
        }
10339
        return false;
10340
    }
10341
 
10342
    /**
10343
     * Builds the XHTML to display the control
10344
     *
10345
     * @param string $data Unused
10346
     * @param string $query
10347
     * @return string
10348
     */
10349
    public function output_html($data, $query='') {
10350
        global $CFG, $OUTPUT;
10351
 
10352
        // display strings
10353
        $stradministration = get_string('administration');
10354
        $strsettings = get_string('settings');
10355
        $stredit = get_string('edit');
10356
        $strprotocol = get_string('protocol', 'webservice');
10357
        $strenable = get_string('enable');
10358
        $strdisable = get_string('disable');
10359
        $strversion = get_string('version');
10360
 
10361
        $protocols_available = core_component::get_plugin_list('webservice');
10362
        $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10363
        ksort($protocols_available);
10364
 
10365
        foreach ($activeprotocols as $key => $protocol) {
10366
            if (empty($protocols_available[$protocol])) {
10367
                unset($activeprotocols[$key]);
10368
            }
10369
        }
10370
 
10371
        $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10372
        if (in_array('xmlrpc', $activeprotocols)) {
10373
            $notify = new \core\output\notification(get_string('xmlrpcwebserviceenabled', 'admin'),
10374
                \core\output\notification::NOTIFY_WARNING);
10375
            $return .= $OUTPUT->render($notify);
10376
        }
10377
        $return .= $OUTPUT->box_start('generalbox webservicesui');
10378
 
10379
        $table = new html_table();
10380
        $table->head  = array($strprotocol, $strversion, $strenable, $strsettings);
10381
        $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10382
        $table->id = 'webserviceprotocols';
10383
        $table->attributes['class'] = 'admintable generaltable';
10384
        $table->data  = array();
10385
 
10386
        // iterate through auth plugins and add to the display table
10387
        $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10388
        foreach ($protocols_available as $protocol => $location) {
10389
            $name = get_string('pluginname', 'webservice_'.$protocol);
10390
 
10391
            $plugin = new stdClass();
10392
            if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10393
                include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10394
            }
10395
            $version = isset($plugin->version) ? $plugin->version : '';
10396
 
10397
            // hide/show link
10398
            if (in_array($protocol, $activeprotocols)) {
10399
                $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10400
                $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10401
                $displayname = "<span>$name</span>";
10402
            } else {
10403
                $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10404
                $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10405
                $displayname = "<span class=\"dimmed_text\">$name</span>";
10406
            }
10407
 
10408
            // settings link
10409
            if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10410
                $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10411
            } else {
10412
                $settings = '';
10413
            }
10414
 
10415
            // add a row to the table
10416
            $table->data[] = array($displayname, $version, $hideshow, $settings);
10417
        }
10418
        $return .= html_writer::table($table);
10419
        $return .= get_string('configwebserviceplugins', 'webservice');
10420
        $return .= $OUTPUT->box_end();
10421
 
10422
        return highlight($query, $return);
10423
    }
10424
}
10425
 
10426
/**
10427
 * Colour picker
10428
 *
10429
 * @copyright 2010 Sam Hemelryk
10430
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10431
 */
10432
class admin_setting_configcolourpicker extends admin_setting {
10433
 
10434
    /**
10435
     * Information for previewing the colour
10436
     *
10437
     * @var array|null
10438
     */
10439
    protected $previewconfig = null;
10440
 
10441
    /**
10442
     * Use default when empty.
10443
     */
10444
    protected $usedefaultwhenempty = true;
10445
 
10446
    /**
10447
     *
10448
     * @param string $name
10449
     * @param string $visiblename
10450
     * @param string $description
10451
     * @param string $defaultsetting
10452
     * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10453
     */
10454
    public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
10455
            $usedefaultwhenempty = true) {
10456
        $this->previewconfig = $previewconfig;
10457
        $this->usedefaultwhenempty = $usedefaultwhenempty;
10458
        parent::__construct($name, $visiblename, $description, $defaultsetting);
10459
        $this->set_force_ltr(true);
10460
    }
10461
 
10462
    /**
10463
     * Return the setting
10464
     *
10465
     * @return mixed returns config if successful else null
10466
     */
10467
    public function get_setting() {
10468
        return $this->config_read($this->name);
10469
    }
10470
 
10471
    /**
10472
     * Saves the setting
10473
     *
10474
     * @param string $data
10475
     * @return string error message or empty string on success
10476
     */
10477
    public function write_setting($data) {
10478
        $data = $this->validate($data);
10479
        if ($data === false) {
10480
            return  get_string('validateerror', 'admin');
10481
        }
10482
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10483
    }
10484
 
10485
    /**
10486
     * Validates the colour that was entered by the user
10487
     *
10488
     * @param string $data
10489
     * @return string|false
10490
     */
10491
    protected function validate($data) {
10492
        /**
10493
         * List of valid HTML colour names
10494
         *
10495
         * @var array
10496
         */
10497
         $colornames = array(
10498
            'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10499
            'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10500
            'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10501
            'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10502
            'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10503
            'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10504
            'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10505
            'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10506
            'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10507
            'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10508
            'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10509
            'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10510
            'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10511
            'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10512
            'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10513
            'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10514
            'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10515
            'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10516
            'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10517
            'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10518
            'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10519
            'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10520
            'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10521
            'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10522
            'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10523
            'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10524
            'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10525
            'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10526
            'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10527
            'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10528
            'whitesmoke', 'yellow', 'yellowgreen'
10529
        );
10530
 
10531
        if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10532
            if (strpos($data, '#')!==0) {
10533
                $data = '#'.$data;
10534
            }
10535
            return $data;
10536
        } else if (in_array(strtolower($data), $colornames)) {
10537
            return $data;
10538
        } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10539
            return $data;
10540
        } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10541
            return $data;
10542
        } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10543
            return $data;
10544
        } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10545
            return $data;
10546
        } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10547
            return $data;
10548
        } else if (empty($data)) {
10549
            if ($this->usedefaultwhenempty){
10550
                return $this->defaultsetting;
10551
            } else {
10552
                return '';
10553
            }
10554
        } else {
10555
            return false;
10556
        }
10557
    }
10558
 
10559
    /**
10560
     * Generates the HTML for the setting
10561
     *
10562
     * @global moodle_page $PAGE
10563
     * @global core_renderer $OUTPUT
10564
     * @param string $data
10565
     * @param string $query
10566
     */
10567
    public function output_html($data, $query = '') {
10568
        global $PAGE, $OUTPUT;
10569
 
10570
        $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10571
        $context = (object) [
10572
            'id' => $this->get_id(),
10573
            'name' => $this->get_full_name(),
10574
            'value' => $data,
10575
            'icon' => $icon->export_for_template($OUTPUT),
10576
            'haspreviewconfig' => !empty($this->previewconfig),
10577
            'forceltr' => $this->get_force_ltr(),
10578
            'readonly' => $this->is_readonly(),
10579
        ];
10580
 
10581
        $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10582
        $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10583
 
10584
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10585
            $this->get_defaultsetting(), $query);
10586
    }
10587
 
10588
}
10589
 
10590
 
10591
/**
10592
 * Class used for uploading of one file into file storage,
10593
 * the file name is stored in config table.
10594
 *
10595
 * Please note you need to implement your own '_pluginfile' callback function,
10596
 * this setting only stores the file, it does not deal with file serving.
10597
 *
10598
 * @copyright 2013 Petr Skoda {@link http://skodak.org}
10599
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10600
 */
10601
class admin_setting_configstoredfile extends admin_setting {
10602
    /** @var array file area options - should be one file only */
10603
    protected $options;
10604
    /** @var string name of the file area */
10605
    protected $filearea;
10606
    /** @var int intemid */
10607
    protected $itemid;
10608
    /** @var string used for detection of changes */
10609
    protected $oldhashes;
10610
 
10611
    /**
10612
     * Create new stored file setting.
10613
     *
10614
     * @param string $name low level setting name
10615
     * @param string $visiblename human readable setting name
10616
     * @param string $description description of setting
10617
     * @param mixed $filearea file area for file storage
10618
     * @param int $itemid itemid for file storage
10619
     * @param array $options file area options
10620
     */
10621
    public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
10622
        parent::__construct($name, $visiblename, $description, '');
10623
        $this->filearea = $filearea;
10624
        $this->itemid   = $itemid;
10625
        $this->options  = (array)$options;
10626
        $this->customcontrol = true;
10627
    }
10628
 
10629
    /**
10630
     * Applies defaults and returns all options.
10631
     * @return array
10632
     */
10633
    protected function get_options() {
10634
        global $CFG;
10635
 
10636
        require_once("$CFG->libdir/filelib.php");
10637
        require_once("$CFG->dirroot/repository/lib.php");
10638
        $defaults = array(
10639
            'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10640
            'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10641
            'context' => context_system::instance());
10642
        foreach($this->options as $k => $v) {
10643
            $defaults[$k] = $v;
10644
        }
10645
 
10646
        return $defaults;
10647
    }
10648
 
10649
    public function get_setting() {
10650
        return $this->config_read($this->name);
10651
    }
10652
 
10653
    public function write_setting($data) {
10654
        global $USER;
10655
 
10656
        // Let's not deal with validation here, this is for admins only.
10657
        $current = $this->get_setting();
10658
        if (empty($data) && ($current === null || $current === '')) {
10659
            // This will be the case when applying default settings (installation).
10660
            return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10661
        } else if (!is_number($data)) {
10662
            // Draft item id is expected here!
10663
            return get_string('errorsetting', 'admin');
10664
        }
10665
 
10666
        $options = $this->get_options();
10667
        $fs = get_file_storage();
10668
        $component = is_null($this->plugin) ? 'core' : $this->plugin;
10669
 
10670
        $this->oldhashes = null;
10671
        if ($current) {
10672
            $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10673
            if ($file = $fs->get_file_by_hash($hash)) {
10674
                $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10675
            }
10676
            unset($file);
10677
        }
10678
 
10679
        if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10680
            // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10681
            // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10682
            // with an error because the draft area does not exist, as he did not use it.
10683
            $usercontext = context_user::instance($USER->id);
10684
            if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10685
                return get_string('errorsetting', 'admin');
10686
            }
10687
        }
10688
 
10689
        file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10690
        $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10691
 
10692
        $filepath = '';
10693
        if ($files) {
10694
            /** @var stored_file $file */
10695
            $file = reset($files);
10696
            $filepath = $file->get_filepath().$file->get_filename();
10697
        }
10698
 
10699
        return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10700
    }
10701
 
10702
    public function post_write_settings($original) {
10703
        $options = $this->get_options();
10704
        $fs = get_file_storage();
10705
        $component = is_null($this->plugin) ? 'core' : $this->plugin;
10706
 
10707
        $current = $this->get_setting();
10708
        $newhashes = null;
10709
        if ($current) {
10710
            $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10711
            if ($file = $fs->get_file_by_hash($hash)) {
10712
                $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10713
            }
10714
            unset($file);
10715
        }
10716
 
10717
        if ($this->oldhashes === $newhashes) {
10718
            $this->oldhashes = null;
10719
            return false;
10720
        }
10721
        $this->oldhashes = null;
10722
 
10723
        $callbackfunction = $this->updatedcallback;
10724
        if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10725
            $callbackfunction($this->get_full_name());
10726
        }
10727
        return true;
10728
    }
10729
 
10730
    public function output_html($data, $query = '') {
10731
        global $CFG;
10732
 
10733
        $options = $this->get_options();
10734
        $id = $this->get_id();
10735
        $elname = $this->get_full_name();
10736
        $draftitemid = file_get_submitted_draft_itemid($elname);
10737
        $component = is_null($this->plugin) ? 'core' : $this->plugin;
10738
        file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10739
 
10740
        // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10741
        require_once("$CFG->dirroot/lib/form/filemanager.php");
10742
 
10743
        $fmoptions = new stdClass();
10744
        $fmoptions->mainfile       = $options['mainfile'];
10745
        $fmoptions->maxbytes       = $options['maxbytes'];
10746
        $fmoptions->maxfiles       = $options['maxfiles'];
10747
        $fmoptions->subdirs        = $options['subdirs'];
10748
        $fmoptions->accepted_types = $options['accepted_types'];
10749
        $fmoptions->return_types   = $options['return_types'];
10750
        $fmoptions->context        = $options['context'];
10751
        $fmoptions->areamaxbytes   = $options['areamaxbytes'];
10752
 
10753
        $fm = new MoodleQuickForm_filemanager($elname, $this->visiblename, ['id' => $id], $fmoptions);
10754
        $fm->setValue($draftitemid);
10755
 
10756
        return format_admin_setting($this, $this->visiblename,
10757
            '<div class="form-filemanager" data-fieldtype="filemanager">' . $fm->toHtml() . '</div>',
10758
            $this->description, true, '', '', $query);
10759
    }
10760
}
10761
 
10762
 
10763
/**
10764
 * Administration interface for user specified regular expressions for device detection.
10765
 *
10766
 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10767
 * @todo Final deprecation on Moodle 4.7 MDL-79052
10768
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10769
 */
10770
class admin_setting_devicedetectregex extends admin_setting {
10771
 
10772
    /**
10773
     * Calls parent::__construct with specific args
10774
     *
10775
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10776
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10777
     * @param string $name
10778
     * @param string $visiblename
10779
     * @param string $description
10780
     * @param mixed $defaultsetting
10781
     */
10782
    public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10783
        debugging(
10784
            __FUNCTION__ . '() is deprecated.' .
10785
                'All functions associated with devicedetectregex theme setting are being removed.',
10786
            DEBUG_DEVELOPER
10787
        );
10788
        global $CFG;
10789
        parent::__construct($name, $visiblename, $description, $defaultsetting);
10790
    }
10791
 
10792
    /**
10793
     * Return the current setting(s)
10794
     *
10795
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10796
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10797
     * @return ?array Current settings array
10798
     */
10799
    public function get_setting() {
10800
        debugging(
10801
            __FUNCTION__ . '() is deprecated.' .
10802
                'All functions associated with devicedetectregex theme setting are being removed.',
10803
            DEBUG_DEVELOPER
10804
        );
10805
        global $CFG;
10806
 
10807
        $config = $this->config_read($this->name);
10808
        if (is_null($config)) {
10809
            return null;
10810
        }
10811
 
10812
        return $this->prepare_form_data($config);
10813
    }
10814
 
10815
    /**
10816
     * Save selected settings
10817
     *
10818
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10819
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10820
     * @param array $data Array of settings to save
10821
     * @return string error message or empty string on success
10822
     */
10823
    public function write_setting($data) {
10824
        debugging(
10825
            __FUNCTION__ . '() is deprecated.' .
10826
                'All functions associated with devicedetectregex theme setting are being removed.',
10827
            DEBUG_DEVELOPER
10828
        );
10829
        if (empty($data)) {
10830
            $data = array();
10831
        }
10832
 
10833
        if ($this->config_write($this->name, $this->process_form_data($data))) {
10834
            return ''; // success
10835
        } else {
10836
            return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10837
        }
10838
    }
10839
 
10840
    /**
10841
     * Return XHTML field(s) for regexes
10842
     *
10843
     * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed.
10844
     * @todo Final deprecation on Moodle 4.7 MDL-79052
10845
     * @param array $data Array of options to set in HTML
10846
     * @return string XHTML string for the fields and wrapping div(s)
10847
     */
10848
    public function output_html($data, $query='') {
10849
        debugging(
10850
            __FUNCTION__ . '() is deprecated.' .
10851
                'All functions associated with devicedetectregex theme setting are being removed.',
10852
            DEBUG_DEVELOPER
10853
        );
10854
        global $OUTPUT;
10855
 
10856
        $context = (object) [
10857
            'expressions' => [],
10858
            'name' => $this->get_full_name()
10859
        ];
10860
 
10861
        if (empty($data)) {
10862
            $looplimit = 1;
10863
        } else {
10864
            $looplimit = (count($data)/2)+1;
10865
        }
10866
 
10867
        for ($i=0; $i<$looplimit; $i++) {
10868
 
10869
            $expressionname = 'expression'.$i;
10870
 
10871
            if (!empty($data[$expressionname])){
10872
                $expression = $data[$expressionname];
10873
            } else {
10874
                $expression = '';
10875
            }
10876
 
10877
            $valuename = 'value'.$i;
10878
 
10879
            if (!empty($data[$valuename])){
10880
                $value = $data[$valuename];
10881
            } else {
10882
                $value= '';
10883
            }
10884
 
10885
            $context->expressions[] = [
10886
                'index' => $i,
10887
                'expression' => $expression,
10888
                'value' => $value
10889
            ];
10890
        }
10891
 
10892
        $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10893
 
10894
        return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10895
    }
10896
 
10897
    /**
10898
     * Converts the string of regexes
10899
     *
10900
     * @see self::process_form_data()
10901
     * @param $regexes string of regexes
10902
     * @return array of form fields and their values
10903
     */
10904
    protected function prepare_form_data($regexes) {
10905
 
10906
        $regexes = json_decode($regexes);
10907
 
10908
        $form = array();
10909
 
10910
        $i = 0;
10911
 
10912
        foreach ($regexes as $value => $regex) {
10913
            $expressionname  = 'expression'.$i;
10914
            $valuename = 'value'.$i;
10915
 
10916
            $form[$expressionname] = $regex;
10917
            $form[$valuename] = $value;
10918
            $i++;
10919
        }
10920
 
10921
        return $form;
10922
    }
10923
 
10924
    /**
10925
     * Converts the data from admin settings form into a string of regexes
10926
     *
10927
     * @see self::prepare_form_data()
10928
     * @param array $data array of admin form fields and values
10929
     * @return false|string of regexes
10930
     */
10931
    protected function process_form_data(array $form) {
10932
 
10933
        $count = count($form); // number of form field values
10934
 
10935
        if ($count % 2) {
10936
            // we must get five fields per expression
10937
            return false;
10938
        }
10939
 
10940
        $regexes = array();
10941
        for ($i = 0; $i < $count / 2; $i++) {
10942
            $expressionname  = "expression".$i;
10943
            $valuename       = "value".$i;
10944
 
10945
            $expression = trim($form['expression'.$i]);
10946
            $value      = trim($form['value'.$i]);
10947
 
10948
            if (empty($expression)){
10949
                continue;
10950
            }
10951
 
10952
            $regexes[$value] = $expression;
10953
        }
10954
 
10955
        $regexes = json_encode($regexes);
10956
 
10957
        return $regexes;
10958
    }
10959
 
10960
}
10961
 
10962
/**
10963
 * Multiselect for current modules
10964
 *
10965
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10966
 */
10967
class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10968
    private $excludesystem;
10969
 
10970
    /**
10971
     * Calls parent::__construct - note array $choices is not required
10972
     *
10973
     * @param string $name setting name
10974
     * @param string $visiblename localised setting name
10975
     * @param string $description setting description
10976
     * @param array $defaultsetting a plain array of default module ids
10977
     * @param bool $excludesystem If true, excludes modules with 'system' archetype
10978
     */
10979
    public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10980
            $excludesystem = true) {
10981
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10982
        $this->excludesystem = $excludesystem;
10983
    }
10984
 
10985
    /**
10986
     * Loads an array of current module choices
10987
     *
10988
     * @return bool always return true
10989
     */
10990
    public function load_choices() {
10991
        if (is_array($this->choices)) {
10992
            return true;
10993
        }
10994
        $this->choices = array();
10995
 
10996
        global $CFG, $DB;
10997
        $records = $DB->get_records('modules', array('visible'=>1), 'name');
10998
        foreach ($records as $record) {
10999
            // Exclude modules if the code doesn't exist
11000
            if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
11001
                // Also exclude system modules (if specified)
11002
                if (!($this->excludesystem &&
11003
                        plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
11004
                        MOD_ARCHETYPE_SYSTEM)) {
11005
                    $this->choices[$record->id] = $record->name;
11006
                }
11007
            }
11008
        }
11009
        return true;
11010
    }
11011
}
11012
 
11013
/**
11014
 * Admin setting to show if a php extension is enabled or not.
11015
 *
11016
 * @copyright 2013 Damyon Wiese
11017
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11018
 */
11019
class admin_setting_php_extension_enabled extends admin_setting {
11020
 
11021
    /** @var string The name of the extension to check for */
11022
    private $extension;
11023
 
11024
    /**
11025
     * Calls parent::__construct with specific arguments
11026
     */
11027
    public function __construct($name, $visiblename, $description, $extension) {
11028
        $this->extension = $extension;
11029
        $this->nosave = true;
11030
        parent::__construct($name, $visiblename, $description, '');
11031
    }
11032
 
11033
    /**
11034
     * Always returns true, does nothing
11035
     *
11036
     * @return true
11037
     */
11038
    public function get_setting() {
11039
        return true;
11040
    }
11041
 
11042
    /**
11043
     * Always returns true, does nothing
11044
     *
11045
     * @return true
11046
     */
11047
    public function get_defaultsetting() {
11048
        return true;
11049
    }
11050
 
11051
    /**
11052
     * Always returns '', does not write anything
11053
     *
11054
     * @return string Always returns ''
11055
     */
11056
    public function write_setting($data) {
11057
        // Do not write any setting.
11058
        return '';
11059
    }
11060
 
11061
    /**
11062
     * Outputs the html for this setting.
11063
     * @return string Returns an XHTML string
11064
     */
11065
    public function output_html($data, $query='') {
11066
        global $OUTPUT;
11067
 
11068
        $o = '';
11069
        if (!extension_loaded($this->extension)) {
11070
            $warning = $OUTPUT->pix_icon('i/warning', '') . ' ' . $this->description;
11071
 
11072
            $o .= format_admin_setting($this, $this->visiblename, $warning);
11073
        }
11074
        return $o;
11075
    }
11076
}
11077
 
11078
/**
11079
 * Server timezone setting.
11080
 *
11081
 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
11082
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11083
 * @author    Petr Skoda <petr.skoda@totaralms.com>
11084
 */
11085
class admin_setting_servertimezone extends admin_setting_configselect {
11086
    /**
11087
     * Constructor.
11088
     */
11089
    public function __construct() {
11090
        $default = core_date::get_default_php_timezone();
11091
        if ($default === 'UTC') {
11092
            // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
11093
            $default = 'Europe/London';
11094
        }
11095
 
11096
        parent::__construct('timezone',
11097
            new lang_string('timezone', 'core_admin'),
11098
            new lang_string('configtimezone', 'core_admin'), $default, null);
11099
    }
11100
 
11101
    /**
11102
     * Lazy load timezone options.
11103
     * @return bool true if loaded, false if error
11104
     */
11105
    public function load_choices() {
11106
        global $CFG;
11107
        if (is_array($this->choices)) {
11108
            return true;
11109
        }
11110
 
11111
        $current = isset($CFG->timezone) ? $CFG->timezone : null;
11112
        $this->choices = core_date::get_list_of_timezones($current, false);
11113
        if ($current == 99) {
11114
            // Do not show 99 unless it is current value, we want to get rid of it over time.
11115
            $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
11116
                core_date::get_default_php_timezone());
11117
        }
11118
 
11119
        return true;
11120
    }
11121
}
11122
 
11123
/**
11124
 * Forced user timezone setting.
11125
 *
11126
 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
11127
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11128
 * @author    Petr Skoda <petr.skoda@totaralms.com>
11129
 */
11130
class admin_setting_forcetimezone extends admin_setting_configselect {
11131
    /**
11132
     * Constructor.
11133
     */
11134
    public function __construct() {
11135
        parent::__construct('forcetimezone',
11136
            new lang_string('forcetimezone', 'core_admin'),
11137
            new lang_string('helpforcetimezone', 'core_admin'), '99', null);
11138
    }
11139
 
11140
    /**
11141
     * Lazy load timezone options.
11142
     * @return bool true if loaded, false if error
11143
     */
11144
    public function load_choices() {
11145
        global $CFG;
11146
        if (is_array($this->choices)) {
11147
            return true;
11148
        }
11149
 
11150
        $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
11151
        $this->choices = core_date::get_list_of_timezones($current, true);
11152
        $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
11153
 
11154
        return true;
11155
    }
11156
}
11157
 
11158
 
11159
/**
11160
 * Search setup steps info.
11161
 *
11162
 * @package core
11163
 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
11164
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11165
 */
11166
class admin_setting_searchsetupinfo extends admin_setting {
11167
 
11168
    /**
11169
     * Calls parent::__construct with specific arguments
11170
     */
11171
    public function __construct() {
11172
        $this->nosave = true;
11173
        parent::__construct('searchsetupinfo', '', '', '');
11174
    }
11175
 
11176
    /**
11177
     * Always returns true, does nothing
11178
     *
11179
     * @return true
11180
     */
11181
    public function get_setting() {
11182
        return true;
11183
    }
11184
 
11185
    /**
11186
     * Always returns true, does nothing
11187
     *
11188
     * @return true
11189
     */
11190
    public function get_defaultsetting() {
11191
        return true;
11192
    }
11193
 
11194
    /**
11195
     * Always returns '', does not write anything
11196
     *
11197
     * @param array $data
11198
     * @return string Always returns ''
11199
     */
11200
    public function write_setting($data) {
11201
        // Do not write any setting.
11202
        return '';
11203
    }
11204
 
11205
    /**
11206
     * Builds the HTML to display the control
11207
     *
11208
     * @param string $data Unused
11209
     * @param string $query
11210
     * @return string
11211
     */
11212
    public function output_html($data, $query='') {
11213
        global $CFG, $OUTPUT, $ADMIN;
11214
 
11215
        $return = '';
11216
        $brtag = html_writer::empty_tag('br');
11217
 
11218
        $searchareas = \core_search\manager::get_search_areas_list();
11219
        $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
11220
        $anyindexed = false;
11221
        foreach ($searchareas as $areaid => $searcharea) {
11222
            list($componentname, $varname) = $searcharea->get_config_var_name();
11223
            if (get_config($componentname, $varname . '_indexingstart')) {
11224
                $anyindexed = true;
11225
                break;
11226
            }
11227
        }
11228
 
11229
        $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
11230
 
11231
        $table = new html_table();
11232
        $table->head = array(get_string('step', 'search'), get_string('status'));
11233
        $table->colclasses = array('leftalign step', 'leftalign status');
11234
        $table->id = 'searchsetup';
11235
        $table->attributes['class'] = 'admintable generaltable';
11236
        $table->data = array();
11237
 
11238
        $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
11239
 
11240
        // Select a search engine.
11241
        $row = array();
11242
        $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
11243
        $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
11244
                        array('href' => $url));
11245
 
11246
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11247
        if (!empty($CFG->searchengine)) {
11248
            $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
11249
                array('class' => 'badge bg-success text-white'));
11250
 
11251
        }
11252
        $row[1] = $status;
11253
        $table->data[] = $row;
11254
 
11255
        // Available areas.
11256
        $row = array();
11257
        $url = new moodle_url('/admin/searchareas.php');
11258
        $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
11259
                        array('href' => $url));
11260
 
11261
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11262
        if ($anyenabled) {
11263
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11264
 
11265
        }
11266
        $row[1] = $status;
11267
        $table->data[] = $row;
11268
 
11269
        // Setup search engine.
11270
        $row = array();
11271
        if (empty($CFG->searchengine)) {
11272
            $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
11273
            $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11274
        } else {
11275
            if ($ADMIN->locate('search' . $CFG->searchengine)) {
11276
                $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
11277
                $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
11278
            } else {
11279
                $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
11280
            }
11281
 
11282
            // Check the engine status.
11283
            $searchengine = \core_search\manager::search_engine_instance();
11284
            try {
11285
                $serverstatus = $searchengine->is_server_ready();
11286
            } catch (\moodle_exception $e) {
11287
                $serverstatus = $e->getMessage();
11288
            }
11289
            if ($serverstatus === true) {
11290
                $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11291
            } else {
11292
                $status = html_writer::tag('span', $serverstatus, array('class' => 'badge bg-danger text-white'));
11293
            }
11294
            $row[1] = $status;
11295
        }
11296
        $table->data[] = $row;
11297
 
11298
        // Indexed data.
11299
        $row = array();
11300
        $url = new moodle_url('/admin/searchareas.php');
11301
        $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11302
        if ($anyindexed) {
11303
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11304
        } else {
11305
            $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11306
        }
11307
        $row[1] = $status;
11308
        $table->data[] = $row;
11309
 
11310
        // Enable global search.
11311
        $row = array();
11312
        $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11313
        $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11314
                        array('href' => $url));
11315
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11316
        if (\core_search\manager::is_global_search_enabled()) {
11317
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11318
        }
11319
        $row[1] = $status;
11320
        $table->data[] = $row;
11321
 
11322
        // Replace front page search.
11323
        $row = array();
11324
        $url = new moodle_url("/admin/search.php?query=searchincludeallcourses");
11325
        $row[0] = '6. ' . html_writer::tag('a', get_string('replacefrontsearch', 'admin'),
11326
                                           array('href' => $url));
11327
        $status = html_writer::tag('span', get_string('no'), array('class' => 'badge bg-danger text-white'));
11328
        if (\core_search\manager::can_replace_course_search()) {
11329
            $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge bg-success text-white'));
11330
        }
11331
        $row[1] = $status;
11332
        $table->data[] = $row;
11333
 
11334
        $return .= html_writer::table($table);
11335
 
11336
        return highlight($query, $return);
11337
    }
11338
 
11339
}
11340
 
11341
/**
11342
 * Used to validate the contents of SCSS code and ensuring they are parsable.
11343
 *
11344
 * It does not attempt to detect undefined SCSS variables because it is designed
11345
 * to be used without knowledge of other config/scss included.
11346
 *
11347
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11348
 * @copyright 2016 Dan Poltawski <dan@moodle.com>
11349
 */
11350
class admin_setting_scsscode extends admin_setting_configtextarea {
11351
 
11352
    /**
11353
     * Validate the contents of the SCSS to ensure its parsable. Does not
11354
     * attempt to detect undefined scss variables.
11355
     *
11356
     * @param string $data The scss code from text field.
11357
     * @return mixed bool true for success or string:error on failure.
11358
     */
11359
    public function validate($data) {
11360
        if (empty($data)) {
11361
            return true;
11362
        }
11363
 
11364
        $scss = new core_scss();
11365
        try {
11366
            $scss->compile($data);
11367
        } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
11368
            return get_string('scssinvalid', 'admin', $e->getMessage());
11369
        } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
11370
            // Silently ignore this - it could be a scss variable defined from somewhere
11371
            // else which we are not examining here.
11372
            return true;
11373
        }
11374
 
11375
        return true;
11376
    }
11377
}
11378
 
11379
 
11380
/**
11381
 * Administration setting to define a list of file types.
11382
 *
11383
 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
11384
 * @copyright 2017 David Mudrák <david@moodle.com>
11385
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11386
 */
11387
class admin_setting_filetypes extends admin_setting_configtext {
11388
 
11389
    /** @var array Allow selection from these file types only. */
11390
    protected $onlytypes = [];
11391
 
11392
    /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
11393
    protected $allowall = true;
11394
 
11395
    /** @var core_form\filetypes_util instance to use as a helper. */
11396
    protected $util = null;
11397
 
11398
    /**
11399
     * Constructor.
11400
     *
11401
     * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
11402
     * @param string $visiblename Localised label of the setting
11403
     * @param string $description Localised description of the setting
11404
     * @param string $defaultsetting Default setting value.
11405
     * @param array $options Setting widget options, an array with optional keys:
11406
     *   'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
11407
     *   'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
11408
     */
11409
    public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
11410
 
11411
        parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
11412
 
11413
        if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
11414
            $this->onlytypes = $options['onlytypes'];
11415
        }
11416
 
11417
        if (!$this->onlytypes && array_key_exists('allowall', $options)) {
11418
            $this->allowall = (bool)$options['allowall'];
11419
        }
11420
 
11421
        $this->util = new \core_form\filetypes_util();
11422
    }
11423
 
11424
    /**
11425
     * Normalize the user's input and write it to the database as comma separated list.
11426
     *
11427
     * Comma separated list as a text representation of the array was chosen to
11428
     * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
11429
     *
11430
     * @param string $data Value submitted by the admin.
11431
     * @return string Epty string if all good, error message otherwise.
11432
     */
11433
    public function write_setting($data) {
11434
        return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
11435
    }
11436
 
11437
    /**
11438
     * Validate data before storage
11439
     *
11440
     * @param string $data The setting values provided by the admin
11441
     * @return bool|string True if ok, the string if error found
11442
     */
11443
    public function validate($data) {
11444
        $parentcheck = parent::validate($data);
11445
        if ($parentcheck !== true) {
11446
            return $parentcheck;
11447
        }
11448
 
11449
        // Check for unknown file types.
11450
        if ($unknown = $this->util->get_unknown_file_types($data)) {
11451
            return get_string('filetypesunknown', 'core_form', implode(', ', $unknown));
11452
        }
11453
 
11454
        // Check for disallowed file types.
11455
        if ($notlisted = $this->util->get_not_listed($data, $this->onlytypes)) {
11456
            return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted));
11457
        }
11458
 
11459
        return true;
11460
    }
11461
 
11462
    /**
11463
     * Return an HTML string for the setting element.
11464
     *
11465
     * @param string $data The current setting value
11466
     * @param string $query Admin search query to be highlighted
11467
     * @return string HTML to be displayed
11468
     */
11469
    public function output_html($data, $query='') {
11470
        global $OUTPUT, $PAGE;
11471
 
11472
        $default = $this->get_defaultsetting();
11473
        $context = (object) [
11474
            'id' => $this->get_id(),
11475
            'name' => $this->get_full_name(),
11476
            'value' => $data,
11477
            'descriptions' => $this->util->describe_file_types($data),
11478
        ];
11479
        $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11480
 
11481
        $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11482
            $this->get_id(),
11483
            $this->visiblename->out(),
11484
            $this->onlytypes,
11485
            $this->allowall,
11486
        ]);
11487
 
11488
        return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11489
    }
11490
 
11491
    /**
11492
     * Should the values be always displayed in LTR mode?
11493
     *
11494
     * We always return true here because these values are not RTL compatible.
11495
     *
11496
     * @return bool True because these values are not RTL compatible.
11497
     */
11498
    public function get_force_ltr() {
11499
        return true;
11500
    }
11501
}
11502
 
11503
/**
11504
 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11505
 *
11506
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11507
 * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11508
 */
11509
class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11510
 
11511
    /**
11512
     * Constructor.
11513
     *
11514
     * @param string $name
11515
     * @param string $visiblename
11516
     * @param string $description
11517
     * @param mixed $defaultsetting string or array
11518
     * @param mixed $paramtype
11519
     * @param string $cols
11520
     * @param string $rows
11521
     */
11522
    public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11523
                                $cols = '60', $rows = '8') {
11524
        parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11525
        // Pre-set force LTR to false.
11526
        $this->set_force_ltr(false);
11527
    }
11528
 
11529
    /**
11530
     * Validate the content and format of the age of digital consent map to ensure it is parsable.
11531
     *
11532
     * @param string $data The age of digital consent map from text field.
11533
     * @return mixed bool true for success or string:error on failure.
11534
     */
11535
    public function validate($data) {
11536
        if (empty($data)) {
11537
            return true;
11538
        }
11539
 
11540
        try {
11541
            \core_auth\digital_consent::parse_age_digital_consent_map($data);
11542
        } catch (\moodle_exception $e) {
11543
            return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11544
        }
11545
 
11546
        return true;
11547
    }
11548
}
11549
 
11550
/**
11551
 * Selection of plugins that can work as site policy handlers
11552
 *
11553
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11554
 * @copyright 2018 Marina Glancy
11555
 */
11556
class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11557
 
11558
    /**
11559
     * Constructor
11560
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11561
     *        for ones in config_plugins.
11562
     * @param string $visiblename localised
11563
     * @param string $description long localised info
11564
     * @param string $defaultsetting
11565
     */
11566
    public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11567
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11568
    }
11569
 
11570
    /**
11571
     * Lazy-load the available choices for the select box
11572
     */
11573
    public function load_choices() {
11574
        if (during_initial_install()) {
11575
            return false;
11576
        }
11577
        if (is_array($this->choices)) {
11578
            return true;
11579
        }
11580
 
11581
        $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11582
        $manager = new \core_privacy\local\sitepolicy\manager();
11583
        $plugins = $manager->get_all_handlers();
11584
        foreach ($plugins as $pname => $unused) {
11585
            $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11586
                ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11587
        }
11588
 
11589
        return true;
11590
    }
11591
}
11592
 
11593
/**
11594
 * Used to validate theme presets code and ensuring they compile well.
11595
 *
11596
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11597
 * @copyright 2019 Bas Brands <bas@moodle.com>
11598
 */
11599
class admin_setting_configthemepreset extends admin_setting_configselect {
11600
 
11601
    /** @var string The name of the theme to check for */
11602
    private $themename;
11603
 
11604
    /**
11605
     * Constructor
11606
     * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11607
     * or 'myplugin/mysetting' for ones in config_plugins.
11608
     * @param string $visiblename localised
11609
     * @param string $description long localised info
11610
     * @param string|int $defaultsetting
11611
     * @param array $choices array of $value=>$label for each selection
11612
     * @param string $themename name of theme to check presets for.
11613
     */
11614
    public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11615
        $this->themename = $themename;
11616
        parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11617
    }
11618
 
11619
    /**
11620
     * Write settings if validated
11621
     *
11622
     * @param string $data
11623
     * @return string
11624
     */
11625
    public function write_setting($data) {
11626
        $validated = $this->validate($data);
11627
        if ($validated !== true) {
11628
            return $validated;
11629
        }
11630
        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11631
    }
11632
 
11633
    /**
11634
     * Validate the preset file to ensure its parsable.
11635
     *
11636
     * @param string $data The preset file chosen.
11637
     * @return mixed bool true for success or string:error on failure.
11638
     */
11639
    public function validate($data) {
11640
 
11641
        if (in_array($data, ['default.scss', 'plain.scss'])) {
11642
            return true;
11643
        }
11644
 
11645
        $fs = get_file_storage();
11646
        $theme = theme_config::load($this->themename);
11647
        $context = context_system::instance();
11648
 
11649
        // If the preset has not changed there is no need to validate it.
11650
        if ($theme->settings->preset == $data) {
11651
            return true;
11652
        }
11653
 
11654
        if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11655
            // This operation uses a lot of resources.
11656
            raise_memory_limit(MEMORY_EXTRA);
11657
            core_php_time_limit::raise(300);
11658
 
11659
            // TODO: MDL-62757 When changing anything in this method please do not forget to check
11660
            // if the get_css_content_from_scss() method in class theme_config needs updating too.
11661
 
11662
            $compiler = new core_scss();
11663
            $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11664
            $compiler->append_raw_scss($presetfile->get_content());
11665
            if ($scssproperties = $theme->get_scss_property()) {
11666
                $compiler->setImportPaths($scssproperties[0]);
11667
            }
11668
            $compiler->append_raw_scss($theme->get_extra_scss_code());
11669
 
11670
            try {
11671
                $compiler->to_css();
11672
            } catch (Exception $e) {
11673
                return get_string('invalidthemepreset', 'admin', $e->getMessage());
11674
            }
11675
 
11676
            // Try to save memory.
11677
            $compiler = null;
11678
            unset($compiler);
11679
        }
11680
 
11681
        return true;
11682
    }
11683
}
11684
 
11685
/**
11686
 * Selection of plugins that can work as H5P libraries handlers
11687
 *
11688
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11689
 * @copyright 2020 Sara Arjona <sara@moodle.com>
11690
 */
11691
class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11692
 
11693
    /**
11694
     * Constructor
11695
     * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11696
     *        for ones in config_plugins.
11697
     * @param string $visiblename localised
11698
     * @param string $description long localised info
11699
     * @param string $defaultsetting
11700
     */
11701
    public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11702
        parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11703
    }
11704
 
11705
    /**
11706
     * Lazy-load the available choices for the select box
11707
     */
11708
    public function load_choices() {
11709
        if (during_initial_install()) {
11710
            return false;
11711
        }
11712
        if (is_array($this->choices)) {
11713
            return true;
11714
        }
11715
 
11716
        $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11717
        foreach ($this->choices as $name => $class) {
11718
            $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11719
                ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11720
        }
11721
 
11722
        return true;
11723
    }
11724
}
11725
 
11726
/**
11727
 * Displays the result of a check via ajax.
11728
 *
11729
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11730
 * @author Matthew Hilton <matthewhilton@catalyst-au.net>
11731
 * @copyright Catalyst IT, 2023
11732
 */
11733
class admin_setting_check extends admin_setting {
11734
 
11735
    /** @var \core\check\check $check the check to use **/
11736
    private $check;
11737
 
11738
    /** @var bool $includedetails if the details of result are included. **/
11739
    private $includedetails;
11740
 
11741
    /**
11742
     * Creates check setting.
11743
     *
11744
     * @param string $name name of setting
11745
     * @param \core\check\check $check The check linked to this setting.
11746
     * @param bool $includedetails if the details of the result are included
11747
     */
11748
    public function __construct(string $name, \core\check\check $check, bool $includedetails = false) {
11749
        $this->check = $check;
11750
        $this->includedetails = $includedetails;
11751
        $heading = $check->get_name();
11752
 
11753
        parent::__construct($name, $heading, '', '');
11754
    }
11755
 
11756
    /**
11757
     * Returns the check linked to this setting.
11758
     *
11759
     * @return \core\check\check
11760
     */
11761
    public function get_check() {
11762
        return $this->check;
11763
    }
11764
 
11765
    /**
11766
     * Returns setting (unused)
11767
     *
11768
     * @return true
11769
     */
11770
    public function get_setting() {
11771
        return true;
11772
    }
11773
 
11774
    /**
11775
     * Writes the setting (unused)
11776
     *
11777
     * @param mixed $data
11778
     */
11779
    public function write_setting($data) {
11780
        return '';
11781
    }
11782
 
11783
    /**
11784
     * Outputs the admin setting HTML to be rendered.
11785
     *
11786
     * @param mixed $data
11787
     * @param string $query
11788
     * @return string html
11789
     */
11790
    public function output_html($data, $query = '') {
11791
        global $PAGE, $OUTPUT;
11792
 
11793
        $domref = uniqid($this->check->get_ref());
11794
 
11795
        // The actual result is obtained via ajax,
11796
        // since its likely somewhat slow to obtain.
11797
        $context = [
11798
            'domselector' => '[data-check-reference="' . $domref . '"]',
11799
            'admintreeid' => $this->get_id(),
11800
            'settingname' => $this->name,
11801
            'includedetails' => $this->includedetails,
11802
        ];
11803
        $PAGE->requires->js_call_amd('core/check/check_result', 'getAndRender', $context);
11804
 
11805
        // Render a generic loading icon while waiting for ajax.
11806
        $loadingstr = get_string('checkloading', '', $this->check->get_name());
11807
        $loadingicon = $OUTPUT->pix_icon('i/loading', $loadingstr);
11808
 
11809
        // Wrap it in a notification so we reduce style changes when loading is finished.
11810
        $output = $OUTPUT->notification($loadingicon . $loadingstr, \core\output\notification::NOTIFY_INFO, false);
11811
 
11812
        // Add the action link.
11813
        $output .= $OUTPUT->render($this->check->get_action_link());
11814
 
11815
        // Wrap in a div with a reference. The JS getAndRender will replace this with the response from the webservice.
11816
        $statusdiv = \html_writer::div($output, '', ['data-check-reference' => $domref]);
11817
 
11818
        return format_admin_setting($this, $this->visiblename, '', $statusdiv);
11819
    }
11820
}
11821