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