Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
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 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 string */ 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 public abstract 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 public abstract 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 string $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 Checkbox for this flag is displayed in admin page */ 2200 const ENABLED = true; 2201 /** @var 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 2451 /** 2452 * Config text constructor 2453 * 2454 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2455 * @param string $visiblename localised 2456 * @param string $description long localised info 2457 * @param string $defaultsetting 2458 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex 2459 * @param int $size default field size 2460 */ 2461 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) { 2462 $this->paramtype = $paramtype; 2463 if (!is_null($size)) { 2464 $this->size = $size; 2465 } else { 2466 $this->size = ($paramtype === PARAM_INT) ? 5 : 30; 2467 } 2468 parent::__construct($name, $visiblename, $description, $defaultsetting); 2469 } 2470 2471 /** 2472 * Get whether this should be displayed in LTR mode. 2473 * 2474 * Try to guess from the PARAM type unless specifically set. 2475 */ 2476 public function get_force_ltr() { 2477 $forceltr = parent::get_force_ltr(); 2478 if ($forceltr === null) { 2479 return !is_rtl_compatible($this->paramtype); 2480 } 2481 return $forceltr; 2482 } 2483 2484 /** 2485 * Return the setting 2486 * 2487 * @return mixed returns config if successful else null 2488 */ 2489 public function get_setting() { 2490 return $this->config_read($this->name); 2491 } 2492 2493 public function write_setting($data) { 2494 if ($this->paramtype === PARAM_INT and $data === '') { 2495 // do not complain if '' used instead of 0 2496 $data = 0; 2497 } 2498 // $data is a string 2499 $validated = $this->validate($data); 2500 if ($validated !== true) { 2501 return $validated; 2502 } 2503 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 2504 } 2505 2506 /** 2507 * Validate data before storage 2508 * @param string data 2509 * @return mixed true if ok string if error found 2510 */ 2511 public function validate($data) { 2512 // allow paramtype to be a custom regex if it is the form of /pattern/ 2513 if (preg_match('#^/.*/$#', $this->paramtype)) { 2514 if (preg_match($this->paramtype, $data)) { 2515 return true; 2516 } else { 2517 return get_string('validateerror', 'admin'); 2518 } 2519 2520 } else if ($this->paramtype === PARAM_RAW) { 2521 return true; 2522 2523 } else { 2524 $cleaned = clean_param($data, $this->paramtype); 2525 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison 2526 return true; 2527 } else { 2528 return get_string('validateerror', 'admin'); 2529 } 2530 } 2531 } 2532 2533 /** 2534 * Return an XHTML string for the setting 2535 * @return string Returns an XHTML string 2536 */ 2537 public function output_html($data, $query='') { 2538 global $OUTPUT; 2539 2540 $default = $this->get_defaultsetting(); 2541 $context = (object) [ 2542 'size' => $this->size, 2543 'id' => $this->get_id(), 2544 'name' => $this->get_full_name(), 2545 'value' => $data, 2546 'forceltr' => $this->get_force_ltr(), 2547 'readonly' => $this->is_readonly(), 2548 ]; 2549 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context); 2550 2551 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 2552 } 2553 } 2554 2555 /** 2556 * Text input with a maximum length constraint. 2557 * 2558 * @copyright 2015 onwards Ankit Agarwal 2559 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2560 */ 2561 class admin_setting_configtext_with_maxlength extends admin_setting_configtext { 2562 2563 /** @var int maximum number of chars allowed. */ 2564 protected $maxlength; 2565 2566 /** 2567 * Config text constructor 2568 * 2569 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 2570 * or 'myplugin/mysetting' for ones in config_plugins. 2571 * @param string $visiblename localised 2572 * @param string $description long localised info 2573 * @param string $defaultsetting 2574 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex 2575 * @param int $size default field size 2576 * @param mixed $maxlength int maxlength allowed, 0 for infinite. 2577 */ 2578 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, 2579 $size=null, $maxlength = 0) { 2580 $this->maxlength = $maxlength; 2581 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); 2582 } 2583 2584 /** 2585 * Validate data before storage 2586 * 2587 * @param string $data data 2588 * @return mixed true if ok string if error found 2589 */ 2590 public function validate($data) { 2591 $parentvalidation = parent::validate($data); 2592 if ($parentvalidation === true) { 2593 if ($this->maxlength > 0) { 2594 // Max length check. 2595 $length = core_text::strlen($data); 2596 if ($length > $this->maxlength) { 2597 return get_string('maximumchars', 'moodle', $this->maxlength); 2598 } 2599 return true; 2600 } else { 2601 return true; // No max length check needed. 2602 } 2603 } else { 2604 return $parentvalidation; 2605 } 2606 } 2607 } 2608 2609 /** 2610 * General text area without html editor. 2611 * 2612 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2613 */ 2614 class admin_setting_configtextarea extends admin_setting_configtext { 2615 private $rows; 2616 private $cols; 2617 2618 /** 2619 * @param string $name 2620 * @param string $visiblename 2621 * @param string $description 2622 * @param mixed $defaultsetting string or array 2623 * @param mixed $paramtype 2624 * @param string $cols The number of columns to make the editor 2625 * @param string $rows The number of rows to make the editor 2626 */ 2627 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') { 2628 $this->rows = $rows; 2629 $this->cols = $cols; 2630 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype); 2631 } 2632 2633 /** 2634 * Returns an XHTML string for the editor 2635 * 2636 * @param string $data 2637 * @param string $query 2638 * @return string XHTML string for the editor 2639 */ 2640 public function output_html($data, $query='') { 2641 global $OUTPUT; 2642 2643 $default = $this->get_defaultsetting(); 2644 $defaultinfo = $default; 2645 if (!is_null($default) and $default !== '') { 2646 $defaultinfo = "\n".$default; 2647 } 2648 2649 $context = (object) [ 2650 'cols' => $this->cols, 2651 'rows' => $this->rows, 2652 'id' => $this->get_id(), 2653 'name' => $this->get_full_name(), 2654 'value' => $data, 2655 'forceltr' => $this->get_force_ltr(), 2656 'readonly' => $this->is_readonly(), 2657 ]; 2658 $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context); 2659 2660 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 2661 } 2662 } 2663 2664 /** 2665 * General text area with html editor. 2666 */ 2667 class admin_setting_confightmleditor extends admin_setting_configtextarea { 2668 2669 /** 2670 * @param string $name 2671 * @param string $visiblename 2672 * @param string $description 2673 * @param mixed $defaultsetting string or array 2674 * @param mixed $paramtype 2675 */ 2676 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') { 2677 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows); 2678 $this->set_force_ltr(false); 2679 editors_head_setup(); 2680 } 2681 2682 /** 2683 * Returns an XHTML string for the editor 2684 * 2685 * @param string $data 2686 * @param string $query 2687 * @return string XHTML string for the editor 2688 */ 2689 public function output_html($data, $query='') { 2690 $editor = editors_get_preferred_editor(FORMAT_HTML); 2691 $editor->set_text($data); 2692 $editor->use_editor($this->get_id(), array('noclean'=>true)); 2693 return parent::output_html($data, $query); 2694 } 2695 2696 /** 2697 * Checks if data has empty html. 2698 * 2699 * @param string $data 2700 * @return string Empty when no errors. 2701 */ 2702 public function write_setting($data) { 2703 if (trim(html_to_text($data)) === '') { 2704 $data = ''; 2705 } 2706 return parent::write_setting($data); 2707 } 2708 } 2709 2710 2711 /** 2712 * Password field, allows unmasking of password 2713 * 2714 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2715 */ 2716 class admin_setting_configpasswordunmask extends admin_setting_configtext { 2717 2718 /** 2719 * Constructor 2720 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2721 * @param string $visiblename localised 2722 * @param string $description long localised info 2723 * @param string $defaultsetting default password 2724 */ 2725 public function __construct($name, $visiblename, $description, $defaultsetting) { 2726 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30); 2727 } 2728 2729 /** 2730 * Log config changes if necessary. 2731 * @param string $name 2732 * @param string $oldvalue 2733 * @param string $value 2734 */ 2735 protected function add_to_config_log($name, $oldvalue, $value) { 2736 if ($value !== '') { 2737 $value = '********'; 2738 } 2739 if ($oldvalue !== '' and $oldvalue !== null) { 2740 $oldvalue = '********'; 2741 } 2742 parent::add_to_config_log($name, $oldvalue, $value); 2743 } 2744 2745 /** 2746 * Returns HTML for the field. 2747 * 2748 * @param string $data Value for the field 2749 * @param string $query Passed as final argument for format_admin_setting 2750 * @return string Rendered HTML 2751 */ 2752 public function output_html($data, $query='') { 2753 global $OUTPUT; 2754 2755 $context = (object) [ 2756 'id' => $this->get_id(), 2757 'name' => $this->get_full_name(), 2758 'size' => $this->size, 2759 'value' => $this->is_readonly() ? null : $data, 2760 'forceltr' => $this->get_force_ltr(), 2761 'readonly' => $this->is_readonly(), 2762 ]; 2763 $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context); 2764 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query); 2765 } 2766 } 2767 2768 /** 2769 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting. 2770 * 2771 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2772 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk) 2773 */ 2774 class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask { 2775 2776 /** 2777 * Constructor 2778 * 2779 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2780 * @param string $visiblename localised 2781 * @param string $description long localised info 2782 * @param array $defaultsetting ('value'=>string, 'adv'=>bool) 2783 */ 2784 public function __construct($name, $visiblename, $description, $defaultsetting) { 2785 parent::__construct($name, $visiblename, $description, $defaultsetting['value']); 2786 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 2787 } 2788 } 2789 2790 /** 2791 * Admin setting class for encrypted values using secure encryption. 2792 * 2793 * @copyright 2019 The Open University 2794 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2795 */ 2796 class admin_setting_encryptedpassword extends admin_setting { 2797 2798 /** 2799 * Constructor. Same as parent except that the default value is always an empty string. 2800 * 2801 * @param string $name Internal name used in config table 2802 * @param string $visiblename Name shown on form 2803 * @param string $description Description that appears below field 2804 */ 2805 public function __construct(string $name, string $visiblename, string $description) { 2806 parent::__construct($name, $visiblename, $description, ''); 2807 } 2808 2809 public function get_setting() { 2810 return $this->config_read($this->name); 2811 } 2812 2813 public function write_setting($data) { 2814 $data = trim($data); 2815 if ($data === '') { 2816 // Value can really be set to nothing. 2817 $savedata = ''; 2818 } else { 2819 // Encrypt value before saving it. 2820 $savedata = \core\encryption::encrypt($data); 2821 } 2822 return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin')); 2823 } 2824 2825 public function output_html($data, $query='') { 2826 global $OUTPUT; 2827 2828 $default = $this->get_defaultsetting(); 2829 $context = (object) [ 2830 'id' => $this->get_id(), 2831 'name' => $this->get_full_name(), 2832 'set' => $data !== '', 2833 'novalue' => $this->get_setting() === null 2834 ]; 2835 $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context); 2836 2837 return format_admin_setting($this, $this->visiblename, $element, $this->description, 2838 true, '', $default, $query); 2839 } 2840 } 2841 2842 /** 2843 * Empty setting used to allow flags (advanced) on settings that can have no sensible default. 2844 * Note: Only advanced makes sense right now - locked does not. 2845 * 2846 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2847 */ 2848 class admin_setting_configempty extends admin_setting_configtext { 2849 2850 /** 2851 * @param string $name 2852 * @param string $visiblename 2853 * @param string $description 2854 */ 2855 public function __construct($name, $visiblename, $description) { 2856 parent::__construct($name, $visiblename, $description, '', PARAM_RAW); 2857 } 2858 2859 /** 2860 * Returns an XHTML string for the hidden field 2861 * 2862 * @param string $data 2863 * @param string $query 2864 * @return string XHTML string for the editor 2865 */ 2866 public function output_html($data, $query='') { 2867 global $OUTPUT; 2868 2869 $context = (object) [ 2870 'id' => $this->get_id(), 2871 'name' => $this->get_full_name() 2872 ]; 2873 $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context); 2874 2875 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query); 2876 } 2877 } 2878 2879 2880 /** 2881 * Path to directory 2882 * 2883 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2884 */ 2885 class admin_setting_configfile extends admin_setting_configtext { 2886 /** 2887 * Constructor 2888 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2889 * @param string $visiblename localised 2890 * @param string $description long localised info 2891 * @param string $defaultdirectory default directory location 2892 */ 2893 public function __construct($name, $visiblename, $description, $defaultdirectory) { 2894 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50); 2895 } 2896 2897 /** 2898 * Returns XHTML for the field 2899 * 2900 * Returns XHTML for the field and also checks whether the file 2901 * specified in $data exists using file_exists() 2902 * 2903 * @param string $data File name and path to use in value attr 2904 * @param string $query 2905 * @return string XHTML field 2906 */ 2907 public function output_html($data, $query='') { 2908 global $CFG, $OUTPUT; 2909 2910 $default = $this->get_defaultsetting(); 2911 $context = (object) [ 2912 'id' => $this->get_id(), 2913 'name' => $this->get_full_name(), 2914 'size' => $this->size, 2915 'value' => $data, 2916 'showvalidity' => !empty($data), 2917 'valid' => $data && file_exists($data), 2918 'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(), 2919 'forceltr' => $this->get_force_ltr(), 2920 ]; 2921 2922 if ($context->readonly) { 2923 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>'; 2924 } 2925 2926 $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context); 2927 2928 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 2929 } 2930 2931 /** 2932 * Checks if execpatch has been disabled in config.php 2933 */ 2934 public function write_setting($data) { 2935 global $CFG; 2936 if (!empty($CFG->preventexecpath)) { 2937 if ($this->get_setting() === null) { 2938 // Use default during installation. 2939 $data = $this->get_defaultsetting(); 2940 if ($data === null) { 2941 $data = ''; 2942 } 2943 } else { 2944 return ''; 2945 } 2946 } 2947 return parent::write_setting($data); 2948 } 2949 2950 } 2951 2952 2953 /** 2954 * Path to executable file 2955 * 2956 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2957 */ 2958 class admin_setting_configexecutable extends admin_setting_configfile { 2959 2960 /** 2961 * Returns an XHTML field 2962 * 2963 * @param string $data This is the value for the field 2964 * @param string $query 2965 * @return string XHTML field 2966 */ 2967 public function output_html($data, $query='') { 2968 global $CFG, $OUTPUT; 2969 $default = $this->get_defaultsetting(); 2970 require_once("$CFG->libdir/filelib.php"); 2971 2972 $context = (object) [ 2973 'id' => $this->get_id(), 2974 'name' => $this->get_full_name(), 2975 'size' => $this->size, 2976 'value' => $data, 2977 'showvalidity' => !empty($data), 2978 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data), 2979 'readonly' => !empty($CFG->preventexecpath), 2980 'forceltr' => $this->get_force_ltr() 2981 ]; 2982 2983 if (!empty($CFG->preventexecpath)) { 2984 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>'; 2985 } 2986 2987 $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context); 2988 2989 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 2990 } 2991 } 2992 2993 2994 /** 2995 * Path to directory 2996 * 2997 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2998 */ 2999 class admin_setting_configdirectory extends admin_setting_configfile { 3000 3001 /** 3002 * Returns an XHTML field 3003 * 3004 * @param string $data This is the value for the field 3005 * @param string $query 3006 * @return string XHTML 3007 */ 3008 public function output_html($data, $query='') { 3009 global $CFG, $OUTPUT; 3010 $default = $this->get_defaultsetting(); 3011 3012 $context = (object) [ 3013 'id' => $this->get_id(), 3014 'name' => $this->get_full_name(), 3015 'size' => $this->size, 3016 'value' => $data, 3017 'showvalidity' => !empty($data), 3018 'valid' => $data && file_exists($data) && is_dir($data), 3019 'readonly' => !empty($CFG->preventexecpath), 3020 'forceltr' => $this->get_force_ltr() 3021 ]; 3022 3023 if (!empty($CFG->preventexecpath)) { 3024 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>'; 3025 } 3026 3027 $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context); 3028 3029 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 3030 } 3031 } 3032 3033 3034 /** 3035 * Checkbox 3036 * 3037 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3038 */ 3039 class admin_setting_configcheckbox extends admin_setting { 3040 /** @var string Value used when checked */ 3041 public $yes; 3042 /** @var string Value used when not checked */ 3043 public $no; 3044 3045 /** 3046 * Constructor 3047 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3048 * @param string $visiblename localised 3049 * @param string $description long localised info 3050 * @param string $defaultsetting 3051 * @param string $yes value used when checked 3052 * @param string $no value used when not checked 3053 */ 3054 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') { 3055 parent::__construct($name, $visiblename, $description, $defaultsetting); 3056 $this->yes = (string)$yes; 3057 $this->no = (string)$no; 3058 } 3059 3060 /** 3061 * Retrieves the current setting using the objects name 3062 * 3063 * @return string 3064 */ 3065 public function get_setting() { 3066 return $this->config_read($this->name); 3067 } 3068 3069 /** 3070 * Sets the value for the setting 3071 * 3072 * Sets the value for the setting to either the yes or no values 3073 * of the object by comparing $data to yes 3074 * 3075 * @param mixed $data Gets converted to str for comparison against yes value 3076 * @return string empty string or error 3077 */ 3078 public function write_setting($data) { 3079 if ((string)$data === $this->yes) { // convert to strings before comparison 3080 $data = $this->yes; 3081 } else { 3082 $data = $this->no; 3083 } 3084 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 3085 } 3086 3087 /** 3088 * Returns an XHTML checkbox field 3089 * 3090 * @param string $data If $data matches yes then checkbox is checked 3091 * @param string $query 3092 * @return string XHTML field 3093 */ 3094 public function output_html($data, $query='') { 3095 global $OUTPUT; 3096 3097 $context = (object) [ 3098 'id' => $this->get_id(), 3099 'name' => $this->get_full_name(), 3100 'no' => $this->no, 3101 'value' => $this->yes, 3102 'checked' => (string) $data === $this->yes, 3103 'readonly' => $this->is_readonly(), 3104 ]; 3105 3106 $default = $this->get_defaultsetting(); 3107 if (!is_null($default)) { 3108 if ((string)$default === $this->yes) { 3109 $defaultinfo = get_string('checkboxyes', 'admin'); 3110 } else { 3111 $defaultinfo = get_string('checkboxno', 'admin'); 3112 } 3113 } else { 3114 $defaultinfo = NULL; 3115 } 3116 3117 $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context); 3118 3119 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 3120 } 3121 } 3122 3123 3124 /** 3125 * Multiple checkboxes, each represents different value, stored in csv format 3126 * 3127 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3128 */ 3129 class admin_setting_configmulticheckbox extends admin_setting { 3130 /** @var callable|null Loader function for choices */ 3131 protected $choiceloader = null; 3132 3133 /** @var array Array of choices value=>label. */ 3134 public $choices; 3135 3136 /** 3137 * Constructor: uses parent::__construct 3138 * 3139 * The $choices parameter may be either an array of $value => $label format, 3140 * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and 3141 * returns an array in that format. 3142 * 3143 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3144 * @param string $visiblename localised 3145 * @param string $description long localised info 3146 * @param array $defaultsetting array of selected 3147 * @param array|callable $choices array of $value => $label for each checkbox, or a callback 3148 */ 3149 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 3150 if (is_array($choices)) { 3151 $this->choices = $choices; 3152 } 3153 if (is_callable($choices)) { 3154 $this->choiceloader = $choices; 3155 } 3156 parent::__construct($name, $visiblename, $description, $defaultsetting); 3157 } 3158 3159 /** 3160 * This function may be used in ancestors for lazy loading of choices 3161 * 3162 * Override this method if loading of choices is expensive, such 3163 * as when it requires multiple db requests. 3164 * 3165 * @return bool true if loaded, false if error 3166 */ 3167 public function load_choices() { 3168 if ($this->choiceloader) { 3169 if (!is_array($this->choices)) { 3170 $this->choices = call_user_func($this->choiceloader); 3171 } 3172 } 3173 return true; 3174 } 3175 3176 /** 3177 * Is setting related to query text - used when searching 3178 * 3179 * @param string $query 3180 * @return bool true on related, false on not or failure 3181 */ 3182 public function is_related($query) { 3183 if (!$this->load_choices() or empty($this->choices)) { 3184 return false; 3185 } 3186 if (parent::is_related($query)) { 3187 return true; 3188 } 3189 3190 foreach ($this->choices as $desc) { 3191 if (strpos(core_text::strtolower($desc), $query) !== false) { 3192 return true; 3193 } 3194 } 3195 return false; 3196 } 3197 3198 /** 3199 * Returns the current setting if it is set 3200 * 3201 * @return mixed null if null, else an array 3202 */ 3203 public function get_setting() { 3204 $result = $this->config_read($this->name); 3205 3206 if (is_null($result)) { 3207 return NULL; 3208 } 3209 if ($result === '') { 3210 return array(); 3211 } 3212 $enabled = explode(',', $result); 3213 $setting = array(); 3214 foreach ($enabled as $option) { 3215 $setting[$option] = 1; 3216 } 3217 return $setting; 3218 } 3219 3220 /** 3221 * Saves the setting(s) provided in $data 3222 * 3223 * @param array $data An array of data, if not array returns empty str 3224 * @return mixed empty string on useless data or bool true=success, false=failed 3225 */ 3226 public function write_setting($data) { 3227 if (!is_array($data)) { 3228 return ''; // ignore it 3229 } 3230 if (!$this->load_choices() or empty($this->choices)) { 3231 return ''; 3232 } 3233 unset($data['xxxxx']); 3234 $result = array(); 3235 foreach ($data as $key => $value) { 3236 if ($value and array_key_exists($key, $this->choices)) { 3237 $result[] = $key; 3238 } 3239 } 3240 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin'); 3241 } 3242 3243 /** 3244 * Returns XHTML field(s) as required by choices 3245 * 3246 * Relies on data being an array should data ever be another valid vartype with 3247 * acceptable value this may cause a warning/error 3248 * if (!is_array($data)) would fix the problem 3249 * 3250 * @todo Add vartype handling to ensure $data is an array 3251 * 3252 * @param array $data An array of checked values 3253 * @param string $query 3254 * @return string XHTML field 3255 */ 3256 public function output_html($data, $query='') { 3257 global $OUTPUT; 3258 3259 if (!$this->load_choices() or empty($this->choices)) { 3260 return ''; 3261 } 3262 3263 $default = $this->get_defaultsetting(); 3264 if (is_null($default)) { 3265 $default = array(); 3266 } 3267 if (is_null($data)) { 3268 $data = array(); 3269 } 3270 3271 $context = (object) [ 3272 'id' => $this->get_id(), 3273 'name' => $this->get_full_name(), 3274 'readonly' => $this->is_readonly(), 3275 ]; 3276 3277 $options = array(); 3278 $defaults = array(); 3279 foreach ($this->choices as $key => $description) { 3280 if (!empty($default[$key])) { 3281 $defaults[] = $description; 3282 } 3283 3284 $options[] = [ 3285 'key' => $key, 3286 'checked' => !empty($data[$key]), 3287 'label' => highlightfast($query, $description) 3288 ]; 3289 } 3290 3291 if (is_null($default)) { 3292 $defaultinfo = null; 3293 } else if (!empty($defaults)) { 3294 $defaultinfo = implode(', ', $defaults); 3295 } else { 3296 $defaultinfo = get_string('none'); 3297 } 3298 3299 $context->options = $options; 3300 $context->hasoptions = !empty($options); 3301 3302 $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context); 3303 3304 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query); 3305 3306 } 3307 } 3308 3309 3310 /** 3311 * Multiple checkboxes 2, value stored as string 00101011 3312 * 3313 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3314 */ 3315 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox { 3316 3317 /** 3318 * Returns the setting if set 3319 * 3320 * @return mixed null if not set, else an array of set settings 3321 */ 3322 public function get_setting() { 3323 $result = $this->config_read($this->name); 3324 if (is_null($result)) { 3325 return NULL; 3326 } 3327 if (!$this->load_choices()) { 3328 return NULL; 3329 } 3330 $result = str_pad($result, count($this->choices), '0'); 3331 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY); 3332 $setting = array(); 3333 foreach ($this->choices as $key=>$unused) { 3334 $value = array_shift($result); 3335 if ($value) { 3336 $setting[$key] = 1; 3337 } 3338 } 3339 return $setting; 3340 } 3341 3342 /** 3343 * Save setting(s) provided in $data param 3344 * 3345 * @param array $data An array of settings to save 3346 * @return mixed empty string for bad data or bool true=>success, false=>error 3347 */ 3348 public function write_setting($data) { 3349 if (!is_array($data)) { 3350 return ''; // ignore it 3351 } 3352 if (!$this->load_choices() or empty($this->choices)) { 3353 return ''; 3354 } 3355 $result = ''; 3356 foreach ($this->choices as $key=>$unused) { 3357 if (!empty($data[$key])) { 3358 $result .= '1'; 3359 } else { 3360 $result .= '0'; 3361 } 3362 } 3363 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'); 3364 } 3365 } 3366 3367 3368 /** 3369 * Select one value from list 3370 * 3371 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3372 */ 3373 class admin_setting_configselect extends admin_setting { 3374 /** @var array Array of choices value=>label */ 3375 public $choices; 3376 /** @var array Array of choices grouped using optgroups */ 3377 public $optgroups; 3378 /** @var callable|null Loader function for choices */ 3379 protected $choiceloader = null; 3380 /** @var callable|null Validation function */ 3381 protected $validatefunction = null; 3382 3383 /** 3384 * Constructor. 3385 * 3386 * If you want to lazy-load the choices, pass a callback function that returns a choice 3387 * array for the $choices parameter. 3388 * 3389 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3390 * @param string $visiblename localised 3391 * @param string $description long localised info 3392 * @param string|int $defaultsetting 3393 * @param array|callable|null $choices array of $value=>$label for each selection, or callback 3394 */ 3395 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 3396 // Look for optgroup and single options. 3397 if (is_array($choices)) { 3398 $this->choices = []; 3399 foreach ($choices as $key => $val) { 3400 if (is_array($val)) { 3401 $this->optgroups[$key] = $val; 3402 $this->choices = array_merge($this->choices, $val); 3403 } else { 3404 $this->choices[$key] = $val; 3405 } 3406 } 3407 } 3408 if (is_callable($choices)) { 3409 $this->choiceloader = $choices; 3410 } 3411 3412 parent::__construct($name, $visiblename, $description, $defaultsetting); 3413 } 3414 3415 /** 3416 * Sets a validate function. 3417 * 3418 * The callback will be passed one parameter, the new setting value, and should return either 3419 * an empty string '' if the value is OK, or an error message if not. 3420 * 3421 * @param callable|null $validatefunction Validate function or null to clear 3422 * @since Moodle 3.10 3423 */ 3424 public function set_validate_function(?callable $validatefunction = null) { 3425 $this->validatefunction = $validatefunction; 3426 } 3427 3428 /** 3429 * This function may be used in ancestors for lazy loading of choices 3430 * 3431 * Override this method if loading of choices is expensive, such 3432 * as when it requires multiple db requests. 3433 * 3434 * @return bool true if loaded, false if error 3435 */ 3436 public function load_choices() { 3437 if ($this->choiceloader) { 3438 if (!is_array($this->choices)) { 3439 $this->choices = call_user_func($this->choiceloader); 3440 } 3441 return true; 3442 } 3443 return true; 3444 } 3445 3446 /** 3447 * Check if this is $query is related to a choice 3448 * 3449 * @param string $query 3450 * @return bool true if related, false if not 3451 */ 3452 public function is_related($query) { 3453 if (parent::is_related($query)) { 3454 return true; 3455 } 3456 if (!$this->load_choices()) { 3457 return false; 3458 } 3459 foreach ($this->choices as $key=>$value) { 3460 if (strpos(core_text::strtolower($key), $query) !== false) { 3461 return true; 3462 } 3463 if (strpos(core_text::strtolower($value), $query) !== false) { 3464 return true; 3465 } 3466 } 3467 return false; 3468 } 3469 3470 /** 3471 * Return the setting 3472 * 3473 * @return mixed returns config if successful else null 3474 */ 3475 public function get_setting() { 3476 return $this->config_read($this->name); 3477 } 3478 3479 /** 3480 * Save a setting 3481 * 3482 * @param string $data 3483 * @return string empty of error string 3484 */ 3485 public function write_setting($data) { 3486 if (!$this->load_choices() or empty($this->choices)) { 3487 return ''; 3488 } 3489 if (!array_key_exists($data, $this->choices)) { 3490 return ''; // ignore it 3491 } 3492 3493 // Validate the new setting. 3494 $error = $this->validate_setting($data); 3495 if ($error) { 3496 return $error; 3497 } 3498 3499 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 3500 } 3501 3502 /** 3503 * Validate the setting. This uses the callback function if provided; subclasses could override 3504 * to carry out validation directly in the class. 3505 * 3506 * @param string $data New value being set 3507 * @return string Empty string if valid, or error message text 3508 * @since Moodle 3.10 3509 */ 3510 protected function validate_setting(string $data): string { 3511 // If validation function is specified, call it now. 3512 if ($this->validatefunction) { 3513 return call_user_func($this->validatefunction, $data); 3514 } else { 3515 return ''; 3516 } 3517 } 3518 3519 /** 3520 * Returns XHTML select field 3521 * 3522 * Ensure the options are loaded, and generate the XHTML for the select 3523 * element and any warning message. Separating this out from output_html 3524 * makes it easier to subclass this class. 3525 * 3526 * @param string $data the option to show as selected. 3527 * @param string $current the currently selected option in the database, null if none. 3528 * @param string $default the default selected option. 3529 * @return array the HTML for the select element, and a warning message. 3530 * @deprecated since Moodle 3.2 3531 */ 3532 public function output_select_html($data, $current, $default, $extraname = '') { 3533 debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER); 3534 } 3535 3536 /** 3537 * Returns XHTML select field and wrapping div(s) 3538 * 3539 * @see output_select_html() 3540 * 3541 * @param string $data the option to show as selected 3542 * @param string $query 3543 * @return string XHTML field and wrapping div 3544 */ 3545 public function output_html($data, $query='') { 3546 global $OUTPUT; 3547 3548 $default = $this->get_defaultsetting(); 3549 $current = $this->get_setting(); 3550 3551 if (!$this->load_choices() || empty($this->choices)) { 3552 return ''; 3553 } 3554 3555 $context = (object) [ 3556 'id' => $this->get_id(), 3557 'name' => $this->get_full_name(), 3558 ]; 3559 3560 if (!is_null($default) && array_key_exists($default, $this->choices)) { 3561 $defaultinfo = $this->choices[$default]; 3562 } else { 3563 $defaultinfo = NULL; 3564 } 3565 3566 // Warnings. 3567 $warning = ''; 3568 if ($current === null) { 3569 // First run. 3570 } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) { 3571 // No warning. 3572 } else if (!array_key_exists($current, $this->choices)) { 3573 $warning = get_string('warningcurrentsetting', 'admin', $current); 3574 if (!is_null($default) && $data == $current) { 3575 $data = $default; // Use default instead of first value when showing the form. 3576 } 3577 } 3578 3579 $options = []; 3580 $template = 'core_admin/setting_configselect'; 3581 3582 if (!empty($this->optgroups)) { 3583 $optgroups = []; 3584 foreach ($this->optgroups as $label => $choices) { 3585 $optgroup = array('label' => $label, 'options' => []); 3586 foreach ($choices as $value => $name) { 3587 $optgroup['options'][] = [ 3588 'value' => $value, 3589 'name' => $name, 3590 'selected' => (string) $value == $data 3591 ]; 3592 unset($this->choices[$value]); 3593 } 3594 $optgroups[] = $optgroup; 3595 } 3596 $context->options = $options; 3597 $context->optgroups = $optgroups; 3598 $template = 'core_admin/setting_configselect_optgroup'; 3599 } 3600 3601 foreach ($this->choices as $value => $name) { 3602 $options[] = [ 3603 'value' => $value, 3604 'name' => $name, 3605 'selected' => (string) $value == $data 3606 ]; 3607 } 3608 $context->options = $options; 3609 $context->readonly = $this->is_readonly(); 3610 3611 $element = $OUTPUT->render_from_template($template, $context); 3612 3613 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query); 3614 } 3615 } 3616 3617 /** 3618 * Select multiple items from list 3619 * 3620 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3621 */ 3622 class admin_setting_configmultiselect extends admin_setting_configselect { 3623 /** 3624 * Constructor 3625 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3626 * @param string $visiblename localised 3627 * @param string $description long localised info 3628 * @param array $defaultsetting array of selected items 3629 * @param array $choices array of $value=>$label for each list item 3630 */ 3631 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 3632 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices); 3633 } 3634 3635 /** 3636 * Returns the select setting(s) 3637 * 3638 * @return mixed null or array. Null if no settings else array of setting(s) 3639 */ 3640 public function get_setting() { 3641 $result = $this->config_read($this->name); 3642 if (is_null($result)) { 3643 return NULL; 3644 } 3645 if ($result === '') { 3646 return array(); 3647 } 3648 return explode(',', $result); 3649 } 3650 3651 /** 3652 * Saves setting(s) provided through $data 3653 * 3654 * Potential bug in the works should anyone call with this function 3655 * using a vartype that is not an array 3656 * 3657 * @param array $data 3658 */ 3659 public function write_setting($data) { 3660 if (!is_array($data)) { 3661 return ''; //ignore it 3662 } 3663 if (!$this->load_choices() or empty($this->choices)) { 3664 return ''; 3665 } 3666 3667 unset($data['xxxxx']); 3668 3669 $save = array(); 3670 foreach ($data as $value) { 3671 if (!array_key_exists($value, $this->choices)) { 3672 continue; // ignore it 3673 } 3674 $save[] = $value; 3675 } 3676 3677 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin')); 3678 } 3679 3680 /** 3681 * Is setting related to query text - used when searching 3682 * 3683 * @param string $query 3684 * @return bool true if related, false if not 3685 */ 3686 public function is_related($query) { 3687 if (!$this->load_choices() or empty($this->choices)) { 3688 return false; 3689 } 3690 if (parent::is_related($query)) { 3691 return true; 3692 } 3693 3694 foreach ($this->choices as $desc) { 3695 if (strpos(core_text::strtolower($desc), $query) !== false) { 3696 return true; 3697 } 3698 } 3699 return false; 3700 } 3701 3702 /** 3703 * Returns XHTML multi-select field 3704 * 3705 * @todo Add vartype handling to ensure $data is an array 3706 * @param array $data Array of values to select by default 3707 * @param string $query 3708 * @return string XHTML multi-select field 3709 */ 3710 public function output_html($data, $query='') { 3711 global $OUTPUT; 3712 3713 if (!$this->load_choices() or empty($this->choices)) { 3714 return ''; 3715 } 3716 3717 $default = $this->get_defaultsetting(); 3718 if (is_null($default)) { 3719 $default = array(); 3720 } 3721 if (is_null($data)) { 3722 $data = array(); 3723 } 3724 3725 $context = (object) [ 3726 'id' => $this->get_id(), 3727 'name' => $this->get_full_name(), 3728 'size' => min(10, count($this->choices)) 3729 ]; 3730 3731 $defaults = []; 3732 $options = []; 3733 $template = 'core_admin/setting_configmultiselect'; 3734 3735 if (!empty($this->optgroups)) { 3736 $optgroups = []; 3737 foreach ($this->optgroups as $label => $choices) { 3738 $optgroup = array('label' => $label, 'options' => []); 3739 foreach ($choices as $value => $name) { 3740 if (in_array($value, $default)) { 3741 $defaults[] = $name; 3742 } 3743 $optgroup['options'][] = [ 3744 'value' => $value, 3745 'name' => $name, 3746 'selected' => in_array($value, $data) 3747 ]; 3748 unset($this->choices[$value]); 3749 } 3750 $optgroups[] = $optgroup; 3751 } 3752 $context->optgroups = $optgroups; 3753 $template = 'core_admin/setting_configmultiselect_optgroup'; 3754 } 3755 3756 foreach ($this->choices as $value => $name) { 3757 if (in_array($value, $default)) { 3758 $defaults[] = $name; 3759 } 3760 $options[] = [ 3761 'value' => $value, 3762 'name' => $name, 3763 'selected' => in_array($value, $data) 3764 ]; 3765 } 3766 $context->options = $options; 3767 $context->readonly = $this->is_readonly(); 3768 3769 if (is_null($default)) { 3770 $defaultinfo = NULL; 3771 } if (!empty($defaults)) { 3772 $defaultinfo = implode(', ', $defaults); 3773 } else { 3774 $defaultinfo = get_string('none'); 3775 } 3776 3777 $element = $OUTPUT->render_from_template($template, $context); 3778 3779 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 3780 } 3781 } 3782 3783 /** 3784 * Time selector 3785 * 3786 * This is a liiitle bit messy. we're using two selects, but we're returning 3787 * them as an array named after $name (so we only use $name2 internally for the setting) 3788 * 3789 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3790 */ 3791 class admin_setting_configtime extends admin_setting { 3792 /** @var string Used for setting second select (minutes) */ 3793 public $name2; 3794 3795 /** 3796 * Constructor 3797 * @param string $hoursname setting for hours 3798 * @param string $minutesname setting for hours 3799 * @param string $visiblename localised 3800 * @param string $description long localised info 3801 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes 3802 */ 3803 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) { 3804 $this->name2 = $minutesname; 3805 parent::__construct($hoursname, $visiblename, $description, $defaultsetting); 3806 } 3807 3808 /** 3809 * Get the selected time 3810 * 3811 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set 3812 */ 3813 public function get_setting() { 3814 $result1 = $this->config_read($this->name); 3815 $result2 = $this->config_read($this->name2); 3816 if (is_null($result1) or is_null($result2)) { 3817 return NULL; 3818 } 3819 3820 return array('h' => $result1, 'm' => $result2); 3821 } 3822 3823 /** 3824 * Store the time (hours and minutes) 3825 * 3826 * @param array $data Must be form 'h'=>xx, 'm'=>xx 3827 * @return bool true if success, false if not 3828 */ 3829 public function write_setting($data) { 3830 if (!is_array($data)) { 3831 return ''; 3832 } 3833 3834 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']); 3835 return ($result ? '' : get_string('errorsetting', 'admin')); 3836 } 3837 3838 /** 3839 * Returns XHTML time select fields 3840 * 3841 * @param array $data Must be form 'h'=>xx, 'm'=>xx 3842 * @param string $query 3843 * @return string XHTML time select fields and wrapping div(s) 3844 */ 3845 public function output_html($data, $query='') { 3846 global $OUTPUT; 3847 3848 $default = $this->get_defaultsetting(); 3849 if (is_array($default)) { 3850 $defaultinfo = $default['h'].':'.$default['m']; 3851 } else { 3852 $defaultinfo = NULL; 3853 } 3854 3855 $context = (object) [ 3856 'id' => $this->get_id(), 3857 'name' => $this->get_full_name(), 3858 'readonly' => $this->is_readonly(), 3859 'hours' => array_map(function($i) use ($data) { 3860 return [ 3861 'value' => $i, 3862 'name' => $i, 3863 'selected' => $i == $data['h'] 3864 ]; 3865 }, range(0, 23)), 3866 'minutes' => array_map(function($i) use ($data) { 3867 return [ 3868 'value' => $i, 3869 'name' => $i, 3870 'selected' => $i == $data['m'] 3871 ]; 3872 }, range(0, 59, 5)) 3873 ]; 3874 3875 $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context); 3876 3877 return format_admin_setting($this, $this->visiblename, $element, $this->description, 3878 $this->get_id() . 'h', '', $defaultinfo, $query); 3879 } 3880 3881 } 3882 3883 3884 /** 3885 * Seconds duration setting. 3886 * 3887 * @copyright 2012 Petr Skoda (http://skodak.org) 3888 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3889 */ 3890 class admin_setting_configduration extends admin_setting { 3891 3892 /** @var int default duration unit */ 3893 protected $defaultunit; 3894 /** @var callable|null Validation function */ 3895 protected $validatefunction = null; 3896 3897 /** @var int The minimum allowed value */ 3898 protected int $minduration = 0; 3899 3900 /** @var null|int The maximum allowed value */ 3901 protected null|int $maxduration = null; 3902 3903 /** 3904 * Constructor 3905 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 3906 * or 'myplugin/mysetting' for ones in config_plugins. 3907 * @param string $visiblename localised name 3908 * @param string $description localised long description 3909 * @param mixed $defaultsetting string or array depending on implementation 3910 * @param int $defaultunit - day, week, etc. (in seconds) 3911 */ 3912 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) { 3913 if (is_number($defaultsetting)) { 3914 $defaultsetting = self::parse_seconds($defaultsetting); 3915 } 3916 $units = self::get_units(); 3917 if (isset($units[$defaultunit])) { 3918 $this->defaultunit = $defaultunit; 3919 } else { 3920 $this->defaultunit = 86400; 3921 } 3922 parent::__construct($name, $visiblename, $description, $defaultsetting); 3923 } 3924 3925 /** 3926 * Set the minimum allowed value. 3927 * This must be at least 0. 3928 * 3929 * @param int $duration 3930 */ 3931 public function set_min_duration(int $duration): void { 3932 if ($duration < 0) { 3933 throw new coding_exception('The minimum duration must be at least 0.'); 3934 } 3935 3936 $this->minduration = $duration; 3937 } 3938 3939 /** 3940 * Set the maximum allowed value. 3941 * 3942 * A value of null will disable the maximum duration value. 3943 * 3944 * @param int|null $duration 3945 */ 3946 public function set_max_duration(?int $duration): void { 3947 $this->maxduration = $duration; 3948 } 3949 3950 /** 3951 * Sets a validate function. 3952 * 3953 * The callback will be passed one parameter, the new setting value, and should return either 3954 * an empty string '' if the value is OK, or an error message if not. 3955 * 3956 * @param callable|null $validatefunction Validate function or null to clear 3957 * @since Moodle 3.10 3958 */ 3959 public function set_validate_function(?callable $validatefunction = null) { 3960 $this->validatefunction = $validatefunction; 3961 } 3962 3963 /** 3964 * Validate the setting. This uses the callback function if provided; subclasses could override 3965 * to carry out validation directly in the class. 3966 * 3967 * @param int $data New value being set 3968 * @return string Empty string if valid, or error message text 3969 * @since Moodle 3.10 3970 */ 3971 protected function validate_setting(int $data): string { 3972 if ($data < $this->minduration) { 3973 return get_string( 3974 'configduration_low', 3975 'admin', 3976 self::get_duration_text($this->minduration, get_string('numseconds', 'core', 0)) 3977 ); 3978 } 3979 3980 if ($this->maxduration && $data > $this->maxduration) { 3981 return get_string('configduration_high', 'admin', self::get_duration_text($this->maxduration)); 3982 } 3983 3984 // If validation function is specified, call it now. 3985 if ($this->validatefunction) { 3986 return call_user_func($this->validatefunction, $data); 3987 } 3988 return ''; 3989 } 3990 3991 /** 3992 * Returns selectable units. 3993 * @static 3994 * @return array 3995 */ 3996 protected static function get_units() { 3997 return array( 3998 604800 => get_string('weeks'), 3999 86400 => get_string('days'), 4000 3600 => get_string('hours'), 4001 60 => get_string('minutes'), 4002 1 => get_string('seconds'), 4003 ); 4004 } 4005 4006 /** 4007 * Converts seconds to some more user friendly string. 4008 * @static 4009 * @param int $seconds 4010 * @param null|string The value to use when the duration is empty. If not specified, a "None" value is used. 4011 * @return string 4012 */ 4013 protected static function get_duration_text(int $seconds, ?string $emptyvalue = null): string { 4014 if (empty($seconds)) { 4015 if ($emptyvalue !== null) { 4016 return $emptyvalue; 4017 } 4018 return get_string('none'); 4019 } 4020 $data = self::parse_seconds($seconds); 4021 switch ($data['u']) { 4022 case (60*60*24*7): 4023 return get_string('numweeks', '', $data['v']); 4024 case (60*60*24): 4025 return get_string('numdays', '', $data['v']); 4026 case (60*60): 4027 return get_string('numhours', '', $data['v']); 4028 case (60): 4029 return get_string('numminutes', '', $data['v']); 4030 default: 4031 return get_string('numseconds', '', $data['v']*$data['u']); 4032 } 4033 } 4034 4035 /** 4036 * Finds suitable units for given duration. 4037 * @static 4038 * @param int $seconds 4039 * @return array 4040 */ 4041 protected static function parse_seconds($seconds) { 4042 foreach (self::get_units() as $unit => $unused) { 4043 if ($seconds % $unit === 0) { 4044 return array('v'=>(int)($seconds/$unit), 'u'=>$unit); 4045 } 4046 } 4047 return array('v'=>(int)$seconds, 'u'=>1); 4048 } 4049 4050 /** 4051 * Get the selected duration as array. 4052 * 4053 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set 4054 */ 4055 public function get_setting() { 4056 $seconds = $this->config_read($this->name); 4057 if (is_null($seconds)) { 4058 return null; 4059 } 4060 4061 return self::parse_seconds($seconds); 4062 } 4063 4064 /** 4065 * Store the duration as seconds. 4066 * 4067 * @param array $data Must be form 'h'=>xx, 'm'=>xx 4068 * @return bool true if success, false if not 4069 */ 4070 public function write_setting($data) { 4071 if (!is_array($data)) { 4072 return ''; 4073 } 4074 4075 $unit = (int)$data['u']; 4076 $value = (int)$data['v']; 4077 $seconds = $value * $unit; 4078 4079 // Validate the new setting. 4080 $error = $this->validate_setting($seconds); 4081 if ($error) { 4082 return $error; 4083 } 4084 4085 $result = $this->config_write($this->name, $seconds); 4086 return ($result ? '' : get_string('errorsetting', 'admin')); 4087 } 4088 4089 /** 4090 * Returns duration text+select fields. 4091 * 4092 * @param array $data Must be form 'v'=>xx, 'u'=>xx 4093 * @param string $query 4094 * @return string duration text+select fields and wrapping div(s) 4095 */ 4096 public function output_html($data, $query='') { 4097 global $OUTPUT; 4098 4099 $default = $this->get_defaultsetting(); 4100 if (is_number($default)) { 4101 $defaultinfo = self::get_duration_text($default); 4102 } else if (is_array($default)) { 4103 $defaultinfo = self::get_duration_text($default['v']*$default['u']); 4104 } else { 4105 $defaultinfo = null; 4106 } 4107 4108 $inputid = $this->get_id() . 'v'; 4109 $units = array_filter(self::get_units(), function($unit): bool { 4110 if (!$this->maxduration) { 4111 // No duration limit. All units are valid. 4112 return true; 4113 } 4114 4115 return $unit <= $this->maxduration; 4116 }, ARRAY_FILTER_USE_KEY); 4117 4118 $defaultunit = $this->defaultunit; 4119 4120 $context = (object) [ 4121 'id' => $this->get_id(), 4122 'name' => $this->get_full_name(), 4123 'value' => $data['v'] ?? '', 4124 'readonly' => $this->is_readonly(), 4125 'options' => array_map(function($unit) use ($units, $data, $defaultunit) { 4126 return [ 4127 'value' => $unit, 4128 'name' => $units[$unit], 4129 'selected' => isset($data) && (($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']) 4130 ]; 4131 }, array_keys($units)) 4132 ]; 4133 4134 $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context); 4135 4136 return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query); 4137 } 4138 } 4139 4140 4141 /** 4142 * Seconds duration setting with an advanced checkbox, that controls a additional 4143 * $name.'_adv' setting. 4144 * 4145 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4146 * @copyright 2014 The Open University 4147 */ 4148 class admin_setting_configduration_with_advanced extends admin_setting_configduration { 4149 /** 4150 * Constructor 4151 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 4152 * or 'myplugin/mysetting' for ones in config_plugins. 4153 * @param string $visiblename localised name 4154 * @param string $description localised long description 4155 * @param array $defaultsetting array of int value, and bool whether it is 4156 * is advanced by default. 4157 * @param int $defaultunit - day, week, etc. (in seconds) 4158 */ 4159 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) { 4160 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit); 4161 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 4162 } 4163 } 4164 4165 4166 /** 4167 * Used to validate a textarea used for ip addresses 4168 * 4169 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4170 * @copyright 2011 Petr Skoda (http://skodak.org) 4171 */ 4172 class admin_setting_configiplist extends admin_setting_configtextarea { 4173 4174 /** 4175 * Validate the contents of the textarea as IP addresses 4176 * 4177 * Used to validate a new line separated list of IP addresses collected from 4178 * a textarea control 4179 * 4180 * @param string $data A list of IP Addresses separated by new lines 4181 * @return mixed bool true for success or string:error on failure 4182 */ 4183 public function validate($data) { 4184 if(!empty($data)) { 4185 $lines = explode("\n", $data); 4186 } else { 4187 return true; 4188 } 4189 $result = true; 4190 $badips = array(); 4191 foreach ($lines as $line) { 4192 $tokens = explode('#', $line); 4193 $ip = trim($tokens[0]); 4194 if (empty($ip)) { 4195 continue; 4196 } 4197 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) || 4198 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) || 4199 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) { 4200 } else { 4201 $result = false; 4202 $badips[] = $ip; 4203 } 4204 } 4205 if($result) { 4206 return true; 4207 } else { 4208 return get_string('validateiperror', 'admin', join(', ', $badips)); 4209 } 4210 } 4211 } 4212 4213 /** 4214 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format). 4215 * 4216 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4217 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com) 4218 */ 4219 class admin_setting_configmixedhostiplist extends admin_setting_configtextarea { 4220 4221 /** 4222 * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592). 4223 * Used to validate a new line separated list of entries collected from a textarea control. 4224 * 4225 * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to 4226 * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched 4227 * via the get_setting() method, which has been overriden. 4228 * 4229 * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines. 4230 * @return mixed bool true for success or string:error on failure 4231 */ 4232 public function validate($data) { 4233 if (empty($data)) { 4234 return true; 4235 } 4236 $entries = explode("\n", $data); 4237 $badentries = []; 4238 4239 foreach ($entries as $key => $entry) { 4240 $entry = trim($entry); 4241 if (empty($entry)) { 4242 return get_string('validateemptylineerror', 'admin'); 4243 } 4244 4245 // Validate each string entry against the supported formats. 4246 if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry) 4247 || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry) 4248 || \core\ip_utils::is_domain_matching_pattern($entry)) { 4249 continue; 4250 } 4251 4252 // Otherwise, the entry is invalid. 4253 $badentries[] = $entry; 4254 } 4255 4256 if ($badentries) { 4257 return get_string('validateerrorlist', 'admin', join(', ', $badentries)); 4258 } 4259 return true; 4260 } 4261 4262 /** 4263 * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE). 4264 * 4265 * @param string $data the setting data, as sent from the web form. 4266 * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version. 4267 */ 4268 protected function ace_encode($data) { 4269 if (empty($data)) { 4270 return $data; 4271 } 4272 $entries = explode("\n", $data); 4273 foreach ($entries as $key => $entry) { 4274 $entry = trim($entry); 4275 // This regex matches any string that has non-ascii character. 4276 if (preg_match('/[^\x00-\x7f]/', $entry)) { 4277 // If we can convert the unicode string to an idn, do so. 4278 // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail). 4279 $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); 4280 $entries[$key] = $val ? $val : $entry; 4281 } 4282 } 4283 return implode("\n", $entries); 4284 } 4285 4286 /** 4287 * Decode any ascii-encoded domain names back to their utf-8 representation for display. 4288 * 4289 * @param string $data the setting data, as found in the database. 4290 * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation. 4291 */ 4292 protected function ace_decode($data) { 4293 $entries = explode("\n", $data); 4294 foreach ($entries as $key => $entry) { 4295 $entry = trim($entry); 4296 if (strpos($entry, 'xn--') !== false) { 4297 $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); 4298 } 4299 } 4300 return implode("\n", $entries); 4301 } 4302 4303 /** 4304 * Override, providing utf8-decoding for ascii-encoded IDN strings. 4305 * 4306 * @return mixed returns punycode-converted setting string if successful, else null. 4307 */ 4308 public function get_setting() { 4309 // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation. 4310 $data = $this->config_read($this->name); 4311 if (function_exists('idn_to_utf8') && !is_null($data)) { 4312 $data = $this->ace_decode($data); 4313 } 4314 return $data; 4315 } 4316 4317 /** 4318 * Override, providing ascii-encoding for utf8 (native) IDN strings. 4319 * 4320 * @param string $data 4321 * @return string 4322 */ 4323 public function write_setting($data) { 4324 if ($this->paramtype === PARAM_INT and $data === '') { 4325 // Do not complain if '' used instead of 0. 4326 $data = 0; 4327 } 4328 4329 // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate! 4330 if (function_exists('idn_to_ascii')) { 4331 $data = $this->ace_encode($data); 4332 } 4333 4334 $validated = $this->validate($data); 4335 if ($validated !== true) { 4336 return $validated; 4337 } 4338 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 4339 } 4340 } 4341 4342 /** 4343 * Used to validate a textarea used for port numbers. 4344 * 4345 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4346 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com) 4347 */ 4348 class admin_setting_configportlist extends admin_setting_configtextarea { 4349 4350 /** 4351 * Validate the contents of the textarea as port numbers. 4352 * Used to validate a new line separated list of ports collected from a textarea control. 4353 * 4354 * @param string $data A list of ports separated by new lines 4355 * @return mixed bool true for success or string:error on failure 4356 */ 4357 public function validate($data) { 4358 if (empty($data)) { 4359 return true; 4360 } 4361 $ports = explode("\n", $data); 4362 $badentries = []; 4363 foreach ($ports as $port) { 4364 $port = trim($port); 4365 if (empty($port)) { 4366 return get_string('validateemptylineerror', 'admin'); 4367 } 4368 4369 // Is the string a valid integer number? 4370 if (strval(intval($port)) !== $port || intval($port) <= 0) { 4371 $badentries[] = $port; 4372 } 4373 } 4374 if ($badentries) { 4375 return get_string('validateerrorlist', 'admin', $badentries); 4376 } 4377 return true; 4378 } 4379 } 4380 4381 4382 /** 4383 * An admin setting for selecting one or more users who have a capability 4384 * in the system context 4385 * 4386 * An admin setting for selecting one or more users, who have a particular capability 4387 * in the system context. Warning, make sure the list will never be too long. There is 4388 * no paging or searching of this list. 4389 * 4390 * To correctly get a list of users from this config setting, you need to call the 4391 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php. 4392 * 4393 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4394 */ 4395 class admin_setting_users_with_capability extends admin_setting_configmultiselect { 4396 /** @var string The capabilities name */ 4397 protected $capability; 4398 /** @var int include admin users too */ 4399 protected $includeadmins; 4400 4401 /** 4402 * Constructor. 4403 * 4404 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 4405 * @param string $visiblename localised name 4406 * @param string $description localised long description 4407 * @param array $defaultsetting array of usernames 4408 * @param string $capability string capability name. 4409 * @param bool $includeadmins include administrators 4410 */ 4411 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) { 4412 $this->capability = $capability; 4413 $this->includeadmins = $includeadmins; 4414 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL); 4415 } 4416 4417 /** 4418 * Load all of the uses who have the capability into choice array 4419 * 4420 * @return bool Always returns true 4421 */ 4422 function load_choices() { 4423 if (is_array($this->choices)) { 4424 return true; 4425 } 4426 list($sort, $sortparams) = users_order_by_sql('u'); 4427 if (!empty($sortparams)) { 4428 throw new coding_exception('users_order_by_sql returned some query parameters. ' . 4429 'This is unexpected, and a problem because there is no way to pass these ' . 4430 'parameters to get_users_by_capability. See MDL-34657.'); 4431 } 4432 $userfieldsapi = \core_user\fields::for_name(); 4433 $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects; 4434 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort); 4435 $this->choices = array( 4436 '$@NONE@$' => get_string('nobody'), 4437 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)), 4438 ); 4439 if ($this->includeadmins) { 4440 $admins = get_admins(); 4441 foreach ($admins as $user) { 4442 $this->choices[$user->id] = fullname($user); 4443 } 4444 } 4445 if (is_array($users)) { 4446 foreach ($users as $user) { 4447 $this->choices[$user->id] = fullname($user); 4448 } 4449 } 4450 return true; 4451 } 4452 4453 /** 4454 * Returns the default setting for class 4455 * 4456 * @return mixed Array, or string. Empty string if no default 4457 */ 4458 public function get_defaultsetting() { 4459 $this->load_choices(); 4460 $defaultsetting = parent::get_defaultsetting(); 4461 if (empty($defaultsetting)) { 4462 return array('$@NONE@$'); 4463 } else if (array_key_exists($defaultsetting, $this->choices)) { 4464 return $defaultsetting; 4465 } else { 4466 return ''; 4467 } 4468 } 4469 4470 /** 4471 * Returns the current setting 4472 * 4473 * @return mixed array or string 4474 */ 4475 public function get_setting() { 4476 $result = parent::get_setting(); 4477 if ($result === null) { 4478 // this is necessary for settings upgrade 4479 return null; 4480 } 4481 if (empty($result)) { 4482 $result = array('$@NONE@$'); 4483 } 4484 return $result; 4485 } 4486 4487 /** 4488 * Save the chosen setting provided as $data 4489 * 4490 * @param array $data 4491 * @return mixed string or array 4492 */ 4493 public function write_setting($data) { 4494 // If all is selected, remove any explicit options. 4495 if (in_array('$@ALL@$', $data)) { 4496 $data = array('$@ALL@$'); 4497 } 4498 // None never needs to be written to the DB. 4499 if (in_array('$@NONE@$', $data)) { 4500 unset($data[array_search('$@NONE@$', $data)]); 4501 } 4502 return parent::write_setting($data); 4503 } 4504 } 4505 4506 4507 /** 4508 * Special checkbox for calendar - resets SESSION vars. 4509 * 4510 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4511 */ 4512 class admin_setting_special_adminseesall extends admin_setting_configcheckbox { 4513 /** 4514 * Calls the parent::__construct with default values 4515 * 4516 * name => calendar_adminseesall 4517 * visiblename => get_string('adminseesall', 'admin') 4518 * description => get_string('helpadminseesall', 'admin') 4519 * defaultsetting => 0 4520 */ 4521 public function __construct() { 4522 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'), 4523 get_string('helpadminseesall', 'admin'), '0'); 4524 } 4525 4526 /** 4527 * Stores the setting passed in $data 4528 * 4529 * @param mixed gets converted to string for comparison 4530 * @return string empty string or error message 4531 */ 4532 public function write_setting($data) { 4533 global $SESSION; 4534 return parent::write_setting($data); 4535 } 4536 } 4537 4538 /** 4539 * Special select for settings that are altered in setup.php and can not be altered on the fly 4540 * 4541 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4542 */ 4543 class admin_setting_special_selectsetup extends admin_setting_configselect { 4544 /** 4545 * Reads the setting directly from the database 4546 * 4547 * @return mixed 4548 */ 4549 public function get_setting() { 4550 // read directly from db! 4551 return get_config(NULL, $this->name); 4552 } 4553 4554 /** 4555 * Save the setting passed in $data 4556 * 4557 * @param string $data The setting to save 4558 * @return string empty or error message 4559 */ 4560 public function write_setting($data) { 4561 global $CFG; 4562 // do not change active CFG setting! 4563 $current = $CFG->{$this->name}; 4564 $result = parent::write_setting($data); 4565 $CFG->{$this->name} = $current; 4566 return $result; 4567 } 4568 } 4569 4570 4571 /** 4572 * Special select for frontpage - stores data in course table 4573 * 4574 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4575 */ 4576 class admin_setting_sitesetselect extends admin_setting_configselect { 4577 /** 4578 * Returns the site name for the selected site 4579 * 4580 * @see get_site() 4581 * @return string The site name of the selected site 4582 */ 4583 public function get_setting() { 4584 $site = course_get_format(get_site())->get_course(); 4585 return $site->{$this->name}; 4586 } 4587 4588 /** 4589 * Updates the database and save the setting 4590 * 4591 * @param string data 4592 * @return string empty or error message 4593 */ 4594 public function write_setting($data) { 4595 global $DB, $SITE, $COURSE; 4596 if (!in_array($data, array_keys($this->choices))) { 4597 return get_string('errorsetting', 'admin'); 4598 } 4599 $record = new stdClass(); 4600 $record->id = SITEID; 4601 $temp = $this->name; 4602 $record->$temp = $data; 4603 $record->timemodified = time(); 4604 4605 course_get_format($SITE)->update_course_format_options($record); 4606 $DB->update_record('course', $record); 4607 4608 // Reset caches. 4609 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4610 if ($SITE->id == $COURSE->id) { 4611 $COURSE = $SITE; 4612 } 4613 core_courseformat\base::reset_course_cache($SITE->id); 4614 4615 return ''; 4616 4617 } 4618 4619 /** 4620 * admin_setting_sitesetselect is not meant to be overridden in config.php. 4621 * 4622 * @return bool 4623 */ 4624 public function is_forceable(): bool { 4625 return false; 4626 } 4627 } 4628 4629 4630 /** 4631 * Select for blog's bloglevel setting: if set to 0, will set blog_menu 4632 * block to hidden. 4633 * 4634 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4635 */ 4636 class admin_setting_bloglevel extends admin_setting_configselect { 4637 /** 4638 * Updates the database and save the setting 4639 * 4640 * @param string data 4641 * @return string empty or error message 4642 */ 4643 public function write_setting($data) { 4644 global $DB, $CFG; 4645 if ($data == 0) { 4646 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1"); 4647 foreach ($blogblocks as $block) { 4648 $DB->set_field('block', 'visible', 0, array('id' => $block->id)); 4649 } 4650 } else { 4651 // reenable all blocks only when switching from disabled blogs 4652 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) { 4653 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0"); 4654 foreach ($blogblocks as $block) { 4655 $DB->set_field('block', 'visible', 1, array('id' => $block->id)); 4656 } 4657 } 4658 } 4659 return parent::write_setting($data); 4660 } 4661 } 4662 4663 4664 /** 4665 * Special select - lists on the frontpage - hacky 4666 * 4667 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4668 */ 4669 class admin_setting_courselist_frontpage extends admin_setting { 4670 4671 /** @var array Array of choices value=>label. */ 4672 public $choices; 4673 4674 /** 4675 * Construct override, requires one param 4676 * 4677 * @param bool $loggedin Is the user logged in 4678 */ 4679 public function __construct($loggedin) { 4680 global $CFG; 4681 require_once($CFG->dirroot.'/course/lib.php'); 4682 $name = 'frontpage'.($loggedin ? 'loggedin' : ''); 4683 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin'); 4684 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin'); 4685 $defaults = array(FRONTPAGEALLCOURSELIST); 4686 parent::__construct($name, $visiblename, $description, $defaults); 4687 } 4688 4689 /** 4690 * Loads the choices available 4691 * 4692 * @return bool always returns true 4693 */ 4694 public function load_choices() { 4695 if (is_array($this->choices)) { 4696 return true; 4697 } 4698 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'), 4699 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'), 4700 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'), 4701 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'), 4702 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'), 4703 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'), 4704 'none' => get_string('none')); 4705 if ($this->name === 'frontpage') { 4706 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]); 4707 } 4708 return true; 4709 } 4710 4711 /** 4712 * Returns the selected settings 4713 * 4714 * @param mixed array or setting or null 4715 */ 4716 public function get_setting() { 4717 $result = $this->config_read($this->name); 4718 if (is_null($result)) { 4719 return NULL; 4720 } 4721 if ($result === '') { 4722 return array(); 4723 } 4724 return explode(',', $result); 4725 } 4726 4727 /** 4728 * Save the selected options 4729 * 4730 * @param array $data 4731 * @return mixed empty string (data is not an array) or bool true=success false=failure 4732 */ 4733 public function write_setting($data) { 4734 if (!is_array($data)) { 4735 return ''; 4736 } 4737 $this->load_choices(); 4738 $save = array(); 4739 foreach($data as $datum) { 4740 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) { 4741 continue; 4742 } 4743 $save[$datum] = $datum; // no duplicates 4744 } 4745 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin')); 4746 } 4747 4748 /** 4749 * Return XHTML select field and wrapping div 4750 * 4751 * @todo Add vartype handling to make sure $data is an array 4752 * @param array $data Array of elements to select by default 4753 * @return string XHTML select field and wrapping div 4754 */ 4755 public function output_html($data, $query='') { 4756 global $OUTPUT; 4757 4758 $this->load_choices(); 4759 $currentsetting = array(); 4760 foreach ($data as $key) { 4761 if ($key != 'none' and array_key_exists($key, $this->choices)) { 4762 $currentsetting[] = $key; // already selected first 4763 } 4764 } 4765 4766 $context = (object) [ 4767 'id' => $this->get_id(), 4768 'name' => $this->get_full_name(), 4769 ]; 4770 4771 $options = $this->choices; 4772 $selects = []; 4773 for ($i = 0; $i < count($this->choices) - 1; $i++) { 4774 if (!array_key_exists($i, $currentsetting)) { 4775 $currentsetting[$i] = 'none'; 4776 } 4777 $selects[] = [ 4778 'key' => $i, 4779 'options' => array_map(function($option) use ($options, $currentsetting, $i) { 4780 return [ 4781 'name' => $options[$option], 4782 'value' => $option, 4783 'selected' => $currentsetting[$i] == $option 4784 ]; 4785 }, array_keys($options)) 4786 ]; 4787 } 4788 $context->selects = $selects; 4789 4790 $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context); 4791 4792 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query); 4793 } 4794 } 4795 4796 4797 /** 4798 * Special checkbox for frontpage - stores data in course table 4799 * 4800 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4801 */ 4802 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox { 4803 /** 4804 * Returns the current sites name 4805 * 4806 * @return string 4807 */ 4808 public function get_setting() { 4809 $site = course_get_format(get_site())->get_course(); 4810 return $site->{$this->name}; 4811 } 4812 4813 /** 4814 * Save the selected setting 4815 * 4816 * @param string $data The selected site 4817 * @return string empty string or error message 4818 */ 4819 public function write_setting($data) { 4820 global $DB, $SITE, $COURSE; 4821 $record = new stdClass(); 4822 $record->id = $SITE->id; 4823 $record->{$this->name} = ($data == '1' ? 1 : 0); 4824 $record->timemodified = time(); 4825 4826 course_get_format($SITE)->update_course_format_options($record); 4827 $DB->update_record('course', $record); 4828 4829 // Reset caches. 4830 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4831 if ($SITE->id == $COURSE->id) { 4832 $COURSE = $SITE; 4833 } 4834 core_courseformat\base::reset_course_cache($SITE->id); 4835 4836 return ''; 4837 } 4838 4839 /** 4840 * admin_setting_sitesetcheckbox is not meant to be overridden in config.php. 4841 * 4842 * @return bool 4843 */ 4844 public function is_forceable(): bool { 4845 return false; 4846 } 4847 } 4848 4849 /** 4850 * Special text for frontpage - stores data in course table. 4851 * Empty string means not set here. Manual setting is required. 4852 * 4853 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4854 */ 4855 class admin_setting_sitesettext extends admin_setting_configtext { 4856 4857 /** 4858 * Constructor. 4859 */ 4860 public function __construct() { 4861 call_user_func_array([parent::class, '__construct'], func_get_args()); 4862 $this->set_force_ltr(false); 4863 } 4864 4865 /** 4866 * Return the current setting 4867 * 4868 * @return mixed string or null 4869 */ 4870 public function get_setting() { 4871 $site = course_get_format(get_site())->get_course(); 4872 return $site->{$this->name} != '' ? $site->{$this->name} : NULL; 4873 } 4874 4875 /** 4876 * Validate the selected data 4877 * 4878 * @param string $data The selected value to validate 4879 * @return mixed true or message string 4880 */ 4881 public function validate($data) { 4882 global $DB, $SITE; 4883 $cleaned = clean_param($data, PARAM_TEXT); 4884 if ($cleaned === '') { 4885 return get_string('required'); 4886 } 4887 if ($this->name ==='shortname' && 4888 $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) { 4889 return get_string('shortnametaken', 'error', $data); 4890 } 4891 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison 4892 return true; 4893 } else { 4894 return get_string('validateerror', 'admin'); 4895 } 4896 } 4897 4898 /** 4899 * Save the selected setting 4900 * 4901 * @param string $data The selected value 4902 * @return string empty or error message 4903 */ 4904 public function write_setting($data) { 4905 global $DB, $SITE, $COURSE; 4906 $data = trim($data); 4907 $validated = $this->validate($data); 4908 if ($validated !== true) { 4909 return $validated; 4910 } 4911 4912 $record = new stdClass(); 4913 $record->id = $SITE->id; 4914 $record->{$this->name} = $data; 4915 $record->timemodified = time(); 4916 4917 course_get_format($SITE)->update_course_format_options($record); 4918 $DB->update_record('course', $record); 4919 4920 // Reset caches. 4921 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4922 if ($SITE->id == $COURSE->id) { 4923 $COURSE = $SITE; 4924 } 4925 core_courseformat\base::reset_course_cache($SITE->id); 4926 4927 return ''; 4928 } 4929 4930 /** 4931 * admin_setting_sitesettext is not meant to be overridden in config.php. 4932 * 4933 * @return bool 4934 */ 4935 public function is_forceable(): bool { 4936 return false; 4937 } 4938 } 4939 4940 4941 /** 4942 * This type of field should be used for mandatory config settings. 4943 * 4944 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4945 */ 4946 class admin_setting_requiredtext extends admin_setting_configtext { 4947 4948 /** 4949 * Validate data before storage. 4950 * 4951 * @param string $data The string to be validated. 4952 * @return bool|string true for success or error string if invalid. 4953 */ 4954 public function validate($data) { 4955 $cleaned = clean_param($data, PARAM_TEXT); 4956 if ($cleaned === '') { 4957 return get_string('required'); 4958 } 4959 4960 return parent::validate($data); 4961 } 4962 } 4963 4964 /** 4965 * This type of field should be used for mandatory config settings where setting password is required. 4966 * 4967 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4968 */ 4969 class admin_setting_requiredpasswordunmask extends admin_setting_configpasswordunmask { 4970 4971 /** 4972 * Validate data before storage. 4973 * 4974 * @param string $data The string to be validated. 4975 * @return bool|string true for success or error string if invalid. 4976 */ 4977 public function validate($data) { 4978 $cleaned = clean_param($data, PARAM_TEXT); 4979 if ($cleaned === '') { 4980 return get_string('required'); 4981 } 4982 4983 return parent::validate($data); 4984 } 4985 } 4986 4987 /** 4988 * Special text editor for site description. 4989 * 4990 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4991 */ 4992 class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor { 4993 4994 /** 4995 * Calls parent::__construct with specific arguments 4996 */ 4997 public function __construct() { 4998 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null, 4999 PARAM_RAW, 60, 15); 5000 } 5001 5002 /** 5003 * Return the current setting 5004 * @return string The current setting 5005 */ 5006 public function get_setting() { 5007 $site = course_get_format(get_site())->get_course(); 5008 return $site->{$this->name}; 5009 } 5010 5011 /** 5012 * Save the new setting 5013 * 5014 * @param string $data The new value to save 5015 * @return string empty or error message 5016 */ 5017 public function write_setting($data) { 5018 global $DB, $SITE, $COURSE; 5019 $record = new stdClass(); 5020 $record->id = $SITE->id; 5021 $record->{$this->name} = $data; 5022 $record->timemodified = time(); 5023 5024 course_get_format($SITE)->update_course_format_options($record); 5025 $DB->update_record('course', $record); 5026 5027 // Reset caches. 5028 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 5029 if ($SITE->id == $COURSE->id) { 5030 $COURSE = $SITE; 5031 } 5032 core_courseformat\base::reset_course_cache($SITE->id); 5033 5034 return ''; 5035 } 5036 5037 /** 5038 * admin_setting_special_frontpagedesc is not meant to be overridden in config.php. 5039 * 5040 * @return bool 5041 */ 5042 public function is_forceable(): bool { 5043 return false; 5044 } 5045 } 5046 5047 5048 /** 5049 * Administration interface for emoticon_manager settings. 5050 * 5051 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5052 */ 5053 class admin_setting_emoticons extends admin_setting { 5054 5055 /** 5056 * Calls parent::__construct with specific args 5057 */ 5058 public function __construct() { 5059 global $CFG; 5060 5061 $manager = get_emoticon_manager(); 5062 $defaults = $this->prepare_form_data($manager->default_emoticons()); 5063 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults); 5064 } 5065 5066 /** 5067 * Return the current setting(s) 5068 * 5069 * @return array Current settings array 5070 */ 5071 public function get_setting() { 5072 global $CFG; 5073 5074 $manager = get_emoticon_manager(); 5075 5076 $config = $this->config_read($this->name); 5077 if (is_null($config)) { 5078 return null; 5079 } 5080 5081 $config = $manager->decode_stored_config($config); 5082 if (is_null($config)) { 5083 return null; 5084 } 5085 5086 return $this->prepare_form_data($config); 5087 } 5088 5089 /** 5090 * Save selected settings 5091 * 5092 * @param array $data Array of settings to save 5093 * @return bool 5094 */ 5095 public function write_setting($data) { 5096 5097 $manager = get_emoticon_manager(); 5098 $emoticons = $this->process_form_data($data); 5099 5100 if ($emoticons === false) { 5101 return false; 5102 } 5103 5104 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) { 5105 return ''; // success 5106 } else { 5107 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br'); 5108 } 5109 } 5110 5111 /** 5112 * Return XHTML field(s) for options 5113 * 5114 * @param array $data Array of options to set in HTML 5115 * @return string XHTML string for the fields and wrapping div(s) 5116 */ 5117 public function output_html($data, $query='') { 5118 global $OUTPUT; 5119 5120 $context = (object) [ 5121 'name' => $this->get_full_name(), 5122 'emoticons' => [], 5123 'forceltr' => true, 5124 ]; 5125 5126 $i = 0; 5127 foreach ($data as $field => $value) { 5128 5129 // When $i == 0: text. 5130 // When $i == 1: imagename. 5131 // When $i == 2: imagecomponent. 5132 // When $i == 3: altidentifier. 5133 // When $i == 4: altcomponent. 5134 $fields[$i] = (object) [ 5135 'field' => $field, 5136 'value' => $value, 5137 'index' => $i 5138 ]; 5139 $i++; 5140 5141 if ($i > 4) { 5142 $icon = null; 5143 if (!empty($fields[1]->value)) { 5144 if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) { 5145 $alt = get_string($fields[3]->value, $fields[4]->value); 5146 } else { 5147 $alt = $fields[0]->value; 5148 } 5149 $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value); 5150 } 5151 $context->emoticons[] = [ 5152 'fields' => $fields, 5153 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null 5154 ]; 5155 $fields = []; 5156 $i = 0; 5157 } 5158 } 5159 5160 $context->reseturl = new moodle_url('/admin/resetemoticons.php'); 5161 $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context); 5162 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query); 5163 } 5164 5165 /** 5166 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data 5167 * 5168 * @see self::process_form_data() 5169 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager} 5170 * @return array of form fields and their values 5171 */ 5172 protected function prepare_form_data(array $emoticons) { 5173 5174 $form = array(); 5175 $i = 0; 5176 foreach ($emoticons as $emoticon) { 5177 $form['text'.$i] = $emoticon->text; 5178 $form['imagename'.$i] = $emoticon->imagename; 5179 $form['imagecomponent'.$i] = $emoticon->imagecomponent; 5180 $form['altidentifier'.$i] = $emoticon->altidentifier; 5181 $form['altcomponent'.$i] = $emoticon->altcomponent; 5182 $i++; 5183 } 5184 // add one more blank field set for new object 5185 $form['text'.$i] = ''; 5186 $form['imagename'.$i] = ''; 5187 $form['imagecomponent'.$i] = ''; 5188 $form['altidentifier'.$i] = ''; 5189 $form['altcomponent'.$i] = ''; 5190 5191 return $form; 5192 } 5193 5194 /** 5195 * Converts the data from admin settings form into an array of emoticon objects 5196 * 5197 * @see self::prepare_form_data() 5198 * @param array $data array of admin form fields and values 5199 * @return false|array of emoticon objects 5200 */ 5201 protected function process_form_data(array $form) { 5202 5203 $count = count($form); // number of form field values 5204 5205 if ($count % 5) { 5206 // we must get five fields per emoticon object 5207 return false; 5208 } 5209 5210 $emoticons = array(); 5211 for ($i = 0; $i < $count / 5; $i++) { 5212 $emoticon = new stdClass(); 5213 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS); 5214 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH); 5215 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT); 5216 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID); 5217 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT); 5218 5219 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) { 5220 // prevent from breaking http://url.addresses by accident 5221 $emoticon->text = ''; 5222 } 5223 5224 if (strlen($emoticon->text) < 2) { 5225 // do not allow single character emoticons 5226 $emoticon->text = ''; 5227 } 5228 5229 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) { 5230 // emoticon text must contain some non-alphanumeric character to prevent 5231 // breaking HTML tags 5232 $emoticon->text = ''; 5233 } 5234 5235 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') { 5236 $emoticons[] = $emoticon; 5237 } 5238 } 5239 return $emoticons; 5240 } 5241 5242 } 5243 5244 5245 /** 5246 * Special setting for limiting of the list of available languages. 5247 * 5248 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5249 */ 5250 class admin_setting_langlist extends admin_setting_configtext { 5251 /** 5252 * Calls parent::__construct with specific arguments 5253 */ 5254 public function __construct() { 5255 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS); 5256 } 5257 5258 /** 5259 * Validate that each language identifier exists on the site 5260 * 5261 * @param string $data 5262 * @return bool|string True if validation successful, otherwise error string 5263 */ 5264 public function validate($data) { 5265 $parentcheck = parent::validate($data); 5266 if ($parentcheck !== true) { 5267 return $parentcheck; 5268 } 5269 5270 if ($data === '') { 5271 return true; 5272 } 5273 5274 // Normalize language identifiers. 5275 $langcodes = array_map('trim', explode(',', $data)); 5276 foreach ($langcodes as $langcode) { 5277 // If the langcode contains optional alias, split it out. 5278 [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2); 5279 5280 if (!get_string_manager()->translation_exists($langcode)) { 5281 return get_string('invalidlanguagecode', 'error', $langcode); 5282 } 5283 } 5284 5285 return true; 5286 } 5287 5288 /** 5289 * Save the new setting 5290 * 5291 * @param string $data The new setting 5292 * @return bool 5293 */ 5294 public function write_setting($data) { 5295 $return = parent::write_setting($data); 5296 get_string_manager()->reset_caches(); 5297 return $return; 5298 } 5299 } 5300 5301 5302 /** 5303 * Allows to specify comma separated list of known country codes. 5304 * 5305 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually 5306 * known codes. 5307 * 5308 * @package core 5309 * @category admin 5310 * @copyright 2020 David Mudrák <david@moodle.com> 5311 * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5312 */ 5313 class admin_setting_countrycodes extends admin_setting_configtext { 5314 5315 /** 5316 * Construct the instance of the setting. 5317 * 5318 * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'. 5319 * @param lang_string|string $visiblename Language string with the field label text. 5320 * @param lang_string|string $description Language string with the field description text. 5321 * @param string $defaultsetting Default value of the setting. 5322 * @param int $size Input text field size. 5323 */ 5324 public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) { 5325 parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size); 5326 } 5327 5328 /** 5329 * Validate the setting value before storing it. 5330 * 5331 * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or 5332 * a comma separated list of such words. 5333 * 5334 * @param string $data Value inserted into the setting field. 5335 * @return bool|string True if the value is OK, error string otherwise. 5336 */ 5337 public function validate($data) { 5338 5339 $parentcheck = parent::validate($data); 5340 5341 if ($parentcheck !== true) { 5342 return $parentcheck; 5343 } 5344 5345 if ($data === '') { 5346 return true; 5347 } 5348 5349 $allcountries = get_string_manager()->get_list_of_countries(true); 5350 5351 foreach (explode(',', $data) as $code) { 5352 if (!isset($allcountries[$code])) { 5353 return get_string('invalidcountrycode', 'core_error', $code); 5354 } 5355 } 5356 5357 return true; 5358 } 5359 } 5360 5361 5362 /** 5363 * Selection of one of the recognised countries using the list 5364 * returned by {@link get_list_of_countries()}. 5365 * 5366 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5367 */ 5368 class admin_settings_country_select extends admin_setting_configselect { 5369 protected $includeall; 5370 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) { 5371 $this->includeall = $includeall; 5372 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 5373 } 5374 5375 /** 5376 * Lazy-load the available choices for the select box 5377 */ 5378 public function load_choices() { 5379 global $CFG; 5380 if (is_array($this->choices)) { 5381 return true; 5382 } 5383 $this->choices = array_merge( 5384 array('0' => get_string('choosedots')), 5385 get_string_manager()->get_list_of_countries($this->includeall)); 5386 return true; 5387 } 5388 } 5389 5390 5391 /** 5392 * admin_setting_configselect for the default number of sections in a course, 5393 * simply so we can lazy-load the choices. 5394 * 5395 * @copyright 2011 The Open University 5396 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5397 */ 5398 class admin_settings_num_course_sections extends admin_setting_configselect { 5399 public function __construct($name, $visiblename, $description, $defaultsetting) { 5400 parent::__construct($name, $visiblename, $description, $defaultsetting, array()); 5401 } 5402 5403 /** Lazy-load the available choices for the select box */ 5404 public function load_choices() { 5405 $max = get_config('moodlecourse', 'maxsections'); 5406 if (!isset($max) || !is_numeric($max)) { 5407 $max = 52; 5408 } 5409 for ($i = 0; $i <= $max; $i++) { 5410 $this->choices[$i] = "$i"; 5411 } 5412 return true; 5413 } 5414 } 5415 5416 5417 /** 5418 * Course category selection 5419 * 5420 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5421 */ 5422 class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete { 5423 /** 5424 * Calls parent::__construct with specific arguments 5425 */ 5426 public function __construct($name, $visiblename, $description, $defaultsetting = 1) { 5427 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null); 5428 } 5429 5430 /** 5431 * Load the available choices for the select box 5432 * 5433 * @return bool 5434 */ 5435 public function load_choices() { 5436 if (is_array($this->choices)) { 5437 return true; 5438 } 5439 $this->choices = core_course_category::make_categories_list('', 0, ' / '); 5440 return true; 5441 } 5442 } 5443 5444 5445 /** 5446 * Special control for selecting days to backup 5447 * 5448 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5449 */ 5450 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 { 5451 /** 5452 * Calls parent::__construct with specific arguments 5453 */ 5454 public function __construct() { 5455 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL); 5456 $this->plugin = 'backup'; 5457 } 5458 5459 /** 5460 * Load the available choices for the select box 5461 * 5462 * @return bool Always returns true 5463 */ 5464 public function load_choices() { 5465 if (is_array($this->choices)) { 5466 return true; 5467 } 5468 $this->choices = array(); 5469 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); 5470 foreach ($days as $day) { 5471 $this->choices[$day] = get_string($day, 'calendar'); 5472 } 5473 return true; 5474 } 5475 } 5476 5477 /** 5478 * Special setting for backup auto destination. 5479 * 5480 * @package core 5481 * @subpackage admin 5482 * @copyright 2014 Frédéric Massart - FMCorz.net 5483 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5484 */ 5485 class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory { 5486 5487 /** 5488 * Calls parent::__construct with specific arguments. 5489 */ 5490 public function __construct() { 5491 parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), ''); 5492 } 5493 5494 /** 5495 * Check if the directory must be set, depending on backup/backup_auto_storage. 5496 * 5497 * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise 5498 * there will be conflicts if this validation happens before the other one. 5499 * 5500 * @param string $data Form data. 5501 * @return string Empty when no errors. 5502 */ 5503 public function write_setting($data) { 5504 $storage = (int) get_config('backup', 'backup_auto_storage'); 5505 if ($storage !== 0) { 5506 if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) { 5507 // The directory must exist and be writable. 5508 return get_string('backuperrorinvaliddestination'); 5509 } 5510 } 5511 return parent::write_setting($data); 5512 } 5513 } 5514 5515 5516 /** 5517 * Special debug setting 5518 * 5519 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5520 */ 5521 class admin_setting_special_debug extends admin_setting_configselect { 5522 /** 5523 * Calls parent::__construct with specific arguments 5524 */ 5525 public function __construct() { 5526 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL); 5527 } 5528 5529 /** 5530 * Load the available choices for the select box 5531 * 5532 * @return bool 5533 */ 5534 public function load_choices() { 5535 if (is_array($this->choices)) { 5536 return true; 5537 } 5538 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'), 5539 DEBUG_MINIMAL => get_string('debugminimal', 'admin'), 5540 DEBUG_NORMAL => get_string('debugnormal', 'admin'), 5541 DEBUG_ALL => get_string('debugall', 'admin'), 5542 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin')); 5543 return true; 5544 } 5545 } 5546 5547 5548 /** 5549 * Special admin control 5550 * 5551 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5552 */ 5553 class admin_setting_special_calendar_weekend extends admin_setting { 5554 /** 5555 * Calls parent::__construct with specific arguments 5556 */ 5557 public function __construct() { 5558 $name = 'calendar_weekend'; 5559 $visiblename = get_string('calendar_weekend', 'admin'); 5560 $description = get_string('helpweekenddays', 'admin'); 5561 $default = array ('0', '6'); // Saturdays and Sundays 5562 parent::__construct($name, $visiblename, $description, $default); 5563 } 5564 5565 /** 5566 * Gets the current settings as an array 5567 * 5568 * @return mixed Null if none, else array of settings 5569 */ 5570 public function get_setting() { 5571 $result = $this->config_read($this->name); 5572 if (is_null($result)) { 5573 return NULL; 5574 } 5575 if ($result === '') { 5576 return array(); 5577 } 5578 $settings = array(); 5579 for ($i=0; $i<7; $i++) { 5580 if ($result & (1 << $i)) { 5581 $settings[] = $i; 5582 } 5583 } 5584 return $settings; 5585 } 5586 5587 /** 5588 * Save the new settings 5589 * 5590 * @param array $data Array of new settings 5591 * @return bool 5592 */ 5593 public function write_setting($data) { 5594 if (!is_array($data)) { 5595 return ''; 5596 } 5597 unset($data['xxxxx']); 5598 $result = 0; 5599 foreach($data as $index) { 5600 $result |= 1 << $index; 5601 } 5602 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin')); 5603 } 5604 5605 /** 5606 * Return XHTML to display the control 5607 * 5608 * @param array $data array of selected days 5609 * @param string $query 5610 * @return string XHTML for display (field + wrapping div(s) 5611 */ 5612 public function output_html($data, $query='') { 5613 global $OUTPUT; 5614 5615 // The order matters very much because of the implied numeric keys. 5616 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); 5617 $context = (object) [ 5618 'name' => $this->get_full_name(), 5619 'id' => $this->get_id(), 5620 'days' => array_map(function($index) use ($days, $data) { 5621 return [ 5622 'index' => $index, 5623 'label' => get_string($days[$index], 'calendar'), 5624 'checked' => in_array($index, $data) 5625 ]; 5626 }, array_keys($days)) 5627 ]; 5628 5629 $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context); 5630 5631 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query); 5632 5633 } 5634 } 5635 5636 5637 /** 5638 * Admin setting that allows a user to pick a behaviour. 5639 * 5640 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5641 */ 5642 class admin_setting_question_behaviour extends admin_setting_configselect { 5643 /** 5644 * @param string $name name of config variable 5645 * @param string $visiblename display name 5646 * @param string $description description 5647 * @param string $default default. 5648 */ 5649 public function __construct($name, $visiblename, $description, $default) { 5650 parent::__construct($name, $visiblename, $description, $default, null); 5651 } 5652 5653 /** 5654 * Load list of behaviours as choices 5655 * @return bool true => success, false => error. 5656 */ 5657 public function load_choices() { 5658 global $CFG; 5659 require_once($CFG->dirroot . '/question/engine/lib.php'); 5660 $this->choices = question_engine::get_behaviour_options(''); 5661 return true; 5662 } 5663 } 5664 5665 5666 /** 5667 * Admin setting that allows a user to pick appropriate roles for something. 5668 * 5669 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5670 */ 5671 class admin_setting_pickroles extends admin_setting_configmulticheckbox { 5672 /** @var array Array of capabilities which identify roles */ 5673 private $types; 5674 5675 /** 5676 * @param string $name Name of config variable 5677 * @param string $visiblename Display name 5678 * @param string $description Description 5679 * @param array $types Array of archetypes which identify 5680 * roles that will be enabled by default. 5681 */ 5682 public function __construct($name, $visiblename, $description, $types) { 5683 parent::__construct($name, $visiblename, $description, NULL, NULL); 5684 $this->types = $types; 5685 } 5686 5687 /** 5688 * Load roles as choices 5689 * 5690 * @return bool true=>success, false=>error 5691 */ 5692 public function load_choices() { 5693 global $CFG, $DB; 5694 if (during_initial_install()) { 5695 return false; 5696 } 5697 if (is_array($this->choices)) { 5698 return true; 5699 } 5700 if ($roles = get_all_roles()) { 5701 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true); 5702 return true; 5703 } else { 5704 return false; 5705 } 5706 } 5707 5708 /** 5709 * Return the default setting for this control 5710 * 5711 * @return array Array of default settings 5712 */ 5713 public function get_defaultsetting() { 5714 global $CFG; 5715 5716 if (during_initial_install()) { 5717 return null; 5718 } 5719 $result = array(); 5720 foreach($this->types as $archetype) { 5721 if ($caproles = get_archetype_roles($archetype)) { 5722 foreach ($caproles as $caprole) { 5723 $result[$caprole->id] = 1; 5724 } 5725 } 5726 } 5727 return $result; 5728 } 5729 } 5730 5731 5732 /** 5733 * Admin setting that is a list of installed filter plugins. 5734 * 5735 * @copyright 2015 The Open University 5736 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5737 */ 5738 class admin_setting_pickfilters extends admin_setting_configmulticheckbox { 5739 5740 /** 5741 * Constructor 5742 * 5743 * @param string $name unique ascii name, either 'mysetting' for settings 5744 * that in config, or 'myplugin/mysetting' for ones in config_plugins. 5745 * @param string $visiblename localised name 5746 * @param string $description localised long description 5747 * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1) 5748 */ 5749 public function __construct($name, $visiblename, $description, $default) { 5750 if (empty($default)) { 5751 $default = array(); 5752 } 5753 $this->load_choices(); 5754 foreach ($default as $plugin) { 5755 if (!isset($this->choices[$plugin])) { 5756 unset($default[$plugin]); 5757 } 5758 } 5759 parent::__construct($name, $visiblename, $description, $default, null); 5760 } 5761 5762 public function load_choices() { 5763 if (is_array($this->choices)) { 5764 return true; 5765 } 5766 $this->choices = array(); 5767 5768 foreach (core_component::get_plugin_list('filter') as $plugin => $unused) { 5769 $this->choices[$plugin] = filter_get_name($plugin); 5770 } 5771 return true; 5772 } 5773 } 5774 5775 5776 /** 5777 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting. 5778 * 5779 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5780 */ 5781 class admin_setting_configtext_with_advanced extends admin_setting_configtext { 5782 /** 5783 * Constructor 5784 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 5785 * @param string $visiblename localised 5786 * @param string $description long localised info 5787 * @param array $defaultsetting ('value'=>string, '__construct'=>bool) 5788 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex 5789 * @param int $size default field size 5790 */ 5791 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) { 5792 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size); 5793 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 5794 } 5795 } 5796 5797 5798 /** 5799 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting. 5800 * 5801 * @copyright 2009 Petr Skoda (http://skodak.org) 5802 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5803 */ 5804 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox { 5805 5806 /** 5807 * Constructor 5808 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 5809 * @param string $visiblename localised 5810 * @param string $description long localised info 5811 * @param array $defaultsetting ('value'=>string, 'adv'=>bool) 5812 * @param string $yes value used when checked 5813 * @param string $no value used when not checked 5814 */ 5815 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') { 5816 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no); 5817 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 5818 } 5819 5820 } 5821 5822 5823 /** 5824 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting. 5825 * 5826 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv 5827 * 5828 * @copyright 2010 Sam Hemelryk 5829 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5830 */ 5831 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox { 5832 /** 5833 * Constructor 5834 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 5835 * @param string $visiblename localised 5836 * @param string $description long localised info 5837 * @param array $defaultsetting ('value'=>string, 'locked'=>bool) 5838 * @param string $yes value used when checked 5839 * @param string $no value used when not checked 5840 */ 5841 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') { 5842 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no); 5843 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked'])); 5844 } 5845 5846 } 5847 5848 /** 5849 * Autocomplete as you type form element. 5850 * 5851 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5852 */ 5853 class admin_setting_configselect_autocomplete extends admin_setting_configselect { 5854 /** @var boolean $tags Should we allow typing new entries to the field? */ 5855 protected $tags = false; 5856 /** @var string $ajax Name of an AMD module to send/process ajax requests. */ 5857 protected $ajax = ''; 5858 /** @var string $placeholder Placeholder text for an empty list. */ 5859 protected $placeholder = ''; 5860 /** @var bool $casesensitive Whether the search has to be case-sensitive. */ 5861 protected $casesensitive = false; 5862 /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */ 5863 protected $showsuggestions = true; 5864 /** @var string $noselectionstring String that is shown when there are no selections. */ 5865 protected $noselectionstring = ''; 5866 5867 /** 5868 * Returns XHTML select field and wrapping div(s) 5869 * 5870 * @see output_select_html() 5871 * 5872 * @param string $data the option to show as selected 5873 * @param string $query 5874 * @return string XHTML field and wrapping div 5875 */ 5876 public function output_html($data, $query='') { 5877 global $PAGE; 5878 5879 $html = parent::output_html($data, $query); 5880 5881 if ($html === '') { 5882 return $html; 5883 } 5884 5885 $this->placeholder = get_string('search'); 5886 5887 $params = array('#' . $this->get_id(), $this->tags, $this->ajax, 5888 $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring); 5889 5890 // Load autocomplete wrapper for select2 library. 5891 $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params); 5892 5893 return $html; 5894 } 5895 } 5896 5897 /** 5898 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting. 5899 * 5900 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5901 */ 5902 class admin_setting_configselect_with_advanced extends admin_setting_configselect { 5903 /** 5904 * Calls parent::__construct with specific arguments 5905 */ 5906 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 5907 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices); 5908 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 5909 } 5910 5911 } 5912 5913 /** 5914 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting. 5915 * 5916 * @copyright 2017 Marina Glancy 5917 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5918 */ 5919 class admin_setting_configselect_with_lock extends admin_setting_configselect { 5920 /** 5921 * Constructor 5922 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 5923 * or 'myplugin/mysetting' for ones in config_plugins. 5924 * @param string $visiblename localised 5925 * @param string $description long localised info 5926 * @param array $defaultsetting ('value'=>string, 'locked'=>bool) 5927 * @param array $choices array of $value=>$label for each selection 5928 */ 5929 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 5930 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices); 5931 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked'])); 5932 } 5933 } 5934 5935 5936 /** 5937 * Graded roles in gradebook 5938 * 5939 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5940 */ 5941 class admin_setting_special_gradebookroles extends admin_setting_pickroles { 5942 /** 5943 * Calls parent::__construct with specific arguments 5944 */ 5945 public function __construct() { 5946 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'), 5947 get_string('configgradebookroles', 'admin'), 5948 array('student')); 5949 } 5950 } 5951 5952 5953 /** 5954 * 5955 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5956 */ 5957 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox { 5958 /** 5959 * Saves the new settings passed in $data 5960 * 5961 * @param string $data 5962 * @return mixed string or Array 5963 */ 5964 public function write_setting($data) { 5965 global $CFG, $DB; 5966 5967 $oldvalue = $this->config_read($this->name); 5968 $return = parent::write_setting($data); 5969 $newvalue = $this->config_read($this->name); 5970 5971 if ($oldvalue !== $newvalue) { 5972 // force full regrading 5973 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0)); 5974 } 5975 5976 return $return; 5977 } 5978 } 5979 5980 5981 /** 5982 * Which roles to show on course description page 5983 * 5984 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5985 */ 5986 class admin_setting_special_coursecontact extends admin_setting_pickroles { 5987 /** 5988 * Calls parent::__construct with specific arguments 5989 */ 5990 public function __construct() { 5991 parent::__construct('coursecontact', get_string('coursecontact', 'admin'), 5992 get_string('coursecontact_desc', 'admin'), 5993 array('editingteacher')); 5994 $this->set_updatedcallback(function (){ 5995 cache::make('core', 'coursecontacts')->purge(); 5996 }); 5997 } 5998 } 5999 6000 6001 /** 6002 * 6003 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6004 */ 6005 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox { 6006 /** 6007 * Calls parent::__construct with specific arguments 6008 */ 6009 public function __construct() { 6010 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'), 6011 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0'); 6012 } 6013 6014 /** 6015 * Old syntax of class constructor. Deprecated in PHP7. 6016 * 6017 * @deprecated since Moodle 3.1 6018 */ 6019 public function admin_setting_special_gradelimiting() { 6020 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 6021 self::__construct(); 6022 } 6023 6024 /** 6025 * Force site regrading 6026 */ 6027 function regrade_all() { 6028 global $CFG; 6029 require_once("$CFG->libdir/gradelib.php"); 6030 grade_force_site_regrading(); 6031 } 6032 6033 /** 6034 * Saves the new settings 6035 * 6036 * @param mixed $data 6037 * @return string empty string or error message 6038 */ 6039 function write_setting($data) { 6040 $previous = $this->get_setting(); 6041 6042 if ($previous === null) { 6043 if ($data) { 6044 $this->regrade_all(); 6045 } 6046 } else { 6047 if ($data != $previous) { 6048 $this->regrade_all(); 6049 } 6050 } 6051 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 6052 } 6053 6054 } 6055 6056 /** 6057 * Special setting for $CFG->grade_minmaxtouse. 6058 * 6059 * @package core 6060 * @copyright 2015 Frédéric Massart - FMCorz.net 6061 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6062 */ 6063 class admin_setting_special_grademinmaxtouse extends admin_setting_configselect { 6064 6065 /** 6066 * Constructor. 6067 */ 6068 public function __construct() { 6069 parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'), 6070 new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM, 6071 array( 6072 GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'), 6073 GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades') 6074 ) 6075 ); 6076 } 6077 6078 /** 6079 * Saves the new setting. 6080 * 6081 * @param mixed $data 6082 * @return string empty string or error message 6083 */ 6084 function write_setting($data) { 6085 global $CFG; 6086 6087 $previous = $this->get_setting(); 6088 $result = parent::write_setting($data); 6089 6090 // If saved and the value has changed. 6091 if (empty($result) && $previous != $data) { 6092 require_once($CFG->libdir . '/gradelib.php'); 6093 grade_force_site_regrading(); 6094 } 6095 6096 return $result; 6097 } 6098 6099 } 6100 6101 6102 /** 6103 * Primary grade export plugin - has state tracking. 6104 * 6105 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6106 */ 6107 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox { 6108 /** 6109 * Calls parent::__construct with specific arguments 6110 */ 6111 public function __construct() { 6112 parent::__construct('gradeexport', get_string('gradeexport', 'admin'), 6113 get_string('configgradeexport', 'admin'), array(), NULL); 6114 } 6115 6116 /** 6117 * Load the available choices for the multicheckbox 6118 * 6119 * @return bool always returns true 6120 */ 6121 public function load_choices() { 6122 if (is_array($this->choices)) { 6123 return true; 6124 } 6125 $this->choices = array(); 6126 6127 if ($plugins = core_component::get_plugin_list('gradeexport')) { 6128 foreach($plugins as $plugin => $unused) { 6129 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin); 6130 } 6131 } 6132 return true; 6133 } 6134 } 6135 6136 6137 /** 6138 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax. 6139 * 6140 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6141 */ 6142 class admin_setting_special_gradepointdefault extends admin_setting_configtext { 6143 /** 6144 * Config gradepointmax constructor 6145 * 6146 * @param string $name Overidden by "gradepointmax" 6147 * @param string $visiblename Overridden by "gradepointmax" language string. 6148 * @param string $description Overridden by "gradepointmax_help" language string. 6149 * @param string $defaultsetting Not used, overridden by 100. 6150 * @param mixed $paramtype Overridden by PARAM_INT. 6151 * @param int $size Overridden by 5. 6152 */ 6153 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) { 6154 $name = 'gradepointdefault'; 6155 $visiblename = get_string('gradepointdefault', 'grades'); 6156 $description = get_string('gradepointdefault_help', 'grades'); 6157 $defaultsetting = 100; 6158 $paramtype = PARAM_INT; 6159 $size = 5; 6160 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); 6161 } 6162 6163 /** 6164 * Validate data before storage 6165 * @param string $data The submitted data 6166 * @return bool|string true if ok, string if error found 6167 */ 6168 public function validate($data) { 6169 global $CFG; 6170 if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) { 6171 return true; 6172 } else { 6173 return get_string('gradepointdefault_validateerror', 'grades'); 6174 } 6175 } 6176 } 6177 6178 6179 /** 6180 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000. 6181 * 6182 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6183 */ 6184 class admin_setting_special_gradepointmax extends admin_setting_configtext { 6185 6186 /** 6187 * Config gradepointmax constructor 6188 * 6189 * @param string $name Overidden by "gradepointmax" 6190 * @param string $visiblename Overridden by "gradepointmax" language string. 6191 * @param string $description Overridden by "gradepointmax_help" language string. 6192 * @param string $defaultsetting Not used, overridden by 100. 6193 * @param mixed $paramtype Overridden by PARAM_INT. 6194 * @param int $size Overridden by 5. 6195 */ 6196 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) { 6197 $name = 'gradepointmax'; 6198 $visiblename = get_string('gradepointmax', 'grades'); 6199 $description = get_string('gradepointmax_help', 'grades'); 6200 $defaultsetting = 100; 6201 $paramtype = PARAM_INT; 6202 $size = 5; 6203 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); 6204 } 6205 6206 /** 6207 * Save the selected setting 6208 * 6209 * @param string $data The selected site 6210 * @return string empty string or error message 6211 */ 6212 public function write_setting($data) { 6213 if ($data === '') { 6214 $data = (int)$this->defaultsetting; 6215 } else { 6216 $data = $data; 6217 } 6218 return parent::write_setting($data); 6219 } 6220 6221 /** 6222 * Validate data before storage 6223 * @param string $data The submitted data 6224 * @return bool|string true if ok, string if error found 6225 */ 6226 public function validate($data) { 6227 if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) { 6228 return true; 6229 } else { 6230 return get_string('gradepointmax_validateerror', 'grades'); 6231 } 6232 } 6233 6234 /** 6235 * Return an XHTML string for the setting 6236 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx 6237 * @param string $query search query to be highlighted 6238 * @return string XHTML to display control 6239 */ 6240 public function output_html($data, $query = '') { 6241 global $OUTPUT; 6242 6243 $default = $this->get_defaultsetting(); 6244 $context = (object) [ 6245 'size' => $this->size, 6246 'id' => $this->get_id(), 6247 'name' => $this->get_full_name(), 6248 'value' => $data, 6249 'attributes' => [ 6250 'maxlength' => 5 6251 ], 6252 'forceltr' => $this->get_force_ltr() 6253 ]; 6254 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context); 6255 6256 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 6257 } 6258 } 6259 6260 6261 /** 6262 * Grade category settings 6263 * 6264 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6265 */ 6266 class admin_setting_gradecat_combo extends admin_setting { 6267 6268 /** @var array Array of choices value=>label. */ 6269 public $choices; 6270 6271 /** 6272 * Sets choices and calls parent::__construct with passed arguments 6273 * @param string $name 6274 * @param string $visiblename 6275 * @param string $description 6276 * @param mixed $defaultsetting string or array depending on implementation 6277 * @param array $choices An array of choices for the control 6278 */ 6279 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 6280 $this->choices = $choices; 6281 parent::__construct($name, $visiblename, $description, $defaultsetting); 6282 } 6283 6284 /** 6285 * Return the current setting(s) array 6286 * 6287 * @return array Array of value=>xx, forced=>xx, adv=>xx 6288 */ 6289 public function get_setting() { 6290 global $CFG; 6291 6292 $value = $this->config_read($this->name); 6293 $flag = $this->config_read($this->name.'_flag'); 6294 6295 if (is_null($value) or is_null($flag)) { 6296 return NULL; 6297 } 6298 6299 $flag = (int)$flag; 6300 $forced = (boolean)(1 & $flag); // first bit 6301 $adv = (boolean)(2 & $flag); // second bit 6302 6303 return array('value' => $value, 'forced' => $forced, 'adv' => $adv); 6304 } 6305 6306 /** 6307 * Save the new settings passed in $data 6308 * 6309 * @todo Add vartype handling to ensure $data is array 6310 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx 6311 * @return string empty or error message 6312 */ 6313 public function write_setting($data) { 6314 global $CFG; 6315 6316 $value = $data['value']; 6317 $forced = empty($data['forced']) ? 0 : 1; 6318 $adv = empty($data['adv']) ? 0 : 2; 6319 $flag = ($forced | $adv); //bitwise or 6320 6321 if (!in_array($value, array_keys($this->choices))) { 6322 return 'Error setting '; 6323 } 6324 6325 $oldvalue = $this->config_read($this->name); 6326 $oldflag = (int)$this->config_read($this->name.'_flag'); 6327 $oldforced = (1 & $oldflag); // first bit 6328 6329 $result1 = $this->config_write($this->name, $value); 6330 $result2 = $this->config_write($this->name.'_flag', $flag); 6331 6332 // force regrade if needed 6333 if ($oldforced != $forced or ($forced and $value != $oldvalue)) { 6334 require_once($CFG->libdir.'/gradelib.php'); 6335 grade_category::updated_forced_settings(); 6336 } 6337 6338 if ($result1 and $result2) { 6339 return ''; 6340 } else { 6341 return get_string('errorsetting', 'admin'); 6342 } 6343 } 6344 6345 /** 6346 * Return XHTML to display the field and wrapping div 6347 * 6348 * @todo Add vartype handling to ensure $data is array 6349 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx 6350 * @param string $query 6351 * @return string XHTML to display control 6352 */ 6353 public function output_html($data, $query='') { 6354 global $OUTPUT; 6355 6356 $value = $data['value']; 6357 6358 $default = $this->get_defaultsetting(); 6359 if (!is_null($default)) { 6360 $defaultinfo = array(); 6361 if (isset($this->choices[$default['value']])) { 6362 $defaultinfo[] = $this->choices[$default['value']]; 6363 } 6364 if (!empty($default['forced'])) { 6365 $defaultinfo[] = get_string('force'); 6366 } 6367 if (!empty($default['adv'])) { 6368 $defaultinfo[] = get_string('advanced'); 6369 } 6370 $defaultinfo = implode(', ', $defaultinfo); 6371 6372 } else { 6373 $defaultinfo = NULL; 6374 } 6375 6376 $options = $this->choices; 6377 $context = (object) [ 6378 'id' => $this->get_id(), 6379 'name' => $this->get_full_name(), 6380 'forced' => !empty($data['forced']), 6381 'advanced' => !empty($data['adv']), 6382 'options' => array_map(function($option) use ($options, $value) { 6383 return [ 6384 'value' => $option, 6385 'name' => $options[$option], 6386 'selected' => $option == $value 6387 ]; 6388 }, array_keys($options)), 6389 ]; 6390 6391 $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context); 6392 6393 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 6394 } 6395 } 6396 6397 6398 /** 6399 * Selection of grade report in user profiles 6400 * 6401 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6402 */ 6403 class admin_setting_grade_profilereport extends admin_setting_configselect { 6404 /** 6405 * Calls parent::__construct with specific arguments 6406 */ 6407 public function __construct() { 6408 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null); 6409 } 6410 6411 /** 6412 * Loads an array of choices for the configselect control 6413 * 6414 * @return bool always return true 6415 */ 6416 public function load_choices() { 6417 if (is_array($this->choices)) { 6418 return true; 6419 } 6420 $this->choices = array(); 6421 6422 global $CFG; 6423 require_once($CFG->libdir.'/gradelib.php'); 6424 6425 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) { 6426 if (file_exists($plugindir.'/lib.php')) { 6427 require_once($plugindir.'/lib.php'); 6428 $functionname = 'grade_report_'.$plugin.'_profilereport'; 6429 if (function_exists($functionname)) { 6430 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin); 6431 } 6432 } 6433 } 6434 return true; 6435 } 6436 } 6437 6438 /** 6439 * Provides a selection of grade reports to be used for "grades". 6440 * 6441 * @copyright 2015 Adrian Greeve <adrian@moodle.com> 6442 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6443 */ 6444 class admin_setting_my_grades_report extends admin_setting_configselect { 6445 6446 /** 6447 * Calls parent::__construct with specific arguments. 6448 */ 6449 public function __construct() { 6450 parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'), 6451 new lang_string('mygrades_desc', 'grades'), 'overview', null); 6452 } 6453 6454 /** 6455 * Loads an array of choices for the configselect control. 6456 * 6457 * @return bool always returns true. 6458 */ 6459 public function load_choices() { 6460 global $CFG; // Remove this line and behold the horror of behat test failures! 6461 $this->choices = array(); 6462 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) { 6463 if (file_exists($plugindir . '/lib.php')) { 6464 require_once($plugindir . '/lib.php'); 6465 // Check to see if the class exists. Check the correct plugin convention first. 6466 if (class_exists('gradereport_' . $plugin)) { 6467 $classname = 'gradereport_' . $plugin; 6468 } else if (class_exists('grade_report_' . $plugin)) { 6469 // We are using the old plugin naming convention. 6470 $classname = 'grade_report_' . $plugin; 6471 } else { 6472 continue; 6473 } 6474 if ($classname::supports_mygrades()) { 6475 $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin); 6476 } 6477 } 6478 } 6479 // Add an option to specify an external url. 6480 $this->choices['external'] = get_string('externalurl', 'grades'); 6481 return true; 6482 } 6483 } 6484 6485 /** 6486 * Special class for register auth selection 6487 * 6488 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6489 */ 6490 class admin_setting_special_registerauth extends admin_setting_configselect { 6491 /** 6492 * Calls parent::__construct with specific arguments 6493 */ 6494 public function __construct() { 6495 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null); 6496 } 6497 6498 /** 6499 * Returns the default option 6500 * 6501 * @return string empty or default option 6502 */ 6503 public function get_defaultsetting() { 6504 $this->load_choices(); 6505 $defaultsetting = parent::get_defaultsetting(); 6506 if (array_key_exists($defaultsetting, $this->choices)) { 6507 return $defaultsetting; 6508 } else { 6509 return ''; 6510 } 6511 } 6512 6513 /** 6514 * Loads the possible choices for the array 6515 * 6516 * @return bool always returns true 6517 */ 6518 public function load_choices() { 6519 global $CFG; 6520 6521 if (is_array($this->choices)) { 6522 return true; 6523 } 6524 $this->choices = array(); 6525 $this->choices[''] = get_string('disable'); 6526 6527 $authsenabled = get_enabled_auth_plugins(); 6528 6529 foreach ($authsenabled as $auth) { 6530 $authplugin = get_auth_plugin($auth); 6531 if (!$authplugin->can_signup()) { 6532 continue; 6533 } 6534 // Get the auth title (from core or own auth lang files) 6535 $authtitle = $authplugin->get_title(); 6536 $this->choices[$auth] = $authtitle; 6537 } 6538 return true; 6539 } 6540 } 6541 6542 6543 /** 6544 * General plugins manager 6545 */ 6546 class admin_page_pluginsoverview extends admin_externalpage { 6547 6548 /** 6549 * Sets basic information about the external page 6550 */ 6551 public function __construct() { 6552 global $CFG; 6553 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'), 6554 "$CFG->wwwroot/$CFG->admin/plugins.php"); 6555 } 6556 } 6557 6558 /** 6559 * Module manage page 6560 * 6561 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6562 */ 6563 class admin_page_managemods extends admin_externalpage { 6564 /** 6565 * Calls parent::__construct with specific arguments 6566 */ 6567 public function __construct() { 6568 global $CFG; 6569 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php"); 6570 } 6571 6572 /** 6573 * Try to find the specified module 6574 * 6575 * @param string $query The module to search for 6576 * @return array 6577 */ 6578 public function search($query) { 6579 global $CFG, $DB; 6580 if ($result = parent::search($query)) { 6581 return $result; 6582 } 6583 6584 $found = false; 6585 if ($modules = $DB->get_records('modules')) { 6586 foreach ($modules as $module) { 6587 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) { 6588 continue; 6589 } 6590 if (strpos($module->name, $query) !== false) { 6591 $found = true; 6592 break; 6593 } 6594 $strmodulename = get_string('modulename', $module->name); 6595 if (strpos(core_text::strtolower($strmodulename), $query) !== false) { 6596 $found = true; 6597 break; 6598 } 6599 } 6600 } 6601 if ($found) { 6602 $result = new stdClass(); 6603 $result->page = $this; 6604 $result->settings = array(); 6605 return array($this->name => $result); 6606 } else { 6607 return array(); 6608 } 6609 } 6610 } 6611 6612 6613 /** 6614 * Special class for enrol plugins management. 6615 * 6616 * @copyright 2010 Petr Skoda {@link http://skodak.org} 6617 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6618 */ 6619 class admin_setting_manageenrols extends admin_setting { 6620 /** 6621 * Calls parent::__construct with specific arguments 6622 */ 6623 public function __construct() { 6624 $this->nosave = true; 6625 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', ''); 6626 } 6627 6628 /** 6629 * Always returns true, does nothing 6630 * 6631 * @return true 6632 */ 6633 public function get_setting() { 6634 return true; 6635 } 6636 6637 /** 6638 * Always returns true, does nothing 6639 * 6640 * @return true 6641 */ 6642 public function get_defaultsetting() { 6643 return true; 6644 } 6645 6646 /** 6647 * Always returns '', does not write anything 6648 * 6649 * @return string Always returns '' 6650 */ 6651 public function write_setting($data) { 6652 // do not write any setting 6653 return ''; 6654 } 6655 6656 /** 6657 * Checks if $query is one of the available enrol plugins 6658 * 6659 * @param string $query The string to search for 6660 * @return bool Returns true if found, false if not 6661 */ 6662 public function is_related($query) { 6663 if (parent::is_related($query)) { 6664 return true; 6665 } 6666 6667 $query = core_text::strtolower($query); 6668 $enrols = enrol_get_plugins(false); 6669 foreach ($enrols as $name=>$enrol) { 6670 $localised = get_string('pluginname', 'enrol_'.$name); 6671 if (strpos(core_text::strtolower($name), $query) !== false) { 6672 return true; 6673 } 6674 if (strpos(core_text::strtolower($localised), $query) !== false) { 6675 return true; 6676 } 6677 } 6678 return false; 6679 } 6680 6681 /** 6682 * Builds the XHTML to display the control 6683 * 6684 * @param string $data Unused 6685 * @param string $query 6686 * @return string 6687 */ 6688 public function output_html($data, $query='') { 6689 global $CFG, $OUTPUT, $DB, $PAGE; 6690 6691 // Display strings. 6692 $strup = get_string('up'); 6693 $strdown = get_string('down'); 6694 $strsettings = get_string('settings'); 6695 $strenable = get_string('enable'); 6696 $strdisable = get_string('disable'); 6697 $struninstall = get_string('uninstallplugin', 'core_admin'); 6698 $strusage = get_string('enrolusage', 'enrol'); 6699 $strversion = get_string('version'); 6700 $strtest = get_string('testsettings', 'core_enrol'); 6701 6702 $pluginmanager = core_plugin_manager::instance(); 6703 6704 $enrols_available = enrol_get_plugins(false); 6705 $active_enrols = enrol_get_plugins(true); 6706 6707 $allenrols = array(); 6708 foreach ($active_enrols as $key=>$enrol) { 6709 $allenrols[$key] = true; 6710 } 6711 foreach ($enrols_available as $key=>$enrol) { 6712 $allenrols[$key] = true; 6713 } 6714 // Now find all borked plugins and at least allow then to uninstall. 6715 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}"); 6716 foreach ($condidates as $candidate) { 6717 if (empty($allenrols[$candidate])) { 6718 $allenrols[$candidate] = true; 6719 } 6720 } 6721 6722 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true); 6723 $return .= $OUTPUT->box_start('generalbox enrolsui'); 6724 6725 $table = new html_table(); 6726 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall); 6727 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 6728 $table->id = 'courseenrolmentplugins'; 6729 $table->attributes['class'] = 'admintable generaltable'; 6730 $table->data = array(); 6731 6732 // Iterate through enrol plugins and add to the display table. 6733 $updowncount = 1; 6734 $enrolcount = count($active_enrols); 6735 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey())); 6736 $printed = array(); 6737 foreach($allenrols as $enrol => $unused) { 6738 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol); 6739 $version = get_config('enrol_'.$enrol, 'version'); 6740 if ($version === false) { 6741 $version = ''; 6742 } 6743 6744 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) { 6745 $name = get_string('pluginname', 'enrol_'.$enrol); 6746 } else { 6747 $name = $enrol; 6748 } 6749 // Usage. 6750 $ci = $DB->count_records('enrol', array('enrol'=>$enrol)); 6751 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol)); 6752 $usage = "$ci / $cp"; 6753 6754 // Hide/show links. 6755 $class = ''; 6756 if (isset($active_enrols[$enrol])) { 6757 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol)); 6758 $hideshow = "<a href=\"$aurl\">"; 6759 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>'; 6760 $enabled = true; 6761 $displayname = $name; 6762 } else if (isset($enrols_available[$enrol])) { 6763 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol)); 6764 $hideshow = "<a href=\"$aurl\">"; 6765 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>'; 6766 $enabled = false; 6767 $displayname = $name; 6768 $class = 'dimmed_text'; 6769 } else { 6770 $hideshow = ''; 6771 $enabled = false; 6772 $displayname = '<span class="notifyproblem">'.$name.'</span>'; 6773 } 6774 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) { 6775 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon')); 6776 } else { 6777 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon')); 6778 } 6779 6780 // Up/down link (only if enrol is enabled). 6781 $updown = ''; 6782 if ($enabled) { 6783 if ($updowncount > 1) { 6784 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol)); 6785 $updown .= "<a href=\"$aurl\">"; 6786 $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a> '; 6787 } else { 6788 $updown .= $OUTPUT->spacer() . ' '; 6789 } 6790 if ($updowncount < $enrolcount) { 6791 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol)); 6792 $updown .= "<a href=\"$aurl\">"; 6793 $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a> '; 6794 } else { 6795 $updown .= $OUTPUT->spacer() . ' '; 6796 } 6797 ++$updowncount; 6798 } 6799 6800 // Add settings link. 6801 if (!$version) { 6802 $settings = ''; 6803 } else if ($surl = $plugininfo->get_settings_url()) { 6804 $settings = html_writer::link($surl, $strsettings); 6805 } else { 6806 $settings = ''; 6807 } 6808 6809 // Add uninstall info. 6810 $uninstall = ''; 6811 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) { 6812 $uninstall = html_writer::link($uninstallurl, $struninstall); 6813 } 6814 6815 $test = ''; 6816 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) { 6817 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey())); 6818 $test = html_writer::link($testsettingsurl, $strtest); 6819 } 6820 6821 // Add a row to the table. 6822 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall)); 6823 if ($class) { 6824 $row->attributes['class'] = $class; 6825 } 6826 $table->data[] = $row; 6827 6828 $printed[$enrol] = true; 6829 } 6830 6831 $return .= html_writer::table($table); 6832 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin'); 6833 $return .= $OUTPUT->box_end(); 6834 return highlight($query, $return); 6835 } 6836 } 6837 6838 6839 /** 6840 * Blocks manage page 6841 * 6842 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6843 */ 6844 class admin_page_manageblocks extends admin_externalpage { 6845 /** 6846 * Calls parent::__construct with specific arguments 6847 */ 6848 public function __construct() { 6849 global $CFG; 6850 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php"); 6851 } 6852 6853 /** 6854 * Search for a specific block 6855 * 6856 * @param string $query The string to search for 6857 * @return array 6858 */ 6859 public function search($query) { 6860 global $CFG, $DB; 6861 if ($result = parent::search($query)) { 6862 return $result; 6863 } 6864 6865 $found = false; 6866 if ($blocks = $DB->get_records('block')) { 6867 foreach ($blocks as $block) { 6868 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) { 6869 continue; 6870 } 6871 if (strpos($block->name, $query) !== false) { 6872 $found = true; 6873 break; 6874 } 6875 $strblockname = get_string('pluginname', 'block_'.$block->name); 6876 if (strpos(core_text::strtolower($strblockname), $query) !== false) { 6877 $found = true; 6878 break; 6879 } 6880 } 6881 } 6882 if ($found) { 6883 $result = new stdClass(); 6884 $result->page = $this; 6885 $result->settings = array(); 6886 return array($this->name => $result); 6887 } else { 6888 return array(); 6889 } 6890 } 6891 } 6892 6893 /** 6894 * Message outputs configuration 6895 * 6896 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6897 */ 6898 class admin_page_managemessageoutputs extends admin_externalpage { 6899 /** 6900 * Calls parent::__construct with specific arguments 6901 */ 6902 public function __construct() { 6903 global $CFG; 6904 parent::__construct('managemessageoutputs', 6905 get_string('defaultmessageoutputs', 'message'), 6906 new moodle_url('/admin/message.php') 6907 ); 6908 } 6909 6910 /** 6911 * Search for a specific message processor 6912 * 6913 * @param string $query The string to search for 6914 * @return array 6915 */ 6916 public function search($query) { 6917 global $CFG, $DB; 6918 if ($result = parent::search($query)) { 6919 return $result; 6920 } 6921 6922 $found = false; 6923 if ($processors = get_message_processors()) { 6924 foreach ($processors as $processor) { 6925 if (!$processor->available) { 6926 continue; 6927 } 6928 if (strpos($processor->name, $query) !== false) { 6929 $found = true; 6930 break; 6931 } 6932 $strprocessorname = get_string('pluginname', 'message_'.$processor->name); 6933 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) { 6934 $found = true; 6935 break; 6936 } 6937 } 6938 } 6939 if ($found) { 6940 $result = new stdClass(); 6941 $result->page = $this; 6942 $result->settings = array(); 6943 return array($this->name => $result); 6944 } else { 6945 return array(); 6946 } 6947 } 6948 } 6949 6950 /** 6951 * Manage question behaviours page 6952 * 6953 * @copyright 2011 The Open University 6954 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6955 */ 6956 class admin_page_manageqbehaviours extends admin_externalpage { 6957 /** 6958 * Constructor 6959 */ 6960 public function __construct() { 6961 global $CFG; 6962 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'), 6963 new moodle_url('/admin/qbehaviours.php')); 6964 } 6965 6966 /** 6967 * Search question behaviours for the specified string 6968 * 6969 * @param string $query The string to search for in question behaviours 6970 * @return array 6971 */ 6972 public function search($query) { 6973 global $CFG; 6974 if ($result = parent::search($query)) { 6975 return $result; 6976 } 6977 6978 $found = false; 6979 require_once($CFG->dirroot . '/question/engine/lib.php'); 6980 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) { 6981 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)), 6982 $query) !== false) { 6983 $found = true; 6984 break; 6985 } 6986 } 6987 if ($found) { 6988 $result = new stdClass(); 6989 $result->page = $this; 6990 $result->settings = array(); 6991 return array($this->name => $result); 6992 } else { 6993 return array(); 6994 } 6995 } 6996 } 6997 6998 6999 /** 7000 * Question type manage page 7001 * 7002 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7003 */ 7004 class admin_page_manageqtypes extends admin_externalpage { 7005 /** 7006 * Calls parent::__construct with specific arguments 7007 */ 7008 public function __construct() { 7009 global $CFG; 7010 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'), 7011 new moodle_url('/admin/qtypes.php')); 7012 } 7013 7014 /** 7015 * Search question types for the specified string 7016 * 7017 * @param string $query The string to search for in question types 7018 * @return array 7019 */ 7020 public function search($query) { 7021 global $CFG; 7022 if ($result = parent::search($query)) { 7023 return $result; 7024 } 7025 7026 $found = false; 7027 require_once($CFG->dirroot . '/question/engine/bank.php'); 7028 foreach (question_bank::get_all_qtypes() as $qtype) { 7029 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) { 7030 $found = true; 7031 break; 7032 } 7033 } 7034 if ($found) { 7035 $result = new stdClass(); 7036 $result->page = $this; 7037 $result->settings = array(); 7038 return array($this->name => $result); 7039 } else { 7040 return array(); 7041 } 7042 } 7043 } 7044 7045 7046 class admin_page_manageportfolios extends admin_externalpage { 7047 /** 7048 * Calls parent::__construct with specific arguments 7049 */ 7050 public function __construct() { 7051 global $CFG; 7052 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'), 7053 "$CFG->wwwroot/$CFG->admin/portfolio.php"); 7054 } 7055 7056 /** 7057 * Searches page for the specified string. 7058 * @param string $query The string to search for 7059 * @return bool True if it is found on this page 7060 */ 7061 public function search($query) { 7062 global $CFG; 7063 if ($result = parent::search($query)) { 7064 return $result; 7065 } 7066 7067 $found = false; 7068 $portfolios = core_component::get_plugin_list('portfolio'); 7069 foreach ($portfolios as $p => $dir) { 7070 if (strpos($p, $query) !== false) { 7071 $found = true; 7072 break; 7073 } 7074 } 7075 if (!$found) { 7076 foreach (portfolio_instances(false, false) as $instance) { 7077 $title = $instance->get('name'); 7078 if (strpos(core_text::strtolower($title), $query) !== false) { 7079 $found = true; 7080 break; 7081 } 7082 } 7083 } 7084 7085 if ($found) { 7086 $result = new stdClass(); 7087 $result->page = $this; 7088 $result->settings = array(); 7089 return array($this->name => $result); 7090 } else { 7091 return array(); 7092 } 7093 } 7094 } 7095 7096 7097 class admin_page_managerepositories extends admin_externalpage { 7098 /** 7099 * Calls parent::__construct with specific arguments 7100 */ 7101 public function __construct() { 7102 global $CFG; 7103 parent::__construct('managerepositories', get_string('manage', 7104 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php"); 7105 } 7106 7107 /** 7108 * Searches page for the specified string. 7109 * @param string $query The string to search for 7110 * @return bool True if it is found on this page 7111 */ 7112 public function search($query) { 7113 global $CFG; 7114 if ($result = parent::search($query)) { 7115 return $result; 7116 } 7117 7118 $found = false; 7119 $repositories= core_component::get_plugin_list('repository'); 7120 foreach ($repositories as $p => $dir) { 7121 if (strpos($p, $query) !== false) { 7122 $found = true; 7123 break; 7124 } 7125 } 7126 if (!$found) { 7127 foreach (repository::get_types() as $instance) { 7128 $title = $instance->get_typename(); 7129 if (strpos(core_text::strtolower($title), $query) !== false) { 7130 $found = true; 7131 break; 7132 } 7133 } 7134 } 7135 7136 if ($found) { 7137 $result = new stdClass(); 7138 $result->page = $this; 7139 $result->settings = array(); 7140 return array($this->name => $result); 7141 } else { 7142 return array(); 7143 } 7144 } 7145 } 7146 7147 7148 /** 7149 * Special class for authentication administration. 7150 * 7151 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7152 */ 7153 class admin_setting_manageauths extends admin_setting { 7154 /** 7155 * Calls parent::__construct with specific arguments 7156 */ 7157 public function __construct() { 7158 $this->nosave = true; 7159 parent::__construct('authsui', get_string('authsettings', 'admin'), '', ''); 7160 } 7161 7162 /** 7163 * Always returns true 7164 * 7165 * @return true 7166 */ 7167 public function get_setting() { 7168 return true; 7169 } 7170 7171 /** 7172 * Always returns true 7173 * 7174 * @return true 7175 */ 7176 public function get_defaultsetting() { 7177 return true; 7178 } 7179 7180 /** 7181 * Always returns '' and doesn't write anything 7182 * 7183 * @return string Always returns '' 7184 */ 7185 public function write_setting($data) { 7186 // do not write any setting 7187 return ''; 7188 } 7189 7190 /** 7191 * Search to find if Query is related to auth plugin 7192 * 7193 * @param string $query The string to search for 7194 * @return bool true for related false for not 7195 */ 7196 public function is_related($query) { 7197 if (parent::is_related($query)) { 7198 return true; 7199 } 7200 7201 $authsavailable = core_component::get_plugin_list('auth'); 7202 foreach ($authsavailable as $auth => $dir) { 7203 if (strpos($auth, $query) !== false) { 7204 return true; 7205 } 7206 $authplugin = get_auth_plugin($auth); 7207 $authtitle = $authplugin->get_title(); 7208 if (strpos(core_text::strtolower($authtitle), $query) !== false) { 7209 return true; 7210 } 7211 } 7212 return false; 7213 } 7214 7215 /** 7216 * Return XHTML to display control 7217 * 7218 * @param mixed $data Unused 7219 * @param string $query 7220 * @return string highlight 7221 */ 7222 public function output_html($data, $query='') { 7223 global $CFG, $OUTPUT, $DB; 7224 7225 // display strings 7226 $txt = get_strings(array('authenticationplugins', 'users', 'administration', 7227 'settings', 'edit', 'name', 'enable', 'disable', 7228 'up', 'down', 'none', 'users')); 7229 $txt->updown = "$txt->up/$txt->down"; 7230 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7231 $txt->testsettings = get_string('testsettings', 'core_auth'); 7232 7233 $authsavailable = core_component::get_plugin_list('auth'); 7234 get_enabled_auth_plugins(true); // fix the list of enabled auths 7235 if (empty($CFG->auth)) { 7236 $authsenabled = array(); 7237 } else { 7238 $authsenabled = explode(',', $CFG->auth); 7239 } 7240 7241 // construct the display array, with enabled auth plugins at the top, in order 7242 $displayauths = array(); 7243 $registrationauths = array(); 7244 $registrationauths[''] = $txt->disable; 7245 $authplugins = array(); 7246 foreach ($authsenabled as $auth) { 7247 $authplugin = get_auth_plugin($auth); 7248 $authplugins[$auth] = $authplugin; 7249 /// Get the auth title (from core or own auth lang files) 7250 $authtitle = $authplugin->get_title(); 7251 /// Apply titles 7252 $displayauths[$auth] = $authtitle; 7253 if ($authplugin->can_signup()) { 7254 $registrationauths[$auth] = $authtitle; 7255 } 7256 } 7257 7258 foreach ($authsavailable as $auth => $dir) { 7259 if (array_key_exists($auth, $displayauths)) { 7260 continue; //already in the list 7261 } 7262 $authplugin = get_auth_plugin($auth); 7263 $authplugins[$auth] = $authplugin; 7264 /// Get the auth title (from core or own auth lang files) 7265 $authtitle = $authplugin->get_title(); 7266 /// Apply titles 7267 $displayauths[$auth] = $authtitle; 7268 if ($authplugin->can_signup()) { 7269 $registrationauths[$auth] = $authtitle; 7270 } 7271 } 7272 7273 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main'); 7274 $return .= $OUTPUT->box_start('generalbox authsui'); 7275 7276 $table = new html_table(); 7277 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall); 7278 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 7279 $table->data = array(); 7280 $table->attributes['class'] = 'admintable generaltable'; 7281 $table->id = 'manageauthtable'; 7282 7283 //add always enabled plugins first 7284 $displayname = $displayauths['manual']; 7285 $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>"; 7286 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0)); 7287 $table->data[] = array($displayname, $usercount, '', '', $settings, '', ''); 7288 $displayname = $displayauths['nologin']; 7289 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0)); 7290 $table->data[] = array($displayname, $usercount, '', '', '', '', ''); 7291 7292 7293 // iterate through auth plugins and add to the display table 7294 $updowncount = 1; 7295 $authcount = count($authsenabled); 7296 $url = "auth.php?sesskey=" . sesskey(); 7297 foreach ($displayauths as $auth => $name) { 7298 if ($auth == 'manual' or $auth == 'nologin') { 7299 continue; 7300 } 7301 $class = ''; 7302 // hide/show link 7303 if (in_array($auth, $authsenabled)) { 7304 $hideshow = "<a href=\"$url&action=disable&auth=$auth\">"; 7305 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>'; 7306 $enabled = true; 7307 $displayname = $name; 7308 } 7309 else { 7310 $hideshow = "<a href=\"$url&action=enable&auth=$auth\">"; 7311 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>'; 7312 $enabled = false; 7313 $displayname = $name; 7314 $class = 'dimmed_text'; 7315 } 7316 7317 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0)); 7318 7319 // up/down link (only if auth is enabled) 7320 $updown = ''; 7321 if ($enabled) { 7322 if ($updowncount > 1) { 7323 $updown .= "<a href=\"$url&action=up&auth=$auth\">"; 7324 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a> '; 7325 } 7326 else { 7327 $updown .= $OUTPUT->spacer() . ' '; 7328 } 7329 if ($updowncount < $authcount) { 7330 $updown .= "<a href=\"$url&action=down&auth=$auth\">"; 7331 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a> '; 7332 } 7333 else { 7334 $updown .= $OUTPUT->spacer() . ' '; 7335 } 7336 ++ $updowncount; 7337 } 7338 7339 // settings link 7340 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) { 7341 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>"; 7342 } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) { 7343 throw new \coding_exception('config.html is no longer supported, please use settings.php instead.'); 7344 } else { 7345 $settings = ''; 7346 } 7347 7348 // Uninstall link. 7349 $uninstall = ''; 7350 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) { 7351 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7352 } 7353 7354 $test = ''; 7355 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) { 7356 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey())); 7357 $test = html_writer::link($testurl, $txt->testsettings); 7358 } 7359 7360 // Add a row to the table. 7361 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall)); 7362 if ($class) { 7363 $row->attributes['class'] = $class; 7364 } 7365 $table->data[] = $row; 7366 } 7367 $return .= html_writer::table($table); 7368 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters'); 7369 $return .= $OUTPUT->box_end(); 7370 return highlight($query, $return); 7371 } 7372 } 7373 7374 /** 7375 * Special class for antiviruses administration. 7376 * 7377 * @copyright 2015 Ruslan Kabalin, Lancaster University. 7378 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7379 */ 7380 class admin_setting_manageantiviruses extends admin_setting { 7381 /** 7382 * Calls parent::__construct with specific arguments 7383 */ 7384 public function __construct() { 7385 $this->nosave = true; 7386 parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', ''); 7387 } 7388 7389 /** 7390 * Always returns true, does nothing 7391 * 7392 * @return true 7393 */ 7394 public function get_setting() { 7395 return true; 7396 } 7397 7398 /** 7399 * Always returns true, does nothing 7400 * 7401 * @return true 7402 */ 7403 public function get_defaultsetting() { 7404 return true; 7405 } 7406 7407 /** 7408 * Always returns '', does not write anything 7409 * 7410 * @param string $data Unused 7411 * @return string Always returns '' 7412 */ 7413 public function write_setting($data) { 7414 // Do not write any setting. 7415 return ''; 7416 } 7417 7418 /** 7419 * Checks if $query is one of the available editors 7420 * 7421 * @param string $query The string to search for 7422 * @return bool Returns true if found, false if not 7423 */ 7424 public function is_related($query) { 7425 if (parent::is_related($query)) { 7426 return true; 7427 } 7428 7429 $antivirusesavailable = \core\antivirus\manager::get_available(); 7430 foreach ($antivirusesavailable as $antivirus => $antivirusstr) { 7431 if (strpos($antivirus, $query) !== false) { 7432 return true; 7433 } 7434 if (strpos(core_text::strtolower($antivirusstr), $query) !== false) { 7435 return true; 7436 } 7437 } 7438 return false; 7439 } 7440 7441 /** 7442 * Builds the XHTML to display the control 7443 * 7444 * @param string $data Unused 7445 * @param string $query 7446 * @return string 7447 */ 7448 public function output_html($data, $query='') { 7449 global $CFG, $OUTPUT; 7450 7451 // Display strings. 7452 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable', 7453 'up', 'down', 'none')); 7454 $struninstall = get_string('uninstallplugin', 'core_admin'); 7455 7456 $txt->updown = "$txt->up/$txt->down"; 7457 7458 $antivirusesavailable = \core\antivirus\manager::get_available(); 7459 $activeantiviruses = explode(',', $CFG->antiviruses); 7460 7461 $activeantiviruses = array_reverse($activeantiviruses); 7462 foreach ($activeantiviruses as $key => $antivirus) { 7463 if (empty($antivirusesavailable[$antivirus])) { 7464 unset($activeantiviruses[$key]); 7465 } else { 7466 $name = $antivirusesavailable[$antivirus]; 7467 unset($antivirusesavailable[$antivirus]); 7468 $antivirusesavailable[$antivirus] = $name; 7469 } 7470 } 7471 $antivirusesavailable = array_reverse($antivirusesavailable, true); 7472 $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true); 7473 $return .= $OUTPUT->box_start('generalbox antivirusesui'); 7474 7475 $table = new html_table(); 7476 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall); 7477 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 7478 $table->id = 'antivirusmanagement'; 7479 $table->attributes['class'] = 'admintable generaltable'; 7480 $table->data = array(); 7481 7482 // Iterate through auth plugins and add to the display table. 7483 $updowncount = 1; 7484 $antiviruscount = count($activeantiviruses); 7485 $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey())); 7486 foreach ($antivirusesavailable as $antivirus => $name) { 7487 // Hide/show link. 7488 $class = ''; 7489 if (in_array($antivirus, $activeantiviruses)) { 7490 $hideshowurl = $baseurl; 7491 $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus)); 7492 $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable')); 7493 $hideshow = html_writer::link($hideshowurl, $hideshowimg); 7494 $enabled = true; 7495 $displayname = $name; 7496 } else { 7497 $hideshowurl = $baseurl; 7498 $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus)); 7499 $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable')); 7500 $hideshow = html_writer::link($hideshowurl, $hideshowimg); 7501 $enabled = false; 7502 $displayname = $name; 7503 $class = 'dimmed_text'; 7504 } 7505 7506 // Up/down link. 7507 $updown = ''; 7508 if ($enabled) { 7509 if ($updowncount > 1) { 7510 $updownurl = $baseurl; 7511 $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus)); 7512 $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup')); 7513 $updown = html_writer::link($updownurl, $updownimg); 7514 } else { 7515 $updownimg = $OUTPUT->spacer(); 7516 } 7517 if ($updowncount < $antiviruscount) { 7518 $updownurl = $baseurl; 7519 $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus)); 7520 $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown')); 7521 $updown = html_writer::link($updownurl, $updownimg); 7522 } else { 7523 $updownimg = $OUTPUT->spacer(); 7524 } 7525 ++ $updowncount; 7526 } 7527 7528 // Settings link. 7529 if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) { 7530 $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus)); 7531 $settings = html_writer::link($eurl, $txt->settings); 7532 } else { 7533 $settings = ''; 7534 } 7535 7536 $uninstall = ''; 7537 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) { 7538 $uninstall = html_writer::link($uninstallurl, $struninstall); 7539 } 7540 7541 // Add a row to the table. 7542 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall)); 7543 if ($class) { 7544 $row->attributes['class'] = $class; 7545 } 7546 $table->data[] = $row; 7547 } 7548 $return .= html_writer::table($table); 7549 $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin'); 7550 $return .= $OUTPUT->box_end(); 7551 return highlight($query, $return); 7552 } 7553 } 7554 7555 /** 7556 * Course formats manager. Allows to enable/disable formats and jump to settings 7557 */ 7558 class admin_setting_manageformats extends admin_setting { 7559 7560 /** 7561 * Calls parent::__construct with specific arguments 7562 */ 7563 public function __construct() { 7564 $this->nosave = true; 7565 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', ''); 7566 } 7567 7568 /** 7569 * Always returns true 7570 * 7571 * @return true 7572 */ 7573 public function get_setting() { 7574 return true; 7575 } 7576 7577 /** 7578 * Always returns true 7579 * 7580 * @return true 7581 */ 7582 public function get_defaultsetting() { 7583 return true; 7584 } 7585 7586 /** 7587 * Always returns '' and doesn't write anything 7588 * 7589 * @param mixed $data string or array, must not be NULL 7590 * @return string Always returns '' 7591 */ 7592 public function write_setting($data) { 7593 // do not write any setting 7594 return ''; 7595 } 7596 7597 /** 7598 * Search to find if Query is related to format plugin 7599 * 7600 * @param string $query The string to search for 7601 * @return bool true for related false for not 7602 */ 7603 public function is_related($query) { 7604 if (parent::is_related($query)) { 7605 return true; 7606 } 7607 $formats = core_plugin_manager::instance()->get_plugins_of_type('format'); 7608 foreach ($formats as $format) { 7609 if (strpos($format->component, $query) !== false || 7610 strpos(core_text::strtolower($format->displayname), $query) !== false) { 7611 return true; 7612 } 7613 } 7614 return false; 7615 } 7616 7617 /** 7618 * Return XHTML to display control 7619 * 7620 * @param mixed $data Unused 7621 * @param string $query 7622 * @return string highlight 7623 */ 7624 public function output_html($data, $query='') { 7625 global $CFG, $OUTPUT; 7626 $return = ''; 7627 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main'); 7628 $return .= $OUTPUT->box_start('generalbox formatsui'); 7629 7630 $formats = core_plugin_manager::instance()->get_plugins_of_type('format'); 7631 7632 // display strings 7633 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default')); 7634 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7635 $txt->updown = "$txt->up/$txt->down"; 7636 7637 $table = new html_table(); 7638 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings); 7639 $table->align = array('left', 'center', 'center', 'center', 'center'); 7640 $table->attributes['class'] = 'manageformattable generaltable admintable'; 7641 $table->data = array(); 7642 7643 $cnt = 0; 7644 $defaultformat = get_config('moodlecourse', 'format'); 7645 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 7646 foreach ($formats as $format) { 7647 $url = new moodle_url('/admin/courseformats.php', 7648 array('sesskey' => sesskey(), 'format' => $format->name)); 7649 $isdefault = ''; 7650 $class = ''; 7651 if ($format->is_enabled()) { 7652 $strformatname = $format->displayname; 7653 if ($defaultformat === $format->name) { 7654 $hideshow = $txt->default; 7655 } else { 7656 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 7657 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 7658 } 7659 } else { 7660 $strformatname = $format->displayname; 7661 $class = 'dimmed_text'; 7662 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 7663 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 7664 } 7665 $updown = ''; 7666 if ($cnt) { 7667 $updown .= html_writer::link($url->out(false, array('action' => 'up')), 7668 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). ''; 7669 } else { 7670 $updown .= $spacer; 7671 } 7672 if ($cnt < count($formats) - 1) { 7673 $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')), 7674 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall'))); 7675 } else { 7676 $updown .= $spacer; 7677 } 7678 $cnt++; 7679 $settings = ''; 7680 if ($format->get_settings_url()) { 7681 $settings = html_writer::link($format->get_settings_url(), $txt->settings); 7682 } 7683 $uninstall = ''; 7684 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) { 7685 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7686 } 7687 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings)); 7688 if ($class) { 7689 $row->attributes['class'] = $class; 7690 } 7691 $table->data[] = $row; 7692 } 7693 $return .= html_writer::table($table); 7694 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings')); 7695 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link)); 7696 $return .= $OUTPUT->box_end(); 7697 return highlight($query, $return); 7698 } 7699 } 7700 7701 /** 7702 * Custom fields manager. Allows to enable/disable custom fields and jump to settings. 7703 * 7704 * @package core 7705 * @copyright 2018 Toni Barbera 7706 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7707 */ 7708 class admin_setting_managecustomfields extends admin_setting { 7709 7710 /** 7711 * Calls parent::__construct with specific arguments 7712 */ 7713 public function __construct() { 7714 $this->nosave = true; 7715 parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', ''); 7716 } 7717 7718 /** 7719 * Always returns true 7720 * 7721 * @return true 7722 */ 7723 public function get_setting() { 7724 return true; 7725 } 7726 7727 /** 7728 * Always returns true 7729 * 7730 * @return true 7731 */ 7732 public function get_defaultsetting() { 7733 return true; 7734 } 7735 7736 /** 7737 * Always returns '' and doesn't write anything 7738 * 7739 * @param mixed $data string or array, must not be NULL 7740 * @return string Always returns '' 7741 */ 7742 public function write_setting($data) { 7743 // Do not write any setting. 7744 return ''; 7745 } 7746 7747 /** 7748 * Search to find if Query is related to format plugin 7749 * 7750 * @param string $query The string to search for 7751 * @return bool true for related false for not 7752 */ 7753 public function is_related($query) { 7754 if (parent::is_related($query)) { 7755 return true; 7756 } 7757 $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield'); 7758 foreach ($formats as $format) { 7759 if (strpos($format->component, $query) !== false || 7760 strpos(core_text::strtolower($format->displayname), $query) !== false) { 7761 return true; 7762 } 7763 } 7764 return false; 7765 } 7766 7767 /** 7768 * Return XHTML to display control 7769 * 7770 * @param mixed $data Unused 7771 * @param string $query 7772 * @return string highlight 7773 */ 7774 public function output_html($data, $query='') { 7775 global $CFG, $OUTPUT; 7776 $return = ''; 7777 $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main'); 7778 $return .= $OUTPUT->box_start('generalbox customfieldsui'); 7779 7780 $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield'); 7781 7782 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down')); 7783 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7784 $txt->updown = "$txt->up/$txt->down"; 7785 7786 $table = new html_table(); 7787 $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings); 7788 $table->align = array('left', 'center', 'center', 'center'); 7789 $table->attributes['class'] = 'managecustomfieldtable generaltable admintable'; 7790 $table->data = array(); 7791 7792 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 7793 foreach ($fields as $field) { 7794 $url = new moodle_url('/admin/customfields.php', 7795 array('sesskey' => sesskey(), 'field' => $field->name)); 7796 7797 if ($field->is_enabled()) { 7798 $strfieldname = $field->displayname; 7799 $class = ''; 7800 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 7801 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 7802 } else { 7803 $strfieldname = $field->displayname; 7804 $class = 'dimmed_text'; 7805 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 7806 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 7807 } 7808 $settings = ''; 7809 if ($field->get_settings_url()) { 7810 $settings = html_writer::link($field->get_settings_url(), $txt->settings); 7811 } 7812 $uninstall = ''; 7813 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) { 7814 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7815 } 7816 $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings)); 7817 $row->attributes['class'] = $class; 7818 $table->data[] = $row; 7819 } 7820 $return .= html_writer::table($table); 7821 $return .= $OUTPUT->box_end(); 7822 return highlight($query, $return); 7823 } 7824 } 7825 7826 /** 7827 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings 7828 * 7829 * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net) 7830 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7831 */ 7832 class admin_setting_managedataformats extends admin_setting { 7833 7834 /** 7835 * Calls parent::__construct with specific arguments 7836 */ 7837 public function __construct() { 7838 $this->nosave = true; 7839 parent::__construct('managedataformats', new lang_string('managedataformats'), '', ''); 7840 } 7841 7842 /** 7843 * Always returns true 7844 * 7845 * @return true 7846 */ 7847 public function get_setting() { 7848 return true; 7849 } 7850 7851 /** 7852 * Always returns true 7853 * 7854 * @return true 7855 */ 7856 public function get_defaultsetting() { 7857 return true; 7858 } 7859 7860 /** 7861 * Always returns '' and doesn't write anything 7862 * 7863 * @param mixed $data string or array, must not be NULL 7864 * @return string Always returns '' 7865 */ 7866 public function write_setting($data) { 7867 // Do not write any setting. 7868 return ''; 7869 } 7870 7871 /** 7872 * Search to find if Query is related to format plugin 7873 * 7874 * @param string $query The string to search for 7875 * @return bool true for related false for not 7876 */ 7877 public function is_related($query) { 7878 if (parent::is_related($query)) { 7879 return true; 7880 } 7881 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat'); 7882 foreach ($formats as $format) { 7883 if (strpos($format->component, $query) !== false || 7884 strpos(core_text::strtolower($format->displayname), $query) !== false) { 7885 return true; 7886 } 7887 } 7888 return false; 7889 } 7890 7891 /** 7892 * Return XHTML to display control 7893 * 7894 * @param mixed $data Unused 7895 * @param string $query 7896 * @return string highlight 7897 */ 7898 public function output_html($data, $query='') { 7899 global $CFG, $OUTPUT; 7900 $return = ''; 7901 7902 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat'); 7903 7904 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default')); 7905 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7906 $txt->updown = "$txt->up/$txt->down"; 7907 7908 $table = new html_table(); 7909 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings); 7910 $table->align = array('left', 'center', 'center', 'center', 'center'); 7911 $table->attributes['class'] = 'manageformattable generaltable admintable'; 7912 $table->data = array(); 7913 7914 $cnt = 0; 7915 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 7916 $totalenabled = 0; 7917 foreach ($formats as $format) { 7918 if ($format->is_enabled() && $format->is_installed_and_upgraded()) { 7919 $totalenabled++; 7920 } 7921 } 7922 foreach ($formats as $format) { 7923 $status = $format->get_status(); 7924 $url = new moodle_url('/admin/dataformats.php', 7925 array('sesskey' => sesskey(), 'name' => $format->name)); 7926 7927 $class = ''; 7928 if ($format->is_enabled()) { 7929 $strformatname = $format->displayname; 7930 if ($totalenabled == 1&& $format->is_enabled()) { 7931 $hideshow = ''; 7932 } else { 7933 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 7934 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 7935 } 7936 } else { 7937 $class = 'dimmed_text'; 7938 $strformatname = $format->displayname; 7939 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 7940 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 7941 } 7942 7943 $updown = ''; 7944 if ($cnt) { 7945 $updown .= html_writer::link($url->out(false, array('action' => 'up')), 7946 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). ''; 7947 } else { 7948 $updown .= $spacer; 7949 } 7950 if ($cnt < count($formats) - 1) { 7951 $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')), 7952 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall'))); 7953 } else { 7954 $updown .= $spacer; 7955 } 7956 7957 $uninstall = ''; 7958 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) { 7959 $uninstall = get_string('status_missing', 'core_plugin'); 7960 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) { 7961 $uninstall = get_string('status_new', 'core_plugin'); 7962 } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) { 7963 if ($totalenabled != 1 || !$format->is_enabled()) { 7964 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7965 } 7966 } 7967 7968 $settings = ''; 7969 if ($format->get_settings_url()) { 7970 $settings = html_writer::link($format->get_settings_url(), $txt->settings); 7971 } 7972 7973 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings)); 7974 if ($class) { 7975 $row->attributes['class'] = $class; 7976 } 7977 $table->data[] = $row; 7978 $cnt++; 7979 } 7980 $return .= html_writer::table($table); 7981 return highlight($query, $return); 7982 } 7983 } 7984 7985 /** 7986 * Special class for filter administration. 7987 * 7988 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7989 */ 7990 class admin_page_managefilters extends admin_externalpage { 7991 /** 7992 * Calls parent::__construct with specific arguments 7993 */ 7994 public function __construct() { 7995 global $CFG; 7996 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php"); 7997 } 7998 7999 /** 8000 * Searches all installed filters for specified filter 8001 * 8002 * @param string $query The filter(string) to search for 8003 * @param string $query 8004 */ 8005 public function search($query) { 8006 global $CFG; 8007 if ($result = parent::search($query)) { 8008 return $result; 8009 } 8010 8011 $found = false; 8012 $filternames = filter_get_all_installed(); 8013 foreach ($filternames as $path => $strfiltername) { 8014 if (strpos(core_text::strtolower($strfiltername), $query) !== false) { 8015 $found = true; 8016 break; 8017 } 8018 if (strpos($path, $query) !== false) { 8019 $found = true; 8020 break; 8021 } 8022 } 8023 8024 if ($found) { 8025 $result = new stdClass; 8026 $result->page = $this; 8027 $result->settings = array(); 8028 return array($this->name => $result); 8029 } else { 8030 return array(); 8031 } 8032 } 8033 } 8034 8035 /** 8036 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin. 8037 * Requires a get_rank method on the plugininfo class for sorting. 8038 * 8039 * @copyright 2017 Damyon Wiese 8040 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8041 */ 8042 abstract class admin_setting_manage_plugins extends admin_setting { 8043 8044 /** 8045 * Get the admin settings section name (just a unique string) 8046 * 8047 * @return string 8048 */ 8049 public function get_section_name() { 8050 return 'manage' . $this->get_plugin_type() . 'plugins'; 8051 } 8052 8053 /** 8054 * Get the admin settings section title (use get_string). 8055 * 8056 * @return string 8057 */ 8058 abstract public function get_section_title(); 8059 8060 /** 8061 * Get the type of plugin to manage. 8062 * 8063 * @return string 8064 */ 8065 abstract public function get_plugin_type(); 8066 8067 /** 8068 * Get the name of the second column. 8069 * 8070 * @return string 8071 */ 8072 public function get_info_column_name() { 8073 return ''; 8074 } 8075 8076 /** 8077 * Get the type of plugin to manage. 8078 * 8079 * @param plugininfo The plugin info class. 8080 * @return string 8081 */ 8082 abstract public function get_info_column($plugininfo); 8083 8084 /** 8085 * Calls parent::__construct with specific arguments 8086 */ 8087 public function __construct() { 8088 $this->nosave = true; 8089 parent::__construct($this->get_section_name(), $this->get_section_title(), '', ''); 8090 } 8091 8092 /** 8093 * Always returns true, does nothing 8094 * 8095 * @return true 8096 */ 8097 public function get_setting() { 8098 return true; 8099 } 8100 8101 /** 8102 * Always returns true, does nothing 8103 * 8104 * @return true 8105 */ 8106 public function get_defaultsetting() { 8107 return true; 8108 } 8109 8110 /** 8111 * Always returns '', does not write anything 8112 * 8113 * @param mixed $data 8114 * @return string Always returns '' 8115 */ 8116 public function write_setting($data) { 8117 // Do not write any setting. 8118 return ''; 8119 } 8120 8121 /** 8122 * Checks if $query is one of the available plugins of this type 8123 * 8124 * @param string $query The string to search for 8125 * @return bool Returns true if found, false if not 8126 */ 8127 public function is_related($query) { 8128 if (parent::is_related($query)) { 8129 return true; 8130 } 8131 8132 $query = core_text::strtolower($query); 8133 $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type()); 8134 foreach ($plugins as $name => $plugin) { 8135 $localised = $plugin->displayname; 8136 if (strpos(core_text::strtolower($name), $query) !== false) { 8137 return true; 8138 } 8139 if (strpos(core_text::strtolower($localised), $query) !== false) { 8140 return true; 8141 } 8142 } 8143 return false; 8144 } 8145 8146 /** 8147 * The URL for the management page for this plugintype. 8148 * 8149 * @return moodle_url 8150 */ 8151 protected function get_manage_url() { 8152 return new moodle_url('/admin/updatesetting.php'); 8153 } 8154 8155 /** 8156 * Builds the HTML to display the control. 8157 * 8158 * @param string $data Unused 8159 * @param string $query 8160 * @return string 8161 */ 8162 public function output_html($data, $query = '') { 8163 global $CFG, $OUTPUT, $DB, $PAGE; 8164 8165 $context = (object) [ 8166 'manageurl' => new moodle_url($this->get_manage_url(), [ 8167 'type' => $this->get_plugin_type(), 8168 'sesskey' => sesskey(), 8169 ]), 8170 'infocolumnname' => $this->get_info_column_name(), 8171 'plugins' => [], 8172 ]; 8173 8174 $pluginmanager = core_plugin_manager::instance(); 8175 $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type()); 8176 $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type()); 8177 $plugins = array_merge($enabled, $allplugins); 8178 foreach ($plugins as $key => $plugin) { 8179 $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]); 8180 8181 $pluginkey = (object) [ 8182 'plugin' => $plugin->displayname, 8183 'enabled' => $plugin->is_enabled(), 8184 'togglelink' => '', 8185 'moveuplink' => '', 8186 'movedownlink' => '', 8187 'settingslink' => $plugin->get_settings_url(), 8188 'uninstalllink' => '', 8189 'info' => '', 8190 ]; 8191 8192 // Enable/Disable link. 8193 $togglelink = new moodle_url($pluginlink); 8194 if ($plugin->is_enabled()) { 8195 $toggletarget = false; 8196 $togglelink->param('action', 'disable'); 8197 8198 if (count($context->plugins)) { 8199 // This is not the first plugin. 8200 $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']); 8201 } 8202 8203 if (count($enabled) > count($context->plugins) + 1) { 8204 // This is not the last plugin. 8205 $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']); 8206 } 8207 8208 $pluginkey->info = $this->get_info_column($plugin); 8209 } else { 8210 $toggletarget = true; 8211 $togglelink->param('action', 'enable'); 8212 } 8213 8214 $pluginkey->toggletarget = $toggletarget; 8215 $pluginkey->togglelink = $togglelink; 8216 8217 $frankenstyle = $plugin->type . '_' . $plugin->name; 8218 if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) { 8219 // This plugin supports uninstallation. 8220 $pluginkey->uninstalllink = $uninstalllink; 8221 } 8222 8223 if (!empty($this->get_info_column_name())) { 8224 // This plugintype has an info column. 8225 $pluginkey->info = $this->get_info_column($plugin); 8226 } 8227 8228 $context->plugins[] = $pluginkey; 8229 } 8230 8231 $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context); 8232 return highlight($query, $str); 8233 } 8234 } 8235 8236 /** 8237 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin. 8238 * Requires a get_rank method on the plugininfo class for sorting. 8239 * 8240 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk> 8241 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8242 */ 8243 class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins { 8244 public function get_section_title() { 8245 return get_string('type_fileconverter_plural', 'plugin'); 8246 } 8247 8248 public function get_plugin_type() { 8249 return 'fileconverter'; 8250 } 8251 8252 public function get_info_column_name() { 8253 return get_string('supportedconversions', 'plugin'); 8254 } 8255 8256 public function get_info_column($plugininfo) { 8257 return $plugininfo->get_supported_conversions(); 8258 } 8259 } 8260 8261 /** 8262 * Special class for media player plugins management. 8263 * 8264 * @copyright 2016 Marina Glancy 8265 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8266 */ 8267 class admin_setting_managemediaplayers extends admin_setting { 8268 /** 8269 * Calls parent::__construct with specific arguments 8270 */ 8271 public function __construct() { 8272 $this->nosave = true; 8273 parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', ''); 8274 } 8275 8276 /** 8277 * Always returns true, does nothing 8278 * 8279 * @return true 8280 */ 8281 public function get_setting() { 8282 return true; 8283 } 8284 8285 /** 8286 * Always returns true, does nothing 8287 * 8288 * @return true 8289 */ 8290 public function get_defaultsetting() { 8291 return true; 8292 } 8293 8294 /** 8295 * Always returns '', does not write anything 8296 * 8297 * @param mixed $data 8298 * @return string Always returns '' 8299 */ 8300 public function write_setting($data) { 8301 // Do not write any setting. 8302 return ''; 8303 } 8304 8305 /** 8306 * Checks if $query is one of the available enrol plugins 8307 * 8308 * @param string $query The string to search for 8309 * @return bool Returns true if found, false if not 8310 */ 8311 public function is_related($query) { 8312 if (parent::is_related($query)) { 8313 return true; 8314 } 8315 8316 $query = core_text::strtolower($query); 8317 $plugins = core_plugin_manager::instance()->get_plugins_of_type('media'); 8318 foreach ($plugins as $name => $plugin) { 8319 $localised = $plugin->displayname; 8320 if (strpos(core_text::strtolower($name), $query) !== false) { 8321 return true; 8322 } 8323 if (strpos(core_text::strtolower($localised), $query) !== false) { 8324 return true; 8325 } 8326 } 8327 return false; 8328 } 8329 8330 /** 8331 * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank. 8332 * @return \core\plugininfo\media[] 8333 */ 8334 protected function get_sorted_plugins() { 8335 $pluginmanager = core_plugin_manager::instance(); 8336 8337 $plugins = $pluginmanager->get_plugins_of_type('media'); 8338 $enabledplugins = $pluginmanager->get_enabled_plugins('media'); 8339 8340 // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank. 8341 \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC); 8342 8343 $order = array_values($enabledplugins); 8344 $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order)); 8345 8346 $sortedplugins = array(); 8347 foreach ($order as $name) { 8348 $sortedplugins[$name] = $plugins[$name]; 8349 } 8350 8351 return $sortedplugins; 8352 } 8353 8354 /** 8355 * Builds the XHTML to display the control 8356 * 8357 * @param string $data Unused 8358 * @param string $query 8359 * @return string 8360 */ 8361 public function output_html($data, $query='') { 8362 global $CFG, $OUTPUT, $DB, $PAGE; 8363 8364 // Display strings. 8365 $strup = get_string('up'); 8366 $strdown = get_string('down'); 8367 $strsettings = get_string('settings'); 8368 $strenable = get_string('enable'); 8369 $strdisable = get_string('disable'); 8370 $struninstall = get_string('uninstallplugin', 'core_admin'); 8371 $strversion = get_string('version'); 8372 $strname = get_string('name'); 8373 $strsupports = get_string('supports', 'core_media'); 8374 8375 $pluginmanager = core_plugin_manager::instance(); 8376 8377 $plugins = $this->get_sorted_plugins(); 8378 $enabledplugins = $pluginmanager->get_enabled_plugins('media'); 8379 8380 $return = $OUTPUT->box_start('generalbox mediaplayersui'); 8381 8382 $table = new html_table(); 8383 $table->head = array($strname, $strsupports, $strversion, 8384 $strenable, $strup.'/'.$strdown, $strsettings, $struninstall); 8385 $table->colclasses = array('leftalign', 'leftalign', 'centeralign', 8386 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 8387 $table->id = 'mediaplayerplugins'; 8388 $table->attributes['class'] = 'admintable generaltable'; 8389 $table->data = array(); 8390 8391 // Iterate through media plugins and add to the display table. 8392 $updowncount = 1; 8393 $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey())); 8394 $printed = array(); 8395 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 8396 8397 $usedextensions = []; 8398 foreach ($plugins as $name => $plugin) { 8399 $url->param('media', $name); 8400 /** @var \core\plugininfo\media $plugininfo */ 8401 $plugininfo = $pluginmanager->get_plugin_info('media_'.$name); 8402 $version = $plugininfo->versiondb; 8403 $supports = $plugininfo->supports($usedextensions); 8404 8405 // Hide/show links. 8406 $class = ''; 8407 if (!$plugininfo->is_installed_and_upgraded()) { 8408 $hideshow = ''; 8409 $enabled = false; 8410 $displayname = '<span class="notifyproblem">'.$name.'</span>'; 8411 } else { 8412 $enabled = $plugininfo->is_enabled(); 8413 if ($enabled) { 8414 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')), 8415 $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall'))); 8416 } else { 8417 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')), 8418 $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall'))); 8419 $class = 'dimmed_text'; 8420 } 8421 $displayname = $plugin->displayname; 8422 if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) { 8423 $displayname .= ' ' . $OUTPUT->help_icon('pluginname', 'media_' . $name); 8424 } 8425 } 8426 if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) { 8427 $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon')); 8428 } else { 8429 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon')); 8430 } 8431 8432 // Up/down link (only if enrol is enabled). 8433 $updown = ''; 8434 if ($enabled) { 8435 if ($updowncount > 1) { 8436 $updown = html_writer::link(new moodle_url($url, array('action' => 'up')), 8437 $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall'))); 8438 } else { 8439 $updown = $spacer; 8440 } 8441 if ($updowncount < count($enabledplugins)) { 8442 $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')), 8443 $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall'))); 8444 } else { 8445 $updown .= $spacer; 8446 } 8447 ++$updowncount; 8448 } 8449 8450 $uninstall = ''; 8451 $status = $plugininfo->get_status(); 8452 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) { 8453 $uninstall = get_string('status_missing', 'core_plugin') . '<br/>'; 8454 } 8455 if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) { 8456 $uninstall = get_string('status_new', 'core_plugin'); 8457 } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) { 8458 $uninstall .= html_writer::link($uninstallurl, $struninstall); 8459 } 8460 8461 $settings = ''; 8462 if ($plugininfo->get_settings_url()) { 8463 $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings); 8464 } 8465 8466 // Add a row to the table. 8467 $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall)); 8468 if ($class) { 8469 $row->attributes['class'] = $class; 8470 } 8471 $table->data[] = $row; 8472 8473 $printed[$name] = true; 8474 } 8475 8476 $return .= html_writer::table($table); 8477 $return .= $OUTPUT->box_end(); 8478 return highlight($query, $return); 8479 } 8480 } 8481 8482 8483 /** 8484 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings 8485 * 8486 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com> 8487 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8488 */ 8489 class admin_setting_managecontentbankcontenttypes extends admin_setting { 8490 8491 /** 8492 * Calls parent::__construct with specific arguments 8493 */ 8494 public function __construct() { 8495 $this->nosave = true; 8496 parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', ''); 8497 } 8498 8499 /** 8500 * Always returns true 8501 * 8502 * @return true 8503 */ 8504 public function get_setting() { 8505 return true; 8506 } 8507 8508 /** 8509 * Always returns true 8510 * 8511 * @return true 8512 */ 8513 public function get_defaultsetting() { 8514 return true; 8515 } 8516 8517 /** 8518 * Always returns '' and doesn't write anything 8519 * 8520 * @param mixed $data string or array, must not be NULL 8521 * @return string Always returns '' 8522 */ 8523 public function write_setting($data) { 8524 // Do not write any setting. 8525 return ''; 8526 } 8527 8528 /** 8529 * Search to find if Query is related to content bank plugin 8530 * 8531 * @param string $query The string to search for 8532 * @return bool true for related false for not 8533 */ 8534 public function is_related($query) { 8535 if (parent::is_related($query)) { 8536 return true; 8537 } 8538 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype'); 8539 foreach ($types as $type) { 8540 if (strpos($type->component, $query) !== false || 8541 strpos(core_text::strtolower($type->displayname), $query) !== false) { 8542 return true; 8543 } 8544 } 8545 return false; 8546 } 8547 8548 /** 8549 * Return XHTML to display control 8550 * 8551 * @param mixed $data Unused 8552 * @param string $query 8553 * @return string highlight 8554 */ 8555 public function output_html($data, $query='') { 8556 global $CFG, $OUTPUT; 8557 $return = ''; 8558 8559 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype'); 8560 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default')); 8561 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 8562 8563 $table = new html_table(); 8564 $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall); 8565 $table->align = array('left', 'center', 'center', 'center', 'center'); 8566 $table->attributes['class'] = 'managecontentbanktable generaltable admintable'; 8567 $table->data = array(); 8568 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 8569 8570 $totalenabled = 0; 8571 $count = 0; 8572 foreach ($types as $type) { 8573 if ($type->is_enabled() && $type->is_installed_and_upgraded()) { 8574 $totalenabled++; 8575 } 8576 } 8577 8578 foreach ($types as $type) { 8579 $url = new moodle_url('/admin/contentbank.php', 8580 array('sesskey' => sesskey(), 'name' => $type->name)); 8581 8582 $class = ''; 8583 $strtypename = $type->displayname; 8584 if ($type->is_enabled()) { 8585 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 8586 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 8587 } else { 8588 $class = 'dimmed_text'; 8589 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 8590 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 8591 } 8592 8593 $updown = ''; 8594 if ($count) { 8595 $updown .= html_writer::link($url->out(false, array('action' => 'up')), 8596 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). ''; 8597 } else { 8598 $updown .= $spacer; 8599 } 8600 if ($count < count($types) - 1) { 8601 $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')), 8602 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall'))); 8603 } else { 8604 $updown .= $spacer; 8605 } 8606 8607 $settings = ''; 8608 if ($type->get_settings_url()) { 8609 $settings = html_writer::link($type->get_settings_url(), $txt->settings); 8610 } 8611 8612 $uninstall = ''; 8613 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) { 8614 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 8615 } 8616 8617 $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall)); 8618 if ($class) { 8619 $row->attributes['class'] = $class; 8620 } 8621 $table->data[] = $row; 8622 $count++; 8623 } 8624 $return .= html_writer::table($table); 8625 return highlight($query, $return); 8626 } 8627 } 8628 8629 /** 8630 * Initialise admin page - this function does require login and permission 8631 * checks specified in page definition. 8632 * 8633 * This function must be called on each admin page before other code. 8634 * 8635 * @global moodle_page $PAGE 8636 * 8637 * @param string $section name of page 8638 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button. 8639 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be 8640 * added to the turn blocks editing on/off form, so this page reloads correctly. 8641 * @param string $actualurl if the actual page being viewed is not the normal one for this 8642 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here. 8643 * @param array $options Additional options that can be specified for page setup. 8644 * pagelayout - This option can be used to set a specific pagelyaout, admin is default. 8645 * nosearch - Do not display search bar 8646 */ 8647 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) { 8648 global $CFG, $PAGE, $USER, $SITE, $OUTPUT; 8649 8650 $PAGE->set_context(null); // hack - set context to something, by default to system context 8651 8652 $site = get_site(); 8653 require_login(null, false); 8654 8655 if (!empty($options['pagelayout'])) { 8656 // A specific page layout has been requested. 8657 $PAGE->set_pagelayout($options['pagelayout']); 8658 } else if ($section === 'upgradesettings') { 8659 $PAGE->set_pagelayout('maintenance'); 8660 } else { 8661 $PAGE->set_pagelayout('admin'); 8662 } 8663 8664 $adminroot = admin_get_root(false, false); // settings not required for external pages 8665 $extpage = $adminroot->locate($section, true); 8666 8667 $hassiteconfig = has_capability('moodle/site:config', context_system::instance()); 8668 if (empty($extpage) or !($extpage instanceof admin_externalpage)) { 8669 // The requested section isn't in the admin tree 8670 // It could be because the user has inadequate capapbilities or because the section doesn't exist 8671 if (!$hassiteconfig) { 8672 // The requested section could depend on a different capability 8673 // but most likely the user has inadequate capabilities 8674 throw new \moodle_exception('accessdenied', 'admin'); 8675 } else { 8676 throw new \moodle_exception('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/"); 8677 } 8678 } 8679 8680 // this eliminates our need to authenticate on the actual pages 8681 if (!$extpage->check_access()) { 8682 throw new \moodle_exception('accessdenied', 'admin'); 8683 die; 8684 } 8685 8686 navigation_node::require_admin_tree(); 8687 8688 // $PAGE->set_extra_button($extrabutton); TODO 8689 8690 if (!$actualurl) { 8691 $actualurl = $extpage->url; 8692 } 8693 8694 $PAGE->set_url($actualurl, $extraurlparams); 8695 if (strpos($PAGE->pagetype, 'admin-') !== 0) { 8696 $PAGE->set_pagetype('admin-' . $PAGE->pagetype); 8697 } 8698 8699 if (empty($SITE->fullname) || empty($SITE->shortname)) { 8700 // During initial install. 8701 $strinstallation = get_string('installation', 'install'); 8702 $strsettings = get_string('settings'); 8703 $PAGE->navbar->add($strsettings); 8704 $PAGE->set_title($strinstallation); 8705 $PAGE->set_heading($strinstallation); 8706 $PAGE->set_cacheable(false); 8707 return; 8708 } 8709 8710 // Locate the current item on the navigation and make it active when found. 8711 $path = $extpage->path; 8712 $node = $PAGE->settingsnav; 8713 while ($node && count($path) > 0) { 8714 $node = $node->get(array_pop($path)); 8715 } 8716 if ($node) { 8717 $node->make_active(); 8718 } 8719 8720 // Normal case. 8721 $adminediting = optional_param('adminedit', -1, PARAM_BOOL); 8722 if ($PAGE->user_allowed_editing() && $adminediting != -1) { 8723 $USER->editing = $adminediting; 8724 } 8725 8726 if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) { 8727 if ($PAGE->user_is_editing()) { 8728 $caption = get_string('blockseditoff'); 8729 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey())); 8730 } else { 8731 $caption = get_string('blocksediton'); 8732 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey())); 8733 } 8734 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get')); 8735 } 8736 8737 $PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $extpage->visiblepath)); 8738 $PAGE->set_heading($SITE->fullname); 8739 8740 if ($hassiteconfig && empty($options['nosearch'])) { 8741 $PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [ 8742 'action' => new moodle_url('/admin/search.php'), 8743 'query' => $PAGE->url->get_param('query'), 8744 ])); 8745 } 8746 8747 // prevent caching in nav block 8748 $PAGE->navigation->clear_cache(); 8749 } 8750 8751 /** 8752 * Returns the reference to admin tree root 8753 * 8754 * @return object admin_root object 8755 */ 8756 function admin_get_root($reload=false, $requirefulltree=true) { 8757 global $CFG, $DB, $OUTPUT, $ADMIN; 8758 8759 if (is_null($ADMIN)) { 8760 // create the admin tree! 8761 $ADMIN = new admin_root($requirefulltree); 8762 } 8763 8764 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) { 8765 $ADMIN->purge_children($requirefulltree); 8766 } 8767 8768 if (!$ADMIN->loaded) { 8769 // we process this file first to create categories first and in correct order 8770 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php'); 8771 8772 // now we process all other files in admin/settings to build the admin tree 8773 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) { 8774 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') { 8775 continue; 8776 } 8777 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') { 8778 // plugins are loaded last - they may insert pages anywhere 8779 continue; 8780 } 8781 require($file); 8782 } 8783 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php'); 8784 8785 $ADMIN->loaded = true; 8786 } 8787 8788 return $ADMIN; 8789 } 8790 8791 /// settings utility functions 8792 8793 /** 8794 * This function applies default settings recursively. 8795 * 8796 * Because setting the defaults of some settings can enable other settings, 8797 * this function calls itself repeatedly (max 4 times) until no more new settings are saved. 8798 * 8799 * NOTE: previous "internal" parameters $admindefaultsettings, $settingsoutput were removed in Moodle 4.3. 8800 * 8801 * @param part_of_admin_tree|null $node NULL means apply all settings with repeated recursion 8802 * @param bool $unconditional if true overrides all values with defaults (true for installation, false for CLI upgrade) 8803 * @return array The names and values of the applied setting defaults 8804 */ 8805 function admin_apply_default_settings(?part_of_admin_tree $node = null, bool $unconditional = true): array { 8806 if (is_null($node)) { 8807 // This function relies heavily on config cache, so we need to enable in-memory caches if it 8808 // is used during install when normal caching is disabled. 8809 $token = new \core_cache\allow_temporary_caches(); // Value not used intentionally, see its destructor. 8810 8811 core_plugin_manager::reset_caches(); 8812 $root = admin_get_root(true, true); 8813 $saved = admin_apply_default_settings($root, $unconditional); 8814 if (!$saved) { 8815 return []; 8816 } 8817 8818 for ($i = 1; $i <= 3; $i++) { 8819 core_plugin_manager::reset_caches(); 8820 $root = admin_get_root(true, true); 8821 // No need to force defaults in repeated runs. 8822 $moresaved = admin_apply_default_settings($root, false); 8823 if (!$moresaved) { 8824 // No more setting defaults to save. 8825 return $saved; 8826 } 8827 $saved += $moresaved; 8828 } 8829 8830 // We should not get here unless there are some problematic settings.php files. 8831 core_plugin_manager::reset_caches(); 8832 return $saved; 8833 } 8834 8835 // Recursive applying of defaults in admin tree. 8836 $saved = []; 8837 if ($node instanceof admin_category) { 8838 foreach ($node->children as $child) { 8839 if ($child === null) { 8840 // This should not happen, 8841 // this is to prevent theoretical infinite loops. 8842 continue; 8843 } 8844 if ($child instanceof admin_externalpage) { 8845 continue; 8846 } 8847 $saved += admin_apply_default_settings($child, $unconditional); 8848 } 8849 8850 } else if ($node instanceof admin_settingpage) { 8851 /** @var admin_setting $setting */ 8852 foreach ((array)$node->settings as $setting) { 8853 if ($setting->nosave) { 8854 // Not a real setting, must be a heading or description. 8855 continue; 8856 } 8857 if (!$unconditional && !is_null($setting->get_setting())) { 8858 // Do not override existing defaults. 8859 continue; 8860 } 8861 $defaultsetting = $setting->get_defaultsetting(); 8862 if (is_null($defaultsetting)) { 8863 // No value yet - default maybe applied after admin user creation or in upgradesettings. 8864 continue; 8865 } 8866 // This should be unique-enough setting name that matches administration UI. 8867 if ($setting->plugin === null) { 8868 $settingname = $setting->name; 8869 } else { 8870 $settingname = $setting->plugin . '/' . $setting->name; 8871 } 8872 // Set the default for this setting. 8873 $error = $setting->write_setting($defaultsetting); 8874 if ($error === '') { 8875 $setting->write_setting_flags(null); 8876 if (is_int($defaultsetting) || $defaultsetting instanceof lang_string 8877 || $defaultsetting instanceof moodle_url) { 8878 $defaultsetting = (string)$defaultsetting; 8879 } 8880 $saved[$settingname] = $defaultsetting; 8881 } else { 8882 debugging("Error applying default setting '$settingname': " . $error, DEBUG_DEVELOPER); 8883 } 8884 } 8885 } 8886 8887 return $saved; 8888 } 8889 8890 /** 8891 * Store changed settings, this function updates the errors variable in $ADMIN 8892 * 8893 * @param object $formdata from form 8894 * @return int number of changed settings 8895 */ 8896 function admin_write_settings($formdata) { 8897 global $CFG, $SITE, $DB; 8898 8899 $olddbsessions = !empty($CFG->dbsessions); 8900 $formdata = (array)$formdata; 8901 8902 $data = array(); 8903 foreach ($formdata as $fullname=>$value) { 8904 if (strpos($fullname, 's_') !== 0) { 8905 continue; // not a config value 8906 } 8907 $data[$fullname] = $value; 8908 } 8909 8910 $adminroot = admin_get_root(); 8911 $settings = admin_find_write_settings($adminroot, $data); 8912 8913 $count = 0; 8914 foreach ($settings as $fullname=>$setting) { 8915 /** @var $setting admin_setting */ 8916 $original = $setting->get_setting(); 8917 $error = $setting->write_setting($data[$fullname]); 8918 if ($error !== '') { 8919 $adminroot->errors[$fullname] = new stdClass(); 8920 $adminroot->errors[$fullname]->data = $data[$fullname]; 8921 $adminroot->errors[$fullname]->id = $setting->get_id(); 8922 $adminroot->errors[$fullname]->error = $error; 8923 } else { 8924 $setting->write_setting_flags($data); 8925 } 8926 if ($setting->post_write_settings($original)) { 8927 $count++; 8928 } 8929 } 8930 8931 if ($olddbsessions != !empty($CFG->dbsessions)) { 8932 require_logout(); 8933 } 8934 8935 // Now update $SITE - just update the fields, in case other people have a 8936 // a reference to it (e.g. $PAGE, $COURSE). 8937 $newsite = $DB->get_record('course', array('id'=>$SITE->id)); 8938 foreach (get_object_vars($newsite) as $field => $value) { 8939 $SITE->$field = $value; 8940 } 8941 8942 // now reload all settings - some of them might depend on the changed 8943 admin_get_root(true); 8944 return $count; 8945 } 8946 8947 /** 8948 * Internal recursive function - finds all settings from submitted form 8949 * 8950 * @param object $node Instance of admin_category, or admin_settingpage 8951 * @param array $data 8952 * @return array 8953 */ 8954 function admin_find_write_settings($node, $data) { 8955 $return = array(); 8956 8957 if (empty($data)) { 8958 return $return; 8959 } 8960 8961 if ($node instanceof admin_category) { 8962 if ($node->check_access()) { 8963 $entries = array_keys($node->children); 8964 foreach ($entries as $entry) { 8965 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data)); 8966 } 8967 } 8968 8969 } else if ($node instanceof admin_settingpage) { 8970 if ($node->check_access()) { 8971 foreach ($node->settings as $setting) { 8972 $fullname = $setting->get_full_name(); 8973 if (array_key_exists($fullname, $data)) { 8974 $return[$fullname] = $setting; 8975 } 8976 } 8977 } 8978 8979 } 8980 8981 return $return; 8982 } 8983 8984 /** 8985 * Internal function - prints the search results 8986 * 8987 * @param string $query String to search for 8988 * @return string empty or XHTML 8989 */ 8990 function admin_search_settings_html($query) { 8991 global $CFG, $OUTPUT, $PAGE; 8992 8993 if (core_text::strlen($query) < 2) { 8994 return ''; 8995 } 8996 $query = core_text::strtolower($query); 8997 8998 $adminroot = admin_get_root(); 8999 $findings = $adminroot->search($query); 9000 $savebutton = false; 9001 9002 $tpldata = (object) [ 9003 'actionurl' => $PAGE->url->out(false), 9004 'results' => [], 9005 'sesskey' => sesskey(), 9006 ]; 9007 9008 foreach ($findings as $found) { 9009 $page = $found->page; 9010 $settings = $found->settings; 9011 if ($page->is_hidden()) { 9012 // hidden pages are not displayed in search results 9013 continue; 9014 } 9015 9016 $heading = highlight($query, $page->visiblename); 9017 $headingurl = null; 9018 if ($page instanceof admin_externalpage) { 9019 $headingurl = new moodle_url($page->url); 9020 } else if ($page instanceof admin_settingpage) { 9021 $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]); 9022 } else { 9023 continue; 9024 } 9025 9026 // Locate the page in the admin root and populate its visiblepath attribute. 9027 $path = array(); 9028 $located = $adminroot->locate($page->name, true); 9029 if ($located) { 9030 foreach ($located->visiblepath as $pathitem) { 9031 array_unshift($path, (string) $pathitem); 9032 } 9033 } 9034 9035 $sectionsettings = []; 9036 if (!empty($settings)) { 9037 foreach ($settings as $setting) { 9038 if (empty($setting->nosave)) { 9039 $savebutton = true; 9040 } 9041 $fullname = $setting->get_full_name(); 9042 if (array_key_exists($fullname, $adminroot->errors)) { 9043 $data = $adminroot->errors[$fullname]->data; 9044 } else { 9045 $data = $setting->get_setting(); 9046 // do not use defaults if settings not available - upgradesettings handles the defaults! 9047 } 9048 $sectionsettings[] = $setting->output_html($data, $query); 9049 } 9050 } 9051 9052 $tpldata->results[] = (object) [ 9053 'title' => $heading, 9054 'path' => $path, 9055 'url' => $headingurl->out(false), 9056 'settings' => $sectionsettings 9057 ]; 9058 } 9059 9060 $tpldata->showsave = $savebutton; 9061 $tpldata->hasresults = !empty($tpldata->results); 9062 9063 return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata); 9064 } 9065 9066 /** 9067 * Internal function - returns arrays of html pages with uninitialised settings 9068 * 9069 * @param object $node Instance of admin_category or admin_settingpage 9070 * @return array 9071 */ 9072 function admin_output_new_settings_by_page($node) { 9073 global $OUTPUT; 9074 $return = array(); 9075 9076 if ($node instanceof admin_category) { 9077 $entries = array_keys($node->children); 9078 foreach ($entries as $entry) { 9079 $return += admin_output_new_settings_by_page($node->children[$entry]); 9080 } 9081 9082 } else if ($node instanceof admin_settingpage) { 9083 $newsettings = array(); 9084 foreach ($node->settings as $setting) { 9085 if (is_null($setting->get_setting())) { 9086 $newsettings[] = $setting; 9087 } 9088 } 9089 if (count($newsettings) > 0) { 9090 $adminroot = admin_get_root(); 9091 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main'); 9092 $page .= '<fieldset class="adminsettings">'."\n"; 9093 foreach ($newsettings as $setting) { 9094 $fullname = $setting->get_full_name(); 9095 if (array_key_exists($fullname, $adminroot->errors)) { 9096 $data = $adminroot->errors[$fullname]->data; 9097 } else { 9098 $data = $setting->get_setting(); 9099 if (is_null($data)) { 9100 $data = $setting->get_defaultsetting(); 9101 } 9102 } 9103 $page .= '<div class="clearer"><!-- --></div>'."\n"; 9104 $page .= $setting->output_html($data); 9105 } 9106 $page .= '</fieldset>'; 9107 $return[$node->name] = $page; 9108 } 9109 } 9110 9111 return $return; 9112 } 9113 9114 /** 9115 * Format admin settings 9116 * 9117 * @param object $setting 9118 * @param string $title label element 9119 * @param string $form form fragment, html code - not highlighted automatically 9120 * @param string $description 9121 * @param mixed $label link label to id, true by default or string being the label to connect it to 9122 * @param string $warning warning text 9123 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null 9124 * @param string $query search query to be highlighted 9125 * @return string XHTML 9126 */ 9127 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') { 9128 global $CFG, $OUTPUT; 9129 9130 $context = (object) [ 9131 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name", 9132 'fullname' => $setting->get_full_name(), 9133 ]; 9134 9135 // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate. 9136 if ($label === true) { 9137 $context->labelfor = $setting->get_id(); 9138 } else if ($label === false) { 9139 $context->labelfor = ''; 9140 } else { 9141 $context->labelfor = $label; 9142 } 9143 9144 $form .= $setting->output_setting_flags(); 9145 9146 $context->warning = $warning; 9147 $context->override = ''; 9148 if (empty($setting->plugin)) { 9149 if ($setting->is_forceable() && array_key_exists($setting->name, $CFG->config_php_settings)) { 9150 $context->override = get_string('configoverride', 'admin'); 9151 } 9152 } else { 9153 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) { 9154 $context->override = get_string('configoverride', 'admin'); 9155 } 9156 } 9157 9158 $defaults = array(); 9159 if (!is_null($defaultinfo)) { 9160 if ($defaultinfo === '') { 9161 $defaultinfo = get_string('emptysettingvalue', 'admin'); 9162 } 9163 $defaults[] = $defaultinfo; 9164 } 9165 9166 $context->default = null; 9167 $setting->get_setting_flag_defaults($defaults); 9168 if (!empty($defaults)) { 9169 $defaultinfo = implode(', ', $defaults); 9170 $defaultinfo = highlight($query, nl2br(s($defaultinfo))); 9171 $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo); 9172 } 9173 9174 9175 $context->error = ''; 9176 $adminroot = admin_get_root(); 9177 if (array_key_exists($context->fullname, $adminroot->errors)) { 9178 $context->error = $adminroot->errors[$context->fullname]->error; 9179 } 9180 9181 if ($dependenton = $setting->get_dependent_on()) { 9182 $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton)); 9183 } 9184 9185 $context->id = 'admin-' . $setting->name; 9186 $context->title = highlightfast($query, $title); 9187 $context->name = highlightfast($query, $context->name); 9188 $context->description = highlight($query, markdown_to_html($description)); 9189 $context->element = $form; 9190 $context->forceltr = $setting->get_force_ltr(); 9191 $context->customcontrol = $setting->has_custom_form_control(); 9192 9193 return $OUTPUT->render_from_template('core_admin/setting', $context); 9194 } 9195 9196 /** 9197 * Based on find_new_settings{@link ()} in upgradesettings.php 9198 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any. 9199 * 9200 * @param object $node Instance of admin_category, or admin_settingpage 9201 * @return boolean true if any settings haven't been initialised, false if they all have 9202 */ 9203 function any_new_admin_settings($node) { 9204 9205 if ($node instanceof admin_category) { 9206 $entries = array_keys($node->children); 9207 foreach ($entries as $entry) { 9208 if (any_new_admin_settings($node->children[$entry])) { 9209 return true; 9210 } 9211 } 9212 9213 } else if ($node instanceof admin_settingpage) { 9214 foreach ($node->settings as $setting) { 9215 if ($setting->get_setting() === NULL) { 9216 return true; 9217 } 9218 } 9219 } 9220 9221 return false; 9222 } 9223 9224 /** 9225 * Given a table and optionally a column name should replaces be done? 9226 * 9227 * @param string $table name 9228 * @param string $column name 9229 * @return bool success or fail 9230 */ 9231 function db_should_replace($table, $column = '', $additionalskiptables = ''): bool { 9232 9233 // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing... 9234 $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions', 9235 'events_queue', 'repository_instance_config', 'block_instances', 'files']; 9236 9237 // Additional skip tables. 9238 if (!empty($additionalskiptables)) { 9239 $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '', $additionalskiptables))); 9240 } 9241 9242 // Don't process these. 9243 if (in_array($table, $skiptables)) { 9244 return false; 9245 } 9246 9247 // To be safe never replace inside a table that looks related to logging. 9248 if (preg_match('/(^|_)logs?($|_)/', $table)) { 9249 return false; 9250 } 9251 9252 // Do column based exclusions. 9253 if (!empty($column)) { 9254 // Don't touch anything that looks like a hash. 9255 if (preg_match('/hash$/', $column)) { 9256 return false; 9257 } 9258 } 9259 9260 return true; 9261 } 9262 9263 /** 9264 * Moved from admin/replace.php so that we can use this in cron 9265 * 9266 * @param string $search string to look for 9267 * @param string $replace string to replace 9268 * @return bool success or fail 9269 */ 9270 function db_replace($search, $replace, $additionalskiptables = '') { 9271 global $DB, $CFG, $OUTPUT; 9272 9273 // Turn off time limits, sometimes upgrades can be slow. 9274 core_php_time_limit::raise(); 9275 9276 if (!$tables = $DB->get_tables() ) { // No tables yet at all. 9277 return false; 9278 } 9279 foreach ($tables as $table) { 9280 9281 if (!db_should_replace($table, '', $additionalskiptables)) { 9282 continue; 9283 } 9284 9285 if ($columns = $DB->get_columns($table)) { 9286 $DB->set_debug(true); 9287 foreach ($columns as $column) { 9288 if (!db_should_replace($table, $column->name)) { 9289 continue; 9290 } 9291 $DB->replace_all_text($table, $column, $search, $replace); 9292 } 9293 $DB->set_debug(false); 9294 } 9295 } 9296 9297 // delete modinfo caches 9298 rebuild_course_cache(0, true); 9299 9300 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks... 9301 $blocks = core_component::get_plugin_list('block'); 9302 foreach ($blocks as $blockname=>$fullblock) { 9303 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it 9304 continue; 9305 } 9306 9307 if (!is_readable($fullblock.'/lib.php')) { 9308 continue; 9309 } 9310 9311 $function = 'block_'.$blockname.'_global_db_replace'; 9312 include_once($fullblock.'/lib.php'); 9313 if (!function_exists($function)) { 9314 continue; 9315 } 9316 9317 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess'); 9318 $function($search, $replace); 9319 echo $OUTPUT->notification("...finished", 'notifysuccess'); 9320 } 9321 9322 // Trigger an event. 9323 $eventargs = [ 9324 'context' => context_system::instance(), 9325 'other' => [ 9326 'search' => $search, 9327 'replace' => $replace 9328 ] 9329 ]; 9330 $event = \core\event\database_text_field_content_replaced::create($eventargs); 9331 $event->trigger(); 9332 9333 purge_all_caches(); 9334 9335 return true; 9336 } 9337 9338 /** 9339 * Manage repository settings 9340 * 9341 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 9342 */ 9343 class admin_setting_managerepository extends admin_setting { 9344 /** @var string */ 9345 private $baseurl; 9346 9347 /** 9348 * calls parent::__construct with specific arguments 9349 */ 9350 public function __construct() { 9351 global $CFG; 9352 parent::__construct('managerepository', get_string('manage', 'repository'), '', ''); 9353 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey(); 9354 } 9355 9356 /** 9357 * Always returns true, does nothing 9358 * 9359 * @return true 9360 */ 9361 public function get_setting() { 9362 return true; 9363 } 9364 9365 /** 9366 * Always returns true does nothing 9367 * 9368 * @return true 9369 */ 9370 public function get_defaultsetting() { 9371 return true; 9372 } 9373 9374 /** 9375 * Always returns s_managerepository 9376 * 9377 * @return string Always return 's_managerepository' 9378 */ 9379 public function get_full_name() { 9380 return 's_managerepository'; 9381 } 9382 9383 /** 9384 * Always returns '' doesn't do anything 9385 */ 9386 public function write_setting($data) { 9387 $url = $this->baseurl . '&new=' . $data; 9388 return ''; 9389 // TODO 9390 // Should not use redirect and exit here 9391 // Find a better way to do this. 9392 // redirect($url); 9393 // exit; 9394 } 9395 9396 /** 9397 * Searches repository plugins for one that matches $query 9398 * 9399 * @param string $query The string to search for 9400 * @return bool true if found, false if not 9401 */ 9402 public function is_related($query) { 9403 if (parent::is_related($query)) { 9404 return true; 9405 } 9406 9407 $repositories= core_component::get_plugin_list('repository'); 9408 foreach ($repositories as $p => $dir) { 9409 if (strpos($p, $query) !== false) { 9410 return true; 9411 } 9412 } 9413 foreach (repository::get_types() as $instance) { 9414 $title = $instance->get_typename(); 9415 if (strpos(core_text::strtolower($title), $query) !== false) { 9416 return true; 9417 } 9418 } 9419 return false; 9420 } 9421 9422 /** 9423 * Helper function that generates a moodle_url object 9424 * relevant to the repository 9425 */ 9426 9427 function repository_action_url($repository) { 9428 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository)); 9429 } 9430 9431 /** 9432 * Builds XHTML to display the control 9433 * 9434 * @param string $data Unused 9435 * @param string $query 9436 * @return string XHTML 9437 */ 9438 public function output_html($data, $query='') { 9439 global $CFG, $USER, $OUTPUT; 9440 9441 // Get strings that are used 9442 $strshow = get_string('on', 'repository'); 9443 $strhide = get_string('off', 'repository'); 9444 $strdelete = get_string('disabled', 'repository'); 9445 9446 $actionchoicesforexisting = array( 9447 'show' => $strshow, 9448 'hide' => $strhide, 9449 'delete' => $strdelete 9450 ); 9451 9452 $actionchoicesfornew = array( 9453 'newon' => $strshow, 9454 'newoff' => $strhide, 9455 'delete' => $strdelete 9456 ); 9457 9458 $return = ''; 9459 $return .= $OUTPUT->box_start('generalbox'); 9460 9461 // Set strings that are used multiple times 9462 $settingsstr = get_string('settings'); 9463 $disablestr = get_string('disable'); 9464 9465 // Table to list plug-ins 9466 $table = new html_table(); 9467 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr); 9468 $table->align = array('left', 'center', 'center', 'center', 'center'); 9469 $table->data = array(); 9470 9471 // Get list of used plug-ins 9472 $repositorytypes = repository::get_types(); 9473 if (!empty($repositorytypes)) { 9474 // Array to store plugins being used 9475 $alreadyplugins = array(); 9476 $totalrepositorytypes = count($repositorytypes); 9477 $updowncount = 1; 9478 foreach ($repositorytypes as $i) { 9479 $settings = ''; 9480 $typename = $i->get_typename(); 9481 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config) 9482 $typeoptionnames = repository::static_function($typename, 'get_type_option_names'); 9483 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names'); 9484 9485 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) { 9486 // Calculate number of instances in order to display them for the Moodle administrator 9487 if (!empty($instanceoptionnames)) { 9488 $params = array(); 9489 $params['context'] = array(context_system::instance()); 9490 $params['onlyvisible'] = false; 9491 $params['type'] = $typename; 9492 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params)); 9493 // site instances 9494 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber); 9495 $params['context'] = array(); 9496 $instances = repository::static_function($typename, 'get_instances', $params); 9497 $courseinstances = array(); 9498 $userinstances = array(); 9499 9500 foreach ($instances as $instance) { 9501 $repocontext = context::instance_by_id($instance->instance->contextid); 9502 if ($repocontext->contextlevel == CONTEXT_COURSE) { 9503 $courseinstances[] = $instance; 9504 } else if ($repocontext->contextlevel == CONTEXT_USER) { 9505 $userinstances[] = $instance; 9506 } 9507 } 9508 // course instances 9509 $instancenumber = count($courseinstances); 9510 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber); 9511 9512 // user private instances 9513 $instancenumber = count($userinstances); 9514 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber); 9515 } else { 9516 $admininstancenumbertext = ""; 9517 $courseinstancenumbertext = ""; 9518 $userinstancenumbertext = ""; 9519 } 9520 9521 $settings .= '<a href="' . $this->baseurl . '&action=edit&repos=' . $typename . '">' . $settingsstr .'</a>'; 9522 9523 $settings .= $OUTPUT->container_start('mdl-left'); 9524 $settings .= '<br/>'; 9525 $settings .= $admininstancenumbertext; 9526 $settings .= '<br/>'; 9527 $settings .= $courseinstancenumbertext; 9528 $settings .= '<br/>'; 9529 $settings .= $userinstancenumbertext; 9530 $settings .= $OUTPUT->container_end(); 9531 } 9532 // Get the current visibility 9533 if ($i->get_visible()) { 9534 $currentaction = 'show'; 9535 } else { 9536 $currentaction = 'hide'; 9537 } 9538 9539 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename)); 9540 9541 // Display up/down link 9542 $updown = ''; 9543 // Should be done with CSS instead. 9544 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon')); 9545 9546 if ($updowncount > 1) { 9547 $updown .= "<a href=\"$this->baseurl&action=moveup&repos=".$typename."\">"; 9548 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a> '; 9549 } 9550 else { 9551 $updown .= $spacer; 9552 } 9553 if ($updowncount < $totalrepositorytypes) { 9554 $updown .= "<a href=\"$this->baseurl&action=movedown&repos=".$typename."\">"; 9555 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a> '; 9556 } 9557 else { 9558 $updown .= $spacer; 9559 } 9560 9561 $updowncount++; 9562 9563 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings); 9564 9565 if (!in_array($typename, $alreadyplugins)) { 9566 $alreadyplugins[] = $typename; 9567 } 9568 } 9569 } 9570 9571 // Get all the plugins that exist on disk 9572 $plugins = core_component::get_plugin_list('repository'); 9573 if (!empty($plugins)) { 9574 foreach ($plugins as $plugin => $dir) { 9575 // Check that it has not already been listed 9576 if (!in_array($plugin, $alreadyplugins)) { 9577 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin)); 9578 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', ''); 9579 } 9580 } 9581 } 9582 9583 $return .= html_writer::table($table); 9584 $return .= $OUTPUT->box_end(); 9585 return highlight($query, $return); 9586 } 9587 } 9588 9589 /** 9590 * Special checkbox for enable mobile web service 9591 * If enable then we store the service id of the mobile service into config table 9592 * If disable then we unstore the service id from the config table 9593 */ 9594 class admin_setting_enablemobileservice extends admin_setting_configcheckbox { 9595 9596 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */ 9597 private $restuse; 9598 9599 /** 9600 * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false. 9601 * 9602 * @return boolean 9603 */ 9604 private function is_protocol_cap_allowed() { 9605 global $DB, $CFG; 9606 9607 // If the $this->restuse variable is not set, it needs to be set. 9608 if (empty($this->restuse) and $this->restuse!==false) { 9609 $params = array(); 9610 $params['permission'] = CAP_ALLOW; 9611 $params['roleid'] = $CFG->defaultuserroleid; 9612 $params['capability'] = 'webservice/rest:use'; 9613 $this->restuse = $DB->record_exists('role_capabilities', $params); 9614 } 9615 9616 return $this->restuse; 9617 } 9618 9619 /** 9620 * Set the 'webservice/rest:use' to the Authenticated user role (allow or not) 9621 * @param type $status true to allow, false to not set 9622 */ 9623 private function set_protocol_cap($status) { 9624 global $CFG; 9625 if ($status and !$this->is_protocol_cap_allowed()) { 9626 //need to allow the cap 9627 $permission = CAP_ALLOW; 9628 $assign = true; 9629 } else if (!$status and $this->is_protocol_cap_allowed()){ 9630 //need to disallow the cap 9631 $permission = CAP_INHERIT; 9632 $assign = true; 9633 } 9634 if (!empty($assign)) { 9635 $systemcontext = context_system::instance(); 9636 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true); 9637 } 9638 } 9639 9640 /** 9641 * Builds XHTML to display the control. 9642 * The main purpose of this overloading is to display a warning when https 9643 * is not supported by the server 9644 * @param string $data Unused 9645 * @param string $query 9646 * @return string XHTML 9647 */ 9648 public function output_html($data, $query='') { 9649 global $OUTPUT; 9650 $html = parent::output_html($data, $query); 9651 9652 if ((string)$data === $this->yes) { 9653 $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here. 9654 foreach ($notifications as $notification) { 9655 $message = get_string($notification[0], $notification[1]); 9656 $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING); 9657 } 9658 } 9659 9660 return $html; 9661 } 9662 9663 /** 9664 * Retrieves the current setting using the objects name 9665 * 9666 * @return string 9667 */ 9668 public function get_setting() { 9669 global $CFG; 9670 9671 // First check if is not set. 9672 $result = $this->config_read($this->name); 9673 if (is_null($result)) { 9674 return null; 9675 } 9676 9677 // For install cli script, $CFG->defaultuserroleid is not set so return 0 9678 // Or if web services aren't enabled this can't be, 9679 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) { 9680 return 0; 9681 } 9682 9683 require_once($CFG->dirroot . '/webservice/lib.php'); 9684 $webservicemanager = new webservice(); 9685 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 9686 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) { 9687 return $result; 9688 } else { 9689 return 0; 9690 } 9691 } 9692 9693 /** 9694 * Save the selected setting 9695 * 9696 * @param string $data The selected site 9697 * @return string empty string or error message 9698 */ 9699 public function write_setting($data) { 9700 global $DB, $CFG; 9701 9702 //for install cli script, $CFG->defaultuserroleid is not set so do nothing 9703 if (empty($CFG->defaultuserroleid)) { 9704 return ''; 9705 } 9706 9707 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE; 9708 9709 require_once($CFG->dirroot . '/webservice/lib.php'); 9710 $webservicemanager = new webservice(); 9711 9712 $updateprotocol = false; 9713 if ((string)$data === $this->yes) { 9714 //code run when enable mobile web service 9715 //enable web service systeme if necessary 9716 set_config('enablewebservices', true); 9717 9718 //enable mobile service 9719 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 9720 $mobileservice->enabled = 1; 9721 $webservicemanager->update_external_service($mobileservice); 9722 9723 // Enable REST server. 9724 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols); 9725 9726 if (!in_array('rest', $activeprotocols)) { 9727 $activeprotocols[] = 'rest'; 9728 $updateprotocol = true; 9729 } 9730 9731 if ($updateprotocol) { 9732 set_config('webserviceprotocols', implode(',', $activeprotocols)); 9733 } 9734 9735 // Allow rest:use capability for authenticated user. 9736 $this->set_protocol_cap(true); 9737 } else { 9738 // Disable the mobile service. 9739 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 9740 $mobileservice->enabled = 0; 9741 $webservicemanager->update_external_service($mobileservice); 9742 } 9743 9744 return (parent::write_setting($data)); 9745 } 9746 } 9747 9748 /** 9749 * Special class for management of external services 9750 * 9751 * @author Petr Skoda (skodak) 9752 */ 9753 class admin_setting_manageexternalservices extends admin_setting { 9754 /** 9755 * Calls parent::__construct with specific arguments 9756 */ 9757 public function __construct() { 9758 $this->nosave = true; 9759 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', ''); 9760 } 9761 9762 /** 9763 * Always returns true, does nothing 9764 * 9765 * @return true 9766 */ 9767 public function get_setting() { 9768 return true; 9769 } 9770 9771 /** 9772 * Always returns true, does nothing 9773 * 9774 * @return true 9775 */ 9776 public function get_defaultsetting() { 9777 return true; 9778 } 9779 9780 /** 9781 * Always returns '', does not write anything 9782 * 9783 * @return string Always returns '' 9784 */ 9785 public function write_setting($data) { 9786 // do not write any setting 9787 return ''; 9788 } 9789 9790 /** 9791 * Checks if $query is one of the available external services 9792 * 9793 * @param string $query The string to search for 9794 * @return bool Returns true if found, false if not 9795 */ 9796 public function is_related($query) { 9797 global $DB; 9798 9799 if (parent::is_related($query)) { 9800 return true; 9801 } 9802 9803 $services = $DB->get_records('external_services', array(), 'id, name'); 9804 foreach ($services as $service) { 9805 if (strpos(core_text::strtolower($service->name), $query) !== false) { 9806 return true; 9807 } 9808 } 9809 return false; 9810 } 9811 9812 /** 9813 * Builds the XHTML to display the control 9814 * 9815 * @param string $data Unused 9816 * @param string $query 9817 * @return string 9818 */ 9819 public function output_html($data, $query='') { 9820 global $CFG, $OUTPUT, $DB; 9821 9822 // display strings 9823 $stradministration = get_string('administration'); 9824 $stredit = get_string('edit'); 9825 $strservice = get_string('externalservice', 'webservice'); 9826 $strdelete = get_string('delete'); 9827 $strplugin = get_string('plugin', 'admin'); 9828 $stradd = get_string('add'); 9829 $strfunctions = get_string('functions', 'webservice'); 9830 $strusers = get_string('users'); 9831 $strserviceusers = get_string('serviceusers', 'webservice'); 9832 9833 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php"; 9834 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php"; 9835 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php"; 9836 9837 // built in services 9838 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name'); 9839 $return = ""; 9840 if (!empty($services)) { 9841 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main'); 9842 9843 9844 9845 $table = new html_table(); 9846 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit); 9847 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign '); 9848 $table->id = 'builtinservices'; 9849 $table->attributes['class'] = 'admintable externalservices generaltable'; 9850 $table->data = array(); 9851 9852 // iterate through auth plugins and add to the display table 9853 foreach ($services as $service) { 9854 $name = $service->name; 9855 9856 // hide/show link 9857 if ($service->enabled) { 9858 $displayname = "<span>$name</span>"; 9859 } else { 9860 $displayname = "<span class=\"dimmed_text\">$name</span>"; 9861 } 9862 9863 $plugin = $service->component; 9864 9865 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>"; 9866 9867 if ($service->restrictedusers) { 9868 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>"; 9869 } else { 9870 $users = get_string('allusers', 'webservice'); 9871 } 9872 9873 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>"; 9874 9875 // add a row to the table 9876 $table->data[] = array($displayname, $plugin, $functions, $users, $edit); 9877 } 9878 $return .= html_writer::table($table); 9879 } 9880 9881 // Custom services 9882 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main'); 9883 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name'); 9884 9885 $table = new html_table(); 9886 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit); 9887 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign '); 9888 $table->id = 'customservices'; 9889 $table->attributes['class'] = 'admintable externalservices generaltable'; 9890 $table->data = array(); 9891 9892 // iterate through auth plugins and add to the display table 9893 foreach ($services as $service) { 9894 $name = $service->name; 9895 9896 // hide/show link 9897 if ($service->enabled) { 9898 $displayname = "<span>$name</span>"; 9899 } else { 9900 $displayname = "<span class=\"dimmed_text\">$name</span>"; 9901 } 9902 9903 // delete link 9904 $delete = "<a href=\"$esurl?action=delete&sesskey=".sesskey()."&id=$service->id\">$strdelete</a>"; 9905 9906 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>"; 9907 9908 if ($service->restrictedusers) { 9909 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>"; 9910 } else { 9911 $users = get_string('allusers', 'webservice'); 9912 } 9913 9914 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>"; 9915 9916 // add a row to the table 9917 $table->data[] = array($displayname, $delete, $functions, $users, $edit); 9918 } 9919 // add new custom service option 9920 $return .= html_writer::table($table); 9921 9922 $return .= '<br />'; 9923 // add a token to the table 9924 $return .= "<a href=\"$esurl?id=0\">$stradd</a>"; 9925 9926 return highlight($query, $return); 9927 } 9928 } 9929 9930 /** 9931 * Special class for overview of external services 9932 * 9933 * @author Jerome Mouneyrac 9934 */ 9935 class admin_setting_webservicesoverview extends admin_setting { 9936 9937 /** 9938 * Calls parent::__construct with specific arguments 9939 */ 9940 public function __construct() { 9941 $this->nosave = true; 9942 parent::__construct('webservicesoverviewui', 9943 get_string('webservicesoverview', 'webservice'), '', ''); 9944 } 9945 9946 /** 9947 * Always returns true, does nothing 9948 * 9949 * @return true 9950 */ 9951 public function get_setting() { 9952 return true; 9953 } 9954 9955 /** 9956 * Always returns true, does nothing 9957 * 9958 * @return true 9959 */ 9960 public function get_defaultsetting() { 9961 return true; 9962 } 9963 9964 /** 9965 * Always returns '', does not write anything 9966 * 9967 * @return string Always returns '' 9968 */ 9969 public function write_setting($data) { 9970 // do not write any setting 9971 return ''; 9972 } 9973 9974 /** 9975 * Builds the XHTML to display the control 9976 * 9977 * @param string $data Unused 9978 * @param string $query 9979 * @return string 9980 */ 9981 public function output_html($data, $query='') { 9982 global $CFG, $OUTPUT; 9983 9984 $return = ""; 9985 $brtag = html_writer::empty_tag('br'); 9986 9987 /// One system controlling Moodle with Token 9988 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main'); 9989 $table = new html_table(); 9990 $table->head = array(get_string('step', 'webservice'), get_string('status'), 9991 get_string('description')); 9992 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description'); 9993 $table->id = 'onesystemcontrol'; 9994 $table->attributes['class'] = 'admintable wsoverview generaltable'; 9995 $table->data = array(); 9996 9997 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice') 9998 . $brtag . $brtag; 9999 10000 /// 1. Enable Web Services 10001 $row = array(); 10002 $url = new moodle_url("/admin/search.php?query=enablewebservices"); 10003 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'), 10004 array('href' => $url)); 10005 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 10006 if ($CFG->enablewebservices) { 10007 $status = get_string('yes'); 10008 } 10009 $row[1] = $status; 10010 $row[2] = get_string('enablewsdescription', 'webservice'); 10011 $table->data[] = $row; 10012 10013 /// 2. Enable protocols 10014 $row = array(); 10015 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols"); 10016 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'), 10017 array('href' => $url)); 10018 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger')); 10019 //retrieve activated protocol 10020 $active_protocols = empty($CFG->webserviceprotocols) ? 10021 array() : explode(',', $CFG->webserviceprotocols); 10022 if (!empty($active_protocols)) { 10023 $status = ""; 10024 foreach ($active_protocols as $protocol) { 10025 $status .= $protocol . $brtag; 10026 } 10027 } 10028 $row[1] = $status; 10029 $row[2] = get_string('enableprotocolsdescription', 'webservice'); 10030 $table->data[] = $row; 10031 10032 /// 3. Create user account 10033 $row = array(); 10034 $url = new moodle_url("/user/editadvanced.php?id=-1"); 10035 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'), 10036 array('href' => $url)); 10037 $row[1] = ""; 10038 $row[2] = get_string('createuserdescription', 'webservice'); 10039 $table->data[] = $row; 10040 10041 /// 4. Add capability to users 10042 $row = array(); 10043 $url = new moodle_url("/admin/roles/check.php?contextid=1"); 10044 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'), 10045 array('href' => $url)); 10046 $row[1] = ""; 10047 $row[2] = get_string('checkusercapabilitydescription', 'webservice'); 10048 $table->data[] = $row; 10049 10050 /// 5. Select a web service 10051 $row = array(); 10052 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10053 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'), 10054 array('href' => $url)); 10055 $row[1] = ""; 10056 $row[2] = get_string('createservicedescription', 'webservice'); 10057 $table->data[] = $row; 10058 10059 /// 6. Add functions 10060 $row = array(); 10061 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10062 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'), 10063 array('href' => $url)); 10064 $row[1] = ""; 10065 $row[2] = get_string('addfunctionsdescription', 'webservice'); 10066 $table->data[] = $row; 10067 10068 /// 7. Add the specific user 10069 $row = array(); 10070 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10071 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'), 10072 array('href' => $url)); 10073 $row[1] = ""; 10074 $row[2] = get_string('selectspecificuserdescription', 'webservice'); 10075 $table->data[] = $row; 10076 10077 /// 8. Create token for the specific user 10078 $row = array(); 10079 $url = new moodle_url('/admin/webservice/tokens.php', ['action' => 'create']); 10080 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'), 10081 array('href' => $url)); 10082 $row[1] = ""; 10083 $row[2] = get_string('createtokenforuserdescription', 'webservice'); 10084 $table->data[] = $row; 10085 10086 /// 9. Enable the documentation 10087 $row = array(); 10088 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation"); 10089 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'), 10090 array('href' => $url)); 10091 $status = '<span class="warning">' . get_string('no') . '</span>'; 10092 if ($CFG->enablewsdocumentation) { 10093 $status = get_string('yes'); 10094 } 10095 $row[1] = $status; 10096 $row[2] = get_string('enabledocumentationdescription', 'webservice'); 10097 $table->data[] = $row; 10098 10099 /// 10. Test the service 10100 $row = array(); 10101 $url = new moodle_url("/admin/webservice/testclient.php"); 10102 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'), 10103 array('href' => $url)); 10104 $row[1] = ""; 10105 $row[2] = get_string('testwithtestclientdescription', 'webservice'); 10106 $table->data[] = $row; 10107 10108 $return .= html_writer::table($table); 10109 10110 /// Users as clients with token 10111 $return .= $brtag . $brtag . $brtag; 10112 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main'); 10113 $table = new html_table(); 10114 $table->head = array(get_string('step', 'webservice'), get_string('status'), 10115 get_string('description')); 10116 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description'); 10117 $table->id = 'userasclients'; 10118 $table->attributes['class'] = 'admintable wsoverview generaltable'; 10119 $table->data = array(); 10120 10121 $return .= $brtag . get_string('userasclientsdescription', 'webservice') . 10122 $brtag . $brtag; 10123 10124 /// 1. Enable Web Services 10125 $row = array(); 10126 $url = new moodle_url("/admin/search.php?query=enablewebservices"); 10127 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'), 10128 array('href' => $url)); 10129 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 10130 if ($CFG->enablewebservices) { 10131 $status = get_string('yes'); 10132 } 10133 $row[1] = $status; 10134 $row[2] = get_string('enablewsdescription', 'webservice'); 10135 $table->data[] = $row; 10136 10137 /// 2. Enable protocols 10138 $row = array(); 10139 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols"); 10140 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'), 10141 array('href' => $url)); 10142 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger')); 10143 //retrieve activated protocol 10144 $active_protocols = empty($CFG->webserviceprotocols) ? 10145 array() : explode(',', $CFG->webserviceprotocols); 10146 if (!empty($active_protocols)) { 10147 $status = ""; 10148 foreach ($active_protocols as $protocol) { 10149 $status .= $protocol . $brtag; 10150 } 10151 } 10152 $row[1] = $status; 10153 $row[2] = get_string('enableprotocolsdescription', 'webservice'); 10154 $table->data[] = $row; 10155 10156 10157 /// 3. Select a web service 10158 $row = array(); 10159 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10160 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'), 10161 array('href' => $url)); 10162 $row[1] = ""; 10163 $row[2] = get_string('createserviceforusersdescription', 'webservice'); 10164 $table->data[] = $row; 10165 10166 /// 4. Add functions 10167 $row = array(); 10168 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10169 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'), 10170 array('href' => $url)); 10171 $row[1] = ""; 10172 $row[2] = get_string('addfunctionsdescription', 'webservice'); 10173 $table->data[] = $row; 10174 10175 /// 5. Add capability to users 10176 $row = array(); 10177 $url = new moodle_url("/admin/roles/check.php?contextid=1"); 10178 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'), 10179 array('href' => $url)); 10180 $row[1] = ""; 10181 $row[2] = get_string('addcapabilitytousersdescription', 'webservice'); 10182 $table->data[] = $row; 10183 10184 /// 6. Test the service 10185 $row = array(); 10186 $url = new moodle_url("/admin/webservice/testclient.php"); 10187 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'), 10188 array('href' => $url)); 10189 $row[1] = ""; 10190 $row[2] = get_string('testauserwithtestclientdescription', 'webservice'); 10191 $table->data[] = $row; 10192 10193 $return .= html_writer::table($table); 10194 10195 return highlight($query, $return); 10196 } 10197 10198 } 10199 10200 10201 /** 10202 * Special class for web service protocol administration. 10203 * 10204 * @author Petr Skoda (skodak) 10205 */ 10206 class admin_setting_managewebserviceprotocols extends admin_setting { 10207 10208 /** 10209 * Calls parent::__construct with specific arguments 10210 */ 10211 public function __construct() { 10212 $this->nosave = true; 10213 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', ''); 10214 } 10215 10216 /** 10217 * Always returns true, does nothing 10218 * 10219 * @return true 10220 */ 10221 public function get_setting() { 10222 return true; 10223 } 10224 10225 /** 10226 * Always returns true, does nothing 10227 * 10228 * @return true 10229 */ 10230 public function get_defaultsetting() { 10231 return true; 10232 } 10233 10234 /** 10235 * Always returns '', does not write anything 10236 * 10237 * @return string Always returns '' 10238 */ 10239 public function write_setting($data) { 10240 // do not write any setting 10241 return ''; 10242 } 10243 10244 /** 10245 * Checks if $query is one of the available webservices 10246 * 10247 * @param string $query The string to search for 10248 * @return bool Returns true if found, false if not 10249 */ 10250 public function is_related($query) { 10251 if (parent::is_related($query)) { 10252 return true; 10253 } 10254 10255 $protocols = core_component::get_plugin_list('webservice'); 10256 foreach ($protocols as $protocol=>$location) { 10257 if (strpos($protocol, $query) !== false) { 10258 return true; 10259 } 10260 $protocolstr = get_string('pluginname', 'webservice_'.$protocol); 10261 if (strpos(core_text::strtolower($protocolstr), $query) !== false) { 10262 return true; 10263 } 10264 } 10265 return false; 10266 } 10267 10268 /** 10269 * Builds the XHTML to display the control 10270 * 10271 * @param string $data Unused 10272 * @param string $query 10273 * @return string 10274 */ 10275 public function output_html($data, $query='') { 10276 global $CFG, $OUTPUT; 10277 10278 // display strings 10279 $stradministration = get_string('administration'); 10280 $strsettings = get_string('settings'); 10281 $stredit = get_string('edit'); 10282 $strprotocol = get_string('protocol', 'webservice'); 10283 $strenable = get_string('enable'); 10284 $strdisable = get_string('disable'); 10285 $strversion = get_string('version'); 10286 10287 $protocols_available = core_component::get_plugin_list('webservice'); 10288 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols); 10289 ksort($protocols_available); 10290 10291 foreach ($activeprotocols as $key => $protocol) { 10292 if (empty($protocols_available[$protocol])) { 10293 unset($activeprotocols[$key]); 10294 } 10295 } 10296 10297 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main'); 10298 if (in_array('xmlrpc', $activeprotocols)) { 10299 $notify = new \core\output\notification(get_string('xmlrpcwebserviceenabled', 'admin'), 10300 \core\output\notification::NOTIFY_WARNING); 10301 $return .= $OUTPUT->render($notify); 10302 } 10303 $return .= $OUTPUT->box_start('generalbox webservicesui'); 10304 10305 $table = new html_table(); 10306 $table->head = array($strprotocol, $strversion, $strenable, $strsettings); 10307 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 10308 $table->id = 'webserviceprotocols'; 10309 $table->attributes['class'] = 'admintable generaltable'; 10310 $table->data = array(); 10311 10312 // iterate through auth plugins and add to the display table 10313 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey(); 10314 foreach ($protocols_available as $protocol => $location) { 10315 $name = get_string('pluginname', 'webservice_'.$protocol); 10316 10317 $plugin = new stdClass(); 10318 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) { 10319 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php'); 10320 } 10321 $version = isset($plugin->version) ? $plugin->version : ''; 10322 10323 // hide/show link 10324 if (in_array($protocol, $activeprotocols)) { 10325 $hideshow = "<a href=\"$url&action=disable&webservice=$protocol\">"; 10326 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>'; 10327 $displayname = "<span>$name</span>"; 10328 } else { 10329 $hideshow = "<a href=\"$url&action=enable&webservice=$protocol\">"; 10330 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>'; 10331 $displayname = "<span class=\"dimmed_text\">$name</span>"; 10332 } 10333 10334 // settings link 10335 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) { 10336 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>"; 10337 } else { 10338 $settings = ''; 10339 } 10340 10341 // add a row to the table 10342 $table->data[] = array($displayname, $version, $hideshow, $settings); 10343 } 10344 $return .= html_writer::table($table); 10345 $return .= get_string('configwebserviceplugins', 'webservice'); 10346 $return .= $OUTPUT->box_end(); 10347 10348 return highlight($query, $return); 10349 } 10350 } 10351 10352 /** 10353 * Colour picker 10354 * 10355 * @copyright 2010 Sam Hemelryk 10356 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10357 */ 10358 class admin_setting_configcolourpicker extends admin_setting { 10359 10360 /** 10361 * Information for previewing the colour 10362 * 10363 * @var array|null 10364 */ 10365 protected $previewconfig = null; 10366 10367 /** 10368 * Use default when empty. 10369 */ 10370 protected $usedefaultwhenempty = true; 10371 10372 /** 10373 * 10374 * @param string $name 10375 * @param string $visiblename 10376 * @param string $description 10377 * @param string $defaultsetting 10378 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor'); 10379 */ 10380 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null, 10381 $usedefaultwhenempty = true) { 10382 $this->previewconfig = $previewconfig; 10383 $this->usedefaultwhenempty = $usedefaultwhenempty; 10384 parent::__construct($name, $visiblename, $description, $defaultsetting); 10385 $this->set_force_ltr(true); 10386 } 10387 10388 /** 10389 * Return the setting 10390 * 10391 * @return mixed returns config if successful else null 10392 */ 10393 public function get_setting() { 10394 return $this->config_read($this->name); 10395 } 10396 10397 /** 10398 * Saves the setting 10399 * 10400 * @param string $data 10401 * @return bool 10402 */ 10403 public function write_setting($data) { 10404 $data = $this->validate($data); 10405 if ($data === false) { 10406 return get_string('validateerror', 'admin'); 10407 } 10408 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 10409 } 10410 10411 /** 10412 * Validates the colour that was entered by the user 10413 * 10414 * @param string $data 10415 * @return string|false 10416 */ 10417 protected function validate($data) { 10418 /** 10419 * List of valid HTML colour names 10420 * 10421 * @var array 10422 */ 10423 $colornames = array( 10424 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 10425 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 10426 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 10427 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 10428 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 10429 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta', 10430 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 10431 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 10432 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 10433 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 10434 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 10435 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green', 10436 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo', 10437 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 10438 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 10439 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen', 10440 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 10441 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 10442 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 10443 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 10444 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 10445 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 10446 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 10447 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 10448 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 10449 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red', 10450 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 10451 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 10452 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 10453 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 10454 'whitesmoke', 'yellow', 'yellowgreen' 10455 ); 10456 10457 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) { 10458 if (strpos($data, '#')!==0) { 10459 $data = '#'.$data; 10460 } 10461 return $data; 10462 } else if (in_array(strtolower($data), $colornames)) { 10463 return $data; 10464 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) { 10465 return $data; 10466 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) { 10467 return $data; 10468 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) { 10469 return $data; 10470 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) { 10471 return $data; 10472 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) { 10473 return $data; 10474 } else if (empty($data)) { 10475 if ($this->usedefaultwhenempty){ 10476 return $this->defaultsetting; 10477 } else { 10478 return ''; 10479 } 10480 } else { 10481 return false; 10482 } 10483 } 10484 10485 /** 10486 * Generates the HTML for the setting 10487 * 10488 * @global moodle_page $PAGE 10489 * @global core_renderer $OUTPUT 10490 * @param string $data 10491 * @param string $query 10492 */ 10493 public function output_html($data, $query = '') { 10494 global $PAGE, $OUTPUT; 10495 10496 $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']); 10497 $context = (object) [ 10498 'id' => $this->get_id(), 10499 'name' => $this->get_full_name(), 10500 'value' => $data, 10501 'icon' => $icon->export_for_template($OUTPUT), 10502 'haspreviewconfig' => !empty($this->previewconfig), 10503 'forceltr' => $this->get_force_ltr(), 10504 'readonly' => $this->is_readonly(), 10505 ]; 10506 10507 $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context); 10508 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig)); 10509 10510 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', 10511 $this->get_defaultsetting(), $query); 10512 } 10513 10514 } 10515 10516 10517 /** 10518 * Class used for uploading of one file into file storage, 10519 * the file name is stored in config table. 10520 * 10521 * Please note you need to implement your own '_pluginfile' callback function, 10522 * this setting only stores the file, it does not deal with file serving. 10523 * 10524 * @copyright 2013 Petr Skoda {@link http://skodak.org} 10525 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10526 */ 10527 class admin_setting_configstoredfile extends admin_setting { 10528 /** @var array file area options - should be one file only */ 10529 protected $options; 10530 /** @var string name of the file area */ 10531 protected $filearea; 10532 /** @var int intemid */ 10533 protected $itemid; 10534 /** @var string used for detection of changes */ 10535 protected $oldhashes; 10536 10537 /** 10538 * Create new stored file setting. 10539 * 10540 * @param string $name low level setting name 10541 * @param string $visiblename human readable setting name 10542 * @param string $description description of setting 10543 * @param mixed $filearea file area for file storage 10544 * @param int $itemid itemid for file storage 10545 * @param array $options file area options 10546 */ 10547 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) { 10548 parent::__construct($name, $visiblename, $description, ''); 10549 $this->filearea = $filearea; 10550 $this->itemid = $itemid; 10551 $this->options = (array)$options; 10552 $this->customcontrol = true; 10553 } 10554 10555 /** 10556 * Applies defaults and returns all options. 10557 * @return array 10558 */ 10559 protected function get_options() { 10560 global $CFG; 10561 10562 require_once("$CFG->libdir/filelib.php"); 10563 require_once("$CFG->dirroot/repository/lib.php"); 10564 $defaults = array( 10565 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1, 10566 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED, 10567 'context' => context_system::instance()); 10568 foreach($this->options as $k => $v) { 10569 $defaults[$k] = $v; 10570 } 10571 10572 return $defaults; 10573 } 10574 10575 public function get_setting() { 10576 return $this->config_read($this->name); 10577 } 10578 10579 public function write_setting($data) { 10580 global $USER; 10581 10582 // Let's not deal with validation here, this is for admins only. 10583 $current = $this->get_setting(); 10584 if (empty($data) && ($current === null || $current === '')) { 10585 // This will be the case when applying default settings (installation). 10586 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin')); 10587 } else if (!is_number($data)) { 10588 // Draft item id is expected here! 10589 return get_string('errorsetting', 'admin'); 10590 } 10591 10592 $options = $this->get_options(); 10593 $fs = get_file_storage(); 10594 $component = is_null($this->plugin) ? 'core' : $this->plugin; 10595 10596 $this->oldhashes = null; 10597 if ($current) { 10598 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current); 10599 if ($file = $fs->get_file_by_hash($hash)) { 10600 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash(); 10601 } 10602 unset($file); 10603 } 10604 10605 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) { 10606 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime. 10607 // But we can safely ignore that if the destination area is empty, so that the user is not prompt 10608 // with an error because the draft area does not exist, as he did not use it. 10609 $usercontext = context_user::instance($USER->id); 10610 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') { 10611 return get_string('errorsetting', 'admin'); 10612 } 10613 } 10614 10615 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options); 10616 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false); 10617 10618 $filepath = ''; 10619 if ($files) { 10620 /** @var stored_file $file */ 10621 $file = reset($files); 10622 $filepath = $file->get_filepath().$file->get_filename(); 10623 } 10624 10625 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin')); 10626 } 10627 10628 public function post_write_settings($original) { 10629 $options = $this->get_options(); 10630 $fs = get_file_storage(); 10631 $component = is_null($this->plugin) ? 'core' : $this->plugin; 10632 10633 $current = $this->get_setting(); 10634 $newhashes = null; 10635 if ($current) { 10636 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current); 10637 if ($file = $fs->get_file_by_hash($hash)) { 10638 $newhashes = $file->get_contenthash().$file->get_pathnamehash(); 10639 } 10640 unset($file); 10641 } 10642 10643 if ($this->oldhashes === $newhashes) { 10644 $this->oldhashes = null; 10645 return false; 10646 } 10647 $this->oldhashes = null; 10648 10649 $callbackfunction = $this->updatedcallback; 10650 if (!empty($callbackfunction) and function_exists($callbackfunction)) { 10651 $callbackfunction($this->get_full_name()); 10652 } 10653 return true; 10654 } 10655 10656 public function output_html($data, $query = '') { 10657 global $CFG; 10658 10659 $options = $this->get_options(); 10660 $id = $this->get_id(); 10661 $elname = $this->get_full_name(); 10662 $draftitemid = file_get_submitted_draft_itemid($elname); 10663 $component = is_null($this->plugin) ? 'core' : $this->plugin; 10664 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options); 10665 10666 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it... 10667 require_once("$CFG->dirroot/lib/form/filemanager.php"); 10668 10669 $fmoptions = new stdClass(); 10670 $fmoptions->mainfile = $options['mainfile']; 10671 $fmoptions->maxbytes = $options['maxbytes']; 10672 $fmoptions->maxfiles = $options['maxfiles']; 10673 $fmoptions->subdirs = $options['subdirs']; 10674 $fmoptions->accepted_types = $options['accepted_types']; 10675 $fmoptions->return_types = $options['return_types']; 10676 $fmoptions->context = $options['context']; 10677 $fmoptions->areamaxbytes = $options['areamaxbytes']; 10678 10679 $fm = new MoodleQuickForm_filemanager($elname, $this->visiblename, ['id' => $id], $fmoptions); 10680 $fm->setValue($draftitemid); 10681 10682 return format_admin_setting($this, $this->visiblename, 10683 '<div class="form-filemanager" data-fieldtype="filemanager">' . $fm->toHtml() . '</div>', 10684 $this->description, true, '', '', $query); 10685 } 10686 } 10687 10688 10689 /** 10690 * Administration interface for user specified regular expressions for device detection. 10691 * 10692 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed. 10693 * @todo Final deprecation on Moodle 4.7 MDL-79052 10694 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10695 */ 10696 class admin_setting_devicedetectregex extends admin_setting { 10697 10698 /** 10699 * Calls parent::__construct with specific args 10700 * 10701 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed. 10702 * @todo Final deprecation on Moodle 4.7 MDL-79052 10703 * @param string $name 10704 * @param string $visiblename 10705 * @param string $description 10706 * @param mixed $defaultsetting 10707 */ 10708 public function __construct($name, $visiblename, $description, $defaultsetting = '') { 10709 debugging( 10710 __FUNCTION__ . '() is deprecated.' . 10711 'All functions associated with devicedetectregex theme setting are being removed.', 10712 DEBUG_DEVELOPER 10713 ); 10714 global $CFG; 10715 parent::__construct($name, $visiblename, $description, $defaultsetting); 10716 } 10717 10718 /** 10719 * Return the current setting(s) 10720 * 10721 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed. 10722 * @todo Final deprecation on Moodle 4.7 MDL-79052 10723 * @return array Current settings array 10724 */ 10725 public function get_setting() { 10726 debugging( 10727 __FUNCTION__ . '() is deprecated.' . 10728 'All functions associated with devicedetectregex theme setting are being removed.', 10729 DEBUG_DEVELOPER 10730 ); 10731 global $CFG; 10732 10733 $config = $this->config_read($this->name); 10734 if (is_null($config)) { 10735 return null; 10736 } 10737 10738 return $this->prepare_form_data($config); 10739 } 10740 10741 /** 10742 * Save selected settings 10743 * 10744 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed. 10745 * @todo Final deprecation on Moodle 4.7 MDL-79052 10746 * @param array $data Array of settings to save 10747 * @return bool 10748 */ 10749 public function write_setting($data) { 10750 debugging( 10751 __FUNCTION__ . '() is deprecated.' . 10752 'All functions associated with devicedetectregex theme setting are being removed.', 10753 DEBUG_DEVELOPER 10754 ); 10755 if (empty($data)) { 10756 $data = array(); 10757 } 10758 10759 if ($this->config_write($this->name, $this->process_form_data($data))) { 10760 return ''; // success 10761 } else { 10762 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br'); 10763 } 10764 } 10765 10766 /** 10767 * Return XHTML field(s) for regexes 10768 * 10769 * @deprecated Moodle 4.3 MDL-78468 - No longer used since the devicedetectregex was removed. 10770 * @todo Final deprecation on Moodle 4.7 MDL-79052 10771 * @param array $data Array of options to set in HTML 10772 * @return string XHTML string for the fields and wrapping div(s) 10773 */ 10774 public function output_html($data, $query='') { 10775 debugging( 10776 __FUNCTION__ . '() is deprecated.' . 10777 'All functions associated with devicedetectregex theme setting are being removed.', 10778 DEBUG_DEVELOPER 10779 ); 10780 global $OUTPUT; 10781 10782 $context = (object) [ 10783 'expressions' => [], 10784 'name' => $this->get_full_name() 10785 ]; 10786 10787 if (empty($data)) { 10788 $looplimit = 1; 10789 } else { 10790 $looplimit = (count($data)/2)+1; 10791 } 10792 10793 for ($i=0; $i<$looplimit; $i++) { 10794 10795 $expressionname = 'expression'.$i; 10796 10797 if (!empty($data[$expressionname])){ 10798 $expression = $data[$expressionname]; 10799 } else { 10800 $expression = ''; 10801 } 10802 10803 $valuename = 'value'.$i; 10804 10805 if (!empty($data[$valuename])){ 10806 $value = $data[$valuename]; 10807 } else { 10808 $value= ''; 10809 } 10810 10811 $context->expressions[] = [ 10812 'index' => $i, 10813 'expression' => $expression, 10814 'value' => $value 10815 ]; 10816 } 10817 10818 $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context); 10819 10820 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query); 10821 } 10822 10823 /** 10824 * Converts the string of regexes 10825 * 10826 * @see self::process_form_data() 10827 * @param $regexes string of regexes 10828 * @return array of form fields and their values 10829 */ 10830 protected function prepare_form_data($regexes) { 10831 10832 $regexes = json_decode($regexes); 10833 10834 $form = array(); 10835 10836 $i = 0; 10837 10838 foreach ($regexes as $value => $regex) { 10839 $expressionname = 'expression'.$i; 10840 $valuename = 'value'.$i; 10841 10842 $form[$expressionname] = $regex; 10843 $form[$valuename] = $value; 10844 $i++; 10845 } 10846 10847 return $form; 10848 } 10849 10850 /** 10851 * Converts the data from admin settings form into a string of regexes 10852 * 10853 * @see self::prepare_form_data() 10854 * @param array $data array of admin form fields and values 10855 * @return false|string of regexes 10856 */ 10857 protected function process_form_data(array $form) { 10858 10859 $count = count($form); // number of form field values 10860 10861 if ($count % 2) { 10862 // we must get five fields per expression 10863 return false; 10864 } 10865 10866 $regexes = array(); 10867 for ($i = 0; $i < $count / 2; $i++) { 10868 $expressionname = "expression".$i; 10869 $valuename = "value".$i; 10870 10871 $expression = trim($form['expression'.$i]); 10872 $value = trim($form['value'.$i]); 10873 10874 if (empty($expression)){ 10875 continue; 10876 } 10877 10878 $regexes[$value] = $expression; 10879 } 10880 10881 $regexes = json_encode($regexes); 10882 10883 return $regexes; 10884 } 10885 10886 } 10887 10888 /** 10889 * Multiselect for current modules 10890 * 10891 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10892 */ 10893 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect { 10894 private $excludesystem; 10895 10896 /** 10897 * Calls parent::__construct - note array $choices is not required 10898 * 10899 * @param string $name setting name 10900 * @param string $visiblename localised setting name 10901 * @param string $description setting description 10902 * @param array $defaultsetting a plain array of default module ids 10903 * @param bool $excludesystem If true, excludes modules with 'system' archetype 10904 */ 10905 public function __construct($name, $visiblename, $description, $defaultsetting = array(), 10906 $excludesystem = true) { 10907 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 10908 $this->excludesystem = $excludesystem; 10909 } 10910 10911 /** 10912 * Loads an array of current module choices 10913 * 10914 * @return bool always return true 10915 */ 10916 public function load_choices() { 10917 if (is_array($this->choices)) { 10918 return true; 10919 } 10920 $this->choices = array(); 10921 10922 global $CFG, $DB; 10923 $records = $DB->get_records('modules', array('visible'=>1), 'name'); 10924 foreach ($records as $record) { 10925 // Exclude modules if the code doesn't exist 10926 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) { 10927 // Also exclude system modules (if specified) 10928 if (!($this->excludesystem && 10929 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) === 10930 MOD_ARCHETYPE_SYSTEM)) { 10931 $this->choices[$record->id] = $record->name; 10932 } 10933 } 10934 } 10935 return true; 10936 } 10937 } 10938 10939 /** 10940 * Admin setting to show if a php extension is enabled or not. 10941 * 10942 * @copyright 2013 Damyon Wiese 10943 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10944 */ 10945 class admin_setting_php_extension_enabled extends admin_setting { 10946 10947 /** @var string The name of the extension to check for */ 10948 private $extension; 10949 10950 /** 10951 * Calls parent::__construct with specific arguments 10952 */ 10953 public function __construct($name, $visiblename, $description, $extension) { 10954 $this->extension = $extension; 10955 $this->nosave = true; 10956 parent::__construct($name, $visiblename, $description, ''); 10957 } 10958 10959 /** 10960 * Always returns true, does nothing 10961 * 10962 * @return true 10963 */ 10964 public function get_setting() { 10965 return true; 10966 } 10967 10968 /** 10969 * Always returns true, does nothing 10970 * 10971 * @return true 10972 */ 10973 public function get_defaultsetting() { 10974 return true; 10975 } 10976 10977 /** 10978 * Always returns '', does not write anything 10979 * 10980 * @return string Always returns '' 10981 */ 10982 public function write_setting($data) { 10983 // Do not write any setting. 10984 return ''; 10985 } 10986 10987 /** 10988 * Outputs the html for this setting. 10989 * @return string Returns an XHTML string 10990 */ 10991 public function output_html($data, $query='') { 10992 global $OUTPUT; 10993 10994 $o = ''; 10995 if (!extension_loaded($this->extension)) { 10996 $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description; 10997 10998 $o .= format_admin_setting($this, $this->visiblename, $warning); 10999 } 11000 return $o; 11001 } 11002 } 11003 11004 /** 11005 * Server timezone setting. 11006 * 11007 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} 11008 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11009 * @author Petr Skoda <petr.skoda@totaralms.com> 11010 */ 11011 class admin_setting_servertimezone extends admin_setting_configselect { 11012 /** 11013 * Constructor. 11014 */ 11015 public function __construct() { 11016 $default = core_date::get_default_php_timezone(); 11017 if ($default === 'UTC') { 11018 // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most. 11019 $default = 'Europe/London'; 11020 } 11021 11022 parent::__construct('timezone', 11023 new lang_string('timezone', 'core_admin'), 11024 new lang_string('configtimezone', 'core_admin'), $default, null); 11025 } 11026 11027 /** 11028 * Lazy load timezone options. 11029 * @return bool true if loaded, false if error 11030 */ 11031 public function load_choices() { 11032 global $CFG; 11033 if (is_array($this->choices)) { 11034 return true; 11035 } 11036 11037 $current = isset($CFG->timezone) ? $CFG->timezone : null; 11038 $this->choices = core_date::get_list_of_timezones($current, false); 11039 if ($current == 99) { 11040 // Do not show 99 unless it is current value, we want to get rid of it over time. 11041 $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin', 11042 core_date::get_default_php_timezone()); 11043 } 11044 11045 return true; 11046 } 11047 } 11048 11049 /** 11050 * Forced user timezone setting. 11051 * 11052 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} 11053 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11054 * @author Petr Skoda <petr.skoda@totaralms.com> 11055 */ 11056 class admin_setting_forcetimezone extends admin_setting_configselect { 11057 /** 11058 * Constructor. 11059 */ 11060 public function __construct() { 11061 parent::__construct('forcetimezone', 11062 new lang_string('forcetimezone', 'core_admin'), 11063 new lang_string('helpforcetimezone', 'core_admin'), '99', null); 11064 } 11065 11066 /** 11067 * Lazy load timezone options. 11068 * @return bool true if loaded, false if error 11069 */ 11070 public function load_choices() { 11071 global $CFG; 11072 if (is_array($this->choices)) { 11073 return true; 11074 } 11075 11076 $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null; 11077 $this->choices = core_date::get_list_of_timezones($current, true); 11078 $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin'); 11079 11080 return true; 11081 } 11082 } 11083 11084 11085 /** 11086 * Search setup steps info. 11087 * 11088 * @package core 11089 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com} 11090 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11091 */ 11092 class admin_setting_searchsetupinfo extends admin_setting { 11093 11094 /** 11095 * Calls parent::__construct with specific arguments 11096 */ 11097 public function __construct() { 11098 $this->nosave = true; 11099 parent::__construct('searchsetupinfo', '', '', ''); 11100 } 11101 11102 /** 11103 * Always returns true, does nothing 11104 * 11105 * @return true 11106 */ 11107 public function get_setting() { 11108 return true; 11109 } 11110 11111 /** 11112 * Always returns true, does nothing 11113 * 11114 * @return true 11115 */ 11116 public function get_defaultsetting() { 11117 return true; 11118 } 11119 11120 /** 11121 * Always returns '', does not write anything 11122 * 11123 * @param array $data 11124 * @return string Always returns '' 11125 */ 11126 public function write_setting($data) { 11127 // Do not write any setting. 11128 return ''; 11129 } 11130 11131 /** 11132 * Builds the HTML to display the control 11133 * 11134 * @param string $data Unused 11135 * @param string $query 11136 * @return string 11137 */ 11138 public function output_html($data, $query='') { 11139 global $CFG, $OUTPUT, $ADMIN; 11140 11141 $return = ''; 11142 $brtag = html_writer::empty_tag('br'); 11143 11144 $searchareas = \core_search\manager::get_search_areas_list(); 11145 $anyenabled = !empty(\core_search\manager::get_search_areas_list(true)); 11146 $anyindexed = false; 11147 foreach ($searchareas as $areaid => $searcharea) { 11148 list($componentname, $varname) = $searcharea->get_config_var_name(); 11149 if (get_config($componentname, $varname . '_indexingstart')) { 11150 $anyindexed = true; 11151 break; 11152 } 11153 } 11154 11155 $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main'); 11156 11157 $table = new html_table(); 11158 $table->head = array(get_string('step', 'search'), get_string('status')); 11159 $table->colclasses = array('leftalign step', 'leftalign status'); 11160 $table->id = 'searchsetup'; 11161 $table->attributes['class'] = 'admintable generaltable'; 11162 $table->data = array(); 11163 11164 $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag; 11165 11166 // Select a search engine. 11167 $row = array(); 11168 $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine'); 11169 $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'), 11170 array('href' => $url)); 11171 11172 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11173 if (!empty($CFG->searchengine)) { 11174 $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine), 11175 array('class' => 'badge badge-success')); 11176 11177 } 11178 $row[1] = $status; 11179 $table->data[] = $row; 11180 11181 // Available areas. 11182 $row = array(); 11183 $url = new moodle_url('/admin/searchareas.php'); 11184 $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'), 11185 array('href' => $url)); 11186 11187 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11188 if ($anyenabled) { 11189 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11190 11191 } 11192 $row[1] = $status; 11193 $table->data[] = $row; 11194 11195 // Setup search engine. 11196 $row = array(); 11197 if (empty($CFG->searchengine)) { 11198 $row[0] = '3. ' . get_string('setupsearchengine', 'admin'); 11199 $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11200 } else { 11201 if ($ADMIN->locate('search' . $CFG->searchengine)) { 11202 $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine); 11203 $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin')); 11204 } else { 11205 $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin'); 11206 } 11207 11208 // Check the engine status. 11209 $searchengine = \core_search\manager::search_engine_instance(); 11210 try { 11211 $serverstatus = $searchengine->is_server_ready(); 11212 } catch (\moodle_exception $e) { 11213 $serverstatus = $e->getMessage(); 11214 } 11215 if ($serverstatus === true) { 11216 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11217 } else { 11218 $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger')); 11219 } 11220 $row[1] = $status; 11221 } 11222 $table->data[] = $row; 11223 11224 // Indexed data. 11225 $row = array(); 11226 $url = new moodle_url('/admin/searchareas.php'); 11227 $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url)); 11228 if ($anyindexed) { 11229 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11230 } else { 11231 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11232 } 11233 $row[1] = $status; 11234 $table->data[] = $row; 11235 11236 // Enable global search. 11237 $row = array(); 11238 $url = new moodle_url("/admin/search.php?query=enableglobalsearch"); 11239 $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'), 11240 array('href' => $url)); 11241 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11242 if (\core_search\manager::is_global_search_enabled()) { 11243 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11244 } 11245 $row[1] = $status; 11246 $table->data[] = $row; 11247 11248 // Replace front page search. 11249 $row = array(); 11250 $url = new moodle_url("/admin/search.php?query=searchincludeallcourses"); 11251 $row[0] = '6. ' . html_writer::tag('a', get_string('replacefrontsearch', 'admin'), 11252 array('href' => $url)); 11253 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11254 if (\core_search\manager::can_replace_course_search()) { 11255 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11256 } 11257 $row[1] = $status; 11258 $table->data[] = $row; 11259 11260 $return .= html_writer::table($table); 11261 11262 return highlight($query, $return); 11263 } 11264 11265 } 11266 11267 /** 11268 * Used to validate the contents of SCSS code and ensuring they are parsable. 11269 * 11270 * It does not attempt to detect undefined SCSS variables because it is designed 11271 * to be used without knowledge of other config/scss included. 11272 * 11273 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11274 * @copyright 2016 Dan Poltawski <dan@moodle.com> 11275 */ 11276 class admin_setting_scsscode extends admin_setting_configtextarea { 11277 11278 /** 11279 * Validate the contents of the SCSS to ensure its parsable. Does not 11280 * attempt to detect undefined scss variables. 11281 * 11282 * @param string $data The scss code from text field. 11283 * @return mixed bool true for success or string:error on failure. 11284 */ 11285 public function validate($data) { 11286 if (empty($data)) { 11287 return true; 11288 } 11289 11290 $scss = new core_scss(); 11291 try { 11292 $scss->compile($data); 11293 } catch (ScssPhp\ScssPhp\Exception\ParserException $e) { 11294 return get_string('scssinvalid', 'admin', $e->getMessage()); 11295 } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) { 11296 // Silently ignore this - it could be a scss variable defined from somewhere 11297 // else which we are not examining here. 11298 return true; 11299 } 11300 11301 return true; 11302 } 11303 } 11304 11305 11306 /** 11307 * Administration setting to define a list of file types. 11308 * 11309 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au> 11310 * @copyright 2017 David Mudrák <david@moodle.com> 11311 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11312 */ 11313 class admin_setting_filetypes extends admin_setting_configtext { 11314 11315 /** @var array Allow selection from these file types only. */ 11316 protected $onlytypes = []; 11317 11318 /** @var bool Allow selection of 'All file types' (will be stored as '*'). */ 11319 protected $allowall = true; 11320 11321 /** @var core_form\filetypes_util instance to use as a helper. */ 11322 protected $util = null; 11323 11324 /** 11325 * Constructor. 11326 * 11327 * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting' 11328 * @param string $visiblename Localised label of the setting 11329 * @param string $description Localised description of the setting 11330 * @param string $defaultsetting Default setting value. 11331 * @param array $options Setting widget options, an array with optional keys: 11332 * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']]. 11333 * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set. 11334 */ 11335 public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) { 11336 11337 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW); 11338 11339 if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) { 11340 $this->onlytypes = $options['onlytypes']; 11341 } 11342 11343 if (!$this->onlytypes && array_key_exists('allowall', $options)) { 11344 $this->allowall = (bool)$options['allowall']; 11345 } 11346 11347 $this->util = new \core_form\filetypes_util(); 11348 } 11349 11350 /** 11351 * Normalize the user's input and write it to the database as comma separated list. 11352 * 11353 * Comma separated list as a text representation of the array was chosen to 11354 * make this compatible with how the $CFG->courseoverviewfilesext values are stored. 11355 * 11356 * @param string $data Value submitted by the admin. 11357 * @return string Epty string if all good, error message otherwise. 11358 */ 11359 public function write_setting($data) { 11360 return parent::write_setting(implode(',', $this->util->normalize_file_types($data))); 11361 } 11362 11363 /** 11364 * Validate data before storage 11365 * 11366 * @param string $data The setting values provided by the admin 11367 * @return bool|string True if ok, the string if error found 11368 */ 11369 public function validate($data) { 11370 $parentcheck = parent::validate($data); 11371 if ($parentcheck !== true) { 11372 return $parentcheck; 11373 } 11374 11375 // Check for unknown file types. 11376 if ($unknown = $this->util->get_unknown_file_types($data)) { 11377 return get_string('filetypesunknown', 'core_form', implode(', ', $unknown)); 11378 } 11379 11380 // Check for disallowed file types. 11381 if ($notlisted = $this->util->get_not_listed($data, $this->onlytypes)) { 11382 return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted)); 11383 } 11384 11385 return true; 11386 } 11387 11388 /** 11389 * Return an HTML string for the setting element. 11390 * 11391 * @param string $data The current setting value 11392 * @param string $query Admin search query to be highlighted 11393 * @return string HTML to be displayed 11394 */ 11395 public function output_html($data, $query='') { 11396 global $OUTPUT, $PAGE; 11397 11398 $default = $this->get_defaultsetting(); 11399 $context = (object) [ 11400 'id' => $this->get_id(), 11401 'name' => $this->get_full_name(), 11402 'value' => $data, 11403 'descriptions' => $this->util->describe_file_types($data), 11404 ]; 11405 $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context); 11406 11407 $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [ 11408 $this->get_id(), 11409 $this->visiblename->out(), 11410 $this->onlytypes, 11411 $this->allowall, 11412 ]); 11413 11414 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 11415 } 11416 11417 /** 11418 * Should the values be always displayed in LTR mode? 11419 * 11420 * We always return true here because these values are not RTL compatible. 11421 * 11422 * @return bool True because these values are not RTL compatible. 11423 */ 11424 public function get_force_ltr() { 11425 return true; 11426 } 11427 } 11428 11429 /** 11430 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable. 11431 * 11432 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11433 * @copyright 2018 Mihail Geshoski <mihail@moodle.com> 11434 */ 11435 class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea { 11436 11437 /** 11438 * Constructor. 11439 * 11440 * @param string $name 11441 * @param string $visiblename 11442 * @param string $description 11443 * @param mixed $defaultsetting string or array 11444 * @param mixed $paramtype 11445 * @param string $cols 11446 * @param string $rows 11447 */ 11448 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW, 11449 $cols = '60', $rows = '8') { 11450 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows); 11451 // Pre-set force LTR to false. 11452 $this->set_force_ltr(false); 11453 } 11454 11455 /** 11456 * Validate the content and format of the age of digital consent map to ensure it is parsable. 11457 * 11458 * @param string $data The age of digital consent map from text field. 11459 * @return mixed bool true for success or string:error on failure. 11460 */ 11461 public function validate($data) { 11462 if (empty($data)) { 11463 return true; 11464 } 11465 11466 try { 11467 \core_auth\digital_consent::parse_age_digital_consent_map($data); 11468 } catch (\moodle_exception $e) { 11469 return get_string('invalidagedigitalconsent', 'admin', $e->getMessage()); 11470 } 11471 11472 return true; 11473 } 11474 } 11475 11476 /** 11477 * Selection of plugins that can work as site policy handlers 11478 * 11479 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11480 * @copyright 2018 Marina Glancy 11481 */ 11482 class admin_settings_sitepolicy_handler_select extends admin_setting_configselect { 11483 11484 /** 11485 * Constructor 11486 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' 11487 * for ones in config_plugins. 11488 * @param string $visiblename localised 11489 * @param string $description long localised info 11490 * @param string $defaultsetting 11491 */ 11492 public function __construct($name, $visiblename, $description, $defaultsetting = '') { 11493 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 11494 } 11495 11496 /** 11497 * Lazy-load the available choices for the select box 11498 */ 11499 public function load_choices() { 11500 if (during_initial_install()) { 11501 return false; 11502 } 11503 if (is_array($this->choices)) { 11504 return true; 11505 } 11506 11507 $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')]; 11508 $manager = new \core_privacy\local\sitepolicy\manager(); 11509 $plugins = $manager->get_all_handlers(); 11510 foreach ($plugins as $pname => $unused) { 11511 $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin', 11512 ['name' => new lang_string('pluginname', $pname), 'component' => $pname]); 11513 } 11514 11515 return true; 11516 } 11517 } 11518 11519 /** 11520 * Used to validate theme presets code and ensuring they compile well. 11521 * 11522 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11523 * @copyright 2019 Bas Brands <bas@moodle.com> 11524 */ 11525 class admin_setting_configthemepreset extends admin_setting_configselect { 11526 11527 /** @var string The name of the theme to check for */ 11528 private $themename; 11529 11530 /** 11531 * Constructor 11532 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 11533 * or 'myplugin/mysetting' for ones in config_plugins. 11534 * @param string $visiblename localised 11535 * @param string $description long localised info 11536 * @param string|int $defaultsetting 11537 * @param array $choices array of $value=>$label for each selection 11538 * @param string $themename name of theme to check presets for. 11539 */ 11540 public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) { 11541 $this->themename = $themename; 11542 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices); 11543 } 11544 11545 /** 11546 * Write settings if validated 11547 * 11548 * @param string $data 11549 * @return string 11550 */ 11551 public function write_setting($data) { 11552 $validated = $this->validate($data); 11553 if ($validated !== true) { 11554 return $validated; 11555 } 11556 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 11557 } 11558 11559 /** 11560 * Validate the preset file to ensure its parsable. 11561 * 11562 * @param string $data The preset file chosen. 11563 * @return mixed bool true for success or string:error on failure. 11564 */ 11565 public function validate($data) { 11566 11567 if (in_array($data, ['default.scss', 'plain.scss'])) { 11568 return true; 11569 } 11570 11571 $fs = get_file_storage(); 11572 $theme = theme_config::load($this->themename); 11573 $context = context_system::instance(); 11574 11575 // If the preset has not changed there is no need to validate it. 11576 if ($theme->settings->preset == $data) { 11577 return true; 11578 } 11579 11580 if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) { 11581 // This operation uses a lot of resources. 11582 raise_memory_limit(MEMORY_EXTRA); 11583 core_php_time_limit::raise(300); 11584 11585 // TODO: MDL-62757 When changing anything in this method please do not forget to check 11586 // if the get_css_content_from_scss() method in class theme_config needs updating too. 11587 11588 $compiler = new core_scss(); 11589 $compiler->prepend_raw_scss($theme->get_pre_scss_code()); 11590 $compiler->append_raw_scss($presetfile->get_content()); 11591 if ($scssproperties = $theme->get_scss_property()) { 11592 $compiler->setImportPaths($scssproperties[0]); 11593 } 11594 $compiler->append_raw_scss($theme->get_extra_scss_code()); 11595 11596 try { 11597 $compiler->to_css(); 11598 } catch (Exception $e) { 11599 return get_string('invalidthemepreset', 'admin', $e->getMessage()); 11600 } 11601 11602 // Try to save memory. 11603 $compiler = null; 11604 unset($compiler); 11605 } 11606 11607 return true; 11608 } 11609 } 11610 11611 /** 11612 * Selection of plugins that can work as H5P libraries handlers 11613 * 11614 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11615 * @copyright 2020 Sara Arjona <sara@moodle.com> 11616 */ 11617 class admin_settings_h5plib_handler_select extends admin_setting_configselect { 11618 11619 /** 11620 * Constructor 11621 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' 11622 * for ones in config_plugins. 11623 * @param string $visiblename localised 11624 * @param string $description long localised info 11625 * @param string $defaultsetting 11626 */ 11627 public function __construct($name, $visiblename, $description, $defaultsetting = '') { 11628 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 11629 } 11630 11631 /** 11632 * Lazy-load the available choices for the select box 11633 */ 11634 public function load_choices() { 11635 if (during_initial_install()) { 11636 return false; 11637 } 11638 if (is_array($this->choices)) { 11639 return true; 11640 } 11641 11642 $this->choices = \core_h5p\local\library\autoloader::get_all_handlers(); 11643 foreach ($this->choices as $name => $class) { 11644 $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin', 11645 ['name' => new lang_string('pluginname', $name), 'component' => $name]); 11646 } 11647 11648 return true; 11649 } 11650 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body