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