Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Functions and classes used during installation, upgrades and for admin settings. 19 * 20 * ADMIN SETTINGS TREE INTRODUCTION 21 * 22 * This file performs the following tasks: 23 * -it defines the necessary objects and interfaces to build the Moodle 24 * admin hierarchy 25 * -it defines the admin_externalpage_setup() 26 * 27 * ADMIN_SETTING OBJECTS 28 * 29 * Moodle settings are represented by objects that inherit from the admin_setting 30 * class. These objects encapsulate how to read a setting, how to write a new value 31 * to a setting, and how to appropriately display the HTML to modify the setting. 32 * 33 * ADMIN_SETTINGPAGE OBJECTS 34 * 35 * The admin_setting objects are then grouped into admin_settingpages. The latter 36 * appear in the Moodle admin tree block. All interaction with admin_settingpage 37 * objects is handled by the admin/settings.php file. 38 * 39 * ADMIN_EXTERNALPAGE OBJECTS 40 * 41 * There are some settings in Moodle that are too complex to (efficiently) handle 42 * with admin_settingpages. (Consider, for example, user management and displaying 43 * lists of users.) In this case, we use the admin_externalpage object. This object 44 * places a link to an external PHP file in the admin tree block. 45 * 46 * If you're using an admin_externalpage object for some settings, you can take 47 * advantage of the admin_externalpage_* functions. For example, suppose you wanted 48 * to add a foo.php file into admin. First off, you add the following line to 49 * admin/settings/first.php (at the end of the file) or to some other file in 50 * admin/settings: 51 * <code> 52 * $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'), 53 * $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission')); 54 * </code> 55 * 56 * Next, in foo.php, your file structure would resemble the following: 57 * <code> 58 * require(__DIR__.'/../../config.php'); 59 * require_once($CFG->libdir.'/adminlib.php'); 60 * admin_externalpage_setup('foo'); 61 * // functionality like processing form submissions goes here 62 * echo $OUTPUT->header(); 63 * // your HTML goes here 64 * echo $OUTPUT->footer(); 65 * </code> 66 * 67 * The admin_externalpage_setup() function call ensures the user is logged in, 68 * and makes sure that they have the proper role permission to access the page. 69 * It also configures all $PAGE properties needed for navigation. 70 * 71 * ADMIN_CATEGORY OBJECTS 72 * 73 * Above and beyond all this, we have admin_category objects. These objects 74 * appear as folders in the admin tree block. They contain admin_settingpage's, 75 * admin_externalpage's, and other admin_category's. 76 * 77 * OTHER NOTES 78 * 79 * admin_settingpage's, admin_externalpage's, and admin_category's all inherit 80 * from part_of_admin_tree (a pseudointerface). This interface insists that 81 * a class has a check_access method for access permissions, a locate method 82 * used to find a specific node in the admin tree and find parent path. 83 * 84 * admin_category's inherit from parentable_part_of_admin_tree. This pseudo- 85 * interface ensures that the class implements a recursive add function which 86 * accepts a part_of_admin_tree object and searches for the proper place to 87 * put it. parentable_part_of_admin_tree implies part_of_admin_tree. 88 * 89 * Please note that the $this->name field of any part_of_admin_tree must be 90 * UNIQUE throughout the ENTIRE admin tree. 91 * 92 * The $this->name field of an admin_setting object (which is *not* part_of_ 93 * admin_tree) must be unique on the respective admin_settingpage where it is 94 * used. 95 * 96 * Original author: Vincenzo K. Marcovecchio 97 * Maintainer: Petr Skoda 98 * 99 * @package core 100 * @subpackage admin 101 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com 102 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 103 */ 104 105 use core_admin\local\settings\linkable_settings_page; 106 107 defined('MOODLE_INTERNAL') || die(); 108 109 /// Add libraries 110 require_once($CFG->libdir.'/ddllib.php'); 111 require_once($CFG->libdir.'/xmlize.php'); 112 require_once($CFG->libdir.'/messagelib.php'); 113 114 // Add classes, traits, and interfaces which should be autoloaded. 115 // The autoloader is configured late in setup.php, after ABORT_AFTER_CONFIG. 116 // This is also required where the setup system is not included at all. 117 require_once($CFG->dirroot.'/'.$CFG->admin.'/classes/local/settings/linkable_settings_page.php'); 118 119 define('INSECURE_DATAROOT_WARNING', 1); 120 define('INSECURE_DATAROOT_ERROR', 2); 121 122 /** 123 * Automatically clean-up all plugin data and remove the plugin DB tables 124 * 125 * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead! 126 * 127 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc. 128 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc. 129 * @uses global $OUTPUT to produce notices and other messages 130 * @return void 131 */ 132 function uninstall_plugin($type, $name) { 133 global $CFG, $DB, $OUTPUT; 134 135 // This may take a long time. 136 core_php_time_limit::raise(); 137 138 // Recursively uninstall all subplugins first. 139 $subplugintypes = core_component::get_plugin_types_with_subplugins(); 140 if (isset($subplugintypes[$type])) { 141 $base = core_component::get_plugin_directory($type, $name); 142 143 $subpluginsfile = "{$base}/db/subplugins.json"; 144 if (file_exists($subpluginsfile)) { 145 $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes; 146 } else if (file_exists("{$base}/db/subplugins.php")) { 147 debugging('Use of subplugins.php has been deprecated. ' . 148 'Please update your plugin to provide a subplugins.json file instead.', 149 DEBUG_DEVELOPER); 150 $subplugins = []; 151 include("{$base}/db/subplugins.php"); 152 } 153 154 if (!empty($subplugins)) { 155 foreach (array_keys($subplugins) as $subplugintype) { 156 $instances = core_component::get_plugin_list($subplugintype); 157 foreach ($instances as $subpluginname => $notusedpluginpath) { 158 uninstall_plugin($subplugintype, $subpluginname); 159 } 160 } 161 } 162 } 163 164 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum' 165 166 if ($type === 'mod') { 167 $pluginname = $name; // eg. 'forum' 168 if (get_string_manager()->string_exists('modulename', $component)) { 169 $strpluginname = get_string('modulename', $component); 170 } else { 171 $strpluginname = $component; 172 } 173 174 } else { 175 $pluginname = $component; 176 if (get_string_manager()->string_exists('pluginname', $component)) { 177 $strpluginname = get_string('pluginname', $component); 178 } else { 179 $strpluginname = $component; 180 } 181 } 182 183 echo $OUTPUT->heading($pluginname); 184 185 // Delete all tag areas, collections and instances associated with this plugin. 186 core_tag_area::uninstall($component); 187 188 // Custom plugin uninstall. 189 $plugindirectory = core_component::get_plugin_directory($type, $name); 190 $uninstalllib = $plugindirectory . '/db/uninstall.php'; 191 if (file_exists($uninstalllib)) { 192 require_once($uninstalllib); 193 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()' 194 if (function_exists($uninstallfunction)) { 195 // Do not verify result, let plugin complain if necessary. 196 $uninstallfunction(); 197 } 198 } 199 200 // Specific plugin type cleanup. 201 $plugininfo = core_plugin_manager::instance()->get_plugin_info($component); 202 if ($plugininfo) { 203 $plugininfo->uninstall_cleanup(); 204 core_plugin_manager::reset_caches(); 205 } 206 $plugininfo = null; 207 208 // perform clean-up task common for all the plugin/subplugin types 209 210 //delete the web service functions and pre-built services 211 require_once($CFG->dirroot.'/lib/externallib.php'); 212 external_delete_descriptions($component); 213 214 // delete calendar events 215 $DB->delete_records('event', array('modulename' => $pluginname)); 216 $DB->delete_records('event', ['component' => $component]); 217 218 // Delete scheduled tasks. 219 $DB->delete_records('task_adhoc', ['component' => $component]); 220 $DB->delete_records('task_scheduled', array('component' => $component)); 221 222 // Delete Inbound Message datakeys. 223 $DB->delete_records_select('messageinbound_datakeys', 224 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component)); 225 226 // Delete Inbound Message handlers. 227 $DB->delete_records('messageinbound_handlers', array('component' => $component)); 228 229 // delete all the logs 230 $DB->delete_records('log', array('module' => $pluginname)); 231 232 // delete log_display information 233 $DB->delete_records('log_display', array('component' => $component)); 234 235 // delete the module configuration records 236 unset_all_config_for_plugin($component); 237 if ($type === 'mod') { 238 unset_all_config_for_plugin($pluginname); 239 } 240 241 // delete message provider 242 message_provider_uninstall($component); 243 244 // delete the plugin tables 245 $xmldbfilepath = $plugindirectory . '/db/install.xml'; 246 drop_plugin_tables($component, $xmldbfilepath, false); 247 if ($type === 'mod' or $type === 'block') { 248 // non-frankenstyle table prefixes 249 drop_plugin_tables($name, $xmldbfilepath, false); 250 } 251 252 // delete the capabilities that were defined by this module 253 capabilities_cleanup($component); 254 255 // Delete all remaining files in the filepool owned by the component. 256 $fs = get_file_storage(); 257 $fs->delete_component_files($component); 258 259 // Finally purge all caches. 260 purge_all_caches(); 261 262 // Invalidate the hash used for upgrade detections. 263 set_config('allversionshash', ''); 264 265 echo $OUTPUT->notification(get_string('success'), 'notifysuccess'); 266 } 267 268 /** 269 * Returns the version of installed component 270 * 271 * @param string $component component name 272 * @param string $source either 'disk' or 'installed' - where to get the version information from 273 * @return string|bool version number or false if the component is not found 274 */ 275 function get_component_version($component, $source='installed') { 276 global $CFG, $DB; 277 278 list($type, $name) = core_component::normalize_component($component); 279 280 // moodle core or a core subsystem 281 if ($type === 'core') { 282 if ($source === 'installed') { 283 if (empty($CFG->version)) { 284 return false; 285 } else { 286 return $CFG->version; 287 } 288 } else { 289 if (!is_readable($CFG->dirroot.'/version.php')) { 290 return false; 291 } else { 292 $version = null; //initialize variable for IDEs 293 include($CFG->dirroot.'/version.php'); 294 return $version; 295 } 296 } 297 } 298 299 // activity module 300 if ($type === 'mod') { 301 if ($source === 'installed') { 302 if ($CFG->version < 2013092001.02) { 303 return $DB->get_field('modules', 'version', array('name'=>$name)); 304 } else { 305 return get_config('mod_'.$name, 'version'); 306 } 307 308 } else { 309 $mods = core_component::get_plugin_list('mod'); 310 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) { 311 return false; 312 } else { 313 $plugin = new stdClass(); 314 $plugin->version = null; 315 $module = $plugin; 316 include($mods[$name].'/version.php'); 317 return $plugin->version; 318 } 319 } 320 } 321 322 // block 323 if ($type === 'block') { 324 if ($source === 'installed') { 325 if ($CFG->version < 2013092001.02) { 326 return $DB->get_field('block', 'version', array('name'=>$name)); 327 } else { 328 return get_config('block_'.$name, 'version'); 329 } 330 } else { 331 $blocks = core_component::get_plugin_list('block'); 332 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) { 333 return false; 334 } else { 335 $plugin = new stdclass(); 336 include($blocks[$name].'/version.php'); 337 return $plugin->version; 338 } 339 } 340 } 341 342 // all other plugin types 343 if ($source === 'installed') { 344 return get_config($type.'_'.$name, 'version'); 345 } else { 346 $plugins = core_component::get_plugin_list($type); 347 if (empty($plugins[$name])) { 348 return false; 349 } else { 350 $plugin = new stdclass(); 351 include($plugins[$name].'/version.php'); 352 return $plugin->version; 353 } 354 } 355 } 356 357 /** 358 * Delete all plugin tables 359 * 360 * @param string $name Name of plugin, used as table prefix 361 * @param string $file Path to install.xml file 362 * @param bool $feedback defaults to true 363 * @return bool Always returns true 364 */ 365 function drop_plugin_tables($name, $file, $feedback=true) { 366 global $CFG, $DB; 367 368 // first try normal delete 369 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) { 370 return true; 371 } 372 373 // then try to find all tables that start with name and are not in any xml file 374 $used_tables = get_used_table_names(); 375 376 $tables = $DB->get_tables(); 377 378 /// Iterate over, fixing id fields as necessary 379 foreach ($tables as $table) { 380 if (in_array($table, $used_tables)) { 381 continue; 382 } 383 384 if (strpos($table, $name) !== 0) { 385 continue; 386 } 387 388 // found orphan table --> delete it 389 if ($DB->get_manager()->table_exists($table)) { 390 $xmldb_table = new xmldb_table($table); 391 $DB->get_manager()->drop_table($xmldb_table); 392 } 393 } 394 395 return true; 396 } 397 398 /** 399 * Returns names of all known tables == tables that moodle knows about. 400 * 401 * @return array Array of lowercase table names 402 */ 403 function get_used_table_names() { 404 $table_names = array(); 405 $dbdirs = get_db_directories(); 406 407 foreach ($dbdirs as $dbdir) { 408 $file = $dbdir.'/install.xml'; 409 410 $xmldb_file = new xmldb_file($file); 411 412 if (!$xmldb_file->fileExists()) { 413 continue; 414 } 415 416 $loaded = $xmldb_file->loadXMLStructure(); 417 $structure = $xmldb_file->getStructure(); 418 419 if ($loaded and $tables = $structure->getTables()) { 420 foreach($tables as $table) { 421 $table_names[] = strtolower($table->getName()); 422 } 423 } 424 } 425 426 return $table_names; 427 } 428 429 /** 430 * Returns list of all directories where we expect install.xml files 431 * @return array Array of paths 432 */ 433 function get_db_directories() { 434 global $CFG; 435 436 $dbdirs = array(); 437 438 /// First, the main one (lib/db) 439 $dbdirs[] = $CFG->libdir.'/db'; 440 441 /// Then, all the ones defined by core_component::get_plugin_types() 442 $plugintypes = core_component::get_plugin_types(); 443 foreach ($plugintypes as $plugintype => $pluginbasedir) { 444 if ($plugins = core_component::get_plugin_list($plugintype)) { 445 foreach ($plugins as $plugin => $plugindir) { 446 $dbdirs[] = $plugindir.'/db'; 447 } 448 } 449 } 450 451 return $dbdirs; 452 } 453 454 /** 455 * Try to obtain or release the cron lock. 456 * @param string $name name of lock 457 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally 458 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false 459 * @return bool true if lock obtained 460 */ 461 function set_cron_lock($name, $until, $ignorecurrent=false) { 462 global $DB; 463 if (empty($name)) { 464 debugging("Tried to get a cron lock for a null fieldname"); 465 return false; 466 } 467 468 // remove lock by force == remove from config table 469 if (is_null($until)) { 470 set_config($name, null); 471 return true; 472 } 473 474 if (!$ignorecurrent) { 475 // read value from db - other processes might have changed it 476 $value = $DB->get_field('config', 'value', array('name'=>$name)); 477 478 if ($value and $value > time()) { 479 //lock active 480 return false; 481 } 482 } 483 484 set_config($name, $until); 485 return true; 486 } 487 488 /** 489 * Test if and critical warnings are present 490 * @return bool 491 */ 492 function admin_critical_warnings_present() { 493 global $SESSION; 494 495 if (!has_capability('moodle/site:config', context_system::instance())) { 496 return 0; 497 } 498 499 if (!isset($SESSION->admin_critical_warning)) { 500 $SESSION->admin_critical_warning = 0; 501 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) { 502 $SESSION->admin_critical_warning = 1; 503 } 504 } 505 506 return $SESSION->admin_critical_warning; 507 } 508 509 /** 510 * Detects if float supports at least 10 decimal digits 511 * 512 * Detects if float supports at least 10 decimal digits 513 * and also if float-->string conversion works as expected. 514 * 515 * @return bool true if problem found 516 */ 517 function is_float_problem() { 518 $num1 = 2009010200.01; 519 $num2 = 2009010200.02; 520 521 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1); 522 } 523 524 /** 525 * Try to verify that dataroot is not accessible from web. 526 * 527 * Try to verify that dataroot is not accessible from web. 528 * It is not 100% correct but might help to reduce number of vulnerable sites. 529 * Protection from httpd.conf and .htaccess is not detected properly. 530 * 531 * @uses INSECURE_DATAROOT_WARNING 532 * @uses INSECURE_DATAROOT_ERROR 533 * @param bool $fetchtest try to test public access by fetching file, default false 534 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic 535 */ 536 function is_dataroot_insecure($fetchtest=false) { 537 global $CFG; 538 539 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround 540 541 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1); 542 $rp = strrev(trim($rp, '/')); 543 $rp = explode('/', $rp); 544 foreach($rp as $r) { 545 if (strpos($siteroot, '/'.$r.'/') === 0) { 546 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory 547 } else { 548 break; // probably alias root 549 } 550 } 551 552 $siteroot = strrev($siteroot); 553 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/'); 554 555 if (strpos($dataroot, $siteroot) !== 0) { 556 return false; 557 } 558 559 if (!$fetchtest) { 560 return INSECURE_DATAROOT_WARNING; 561 } 562 563 // now try all methods to fetch a test file using http protocol 564 565 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); 566 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches); 567 $httpdocroot = $matches[1]; 568 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot)); 569 make_upload_directory('diag'); 570 $testfile = $CFG->dataroot.'/diag/public.txt'; 571 if (!file_exists($testfile)) { 572 file_put_contents($testfile, 'test file, do not delete'); 573 @chmod($testfile, $CFG->filepermissions); 574 } 575 $teststr = trim(file_get_contents($testfile)); 576 if (empty($teststr)) { 577 // hmm, strange 578 return INSECURE_DATAROOT_WARNING; 579 } 580 581 $testurl = $datarooturl.'/diag/public.txt'; 582 if (extension_loaded('curl') and 583 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and 584 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and 585 ($ch = @curl_init($testurl)) !== false) { 586 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 587 curl_setopt($ch, CURLOPT_HEADER, false); 588 $data = curl_exec($ch); 589 if (!curl_errno($ch)) { 590 $data = trim($data); 591 if ($data === $teststr) { 592 curl_close($ch); 593 return INSECURE_DATAROOT_ERROR; 594 } 595 } 596 curl_close($ch); 597 } 598 599 if ($data = @file_get_contents($testurl)) { 600 $data = trim($data); 601 if ($data === $teststr) { 602 return INSECURE_DATAROOT_ERROR; 603 } 604 } 605 606 preg_match('|https?://([^/]+)|i', $testurl, $matches); 607 $sitename = $matches[1]; 608 $error = 0; 609 if ($fp = @fsockopen($sitename, 80, $error)) { 610 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches); 611 $localurl = $matches[1]; 612 $out = "GET $localurl HTTP/1.1\r\n"; 613 $out .= "Host: $sitename\r\n"; 614 $out .= "Connection: Close\r\n\r\n"; 615 fwrite($fp, $out); 616 $data = ''; 617 $incoming = false; 618 while (!feof($fp)) { 619 if ($incoming) { 620 $data .= fgets($fp, 1024); 621 } else if (@fgets($fp, 1024) === "\r\n") { 622 $incoming = true; 623 } 624 } 625 fclose($fp); 626 $data = trim($data); 627 if ($data === $teststr) { 628 return INSECURE_DATAROOT_ERROR; 629 } 630 } 631 632 return INSECURE_DATAROOT_WARNING; 633 } 634 635 /** 636 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file. 637 */ 638 function enable_cli_maintenance_mode() { 639 global $CFG, $SITE; 640 641 if (file_exists("$CFG->dataroot/climaintenance.html")) { 642 unlink("$CFG->dataroot/climaintenance.html"); 643 } 644 645 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) { 646 $data = $CFG->maintenance_message; 647 $data = bootstrap_renderer::early_error_content($data, null, null, null); 648 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data); 649 650 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) { 651 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html"); 652 653 } else { 654 $data = get_string('sitemaintenance', 'admin'); 655 $data = bootstrap_renderer::early_error_content($data, null, null, null); 656 $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin', 657 format_string($SITE->fullname, true, ['context' => context_system::instance()])), $data); 658 } 659 660 file_put_contents("$CFG->dataroot/climaintenance.html", $data); 661 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions); 662 } 663 664 /// CLASS DEFINITIONS ///////////////////////////////////////////////////////// 665 666 667 /** 668 * Interface for anything appearing in the admin tree 669 * 670 * The interface that is implemented by anything that appears in the admin tree 671 * block. It forces inheriting classes to define a method for checking user permissions 672 * and methods for finding something in the admin tree. 673 * 674 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 675 */ 676 interface part_of_admin_tree { 677 678 /** 679 * Finds a named part_of_admin_tree. 680 * 681 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree 682 * and not parentable_part_of_admin_tree, then this function should only check if 683 * $this->name matches $name. If it does, it should return a reference to $this, 684 * otherwise, it should return a reference to NULL. 685 * 686 * If a class inherits parentable_part_of_admin_tree, this method should be called 687 * recursively on all child objects (assuming, of course, the parent object's name 688 * doesn't match the search criterion). 689 * 690 * @param string $name The internal name of the part_of_admin_tree we're searching for. 691 * @return mixed An object reference or a NULL reference. 692 */ 693 public function locate($name); 694 695 /** 696 * Removes named part_of_admin_tree. 697 * 698 * @param string $name The internal name of the part_of_admin_tree we want to remove. 699 * @return bool success. 700 */ 701 public function prune($name); 702 703 /** 704 * Search using query 705 * @param string $query 706 * @return mixed array-object structure of found settings and pages 707 */ 708 public function search($query); 709 710 /** 711 * Verifies current user's access to this part_of_admin_tree. 712 * 713 * Used to check if the current user has access to this part of the admin tree or 714 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree, 715 * then this method is usually just a call to has_capability() in the site context. 716 * 717 * If a class inherits parentable_part_of_admin_tree, this method should return the 718 * logical OR of the return of check_access() on all child objects. 719 * 720 * @return bool True if the user has access, false if she doesn't. 721 */ 722 public function check_access(); 723 724 /** 725 * Mostly useful for removing of some parts of the tree in admin tree block. 726 * 727 * @return True is hidden from normal list view 728 */ 729 public function is_hidden(); 730 731 /** 732 * Show we display Save button at the page bottom? 733 * @return bool 734 */ 735 public function show_save(); 736 } 737 738 739 /** 740 * Interface implemented by any part_of_admin_tree that has children. 741 * 742 * The interface implemented by any part_of_admin_tree that can be a parent 743 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart 744 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods 745 * include an add method for adding other part_of_admin_tree objects as children. 746 * 747 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 748 */ 749 interface parentable_part_of_admin_tree extends part_of_admin_tree { 750 751 /** 752 * Adds a part_of_admin_tree object to the admin tree. 753 * 754 * Used to add a part_of_admin_tree object to this object or a child of this 755 * object. $something should only be added if $destinationname matches 756 * $this->name. If it doesn't, add should be called on child objects that are 757 * also parentable_part_of_admin_tree's. 758 * 759 * $something should be appended as the last child in the $destinationname. If the 760 * $beforesibling is specified, $something should be prepended to it. If the given 761 * sibling is not found, $something should be appended to the end of $destinationname 762 * and a developer debugging message should be displayed. 763 * 764 * @param string $destinationname The internal name of the new parent for $something. 765 * @param part_of_admin_tree $something The object to be added. 766 * @return bool True on success, false on failure. 767 */ 768 public function add($destinationname, $something, $beforesibling = null); 769 770 } 771 772 773 /** 774 * The object used to represent folders (a.k.a. categories) in the admin tree block. 775 * 776 * Each admin_category object contains a number of part_of_admin_tree objects. 777 * 778 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 779 */ 780 class admin_category implements parentable_part_of_admin_tree, linkable_settings_page { 781 782 /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */ 783 protected $children; 784 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */ 785 public $name; 786 /** @var string The displayed name for this category. Usually obtained through get_string() */ 787 public $visiblename; 788 /** @var bool Should this category be hidden in admin tree block? */ 789 public $hidden; 790 /** @var mixed Either a string or an array or strings */ 791 public $path; 792 /** @var mixed Either a string or an array or strings */ 793 public $visiblepath; 794 795 /** @var array fast lookup category cache, all categories of one tree point to one cache */ 796 protected $category_cache; 797 798 /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */ 799 protected $sort = false; 800 /** @var bool If set to true children will be sorted in ascending order. */ 801 protected $sortasc = true; 802 /** @var bool If set to true sub categories and pages will be split and then sorted.. */ 803 protected $sortsplit = true; 804 /** @var bool $sorted True if the children have been sorted and don't need resorting */ 805 protected $sorted = false; 806 807 /** 808 * Constructor for an empty admin category 809 * 810 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects 811 * @param string $visiblename The displayed named for this category. Usually obtained through get_string() 812 * @param bool $hidden hide category in admin tree block, defaults to false 813 */ 814 public function __construct($name, $visiblename, $hidden=false) { 815 $this->children = array(); 816 $this->name = $name; 817 $this->visiblename = $visiblename; 818 $this->hidden = $hidden; 819 } 820 821 /** 822 * Get the URL to view this settings page. 823 * 824 * @return moodle_url 825 */ 826 public function get_settings_page_url(): moodle_url { 827 return new moodle_url( 828 '/admin/category.php', 829 [ 830 'category' => $this->name, 831 ] 832 ); 833 } 834 835 /** 836 * Returns a reference to the part_of_admin_tree object with internal name $name. 837 * 838 * @param string $name The internal name of the object we want. 839 * @param bool $findpath initialize path and visiblepath arrays 840 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL. 841 * defaults to false 842 */ 843 public function locate($name, $findpath=false) { 844 if (!isset($this->category_cache[$this->name])) { 845 // somebody much have purged the cache 846 $this->category_cache[$this->name] = $this; 847 } 848 849 if ($this->name == $name) { 850 if ($findpath) { 851 $this->visiblepath[] = $this->visiblename; 852 $this->path[] = $this->name; 853 } 854 return $this; 855 } 856 857 // quick category lookup 858 if (!$findpath and isset($this->category_cache[$name])) { 859 return $this->category_cache[$name]; 860 } 861 862 $return = NULL; 863 foreach($this->children as $childid=>$unused) { 864 if ($return = $this->children[$childid]->locate($name, $findpath)) { 865 break; 866 } 867 } 868 869 if (!is_null($return) and $findpath) { 870 $return->visiblepath[] = $this->visiblename; 871 $return->path[] = $this->name; 872 } 873 874 return $return; 875 } 876 877 /** 878 * Search using query 879 * 880 * @param string query 881 * @return mixed array-object structure of found settings and pages 882 */ 883 public function search($query) { 884 $result = array(); 885 foreach ($this->get_children() as $child) { 886 $subsearch = $child->search($query); 887 if (!is_array($subsearch)) { 888 debugging('Incorrect search result from '.$child->name); 889 continue; 890 } 891 $result = array_merge($result, $subsearch); 892 } 893 return $result; 894 } 895 896 /** 897 * Removes part_of_admin_tree object with internal name $name. 898 * 899 * @param string $name The internal name of the object we want to remove. 900 * @return bool success 901 */ 902 public function prune($name) { 903 904 if ($this->name == $name) { 905 return false; //can not remove itself 906 } 907 908 foreach($this->children as $precedence => $child) { 909 if ($child->name == $name) { 910 // clear cache and delete self 911 while($this->category_cache) { 912 // delete the cache, but keep the original array address 913 array_pop($this->category_cache); 914 } 915 unset($this->children[$precedence]); 916 return true; 917 } else if ($this->children[$precedence]->prune($name)) { 918 return true; 919 } 920 } 921 return false; 922 } 923 924 /** 925 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object. 926 * 927 * By default the new part of the tree is appended as the last child of the parent. You 928 * can specify a sibling node that the new part should be prepended to. If the given 929 * sibling is not found, the part is appended to the end (as it would be by default) and 930 * a developer debugging message is displayed. 931 * 932 * @throws coding_exception if the $beforesibling is empty string or is not string at all. 933 * @param string $destinationame The internal name of the immediate parent that we want for $something. 934 * @param mixed $something A part_of_admin_tree or setting instance to be added. 935 * @param string $beforesibling The name of the parent's child the $something should be prepended to. 936 * @return bool True if successfully added, false if $something can not be added. 937 */ 938 public function add($parentname, $something, $beforesibling = null) { 939 global $CFG; 940 941 $parent = $this->locate($parentname); 942 if (is_null($parent)) { 943 debugging('parent does not exist!'); 944 return false; 945 } 946 947 if ($something instanceof part_of_admin_tree) { 948 if (!($parent instanceof parentable_part_of_admin_tree)) { 949 debugging('error - parts of tree can be inserted only into parentable parts'); 950 return false; 951 } 952 if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) { 953 // The name of the node is already used, simply warn the developer that this should not happen. 954 // It is intentional to check for the debug level before performing the check. 955 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER); 956 } 957 if (is_null($beforesibling)) { 958 // Append $something as the parent's last child. 959 $parent->children[] = $something; 960 } else { 961 if (!is_string($beforesibling) or trim($beforesibling) === '') { 962 throw new coding_exception('Unexpected value of the beforesibling parameter'); 963 } 964 // Try to find the position of the sibling. 965 $siblingposition = null; 966 foreach ($parent->children as $childposition => $child) { 967 if ($child->name === $beforesibling) { 968 $siblingposition = $childposition; 969 break; 970 } 971 } 972 if (is_null($siblingposition)) { 973 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER); 974 $parent->children[] = $something; 975 } else { 976 $parent->children = array_merge( 977 array_slice($parent->children, 0, $siblingposition), 978 array($something), 979 array_slice($parent->children, $siblingposition) 980 ); 981 } 982 } 983 if ($something instanceof admin_category) { 984 if (isset($this->category_cache[$something->name])) { 985 debugging('Duplicate admin category name: '.$something->name); 986 } else { 987 $this->category_cache[$something->name] = $something; 988 $something->category_cache =& $this->category_cache; 989 foreach ($something->children as $child) { 990 // just in case somebody already added subcategories 991 if ($child instanceof admin_category) { 992 if (isset($this->category_cache[$child->name])) { 993 debugging('Duplicate admin category name: '.$child->name); 994 } else { 995 $this->category_cache[$child->name] = $child; 996 $child->category_cache =& $this->category_cache; 997 } 998 } 999 } 1000 } 1001 } 1002 return true; 1003 1004 } else { 1005 debugging('error - can not add this element'); 1006 return false; 1007 } 1008 1009 } 1010 1011 /** 1012 * Checks if the user has access to anything in this category. 1013 * 1014 * @return bool True if the user has access to at least one child in this category, false otherwise. 1015 */ 1016 public function check_access() { 1017 foreach ($this->children as $child) { 1018 if ($child->check_access()) { 1019 return true; 1020 } 1021 } 1022 return false; 1023 } 1024 1025 /** 1026 * Is this category hidden in admin tree block? 1027 * 1028 * @return bool True if hidden 1029 */ 1030 public function is_hidden() { 1031 return $this->hidden; 1032 } 1033 1034 /** 1035 * Show we display Save button at the page bottom? 1036 * @return bool 1037 */ 1038 public function show_save() { 1039 foreach ($this->children as $child) { 1040 if ($child->show_save()) { 1041 return true; 1042 } 1043 } 1044 return false; 1045 } 1046 1047 /** 1048 * Sets sorting on this category. 1049 * 1050 * Please note this function doesn't actually do the sorting. 1051 * It can be called anytime. 1052 * Sorting occurs when the user calls get_children. 1053 * Code using the children array directly won't see the sorted results. 1054 * 1055 * @param bool $sort If set to true children will be sorted, if false they won't be. 1056 * @param bool $asc If true sorting will be ascending, otherwise descending. 1057 * @param bool $split If true we sort pages and sub categories separately. 1058 */ 1059 public function set_sorting($sort, $asc = true, $split = true) { 1060 $this->sort = (bool)$sort; 1061 $this->sortasc = (bool)$asc; 1062 $this->sortsplit = (bool)$split; 1063 } 1064 1065 /** 1066 * Returns the children associated with this category. 1067 * 1068 * @return part_of_admin_tree[] 1069 */ 1070 public function get_children() { 1071 // If we should sort and it hasn't already been sorted. 1072 if ($this->sort && !$this->sorted) { 1073 if ($this->sortsplit) { 1074 $categories = array(); 1075 $pages = array(); 1076 foreach ($this->children as $child) { 1077 if ($child instanceof admin_category) { 1078 $categories[] = $child; 1079 } else { 1080 $pages[] = $child; 1081 } 1082 } 1083 core_collator::asort_objects_by_property($categories, 'visiblename'); 1084 core_collator::asort_objects_by_property($pages, 'visiblename'); 1085 if (!$this->sortasc) { 1086 $categories = array_reverse($categories); 1087 $pages = array_reverse($pages); 1088 } 1089 $this->children = array_merge($pages, $categories); 1090 } else { 1091 core_collator::asort_objects_by_property($this->children, 'visiblename'); 1092 if (!$this->sortasc) { 1093 $this->children = array_reverse($this->children); 1094 } 1095 } 1096 $this->sorted = true; 1097 } 1098 return $this->children; 1099 } 1100 1101 /** 1102 * Magically gets a property from this object. 1103 * 1104 * @param $property 1105 * @return part_of_admin_tree[] 1106 * @throws coding_exception 1107 */ 1108 public function __get($property) { 1109 if ($property === 'children') { 1110 return $this->get_children(); 1111 } 1112 throw new coding_exception('Invalid property requested.'); 1113 } 1114 1115 /** 1116 * Magically sets a property against this object. 1117 * 1118 * @param string $property 1119 * @param mixed $value 1120 * @throws coding_exception 1121 */ 1122 public function __set($property, $value) { 1123 if ($property === 'children') { 1124 $this->sorted = false; 1125 $this->children = $value; 1126 } else { 1127 throw new coding_exception('Invalid property requested.'); 1128 } 1129 } 1130 1131 /** 1132 * Checks if an inaccessible property is set. 1133 * 1134 * @param string $property 1135 * @return bool 1136 * @throws coding_exception 1137 */ 1138 public function __isset($property) { 1139 if ($property === 'children') { 1140 return isset($this->children); 1141 } 1142 throw new coding_exception('Invalid property requested.'); 1143 } 1144 } 1145 1146 1147 /** 1148 * Root of admin settings tree, does not have any parent. 1149 * 1150 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1151 */ 1152 class admin_root extends admin_category { 1153 /** @var array List of errors */ 1154 public $errors; 1155 /** @var string search query */ 1156 public $search; 1157 /** @var bool full tree flag - true means all settings required, false only pages required */ 1158 public $fulltree; 1159 /** @var bool flag indicating loaded tree */ 1160 public $loaded; 1161 /** @var mixed site custom defaults overriding defaults in settings files*/ 1162 public $custom_defaults; 1163 1164 /** 1165 * @param bool $fulltree true means all settings required, 1166 * false only pages required 1167 */ 1168 public function __construct($fulltree) { 1169 global $CFG; 1170 1171 parent::__construct('root', get_string('administration'), false); 1172 $this->errors = array(); 1173 $this->search = ''; 1174 $this->fulltree = $fulltree; 1175 $this->loaded = false; 1176 1177 $this->category_cache = array(); 1178 1179 // load custom defaults if found 1180 $this->custom_defaults = null; 1181 $defaultsfile = "$CFG->dirroot/local/defaults.php"; 1182 if (is_readable($defaultsfile)) { 1183 $defaults = array(); 1184 include($defaultsfile); 1185 if (is_array($defaults) and count($defaults)) { 1186 $this->custom_defaults = $defaults; 1187 } 1188 } 1189 } 1190 1191 /** 1192 * Empties children array, and sets loaded to false 1193 * 1194 * @param bool $requirefulltree 1195 */ 1196 public function purge_children($requirefulltree) { 1197 $this->children = array(); 1198 $this->fulltree = ($requirefulltree || $this->fulltree); 1199 $this->loaded = false; 1200 //break circular dependencies - this helps PHP 5.2 1201 while($this->category_cache) { 1202 array_pop($this->category_cache); 1203 } 1204 $this->category_cache = array(); 1205 } 1206 } 1207 1208 1209 /** 1210 * Links external PHP pages into the admin tree. 1211 * 1212 * See detailed usage example at the top of this document (adminlib.php) 1213 * 1214 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1215 */ 1216 class admin_externalpage implements part_of_admin_tree, linkable_settings_page { 1217 1218 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */ 1219 public $name; 1220 1221 /** @var string The displayed name for this external page. Usually obtained through get_string(). */ 1222 public $visiblename; 1223 1224 /** @var string The external URL that we should link to when someone requests this external page. */ 1225 public $url; 1226 1227 /** @var array The role capability/permission a user must have to access this external page. */ 1228 public $req_capability; 1229 1230 /** @var object The context in which capability/permission should be checked, default is site context. */ 1231 public $context; 1232 1233 /** @var bool hidden in admin tree block. */ 1234 public $hidden; 1235 1236 /** @var mixed either string or array of string */ 1237 public $path; 1238 1239 /** @var array list of visible names of page parents */ 1240 public $visiblepath; 1241 1242 /** 1243 * Constructor for adding an external page into the admin tree. 1244 * 1245 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects. 1246 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string(). 1247 * @param string $url The external URL that we should link to when someone requests this external page. 1248 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'. 1249 * @param boolean $hidden Is this external page hidden in admin tree block? Default false. 1250 * @param stdClass $context The context the page relates to. Not sure what happens 1251 * if you specify something other than system or front page. Defaults to system. 1252 */ 1253 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) { 1254 $this->name = $name; 1255 $this->visiblename = $visiblename; 1256 $this->url = $url; 1257 if (is_array($req_capability)) { 1258 $this->req_capability = $req_capability; 1259 } else { 1260 $this->req_capability = array($req_capability); 1261 } 1262 $this->hidden = $hidden; 1263 $this->context = $context; 1264 } 1265 1266 /** 1267 * Get the URL to view this settings page. 1268 * 1269 * @return moodle_url 1270 */ 1271 public function get_settings_page_url(): moodle_url { 1272 return new moodle_url($this->url); 1273 } 1274 1275 /** 1276 * Returns a reference to the part_of_admin_tree object with internal name $name. 1277 * 1278 * @param string $name The internal name of the object we want. 1279 * @param bool $findpath defaults to false 1280 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL. 1281 */ 1282 public function locate($name, $findpath=false) { 1283 if ($this->name == $name) { 1284 if ($findpath) { 1285 $this->visiblepath = array($this->visiblename); 1286 $this->path = array($this->name); 1287 } 1288 return $this; 1289 } else { 1290 $return = NULL; 1291 return $return; 1292 } 1293 } 1294 1295 /** 1296 * This function always returns false, required function by interface 1297 * 1298 * @param string $name 1299 * @return false 1300 */ 1301 public function prune($name) { 1302 return false; 1303 } 1304 1305 /** 1306 * Search using query 1307 * 1308 * @param string $query 1309 * @return mixed array-object structure of found settings and pages 1310 */ 1311 public function search($query) { 1312 $found = false; 1313 if (strpos(strtolower($this->name), $query) !== false) { 1314 $found = true; 1315 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) { 1316 $found = true; 1317 } 1318 if ($found) { 1319 $result = new stdClass(); 1320 $result->page = $this; 1321 $result->settings = array(); 1322 return array($this->name => $result); 1323 } else { 1324 return array(); 1325 } 1326 } 1327 1328 /** 1329 * Determines if the current user has access to this external page based on $this->req_capability. 1330 * 1331 * @return bool True if user has access, false otherwise. 1332 */ 1333 public function check_access() { 1334 global $CFG; 1335 $context = empty($this->context) ? context_system::instance() : $this->context; 1336 foreach($this->req_capability as $cap) { 1337 if (has_capability($cap, $context)) { 1338 return true; 1339 } 1340 } 1341 return false; 1342 } 1343 1344 /** 1345 * Is this external page hidden in admin tree block? 1346 * 1347 * @return bool True if hidden 1348 */ 1349 public function is_hidden() { 1350 return $this->hidden; 1351 } 1352 1353 /** 1354 * Show we display Save button at the page bottom? 1355 * @return bool 1356 */ 1357 public function show_save() { 1358 return false; 1359 } 1360 } 1361 1362 /** 1363 * Used to store details of the dependency between two settings elements. 1364 * 1365 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1366 * @copyright 2017 Davo Smith, Synergy Learning 1367 */ 1368 class admin_settingdependency { 1369 /** @var string the name of the setting to be shown/hidden */ 1370 public $settingname; 1371 /** @var string the setting this is dependent on */ 1372 public $dependenton; 1373 /** @var string the condition to show/hide the element */ 1374 public $condition; 1375 /** @var string the value to compare against */ 1376 public $value; 1377 1378 /** @var string[] list of valid conditions */ 1379 private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in']; 1380 1381 /** 1382 * admin_settingdependency constructor. 1383 * @param string $settingname 1384 * @param string $dependenton 1385 * @param string $condition 1386 * @param string $value 1387 * @throws \coding_exception 1388 */ 1389 public function __construct($settingname, $dependenton, $condition, $value) { 1390 $this->settingname = $this->parse_name($settingname); 1391 $this->dependenton = $this->parse_name($dependenton); 1392 $this->condition = $condition; 1393 $this->value = $value; 1394 1395 if (!in_array($this->condition, self::$validconditions)) { 1396 throw new coding_exception("Invalid condition '$condition'"); 1397 } 1398 } 1399 1400 /** 1401 * Convert the setting name into the form field name. 1402 * @param string $name 1403 * @return string 1404 */ 1405 private function parse_name($name) { 1406 $bits = explode('/', $name); 1407 $name = array_pop($bits); 1408 $plugin = ''; 1409 if ($bits) { 1410 $plugin = array_pop($bits); 1411 if ($plugin === 'moodle') { 1412 $plugin = ''; 1413 } 1414 } 1415 return 's_'.$plugin.'_'.$name; 1416 } 1417 1418 /** 1419 * Gather together all the dependencies in a format suitable for initialising javascript 1420 * @param admin_settingdependency[] $dependencies 1421 * @return array 1422 */ 1423 public static function prepare_for_javascript($dependencies) { 1424 $result = []; 1425 foreach ($dependencies as $d) { 1426 if (!isset($result[$d->dependenton])) { 1427 $result[$d->dependenton] = []; 1428 } 1429 if (!isset($result[$d->dependenton][$d->condition])) { 1430 $result[$d->dependenton][$d->condition] = []; 1431 } 1432 if (!isset($result[$d->dependenton][$d->condition][$d->value])) { 1433 $result[$d->dependenton][$d->condition][$d->value] = []; 1434 } 1435 $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname; 1436 } 1437 return $result; 1438 } 1439 } 1440 1441 /** 1442 * Used to group a number of admin_setting objects into a page and add them to the admin tree. 1443 * 1444 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1445 */ 1446 class admin_settingpage implements part_of_admin_tree, linkable_settings_page { 1447 1448 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */ 1449 public $name; 1450 1451 /** @var string The displayed name for this external page. Usually obtained through get_string(). */ 1452 public $visiblename; 1453 1454 /** @var mixed An array of admin_setting objects that are part of this setting page. */ 1455 public $settings; 1456 1457 /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */ 1458 protected $dependencies = []; 1459 1460 /** @var array The role capability/permission a user must have to access this external page. */ 1461 public $req_capability; 1462 1463 /** @var object The context in which capability/permission should be checked, default is site context. */ 1464 public $context; 1465 1466 /** @var bool hidden in admin tree block. */ 1467 public $hidden; 1468 1469 /** @var mixed string of paths or array of strings of paths */ 1470 public $path; 1471 1472 /** @var array list of visible names of page parents */ 1473 public $visiblepath; 1474 1475 /** 1476 * see admin_settingpage for details of this function 1477 * 1478 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects. 1479 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string(). 1480 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'. 1481 * @param boolean $hidden Is this external page hidden in admin tree block? Default false. 1482 * @param stdClass $context The context the page relates to. Not sure what happens 1483 * if you specify something other than system or front page. Defaults to system. 1484 */ 1485 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) { 1486 $this->settings = new stdClass(); 1487 $this->name = $name; 1488 $this->visiblename = $visiblename; 1489 if (is_array($req_capability)) { 1490 $this->req_capability = $req_capability; 1491 } else { 1492 $this->req_capability = array($req_capability); 1493 } 1494 $this->hidden = $hidden; 1495 $this->context = $context; 1496 } 1497 1498 /** 1499 * Get the URL to view this page. 1500 * 1501 * @return moodle_url 1502 */ 1503 public function get_settings_page_url(): moodle_url { 1504 return new moodle_url( 1505 '/admin/settings.php', 1506 [ 1507 'section' => $this->name, 1508 ] 1509 ); 1510 } 1511 1512 /** 1513 * see admin_category 1514 * 1515 * @param string $name 1516 * @param bool $findpath 1517 * @return mixed Object (this) if name == this->name, else returns null 1518 */ 1519 public function locate($name, $findpath=false) { 1520 if ($this->name == $name) { 1521 if ($findpath) { 1522 $this->visiblepath = array($this->visiblename); 1523 $this->path = array($this->name); 1524 } 1525 return $this; 1526 } else { 1527 $return = NULL; 1528 return $return; 1529 } 1530 } 1531 1532 /** 1533 * Search string in settings page. 1534 * 1535 * @param string $query 1536 * @return array 1537 */ 1538 public function search($query) { 1539 $found = array(); 1540 1541 foreach ($this->settings as $setting) { 1542 if ($setting->is_related($query)) { 1543 $found[] = $setting; 1544 } 1545 } 1546 1547 if ($found) { 1548 $result = new stdClass(); 1549 $result->page = $this; 1550 $result->settings = $found; 1551 return array($this->name => $result); 1552 } 1553 1554 $found = false; 1555 if (strpos(strtolower($this->name), $query) !== false) { 1556 $found = true; 1557 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) { 1558 $found = true; 1559 } 1560 if ($found) { 1561 $result = new stdClass(); 1562 $result->page = $this; 1563 $result->settings = array(); 1564 return array($this->name => $result); 1565 } else { 1566 return array(); 1567 } 1568 } 1569 1570 /** 1571 * This function always returns false, required by interface 1572 * 1573 * @param string $name 1574 * @return bool Always false 1575 */ 1576 public function prune($name) { 1577 return false; 1578 } 1579 1580 /** 1581 * adds an admin_setting to this admin_settingpage 1582 * 1583 * 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 1584 * n.b. each admin_setting in an admin_settingpage must have a unique internal name 1585 * 1586 * @param object $setting is the admin_setting object you want to add 1587 * @return bool true if successful, false if not 1588 */ 1589 public function add($setting) { 1590 if (!($setting instanceof admin_setting)) { 1591 debugging('error - not a setting instance'); 1592 return false; 1593 } 1594 1595 $name = $setting->name; 1596 if ($setting->plugin) { 1597 $name = $setting->plugin . $name; 1598 } 1599 $this->settings->{$name} = $setting; 1600 return true; 1601 } 1602 1603 /** 1604 * Hide the named setting if the specified condition is matched. 1605 * 1606 * @param string $settingname 1607 * @param string $dependenton 1608 * @param string $condition 1609 * @param string $value 1610 */ 1611 public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') { 1612 $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value); 1613 1614 // Reformat the dependency name to the plugin | name format used in the display. 1615 $dependenton = str_replace('/', ' | ', $dependenton); 1616 1617 // Let the setting know, so it can be displayed underneath. 1618 $findname = str_replace('/', '', $settingname); 1619 foreach ($this->settings as $name => $setting) { 1620 if ($name === $findname) { 1621 $setting->add_dependent_on($dependenton); 1622 } 1623 } 1624 } 1625 1626 /** 1627 * see admin_externalpage 1628 * 1629 * @return bool Returns true for yes false for no 1630 */ 1631 public function check_access() { 1632 global $CFG; 1633 $context = empty($this->context) ? context_system::instance() : $this->context; 1634 foreach($this->req_capability as $cap) { 1635 if (has_capability($cap, $context)) { 1636 return true; 1637 } 1638 } 1639 return false; 1640 } 1641 1642 /** 1643 * outputs this page as html in a table (suitable for inclusion in an admin pagetype) 1644 * @return string Returns an XHTML string 1645 */ 1646 public function output_html() { 1647 $adminroot = admin_get_root(); 1648 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n"; 1649 foreach($this->settings as $setting) { 1650 $fullname = $setting->get_full_name(); 1651 if (array_key_exists($fullname, $adminroot->errors)) { 1652 $data = $adminroot->errors[$fullname]->data; 1653 } else { 1654 $data = $setting->get_setting(); 1655 // do not use defaults if settings not available - upgrade settings handles the defaults! 1656 } 1657 $return .= $setting->output_html($data); 1658 } 1659 $return .= '</fieldset>'; 1660 return $return; 1661 } 1662 1663 /** 1664 * Is this settings page hidden in admin tree block? 1665 * 1666 * @return bool True if hidden 1667 */ 1668 public function is_hidden() { 1669 return $this->hidden; 1670 } 1671 1672 /** 1673 * Show we display Save button at the page bottom? 1674 * @return bool 1675 */ 1676 public function show_save() { 1677 foreach($this->settings as $setting) { 1678 if (empty($setting->nosave)) { 1679 return true; 1680 } 1681 } 1682 return false; 1683 } 1684 1685 /** 1686 * Should any of the settings on this page be shown / hidden based on conditions? 1687 * @return bool 1688 */ 1689 public function has_dependencies() { 1690 return (bool)$this->dependencies; 1691 } 1692 1693 /** 1694 * Format the setting show/hide conditions ready to initialise the page javascript 1695 * @return array 1696 */ 1697 public function get_dependencies_for_javascript() { 1698 if (!$this->has_dependencies()) { 1699 return []; 1700 } 1701 return admin_settingdependency::prepare_for_javascript($this->dependencies); 1702 } 1703 } 1704 1705 1706 /** 1707 * Admin settings class. Only exists on setting pages. 1708 * Read & write happens at this level; no authentication. 1709 * 1710 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1711 */ 1712 abstract class admin_setting { 1713 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */ 1714 public $name; 1715 /** @var string localised name */ 1716 public $visiblename; 1717 /** @var string localised long description in Markdown format */ 1718 public $description; 1719 /** @var mixed Can be string or array of string */ 1720 public $defaultsetting; 1721 /** @var string */ 1722 public $updatedcallback; 1723 /** @var mixed can be String or Null. Null means main config table */ 1724 public $plugin; // null means main config table 1725 /** @var bool true indicates this setting does not actually save anything, just information */ 1726 public $nosave = false; 1727 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */ 1728 public $affectsmodinfo = false; 1729 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */ 1730 private $flags = array(); 1731 /** @var bool Whether this field must be forced LTR. */ 1732 private $forceltr = null; 1733 /** @var array list of other settings that may cause this setting to be hidden */ 1734 private $dependenton = []; 1735 /** @var bool Whether this setting uses a custom form control */ 1736 protected $customcontrol = false; 1737 1738 /** 1739 * Constructor 1740 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 1741 * or 'myplugin/mysetting' for ones in config_plugins. 1742 * @param string $visiblename localised name 1743 * @param string $description localised long description 1744 * @param mixed $defaultsetting string or array depending on implementation 1745 */ 1746 public function __construct($name, $visiblename, $description, $defaultsetting) { 1747 $this->parse_setting_name($name); 1748 $this->visiblename = $visiblename; 1749 $this->description = $description; 1750 $this->defaultsetting = $defaultsetting; 1751 } 1752 1753 /** 1754 * Generic function to add a flag to this admin setting. 1755 * 1756 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED 1757 * @param bool $default - The default for the flag 1758 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name. 1759 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox. 1760 */ 1761 protected function set_flag_options($enabled, $default, $shortname, $displayname) { 1762 if (empty($this->flags[$shortname])) { 1763 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname); 1764 } else { 1765 $this->flags[$shortname]->set_options($enabled, $default); 1766 } 1767 } 1768 1769 /** 1770 * Set the enabled options flag on this admin setting. 1771 * 1772 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED 1773 * @param bool $default - The default for the flag 1774 */ 1775 public function set_enabled_flag_options($enabled, $default) { 1776 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin')); 1777 } 1778 1779 /** 1780 * Set the advanced options flag on this admin setting. 1781 * 1782 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED 1783 * @param bool $default - The default for the flag 1784 */ 1785 public function set_advanced_flag_options($enabled, $default) { 1786 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced')); 1787 } 1788 1789 1790 /** 1791 * Set the locked options flag on this admin setting. 1792 * 1793 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED 1794 * @param bool $default - The default for the flag 1795 */ 1796 public function set_locked_flag_options($enabled, $default) { 1797 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin')); 1798 } 1799 1800 /** 1801 * Set the required options flag on this admin setting. 1802 * 1803 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED. 1804 * @param bool $default - The default for the flag. 1805 */ 1806 public function set_required_flag_options($enabled, $default) { 1807 $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin')); 1808 } 1809 1810 /** 1811 * Is this option forced in config.php? 1812 * 1813 * @return bool 1814 */ 1815 public function is_readonly(): bool { 1816 global $CFG; 1817 1818 if (empty($this->plugin)) { 1819 if (array_key_exists($this->name, $CFG->config_php_settings)) { 1820 return true; 1821 } 1822 } else { 1823 if (array_key_exists($this->plugin, $CFG->forced_plugin_settings) 1824 and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) { 1825 return true; 1826 } 1827 } 1828 return false; 1829 } 1830 1831 /** 1832 * Get the currently saved value for a setting flag 1833 * 1834 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting. 1835 * @return bool 1836 */ 1837 public function get_setting_flag_value(admin_setting_flag $flag) { 1838 $value = $this->config_read($this->name . '_' . $flag->get_shortname()); 1839 if (!isset($value)) { 1840 $value = $flag->get_default(); 1841 } 1842 1843 return !empty($value); 1844 } 1845 1846 /** 1847 * Get the list of defaults for the flags on this setting. 1848 * 1849 * @param array of strings describing the defaults for this setting. This is appended to by this function. 1850 */ 1851 public function get_setting_flag_defaults(& $defaults) { 1852 foreach ($this->flags as $flag) { 1853 if ($flag->is_enabled() && $flag->get_default()) { 1854 $defaults[] = $flag->get_displayname(); 1855 } 1856 } 1857 } 1858 1859 /** 1860 * Output the input fields for the advanced and locked flags on this setting. 1861 * 1862 * @param bool $adv - The current value of the advanced flag. 1863 * @param bool $locked - The current value of the locked flag. 1864 * @return string $output - The html for the flags. 1865 */ 1866 public function output_setting_flags() { 1867 $output = ''; 1868 1869 foreach ($this->flags as $flag) { 1870 if ($flag->is_enabled()) { 1871 $output .= $flag->output_setting_flag($this); 1872 } 1873 } 1874 1875 if (!empty($output)) { 1876 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags')); 1877 } 1878 return $output; 1879 } 1880 1881 /** 1882 * Write the values of the flags for this admin setting. 1883 * 1884 * @param array $data - The data submitted from the form or null to set the default value for new installs. 1885 * @return bool - true if successful. 1886 */ 1887 public function write_setting_flags($data) { 1888 $result = true; 1889 foreach ($this->flags as $flag) { 1890 $result = $result && $flag->write_setting_flag($this, $data); 1891 } 1892 return $result; 1893 } 1894 1895 /** 1896 * Set up $this->name and potentially $this->plugin 1897 * 1898 * Set up $this->name and possibly $this->plugin based on whether $name looks 1899 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking 1900 * on the names, that is, output a developer debug warning if the name 1901 * contains anything other than [a-zA-Z0-9_]+. 1902 * 1903 * @param string $name the setting name passed in to the constructor. 1904 */ 1905 private function parse_setting_name($name) { 1906 $bits = explode('/', $name); 1907 if (count($bits) > 2) { 1908 throw new moodle_exception('invalidadminsettingname', '', '', $name); 1909 } 1910 $this->name = array_pop($bits); 1911 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) { 1912 throw new moodle_exception('invalidadminsettingname', '', '', $name); 1913 } 1914 if (!empty($bits)) { 1915 $this->plugin = array_pop($bits); 1916 if ($this->plugin === 'moodle') { 1917 $this->plugin = null; 1918 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) { 1919 throw new moodle_exception('invalidadminsettingname', '', '', $name); 1920 } 1921 } 1922 } 1923 1924 /** 1925 * Returns the fullname prefixed by the plugin 1926 * @return string 1927 */ 1928 public function get_full_name() { 1929 return 's_'.$this->plugin.'_'.$this->name; 1930 } 1931 1932 /** 1933 * Returns the ID string based on plugin and name 1934 * @return string 1935 */ 1936 public function get_id() { 1937 return 'id_s_'.$this->plugin.'_'.$this->name; 1938 } 1939 1940 /** 1941 * @param bool $affectsmodinfo If true, changes to this setting will 1942 * cause the course cache to be rebuilt 1943 */ 1944 public function set_affects_modinfo($affectsmodinfo) { 1945 $this->affectsmodinfo = $affectsmodinfo; 1946 } 1947 1948 /** 1949 * Returns the config if possible 1950 * 1951 * @return mixed returns config if successful else null 1952 */ 1953 public function config_read($name) { 1954 global $CFG; 1955 if (!empty($this->plugin)) { 1956 $value = get_config($this->plugin, $name); 1957 return $value === false ? NULL : $value; 1958 1959 } else { 1960 if (isset($CFG->$name)) { 1961 return $CFG->$name; 1962 } else { 1963 return NULL; 1964 } 1965 } 1966 } 1967 1968 /** 1969 * Used to set a config pair and log change 1970 * 1971 * @param string $name 1972 * @param mixed $value Gets converted to string if not null 1973 * @return bool Write setting to config table 1974 */ 1975 public function config_write($name, $value) { 1976 global $DB, $USER, $CFG; 1977 1978 if ($this->nosave) { 1979 return true; 1980 } 1981 1982 // make sure it is a real change 1983 $oldvalue = get_config($this->plugin, $name); 1984 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise 1985 $value = is_null($value) ? null : (string)$value; 1986 1987 if ($oldvalue === $value) { 1988 return true; 1989 } 1990 1991 // store change 1992 set_config($name, $value, $this->plugin); 1993 1994 // Some admin settings affect course modinfo 1995 if ($this->affectsmodinfo) { 1996 // Clear course cache for all courses 1997 rebuild_course_cache(0, true); 1998 } 1999 2000 $this->add_to_config_log($name, $oldvalue, $value); 2001 2002 return true; // BC only 2003 } 2004 2005 /** 2006 * Log config changes if necessary. 2007 * @param string $name 2008 * @param string $oldvalue 2009 * @param string $value 2010 */ 2011 protected function add_to_config_log($name, $oldvalue, $value) { 2012 add_to_config_log($name, $oldvalue, $value, $this->plugin); 2013 } 2014 2015 /** 2016 * Returns current value of this setting 2017 * @return mixed array or string depending on instance, NULL means not set yet 2018 */ 2019 public abstract function get_setting(); 2020 2021 /** 2022 * Returns default setting if exists 2023 * @return mixed array or string depending on instance; NULL means no default, user must supply 2024 */ 2025 public function get_defaultsetting() { 2026 $adminroot = admin_get_root(false, false); 2027 if (!empty($adminroot->custom_defaults)) { 2028 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin; 2029 if (isset($adminroot->custom_defaults[$plugin])) { 2030 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-) 2031 return $adminroot->custom_defaults[$plugin][$this->name]; 2032 } 2033 } 2034 } 2035 return $this->defaultsetting; 2036 } 2037 2038 /** 2039 * Store new setting 2040 * 2041 * @param mixed $data string or array, must not be NULL 2042 * @return string empty string if ok, string error message otherwise 2043 */ 2044 public abstract function write_setting($data); 2045 2046 /** 2047 * Return part of form with setting 2048 * This function should always be overwritten 2049 * 2050 * @param mixed $data array or string depending on setting 2051 * @param string $query 2052 * @return string 2053 */ 2054 public function output_html($data, $query='') { 2055 // should be overridden 2056 return; 2057 } 2058 2059 /** 2060 * Function called if setting updated - cleanup, cache reset, etc. 2061 * @param string $functionname Sets the function name 2062 * @return void 2063 */ 2064 public function set_updatedcallback($functionname) { 2065 $this->updatedcallback = $functionname; 2066 } 2067 2068 /** 2069 * Execute postupdatecallback if necessary. 2070 * @param mixed $original original value before write_setting() 2071 * @return bool true if changed, false if not. 2072 */ 2073 public function post_write_settings($original) { 2074 // Comparison must work for arrays too. 2075 if (serialize($original) === serialize($this->get_setting())) { 2076 return false; 2077 } 2078 2079 $callbackfunction = $this->updatedcallback; 2080 if (!empty($callbackfunction) and is_callable($callbackfunction)) { 2081 $callbackfunction($this->get_full_name()); 2082 } 2083 return true; 2084 } 2085 2086 /** 2087 * Is setting related to query text - used when searching 2088 * @param string $query 2089 * @return bool 2090 */ 2091 public function is_related($query) { 2092 if (strpos(strtolower($this->name), $query) !== false) { 2093 return true; 2094 } 2095 if (strpos(core_text::strtolower($this->visiblename), $query) !== false) { 2096 return true; 2097 } 2098 if (strpos(core_text::strtolower($this->description), $query) !== false) { 2099 return true; 2100 } 2101 $current = $this->get_setting(); 2102 if (!is_null($current)) { 2103 if (is_string($current)) { 2104 if (strpos(core_text::strtolower($current), $query) !== false) { 2105 return true; 2106 } 2107 } 2108 } 2109 $default = $this->get_defaultsetting(); 2110 if (!is_null($default)) { 2111 if (is_string($default)) { 2112 if (strpos(core_text::strtolower($default), $query) !== false) { 2113 return true; 2114 } 2115 } 2116 } 2117 return false; 2118 } 2119 2120 /** 2121 * Get whether this should be displayed in LTR mode. 2122 * 2123 * @return bool|null 2124 */ 2125 public function get_force_ltr() { 2126 return $this->forceltr; 2127 } 2128 2129 /** 2130 * Set whether to force LTR or not. 2131 * 2132 * @param bool $value True when forced, false when not force, null when unknown. 2133 */ 2134 public function set_force_ltr($value) { 2135 $this->forceltr = $value; 2136 } 2137 2138 /** 2139 * Add a setting to the list of those that could cause this one to be hidden 2140 * @param string $dependenton 2141 */ 2142 public function add_dependent_on($dependenton) { 2143 $this->dependenton[] = $dependenton; 2144 } 2145 2146 /** 2147 * Get a list of the settings that could cause this one to be hidden. 2148 * @return array 2149 */ 2150 public function get_dependent_on() { 2151 return $this->dependenton; 2152 } 2153 2154 /** 2155 * Whether this setting uses a custom form control. 2156 * This function is especially useful to decide if we should render a label element for this setting or not. 2157 * 2158 * @return bool 2159 */ 2160 public function has_custom_form_control(): bool { 2161 return $this->customcontrol; 2162 } 2163 } 2164 2165 /** 2166 * An additional option that can be applied to an admin setting. 2167 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'. 2168 * 2169 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2170 */ 2171 class admin_setting_flag { 2172 /** @var bool Flag to indicate if this option can be toggled for this setting */ 2173 private $enabled = false; 2174 /** @var bool Flag to indicate if this option defaults to true or false */ 2175 private $default = false; 2176 /** @var string Short string used to create setting name - e.g. 'adv' */ 2177 private $shortname = ''; 2178 /** @var string String used as the label for this flag */ 2179 private $displayname = ''; 2180 /** @const Checkbox for this flag is displayed in admin page */ 2181 const ENABLED = true; 2182 /** @const Checkbox for this flag is not displayed in admin page */ 2183 const DISABLED = false; 2184 2185 /** 2186 * Constructor 2187 * 2188 * @param bool $enabled Can this option can be toggled. 2189 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED. 2190 * @param bool $default The default checked state for this setting option. 2191 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv' 2192 * @param string $displayname The displayname of this flag. Used as a label for the flag. 2193 */ 2194 public function __construct($enabled, $default, $shortname, $displayname) { 2195 $this->shortname = $shortname; 2196 $this->displayname = $displayname; 2197 $this->set_options($enabled, $default); 2198 } 2199 2200 /** 2201 * Update the values of this setting options class 2202 * 2203 * @param bool $enabled Can this option can be toggled. 2204 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED. 2205 * @param bool $default The default checked state for this setting option. 2206 */ 2207 public function set_options($enabled, $default) { 2208 $this->enabled = $enabled; 2209 $this->default = $default; 2210 } 2211 2212 /** 2213 * Should this option appear in the interface and be toggleable? 2214 * 2215 * @return bool Is it enabled? 2216 */ 2217 public function is_enabled() { 2218 return $this->enabled; 2219 } 2220 2221 /** 2222 * Should this option be checked by default? 2223 * 2224 * @return bool Is it on by default? 2225 */ 2226 public function get_default() { 2227 return $this->default; 2228 } 2229 2230 /** 2231 * Return the short name for this flag. e.g. 'adv' or 'locked' 2232 * 2233 * @return string 2234 */ 2235 public function get_shortname() { 2236 return $this->shortname; 2237 } 2238 2239 /** 2240 * Return the display name for this flag. e.g. 'Advanced' or 'Locked' 2241 * 2242 * @return string 2243 */ 2244 public function get_displayname() { 2245 return $this->displayname; 2246 } 2247 2248 /** 2249 * Save the submitted data for this flag - or set it to the default if $data is null. 2250 * 2251 * @param admin_setting $setting - The admin setting for this flag 2252 * @param array $data - The data submitted from the form or null to set the default value for new installs. 2253 * @return bool 2254 */ 2255 public function write_setting_flag(admin_setting $setting, $data) { 2256 $result = true; 2257 if ($this->is_enabled()) { 2258 if (!isset($data)) { 2259 $value = $this->get_default(); 2260 } else { 2261 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]); 2262 } 2263 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value); 2264 } 2265 2266 return $result; 2267 2268 } 2269 2270 /** 2271 * Output the checkbox for this setting flag. Should only be called if the flag is enabled. 2272 * 2273 * @param admin_setting $setting - The admin setting for this flag 2274 * @return string - The html for the checkbox. 2275 */ 2276 public function output_setting_flag(admin_setting $setting) { 2277 global $OUTPUT; 2278 2279 $value = $setting->get_setting_flag_value($this); 2280 2281 $context = new stdClass(); 2282 $context->id = $setting->get_id() . '_' . $this->get_shortname(); 2283 $context->name = $setting->get_full_name() . '_' . $this->get_shortname(); 2284 $context->value = 1; 2285 $context->checked = $value ? true : false; 2286 $context->label = $this->get_displayname(); 2287 2288 return $OUTPUT->render_from_template('core_admin/setting_flag', $context); 2289 } 2290 } 2291 2292 2293 /** 2294 * No setting - just heading and text. 2295 * 2296 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2297 */ 2298 class admin_setting_heading extends admin_setting { 2299 2300 /** 2301 * not a setting, just text 2302 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2303 * @param string $heading heading 2304 * @param string $information text in box 2305 */ 2306 public function __construct($name, $heading, $information) { 2307 $this->nosave = true; 2308 parent::__construct($name, $heading, $information, ''); 2309 } 2310 2311 /** 2312 * Always returns true 2313 * @return bool Always returns true 2314 */ 2315 public function get_setting() { 2316 return true; 2317 } 2318 2319 /** 2320 * Always returns true 2321 * @return bool Always returns true 2322 */ 2323 public function get_defaultsetting() { 2324 return true; 2325 } 2326 2327 /** 2328 * Never write settings 2329 * @return string Always returns an empty string 2330 */ 2331 public function write_setting($data) { 2332 // do not write any setting 2333 return ''; 2334 } 2335 2336 /** 2337 * Returns an HTML string 2338 * @return string Returns an HTML string 2339 */ 2340 public function output_html($data, $query='') { 2341 global $OUTPUT; 2342 $context = new stdClass(); 2343 $context->title = $this->visiblename; 2344 $context->description = $this->description; 2345 $context->descriptionformatted = highlight($query, markdown_to_html($this->description)); 2346 return $OUTPUT->render_from_template('core_admin/setting_heading', $context); 2347 } 2348 } 2349 2350 /** 2351 * No setting - just name and description in same row. 2352 * 2353 * @copyright 2018 onwards Amaia Anabitarte 2354 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2355 */ 2356 class admin_setting_description extends admin_setting { 2357 2358 /** 2359 * Not a setting, just text 2360 * 2361 * @param string $name 2362 * @param string $visiblename 2363 * @param string $description 2364 */ 2365 public function __construct($name, $visiblename, $description) { 2366 $this->nosave = true; 2367 parent::__construct($name, $visiblename, $description, ''); 2368 } 2369 2370 /** 2371 * Always returns true 2372 * 2373 * @return bool Always returns true 2374 */ 2375 public function get_setting() { 2376 return true; 2377 } 2378 2379 /** 2380 * Always returns true 2381 * 2382 * @return bool Always returns true 2383 */ 2384 public function get_defaultsetting() { 2385 return true; 2386 } 2387 2388 /** 2389 * Never write settings 2390 * 2391 * @param mixed $data Gets converted to str for comparison against yes value 2392 * @return string Always returns an empty string 2393 */ 2394 public function write_setting($data) { 2395 // Do not write any setting. 2396 return ''; 2397 } 2398 2399 /** 2400 * Returns an HTML string 2401 * 2402 * @param string $data 2403 * @param string $query 2404 * @return string Returns an HTML string 2405 */ 2406 public function output_html($data, $query='') { 2407 global $OUTPUT; 2408 2409 $context = new stdClass(); 2410 $context->title = $this->visiblename; 2411 $context->description = $this->description; 2412 2413 return $OUTPUT->render_from_template('core_admin/setting_description', $context); 2414 } 2415 } 2416 2417 2418 2419 /** 2420 * The most flexible setting, the user enters text. 2421 * 2422 * This type of field should be used for config settings which are using 2423 * English words and are not localised (passwords, database name, list of values, ...). 2424 * 2425 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2426 */ 2427 class admin_setting_configtext extends admin_setting { 2428 2429 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */ 2430 public $paramtype; 2431 /** @var int default field size */ 2432 public $size; 2433 2434 /** 2435 * Config text constructor 2436 * 2437 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2438 * @param string $visiblename localised 2439 * @param string $description long localised info 2440 * @param string $defaultsetting 2441 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex 2442 * @param int $size default field size 2443 */ 2444 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) { 2445 $this->paramtype = $paramtype; 2446 if (!is_null($size)) { 2447 $this->size = $size; 2448 } else { 2449 $this->size = ($paramtype === PARAM_INT) ? 5 : 30; 2450 } 2451 parent::__construct($name, $visiblename, $description, $defaultsetting); 2452 } 2453 2454 /** 2455 * Get whether this should be displayed in LTR mode. 2456 * 2457 * Try to guess from the PARAM type unless specifically set. 2458 */ 2459 public function get_force_ltr() { 2460 $forceltr = parent::get_force_ltr(); 2461 if ($forceltr === null) { 2462 return !is_rtl_compatible($this->paramtype); 2463 } 2464 return $forceltr; 2465 } 2466 2467 /** 2468 * Return the setting 2469 * 2470 * @return mixed returns config if successful else null 2471 */ 2472 public function get_setting() { 2473 return $this->config_read($this->name); 2474 } 2475 2476 public function write_setting($data) { 2477 if ($this->paramtype === PARAM_INT and $data === '') { 2478 // do not complain if '' used instead of 0 2479 $data = 0; 2480 } 2481 // $data is a string 2482 $validated = $this->validate($data); 2483 if ($validated !== true) { 2484 return $validated; 2485 } 2486 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 2487 } 2488 2489 /** 2490 * Validate data before storage 2491 * @param string data 2492 * @return mixed true if ok string if error found 2493 */ 2494 public function validate($data) { 2495 // allow paramtype to be a custom regex if it is the form of /pattern/ 2496 if (preg_match('#^/.*/$#', $this->paramtype)) { 2497 if (preg_match($this->paramtype, $data)) { 2498 return true; 2499 } else { 2500 return get_string('validateerror', 'admin'); 2501 } 2502 2503 } else if ($this->paramtype === PARAM_RAW) { 2504 return true; 2505 2506 } else { 2507 $cleaned = clean_param($data, $this->paramtype); 2508 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison 2509 return true; 2510 } else { 2511 return get_string('validateerror', 'admin'); 2512 } 2513 } 2514 } 2515 2516 /** 2517 * Return an XHTML string for the setting 2518 * @return string Returns an XHTML string 2519 */ 2520 public function output_html($data, $query='') { 2521 global $OUTPUT; 2522 2523 $default = $this->get_defaultsetting(); 2524 $context = (object) [ 2525 'size' => $this->size, 2526 'id' => $this->get_id(), 2527 'name' => $this->get_full_name(), 2528 'value' => $data, 2529 'forceltr' => $this->get_force_ltr(), 2530 'readonly' => $this->is_readonly(), 2531 ]; 2532 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context); 2533 2534 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 2535 } 2536 } 2537 2538 /** 2539 * Text input with a maximum length constraint. 2540 * 2541 * @copyright 2015 onwards Ankit Agarwal 2542 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2543 */ 2544 class admin_setting_configtext_with_maxlength extends admin_setting_configtext { 2545 2546 /** @var int maximum number of chars allowed. */ 2547 protected $maxlength; 2548 2549 /** 2550 * Config text constructor 2551 * 2552 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 2553 * or 'myplugin/mysetting' for ones in config_plugins. 2554 * @param string $visiblename localised 2555 * @param string $description long localised info 2556 * @param string $defaultsetting 2557 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex 2558 * @param int $size default field size 2559 * @param mixed $maxlength int maxlength allowed, 0 for infinite. 2560 */ 2561 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, 2562 $size=null, $maxlength = 0) { 2563 $this->maxlength = $maxlength; 2564 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); 2565 } 2566 2567 /** 2568 * Validate data before storage 2569 * 2570 * @param string $data data 2571 * @return mixed true if ok string if error found 2572 */ 2573 public function validate($data) { 2574 $parentvalidation = parent::validate($data); 2575 if ($parentvalidation === true) { 2576 if ($this->maxlength > 0) { 2577 // Max length check. 2578 $length = core_text::strlen($data); 2579 if ($length > $this->maxlength) { 2580 return get_string('maximumchars', 'moodle', $this->maxlength); 2581 } 2582 return true; 2583 } else { 2584 return true; // No max length check needed. 2585 } 2586 } else { 2587 return $parentvalidation; 2588 } 2589 } 2590 } 2591 2592 /** 2593 * General text area without html editor. 2594 * 2595 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2596 */ 2597 class admin_setting_configtextarea extends admin_setting_configtext { 2598 private $rows; 2599 private $cols; 2600 2601 /** 2602 * @param string $name 2603 * @param string $visiblename 2604 * @param string $description 2605 * @param mixed $defaultsetting string or array 2606 * @param mixed $paramtype 2607 * @param string $cols The number of columns to make the editor 2608 * @param string $rows The number of rows to make the editor 2609 */ 2610 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') { 2611 $this->rows = $rows; 2612 $this->cols = $cols; 2613 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype); 2614 } 2615 2616 /** 2617 * Returns an XHTML string for the editor 2618 * 2619 * @param string $data 2620 * @param string $query 2621 * @return string XHTML string for the editor 2622 */ 2623 public function output_html($data, $query='') { 2624 global $OUTPUT; 2625 2626 $default = $this->get_defaultsetting(); 2627 $defaultinfo = $default; 2628 if (!is_null($default) and $default !== '') { 2629 $defaultinfo = "\n".$default; 2630 } 2631 2632 $context = (object) [ 2633 'cols' => $this->cols, 2634 'rows' => $this->rows, 2635 'id' => $this->get_id(), 2636 'name' => $this->get_full_name(), 2637 'value' => $data, 2638 'forceltr' => $this->get_force_ltr(), 2639 'readonly' => $this->is_readonly(), 2640 ]; 2641 $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context); 2642 2643 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 2644 } 2645 } 2646 2647 /** 2648 * General text area with html editor. 2649 */ 2650 class admin_setting_confightmleditor extends admin_setting_configtextarea { 2651 2652 /** 2653 * @param string $name 2654 * @param string $visiblename 2655 * @param string $description 2656 * @param mixed $defaultsetting string or array 2657 * @param mixed $paramtype 2658 */ 2659 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') { 2660 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows); 2661 $this->set_force_ltr(false); 2662 editors_head_setup(); 2663 } 2664 2665 /** 2666 * Returns an XHTML string for the editor 2667 * 2668 * @param string $data 2669 * @param string $query 2670 * @return string XHTML string for the editor 2671 */ 2672 public function output_html($data, $query='') { 2673 $editor = editors_get_preferred_editor(FORMAT_HTML); 2674 $editor->set_text($data); 2675 $editor->use_editor($this->get_id(), array('noclean'=>true)); 2676 return parent::output_html($data, $query); 2677 } 2678 2679 /** 2680 * Checks if data has empty html. 2681 * 2682 * @param string $data 2683 * @return string Empty when no errors. 2684 */ 2685 public function write_setting($data) { 2686 if (trim(html_to_text($data)) === '') { 2687 $data = ''; 2688 } 2689 return parent::write_setting($data); 2690 } 2691 } 2692 2693 2694 /** 2695 * Password field, allows unmasking of password 2696 * 2697 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2698 */ 2699 class admin_setting_configpasswordunmask extends admin_setting_configtext { 2700 2701 /** 2702 * Constructor 2703 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2704 * @param string $visiblename localised 2705 * @param string $description long localised info 2706 * @param string $defaultsetting default password 2707 */ 2708 public function __construct($name, $visiblename, $description, $defaultsetting) { 2709 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30); 2710 } 2711 2712 /** 2713 * Log config changes if necessary. 2714 * @param string $name 2715 * @param string $oldvalue 2716 * @param string $value 2717 */ 2718 protected function add_to_config_log($name, $oldvalue, $value) { 2719 if ($value !== '') { 2720 $value = '********'; 2721 } 2722 if ($oldvalue !== '' and $oldvalue !== null) { 2723 $oldvalue = '********'; 2724 } 2725 parent::add_to_config_log($name, $oldvalue, $value); 2726 } 2727 2728 /** 2729 * Returns HTML for the field. 2730 * 2731 * @param string $data Value for the field 2732 * @param string $query Passed as final argument for format_admin_setting 2733 * @return string Rendered HTML 2734 */ 2735 public function output_html($data, $query='') { 2736 global $OUTPUT; 2737 2738 $context = (object) [ 2739 'id' => $this->get_id(), 2740 'name' => $this->get_full_name(), 2741 'size' => $this->size, 2742 'value' => $this->is_readonly() ? null : $data, 2743 'forceltr' => $this->get_force_ltr(), 2744 'readonly' => $this->is_readonly(), 2745 ]; 2746 $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context); 2747 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query); 2748 } 2749 } 2750 2751 /** 2752 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting. 2753 * 2754 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2755 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk) 2756 */ 2757 class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask { 2758 2759 /** 2760 * Constructor 2761 * 2762 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2763 * @param string $visiblename localised 2764 * @param string $description long localised info 2765 * @param array $defaultsetting ('value'=>string, 'adv'=>bool) 2766 */ 2767 public function __construct($name, $visiblename, $description, $defaultsetting) { 2768 parent::__construct($name, $visiblename, $description, $defaultsetting['value']); 2769 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 2770 } 2771 } 2772 2773 /** 2774 * Admin setting class for encrypted values using secure encryption. 2775 * 2776 * @copyright 2019 The Open University 2777 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2778 */ 2779 class admin_setting_encryptedpassword extends admin_setting { 2780 2781 /** 2782 * Constructor. Same as parent except that the default value is always an empty string. 2783 * 2784 * @param string $name Internal name used in config table 2785 * @param string $visiblename Name shown on form 2786 * @param string $description Description that appears below field 2787 */ 2788 public function __construct(string $name, string $visiblename, string $description) { 2789 parent::__construct($name, $visiblename, $description, ''); 2790 } 2791 2792 public function get_setting() { 2793 return $this->config_read($this->name); 2794 } 2795 2796 public function write_setting($data) { 2797 $data = trim($data); 2798 if ($data === '') { 2799 // Value can really be set to nothing. 2800 $savedata = ''; 2801 } else { 2802 // Encrypt value before saving it. 2803 $savedata = \core\encryption::encrypt($data); 2804 } 2805 return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin')); 2806 } 2807 2808 public function output_html($data, $query='') { 2809 global $OUTPUT; 2810 2811 $default = $this->get_defaultsetting(); 2812 $context = (object) [ 2813 'id' => $this->get_id(), 2814 'name' => $this->get_full_name(), 2815 'set' => $data !== '', 2816 'novalue' => $this->get_setting() === null 2817 ]; 2818 $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context); 2819 2820 return format_admin_setting($this, $this->visiblename, $element, $this->description, 2821 true, '', $default, $query); 2822 } 2823 } 2824 2825 /** 2826 * Empty setting used to allow flags (advanced) on settings that can have no sensible default. 2827 * Note: Only advanced makes sense right now - locked does not. 2828 * 2829 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2830 */ 2831 class admin_setting_configempty extends admin_setting_configtext { 2832 2833 /** 2834 * @param string $name 2835 * @param string $visiblename 2836 * @param string $description 2837 */ 2838 public function __construct($name, $visiblename, $description) { 2839 parent::__construct($name, $visiblename, $description, '', PARAM_RAW); 2840 } 2841 2842 /** 2843 * Returns an XHTML string for the hidden field 2844 * 2845 * @param string $data 2846 * @param string $query 2847 * @return string XHTML string for the editor 2848 */ 2849 public function output_html($data, $query='') { 2850 global $OUTPUT; 2851 2852 $context = (object) [ 2853 'id' => $this->get_id(), 2854 'name' => $this->get_full_name() 2855 ]; 2856 $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context); 2857 2858 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query); 2859 } 2860 } 2861 2862 2863 /** 2864 * Path to directory 2865 * 2866 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2867 */ 2868 class admin_setting_configfile extends admin_setting_configtext { 2869 /** 2870 * Constructor 2871 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 2872 * @param string $visiblename localised 2873 * @param string $description long localised info 2874 * @param string $defaultdirectory default directory location 2875 */ 2876 public function __construct($name, $visiblename, $description, $defaultdirectory) { 2877 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50); 2878 } 2879 2880 /** 2881 * Returns XHTML for the field 2882 * 2883 * Returns XHTML for the field and also checks whether the file 2884 * specified in $data exists using file_exists() 2885 * 2886 * @param string $data File name and path to use in value attr 2887 * @param string $query 2888 * @return string XHTML field 2889 */ 2890 public function output_html($data, $query='') { 2891 global $CFG, $OUTPUT; 2892 2893 $default = $this->get_defaultsetting(); 2894 $context = (object) [ 2895 'id' => $this->get_id(), 2896 'name' => $this->get_full_name(), 2897 'size' => $this->size, 2898 'value' => $data, 2899 'showvalidity' => !empty($data), 2900 'valid' => $data && file_exists($data), 2901 'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(), 2902 'forceltr' => $this->get_force_ltr(), 2903 ]; 2904 2905 if ($context->readonly) { 2906 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>'; 2907 } 2908 2909 $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context); 2910 2911 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 2912 } 2913 2914 /** 2915 * Checks if execpatch has been disabled in config.php 2916 */ 2917 public function write_setting($data) { 2918 global $CFG; 2919 if (!empty($CFG->preventexecpath)) { 2920 if ($this->get_setting() === null) { 2921 // Use default during installation. 2922 $data = $this->get_defaultsetting(); 2923 if ($data === null) { 2924 $data = ''; 2925 } 2926 } else { 2927 return ''; 2928 } 2929 } 2930 return parent::write_setting($data); 2931 } 2932 2933 } 2934 2935 2936 /** 2937 * Path to executable file 2938 * 2939 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2940 */ 2941 class admin_setting_configexecutable extends admin_setting_configfile { 2942 2943 /** 2944 * Returns an XHTML field 2945 * 2946 * @param string $data This is the value for the field 2947 * @param string $query 2948 * @return string XHTML field 2949 */ 2950 public function output_html($data, $query='') { 2951 global $CFG, $OUTPUT; 2952 $default = $this->get_defaultsetting(); 2953 require_once("$CFG->libdir/filelib.php"); 2954 2955 $context = (object) [ 2956 'id' => $this->get_id(), 2957 'name' => $this->get_full_name(), 2958 'size' => $this->size, 2959 'value' => $data, 2960 'showvalidity' => !empty($data), 2961 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data), 2962 'readonly' => !empty($CFG->preventexecpath), 2963 'forceltr' => $this->get_force_ltr() 2964 ]; 2965 2966 if (!empty($CFG->preventexecpath)) { 2967 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>'; 2968 } 2969 2970 $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context); 2971 2972 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 2973 } 2974 } 2975 2976 2977 /** 2978 * Path to directory 2979 * 2980 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2981 */ 2982 class admin_setting_configdirectory extends admin_setting_configfile { 2983 2984 /** 2985 * Returns an XHTML field 2986 * 2987 * @param string $data This is the value for the field 2988 * @param string $query 2989 * @return string XHTML 2990 */ 2991 public function output_html($data, $query='') { 2992 global $CFG, $OUTPUT; 2993 $default = $this->get_defaultsetting(); 2994 2995 $context = (object) [ 2996 'id' => $this->get_id(), 2997 'name' => $this->get_full_name(), 2998 'size' => $this->size, 2999 'value' => $data, 3000 'showvalidity' => !empty($data), 3001 'valid' => $data && file_exists($data) && is_dir($data), 3002 'readonly' => !empty($CFG->preventexecpath), 3003 'forceltr' => $this->get_force_ltr() 3004 ]; 3005 3006 if (!empty($CFG->preventexecpath)) { 3007 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>'; 3008 } 3009 3010 $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context); 3011 3012 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 3013 } 3014 } 3015 3016 3017 /** 3018 * Checkbox 3019 * 3020 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3021 */ 3022 class admin_setting_configcheckbox extends admin_setting { 3023 /** @var string Value used when checked */ 3024 public $yes; 3025 /** @var string Value used when not checked */ 3026 public $no; 3027 3028 /** 3029 * Constructor 3030 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3031 * @param string $visiblename localised 3032 * @param string $description long localised info 3033 * @param string $defaultsetting 3034 * @param string $yes value used when checked 3035 * @param string $no value used when not checked 3036 */ 3037 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') { 3038 parent::__construct($name, $visiblename, $description, $defaultsetting); 3039 $this->yes = (string)$yes; 3040 $this->no = (string)$no; 3041 } 3042 3043 /** 3044 * Retrieves the current setting using the objects name 3045 * 3046 * @return string 3047 */ 3048 public function get_setting() { 3049 return $this->config_read($this->name); 3050 } 3051 3052 /** 3053 * Sets the value for the setting 3054 * 3055 * Sets the value for the setting to either the yes or no values 3056 * of the object by comparing $data to yes 3057 * 3058 * @param mixed $data Gets converted to str for comparison against yes value 3059 * @return string empty string or error 3060 */ 3061 public function write_setting($data) { 3062 if ((string)$data === $this->yes) { // convert to strings before comparison 3063 $data = $this->yes; 3064 } else { 3065 $data = $this->no; 3066 } 3067 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 3068 } 3069 3070 /** 3071 * Returns an XHTML checkbox field 3072 * 3073 * @param string $data If $data matches yes then checkbox is checked 3074 * @param string $query 3075 * @return string XHTML field 3076 */ 3077 public function output_html($data, $query='') { 3078 global $OUTPUT; 3079 3080 $context = (object) [ 3081 'id' => $this->get_id(), 3082 'name' => $this->get_full_name(), 3083 'no' => $this->no, 3084 'value' => $this->yes, 3085 'checked' => (string) $data === $this->yes, 3086 'readonly' => $this->is_readonly(), 3087 ]; 3088 3089 $default = $this->get_defaultsetting(); 3090 if (!is_null($default)) { 3091 if ((string)$default === $this->yes) { 3092 $defaultinfo = get_string('checkboxyes', 'admin'); 3093 } else { 3094 $defaultinfo = get_string('checkboxno', 'admin'); 3095 } 3096 } else { 3097 $defaultinfo = NULL; 3098 } 3099 3100 $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context); 3101 3102 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 3103 } 3104 } 3105 3106 3107 /** 3108 * Multiple checkboxes, each represents different value, stored in csv format 3109 * 3110 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3111 */ 3112 class admin_setting_configmulticheckbox extends admin_setting { 3113 /** @var array Array of choices value=>label */ 3114 public $choices; 3115 /** @var callable|null Loader function for choices */ 3116 protected $choiceloader = null; 3117 3118 /** 3119 * Constructor: uses parent::__construct 3120 * 3121 * The $choices parameter may be either an array of $value => $label format, 3122 * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and 3123 * returns an array in that format. 3124 * 3125 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3126 * @param string $visiblename localised 3127 * @param string $description long localised info 3128 * @param array $defaultsetting array of selected 3129 * @param array|callable $choices array of $value => $label for each checkbox, or a callback 3130 */ 3131 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 3132 if (is_array($choices)) { 3133 $this->choices = $choices; 3134 } 3135 if (is_callable($choices)) { 3136 $this->choiceloader = $choices; 3137 } 3138 parent::__construct($name, $visiblename, $description, $defaultsetting); 3139 } 3140 3141 /** 3142 * This function may be used in ancestors for lazy loading of choices 3143 * 3144 * Override this method if loading of choices is expensive, such 3145 * as when it requires multiple db requests. 3146 * 3147 * @return bool true if loaded, false if error 3148 */ 3149 public function load_choices() { 3150 if ($this->choiceloader) { 3151 if (!is_array($this->choices)) { 3152 $this->choices = call_user_func($this->choiceloader); 3153 } 3154 } 3155 return true; 3156 } 3157 3158 /** 3159 * Is setting related to query text - used when searching 3160 * 3161 * @param string $query 3162 * @return bool true on related, false on not or failure 3163 */ 3164 public function is_related($query) { 3165 if (!$this->load_choices() or empty($this->choices)) { 3166 return false; 3167 } 3168 if (parent::is_related($query)) { 3169 return true; 3170 } 3171 3172 foreach ($this->choices as $desc) { 3173 if (strpos(core_text::strtolower($desc), $query) !== false) { 3174 return true; 3175 } 3176 } 3177 return false; 3178 } 3179 3180 /** 3181 * Returns the current setting if it is set 3182 * 3183 * @return mixed null if null, else an array 3184 */ 3185 public function get_setting() { 3186 $result = $this->config_read($this->name); 3187 3188 if (is_null($result)) { 3189 return NULL; 3190 } 3191 if ($result === '') { 3192 return array(); 3193 } 3194 $enabled = explode(',', $result); 3195 $setting = array(); 3196 foreach ($enabled as $option) { 3197 $setting[$option] = 1; 3198 } 3199 return $setting; 3200 } 3201 3202 /** 3203 * Saves the setting(s) provided in $data 3204 * 3205 * @param array $data An array of data, if not array returns empty str 3206 * @return mixed empty string on useless data or bool true=success, false=failed 3207 */ 3208 public function write_setting($data) { 3209 if (!is_array($data)) { 3210 return ''; // ignore it 3211 } 3212 if (!$this->load_choices() or empty($this->choices)) { 3213 return ''; 3214 } 3215 unset($data['xxxxx']); 3216 $result = array(); 3217 foreach ($data as $key => $value) { 3218 if ($value and array_key_exists($key, $this->choices)) { 3219 $result[] = $key; 3220 } 3221 } 3222 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin'); 3223 } 3224 3225 /** 3226 * Returns XHTML field(s) as required by choices 3227 * 3228 * Relies on data being an array should data ever be another valid vartype with 3229 * acceptable value this may cause a warning/error 3230 * if (!is_array($data)) would fix the problem 3231 * 3232 * @todo Add vartype handling to ensure $data is an array 3233 * 3234 * @param array $data An array of checked values 3235 * @param string $query 3236 * @return string XHTML field 3237 */ 3238 public function output_html($data, $query='') { 3239 global $OUTPUT; 3240 3241 if (!$this->load_choices() or empty($this->choices)) { 3242 return ''; 3243 } 3244 3245 $default = $this->get_defaultsetting(); 3246 if (is_null($default)) { 3247 $default = array(); 3248 } 3249 if (is_null($data)) { 3250 $data = array(); 3251 } 3252 3253 $context = (object) [ 3254 'id' => $this->get_id(), 3255 'name' => $this->get_full_name(), 3256 ]; 3257 3258 $options = array(); 3259 $defaults = array(); 3260 foreach ($this->choices as $key => $description) { 3261 if (!empty($default[$key])) { 3262 $defaults[] = $description; 3263 } 3264 3265 $options[] = [ 3266 'key' => $key, 3267 'checked' => !empty($data[$key]), 3268 'label' => highlightfast($query, $description) 3269 ]; 3270 } 3271 3272 if (is_null($default)) { 3273 $defaultinfo = null; 3274 } else if (!empty($defaults)) { 3275 $defaultinfo = implode(', ', $defaults); 3276 } else { 3277 $defaultinfo = get_string('none'); 3278 } 3279 3280 $context->options = $options; 3281 $context->hasoptions = !empty($options); 3282 3283 $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context); 3284 3285 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query); 3286 3287 } 3288 } 3289 3290 3291 /** 3292 * Multiple checkboxes 2, value stored as string 00101011 3293 * 3294 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3295 */ 3296 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox { 3297 3298 /** 3299 * Returns the setting if set 3300 * 3301 * @return mixed null if not set, else an array of set settings 3302 */ 3303 public function get_setting() { 3304 $result = $this->config_read($this->name); 3305 if (is_null($result)) { 3306 return NULL; 3307 } 3308 if (!$this->load_choices()) { 3309 return NULL; 3310 } 3311 $result = str_pad($result, count($this->choices), '0'); 3312 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY); 3313 $setting = array(); 3314 foreach ($this->choices as $key=>$unused) { 3315 $value = array_shift($result); 3316 if ($value) { 3317 $setting[$key] = 1; 3318 } 3319 } 3320 return $setting; 3321 } 3322 3323 /** 3324 * Save setting(s) provided in $data param 3325 * 3326 * @param array $data An array of settings to save 3327 * @return mixed empty string for bad data or bool true=>success, false=>error 3328 */ 3329 public function write_setting($data) { 3330 if (!is_array($data)) { 3331 return ''; // ignore it 3332 } 3333 if (!$this->load_choices() or empty($this->choices)) { 3334 return ''; 3335 } 3336 $result = ''; 3337 foreach ($this->choices as $key=>$unused) { 3338 if (!empty($data[$key])) { 3339 $result .= '1'; 3340 } else { 3341 $result .= '0'; 3342 } 3343 } 3344 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'); 3345 } 3346 } 3347 3348 3349 /** 3350 * Select one value from list 3351 * 3352 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3353 */ 3354 class admin_setting_configselect extends admin_setting { 3355 /** @var array Array of choices value=>label */ 3356 public $choices; 3357 /** @var array Array of choices grouped using optgroups */ 3358 public $optgroups; 3359 /** @var callable|null Loader function for choices */ 3360 protected $choiceloader = null; 3361 /** @var callable|null Validation function */ 3362 protected $validatefunction = null; 3363 3364 /** 3365 * Constructor. 3366 * 3367 * If you want to lazy-load the choices, pass a callback function that returns a choice 3368 * array for the $choices parameter. 3369 * 3370 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3371 * @param string $visiblename localised 3372 * @param string $description long localised info 3373 * @param string|int $defaultsetting 3374 * @param array|callable|null $choices array of $value=>$label for each selection, or callback 3375 */ 3376 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 3377 // Look for optgroup and single options. 3378 if (is_array($choices)) { 3379 $this->choices = []; 3380 foreach ($choices as $key => $val) { 3381 if (is_array($val)) { 3382 $this->optgroups[$key] = $val; 3383 $this->choices = array_merge($this->choices, $val); 3384 } else { 3385 $this->choices[$key] = $val; 3386 } 3387 } 3388 } 3389 if (is_callable($choices)) { 3390 $this->choiceloader = $choices; 3391 } 3392 3393 parent::__construct($name, $visiblename, $description, $defaultsetting); 3394 } 3395 3396 /** 3397 * Sets a validate function. 3398 * 3399 * The callback will be passed one parameter, the new setting value, and should return either 3400 * an empty string '' if the value is OK, or an error message if not. 3401 * 3402 * @param callable|null $validatefunction Validate function or null to clear 3403 * @since Moodle 3.10 3404 */ 3405 public function set_validate_function(?callable $validatefunction = null) { 3406 $this->validatefunction = $validatefunction; 3407 } 3408 3409 /** 3410 * This function may be used in ancestors for lazy loading of choices 3411 * 3412 * Override this method if loading of choices is expensive, such 3413 * as when it requires multiple db requests. 3414 * 3415 * @return bool true if loaded, false if error 3416 */ 3417 public function load_choices() { 3418 if ($this->choiceloader) { 3419 if (!is_array($this->choices)) { 3420 $this->choices = call_user_func($this->choiceloader); 3421 } 3422 return true; 3423 } 3424 return true; 3425 } 3426 3427 /** 3428 * Check if this is $query is related to a choice 3429 * 3430 * @param string $query 3431 * @return bool true if related, false if not 3432 */ 3433 public function is_related($query) { 3434 if (parent::is_related($query)) { 3435 return true; 3436 } 3437 if (!$this->load_choices()) { 3438 return false; 3439 } 3440 foreach ($this->choices as $key=>$value) { 3441 if (strpos(core_text::strtolower($key), $query) !== false) { 3442 return true; 3443 } 3444 if (strpos(core_text::strtolower($value), $query) !== false) { 3445 return true; 3446 } 3447 } 3448 return false; 3449 } 3450 3451 /** 3452 * Return the setting 3453 * 3454 * @return mixed returns config if successful else null 3455 */ 3456 public function get_setting() { 3457 return $this->config_read($this->name); 3458 } 3459 3460 /** 3461 * Save a setting 3462 * 3463 * @param string $data 3464 * @return string empty of error string 3465 */ 3466 public function write_setting($data) { 3467 if (!$this->load_choices() or empty($this->choices)) { 3468 return ''; 3469 } 3470 if (!array_key_exists($data, $this->choices)) { 3471 return ''; // ignore it 3472 } 3473 3474 // Validate the new setting. 3475 $error = $this->validate_setting($data); 3476 if ($error) { 3477 return $error; 3478 } 3479 3480 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 3481 } 3482 3483 /** 3484 * Validate the setting. This uses the callback function if provided; subclasses could override 3485 * to carry out validation directly in the class. 3486 * 3487 * @param string $data New value being set 3488 * @return string Empty string if valid, or error message text 3489 * @since Moodle 3.10 3490 */ 3491 protected function validate_setting(string $data): string { 3492 // If validation function is specified, call it now. 3493 if ($this->validatefunction) { 3494 return call_user_func($this->validatefunction, $data); 3495 } else { 3496 return ''; 3497 } 3498 } 3499 3500 /** 3501 * Returns XHTML select field 3502 * 3503 * Ensure the options are loaded, and generate the XHTML for the select 3504 * element and any warning message. Separating this out from output_html 3505 * makes it easier to subclass this class. 3506 * 3507 * @param string $data the option to show as selected. 3508 * @param string $current the currently selected option in the database, null if none. 3509 * @param string $default the default selected option. 3510 * @return array the HTML for the select element, and a warning message. 3511 * @deprecated since Moodle 3.2 3512 */ 3513 public function output_select_html($data, $current, $default, $extraname = '') { 3514 debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER); 3515 } 3516 3517 /** 3518 * Returns XHTML select field and wrapping div(s) 3519 * 3520 * @see output_select_html() 3521 * 3522 * @param string $data the option to show as selected 3523 * @param string $query 3524 * @return string XHTML field and wrapping div 3525 */ 3526 public function output_html($data, $query='') { 3527 global $OUTPUT; 3528 3529 $default = $this->get_defaultsetting(); 3530 $current = $this->get_setting(); 3531 3532 if (!$this->load_choices() || empty($this->choices)) { 3533 return ''; 3534 } 3535 3536 $context = (object) [ 3537 'id' => $this->get_id(), 3538 'name' => $this->get_full_name(), 3539 ]; 3540 3541 if (!is_null($default) && array_key_exists($default, $this->choices)) { 3542 $defaultinfo = $this->choices[$default]; 3543 } else { 3544 $defaultinfo = NULL; 3545 } 3546 3547 // Warnings. 3548 $warning = ''; 3549 if ($current === null) { 3550 // First run. 3551 } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) { 3552 // No warning. 3553 } else if (!array_key_exists($current, $this->choices)) { 3554 $warning = get_string('warningcurrentsetting', 'admin', $current); 3555 if (!is_null($default) && $data == $current) { 3556 $data = $default; // Use default instead of first value when showing the form. 3557 } 3558 } 3559 3560 $options = []; 3561 $template = 'core_admin/setting_configselect'; 3562 3563 if (!empty($this->optgroups)) { 3564 $optgroups = []; 3565 foreach ($this->optgroups as $label => $choices) { 3566 $optgroup = array('label' => $label, 'options' => []); 3567 foreach ($choices as $value => $name) { 3568 $optgroup['options'][] = [ 3569 'value' => $value, 3570 'name' => $name, 3571 'selected' => (string) $value == $data 3572 ]; 3573 unset($this->choices[$value]); 3574 } 3575 $optgroups[] = $optgroup; 3576 } 3577 $context->options = $options; 3578 $context->optgroups = $optgroups; 3579 $template = 'core_admin/setting_configselect_optgroup'; 3580 } 3581 3582 foreach ($this->choices as $value => $name) { 3583 $options[] = [ 3584 'value' => $value, 3585 'name' => $name, 3586 'selected' => (string) $value == $data 3587 ]; 3588 } 3589 $context->options = $options; 3590 $context->readonly = $this->is_readonly(); 3591 3592 $element = $OUTPUT->render_from_template($template, $context); 3593 3594 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query); 3595 } 3596 } 3597 3598 /** 3599 * Select multiple items from list 3600 * 3601 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3602 */ 3603 class admin_setting_configmultiselect extends admin_setting_configselect { 3604 /** 3605 * Constructor 3606 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 3607 * @param string $visiblename localised 3608 * @param string $description long localised info 3609 * @param array $defaultsetting array of selected items 3610 * @param array $choices array of $value=>$label for each list item 3611 */ 3612 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 3613 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices); 3614 } 3615 3616 /** 3617 * Returns the select setting(s) 3618 * 3619 * @return mixed null or array. Null if no settings else array of setting(s) 3620 */ 3621 public function get_setting() { 3622 $result = $this->config_read($this->name); 3623 if (is_null($result)) { 3624 return NULL; 3625 } 3626 if ($result === '') { 3627 return array(); 3628 } 3629 return explode(',', $result); 3630 } 3631 3632 /** 3633 * Saves setting(s) provided through $data 3634 * 3635 * Potential bug in the works should anyone call with this function 3636 * using a vartype that is not an array 3637 * 3638 * @param array $data 3639 */ 3640 public function write_setting($data) { 3641 if (!is_array($data)) { 3642 return ''; //ignore it 3643 } 3644 if (!$this->load_choices() or empty($this->choices)) { 3645 return ''; 3646 } 3647 3648 unset($data['xxxxx']); 3649 3650 $save = array(); 3651 foreach ($data as $value) { 3652 if (!array_key_exists($value, $this->choices)) { 3653 continue; // ignore it 3654 } 3655 $save[] = $value; 3656 } 3657 3658 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin')); 3659 } 3660 3661 /** 3662 * Is setting related to query text - used when searching 3663 * 3664 * @param string $query 3665 * @return bool true if related, false if not 3666 */ 3667 public function is_related($query) { 3668 if (!$this->load_choices() or empty($this->choices)) { 3669 return false; 3670 } 3671 if (parent::is_related($query)) { 3672 return true; 3673 } 3674 3675 foreach ($this->choices as $desc) { 3676 if (strpos(core_text::strtolower($desc), $query) !== false) { 3677 return true; 3678 } 3679 } 3680 return false; 3681 } 3682 3683 /** 3684 * Returns XHTML multi-select field 3685 * 3686 * @todo Add vartype handling to ensure $data is an array 3687 * @param array $data Array of values to select by default 3688 * @param string $query 3689 * @return string XHTML multi-select field 3690 */ 3691 public function output_html($data, $query='') { 3692 global $OUTPUT; 3693 3694 if (!$this->load_choices() or empty($this->choices)) { 3695 return ''; 3696 } 3697 3698 $default = $this->get_defaultsetting(); 3699 if (is_null($default)) { 3700 $default = array(); 3701 } 3702 if (is_null($data)) { 3703 $data = array(); 3704 } 3705 3706 $context = (object) [ 3707 'id' => $this->get_id(), 3708 'name' => $this->get_full_name(), 3709 'size' => min(10, count($this->choices)) 3710 ]; 3711 3712 $defaults = []; 3713 $options = []; 3714 $template = 'core_admin/setting_configmultiselect'; 3715 3716 if (!empty($this->optgroups)) { 3717 $optgroups = []; 3718 foreach ($this->optgroups as $label => $choices) { 3719 $optgroup = array('label' => $label, 'options' => []); 3720 foreach ($choices as $value => $name) { 3721 if (in_array($value, $default)) { 3722 $defaults[] = $name; 3723 } 3724 $optgroup['options'][] = [ 3725 'value' => $value, 3726 'name' => $name, 3727 'selected' => in_array($value, $data) 3728 ]; 3729 unset($this->choices[$value]); 3730 } 3731 $optgroups[] = $optgroup; 3732 } 3733 $context->optgroups = $optgroups; 3734 $template = 'core_admin/setting_configmultiselect_optgroup'; 3735 } 3736 3737 foreach ($this->choices as $value => $name) { 3738 if (in_array($value, $default)) { 3739 $defaults[] = $name; 3740 } 3741 $options[] = [ 3742 'value' => $value, 3743 'name' => $name, 3744 'selected' => in_array($value, $data) 3745 ]; 3746 } 3747 $context->options = $options; 3748 $context->readonly = $this->is_readonly(); 3749 3750 if (is_null($default)) { 3751 $defaultinfo = NULL; 3752 } if (!empty($defaults)) { 3753 $defaultinfo = implode(', ', $defaults); 3754 } else { 3755 $defaultinfo = get_string('none'); 3756 } 3757 3758 $element = $OUTPUT->render_from_template($template, $context); 3759 3760 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 3761 } 3762 } 3763 3764 /** 3765 * Time selector 3766 * 3767 * This is a liiitle bit messy. we're using two selects, but we're returning 3768 * them as an array named after $name (so we only use $name2 internally for the setting) 3769 * 3770 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3771 */ 3772 class admin_setting_configtime extends admin_setting { 3773 /** @var string Used for setting second select (minutes) */ 3774 public $name2; 3775 3776 /** 3777 * Constructor 3778 * @param string $hoursname setting for hours 3779 * @param string $minutesname setting for hours 3780 * @param string $visiblename localised 3781 * @param string $description long localised info 3782 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes 3783 */ 3784 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) { 3785 $this->name2 = $minutesname; 3786 parent::__construct($hoursname, $visiblename, $description, $defaultsetting); 3787 } 3788 3789 /** 3790 * Get the selected time 3791 * 3792 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set 3793 */ 3794 public function get_setting() { 3795 $result1 = $this->config_read($this->name); 3796 $result2 = $this->config_read($this->name2); 3797 if (is_null($result1) or is_null($result2)) { 3798 return NULL; 3799 } 3800 3801 return array('h' => $result1, 'm' => $result2); 3802 } 3803 3804 /** 3805 * Store the time (hours and minutes) 3806 * 3807 * @param array $data Must be form 'h'=>xx, 'm'=>xx 3808 * @return bool true if success, false if not 3809 */ 3810 public function write_setting($data) { 3811 if (!is_array($data)) { 3812 return ''; 3813 } 3814 3815 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']); 3816 return ($result ? '' : get_string('errorsetting', 'admin')); 3817 } 3818 3819 /** 3820 * Returns XHTML time select fields 3821 * 3822 * @param array $data Must be form 'h'=>xx, 'm'=>xx 3823 * @param string $query 3824 * @return string XHTML time select fields and wrapping div(s) 3825 */ 3826 public function output_html($data, $query='') { 3827 global $OUTPUT; 3828 3829 $default = $this->get_defaultsetting(); 3830 if (is_array($default)) { 3831 $defaultinfo = $default['h'].':'.$default['m']; 3832 } else { 3833 $defaultinfo = NULL; 3834 } 3835 3836 $context = (object) [ 3837 'id' => $this->get_id(), 3838 'name' => $this->get_full_name(), 3839 'readonly' => $this->is_readonly(), 3840 'hours' => array_map(function($i) use ($data) { 3841 return [ 3842 'value' => $i, 3843 'name' => $i, 3844 'selected' => $i == $data['h'] 3845 ]; 3846 }, range(0, 23)), 3847 'minutes' => array_map(function($i) use ($data) { 3848 return [ 3849 'value' => $i, 3850 'name' => $i, 3851 'selected' => $i == $data['m'] 3852 ]; 3853 }, range(0, 59, 5)) 3854 ]; 3855 3856 $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context); 3857 3858 return format_admin_setting($this, $this->visiblename, $element, $this->description, 3859 $this->get_id() . 'h', '', $defaultinfo, $query); 3860 } 3861 3862 } 3863 3864 3865 /** 3866 * Seconds duration setting. 3867 * 3868 * @copyright 2012 Petr Skoda (http://skodak.org) 3869 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 3870 */ 3871 class admin_setting_configduration extends admin_setting { 3872 3873 /** @var int default duration unit */ 3874 protected $defaultunit; 3875 /** @var callable|null Validation function */ 3876 protected $validatefunction = null; 3877 3878 /** 3879 * Constructor 3880 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 3881 * or 'myplugin/mysetting' for ones in config_plugins. 3882 * @param string $visiblename localised name 3883 * @param string $description localised long description 3884 * @param mixed $defaultsetting string or array depending on implementation 3885 * @param int $defaultunit - day, week, etc. (in seconds) 3886 */ 3887 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) { 3888 if (is_number($defaultsetting)) { 3889 $defaultsetting = self::parse_seconds($defaultsetting); 3890 } 3891 $units = self::get_units(); 3892 if (isset($units[$defaultunit])) { 3893 $this->defaultunit = $defaultunit; 3894 } else { 3895 $this->defaultunit = 86400; 3896 } 3897 parent::__construct($name, $visiblename, $description, $defaultsetting); 3898 } 3899 3900 /** 3901 * Sets a validate function. 3902 * 3903 * The callback will be passed one parameter, the new setting value, and should return either 3904 * an empty string '' if the value is OK, or an error message if not. 3905 * 3906 * @param callable|null $validatefunction Validate function or null to clear 3907 * @since Moodle 3.10 3908 */ 3909 public function set_validate_function(?callable $validatefunction = null) { 3910 $this->validatefunction = $validatefunction; 3911 } 3912 3913 /** 3914 * Validate the setting. This uses the callback function if provided; subclasses could override 3915 * to carry out validation directly in the class. 3916 * 3917 * @param int $data New value being set 3918 * @return string Empty string if valid, or error message text 3919 * @since Moodle 3.10 3920 */ 3921 protected function validate_setting(int $data): string { 3922 // If validation function is specified, call it now. 3923 if ($this->validatefunction) { 3924 return call_user_func($this->validatefunction, $data); 3925 } else { 3926 if ($data < 0) { 3927 return get_string('errorsetting', 'admin'); 3928 } 3929 return ''; 3930 } 3931 } 3932 3933 /** 3934 * Returns selectable units. 3935 * @static 3936 * @return array 3937 */ 3938 protected static function get_units() { 3939 return array( 3940 604800 => get_string('weeks'), 3941 86400 => get_string('days'), 3942 3600 => get_string('hours'), 3943 60 => get_string('minutes'), 3944 1 => get_string('seconds'), 3945 ); 3946 } 3947 3948 /** 3949 * Converts seconds to some more user friendly string. 3950 * @static 3951 * @param int $seconds 3952 * @return string 3953 */ 3954 protected static function get_duration_text($seconds) { 3955 if (empty($seconds)) { 3956 return get_string('none'); 3957 } 3958 $data = self::parse_seconds($seconds); 3959 switch ($data['u']) { 3960 case (60*60*24*7): 3961 return get_string('numweeks', '', $data['v']); 3962 case (60*60*24): 3963 return get_string('numdays', '', $data['v']); 3964 case (60*60): 3965 return get_string('numhours', '', $data['v']); 3966 case (60): 3967 return get_string('numminutes', '', $data['v']); 3968 default: 3969 return get_string('numseconds', '', $data['v']*$data['u']); 3970 } 3971 } 3972 3973 /** 3974 * Finds suitable units for given duration. 3975 * @static 3976 * @param int $seconds 3977 * @return array 3978 */ 3979 protected static function parse_seconds($seconds) { 3980 foreach (self::get_units() as $unit => $unused) { 3981 if ($seconds % $unit === 0) { 3982 return array('v'=>(int)($seconds/$unit), 'u'=>$unit); 3983 } 3984 } 3985 return array('v'=>(int)$seconds, 'u'=>1); 3986 } 3987 3988 /** 3989 * Get the selected duration as array. 3990 * 3991 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set 3992 */ 3993 public function get_setting() { 3994 $seconds = $this->config_read($this->name); 3995 if (is_null($seconds)) { 3996 return null; 3997 } 3998 3999 return self::parse_seconds($seconds); 4000 } 4001 4002 /** 4003 * Store the duration as seconds. 4004 * 4005 * @param array $data Must be form 'h'=>xx, 'm'=>xx 4006 * @return bool true if success, false if not 4007 */ 4008 public function write_setting($data) { 4009 if (!is_array($data)) { 4010 return ''; 4011 } 4012 4013 $unit = (int)$data['u']; 4014 $value = (int)$data['v']; 4015 $seconds = $value * $unit; 4016 4017 // Validate the new setting. 4018 $error = $this->validate_setting($seconds); 4019 if ($error) { 4020 return $error; 4021 } 4022 4023 $result = $this->config_write($this->name, $seconds); 4024 return ($result ? '' : get_string('errorsetting', 'admin')); 4025 } 4026 4027 /** 4028 * Returns duration text+select fields. 4029 * 4030 * @param array $data Must be form 'v'=>xx, 'u'=>xx 4031 * @param string $query 4032 * @return string duration text+select fields and wrapping div(s) 4033 */ 4034 public function output_html($data, $query='') { 4035 global $OUTPUT; 4036 4037 $default = $this->get_defaultsetting(); 4038 if (is_number($default)) { 4039 $defaultinfo = self::get_duration_text($default); 4040 } else if (is_array($default)) { 4041 $defaultinfo = self::get_duration_text($default['v']*$default['u']); 4042 } else { 4043 $defaultinfo = null; 4044 } 4045 4046 $inputid = $this->get_id() . 'v'; 4047 $units = self::get_units(); 4048 $defaultunit = $this->defaultunit; 4049 4050 $context = (object) [ 4051 'id' => $this->get_id(), 4052 'name' => $this->get_full_name(), 4053 'value' => $data['v'] ?? '', 4054 'readonly' => $this->is_readonly(), 4055 'options' => array_map(function($unit) use ($units, $data, $defaultunit) { 4056 return [ 4057 'value' => $unit, 4058 'name' => $units[$unit], 4059 'selected' => isset($data) && (($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']) 4060 ]; 4061 }, array_keys($units)) 4062 ]; 4063 4064 $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context); 4065 4066 return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query); 4067 } 4068 } 4069 4070 4071 /** 4072 * Seconds duration setting with an advanced checkbox, that controls a additional 4073 * $name.'_adv' setting. 4074 * 4075 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4076 * @copyright 2014 The Open University 4077 */ 4078 class admin_setting_configduration_with_advanced extends admin_setting_configduration { 4079 /** 4080 * Constructor 4081 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 4082 * or 'myplugin/mysetting' for ones in config_plugins. 4083 * @param string $visiblename localised name 4084 * @param string $description localised long description 4085 * @param array $defaultsetting array of int value, and bool whether it is 4086 * is advanced by default. 4087 * @param int $defaultunit - day, week, etc. (in seconds) 4088 */ 4089 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) { 4090 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit); 4091 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 4092 } 4093 } 4094 4095 4096 /** 4097 * Used to validate a textarea used for ip addresses 4098 * 4099 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4100 * @copyright 2011 Petr Skoda (http://skodak.org) 4101 */ 4102 class admin_setting_configiplist extends admin_setting_configtextarea { 4103 4104 /** 4105 * Validate the contents of the textarea as IP addresses 4106 * 4107 * Used to validate a new line separated list of IP addresses collected from 4108 * a textarea control 4109 * 4110 * @param string $data A list of IP Addresses separated by new lines 4111 * @return mixed bool true for success or string:error on failure 4112 */ 4113 public function validate($data) { 4114 if(!empty($data)) { 4115 $lines = explode("\n", $data); 4116 } else { 4117 return true; 4118 } 4119 $result = true; 4120 $badips = array(); 4121 foreach ($lines as $line) { 4122 $tokens = explode('#', $line); 4123 $ip = trim($tokens[0]); 4124 if (empty($ip)) { 4125 continue; 4126 } 4127 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) || 4128 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) || 4129 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) { 4130 } else { 4131 $result = false; 4132 $badips[] = $ip; 4133 } 4134 } 4135 if($result) { 4136 return true; 4137 } else { 4138 return get_string('validateiperror', 'admin', join(', ', $badips)); 4139 } 4140 } 4141 } 4142 4143 /** 4144 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format). 4145 * 4146 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4147 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com) 4148 */ 4149 class admin_setting_configmixedhostiplist extends admin_setting_configtextarea { 4150 4151 /** 4152 * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592). 4153 * Used to validate a new line separated list of entries collected from a textarea control. 4154 * 4155 * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to 4156 * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched 4157 * via the get_setting() method, which has been overriden. 4158 * 4159 * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines. 4160 * @return mixed bool true for success or string:error on failure 4161 */ 4162 public function validate($data) { 4163 if (empty($data)) { 4164 return true; 4165 } 4166 $entries = explode("\n", $data); 4167 $badentries = []; 4168 4169 foreach ($entries as $key => $entry) { 4170 $entry = trim($entry); 4171 if (empty($entry)) { 4172 return get_string('validateemptylineerror', 'admin'); 4173 } 4174 4175 // Validate each string entry against the supported formats. 4176 if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry) 4177 || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry) 4178 || \core\ip_utils::is_domain_matching_pattern($entry)) { 4179 continue; 4180 } 4181 4182 // Otherwise, the entry is invalid. 4183 $badentries[] = $entry; 4184 } 4185 4186 if ($badentries) { 4187 return get_string('validateerrorlist', 'admin', join(', ', $badentries)); 4188 } 4189 return true; 4190 } 4191 4192 /** 4193 * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE). 4194 * 4195 * @param string $data the setting data, as sent from the web form. 4196 * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version. 4197 */ 4198 protected function ace_encode($data) { 4199 if (empty($data)) { 4200 return $data; 4201 } 4202 $entries = explode("\n", $data); 4203 foreach ($entries as $key => $entry) { 4204 $entry = trim($entry); 4205 // This regex matches any string that has non-ascii character. 4206 if (preg_match('/[^\x00-\x7f]/', $entry)) { 4207 // If we can convert the unicode string to an idn, do so. 4208 // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail). 4209 $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); 4210 $entries[$key] = $val ? $val : $entry; 4211 } 4212 } 4213 return implode("\n", $entries); 4214 } 4215 4216 /** 4217 * Decode any ascii-encoded domain names back to their utf-8 representation for display. 4218 * 4219 * @param string $data the setting data, as found in the database. 4220 * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation. 4221 */ 4222 protected function ace_decode($data) { 4223 $entries = explode("\n", $data); 4224 foreach ($entries as $key => $entry) { 4225 $entry = trim($entry); 4226 if (strpos($entry, 'xn--') !== false) { 4227 $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); 4228 } 4229 } 4230 return implode("\n", $entries); 4231 } 4232 4233 /** 4234 * Override, providing utf8-decoding for ascii-encoded IDN strings. 4235 * 4236 * @return mixed returns punycode-converted setting string if successful, else null. 4237 */ 4238 public function get_setting() { 4239 // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation. 4240 $data = $this->config_read($this->name); 4241 if (function_exists('idn_to_utf8') && !is_null($data)) { 4242 $data = $this->ace_decode($data); 4243 } 4244 return $data; 4245 } 4246 4247 /** 4248 * Override, providing ascii-encoding for utf8 (native) IDN strings. 4249 * 4250 * @param string $data 4251 * @return string 4252 */ 4253 public function write_setting($data) { 4254 if ($this->paramtype === PARAM_INT and $data === '') { 4255 // Do not complain if '' used instead of 0. 4256 $data = 0; 4257 } 4258 4259 // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate! 4260 if (function_exists('idn_to_ascii')) { 4261 $data = $this->ace_encode($data); 4262 } 4263 4264 $validated = $this->validate($data); 4265 if ($validated !== true) { 4266 return $validated; 4267 } 4268 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 4269 } 4270 } 4271 4272 /** 4273 * Used to validate a textarea used for port numbers. 4274 * 4275 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4276 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com) 4277 */ 4278 class admin_setting_configportlist extends admin_setting_configtextarea { 4279 4280 /** 4281 * Validate the contents of the textarea as port numbers. 4282 * Used to validate a new line separated list of ports collected from a textarea control. 4283 * 4284 * @param string $data A list of ports separated by new lines 4285 * @return mixed bool true for success or string:error on failure 4286 */ 4287 public function validate($data) { 4288 if (empty($data)) { 4289 return true; 4290 } 4291 $ports = explode("\n", $data); 4292 $badentries = []; 4293 foreach ($ports as $port) { 4294 $port = trim($port); 4295 if (empty($port)) { 4296 return get_string('validateemptylineerror', 'admin'); 4297 } 4298 4299 // Is the string a valid integer number? 4300 if (strval(intval($port)) !== $port || intval($port) <= 0) { 4301 $badentries[] = $port; 4302 } 4303 } 4304 if ($badentries) { 4305 return get_string('validateerrorlist', 'admin', $badentries); 4306 } 4307 return true; 4308 } 4309 } 4310 4311 4312 /** 4313 * An admin setting for selecting one or more users who have a capability 4314 * in the system context 4315 * 4316 * An admin setting for selecting one or more users, who have a particular capability 4317 * in the system context. Warning, make sure the list will never be too long. There is 4318 * no paging or searching of this list. 4319 * 4320 * To correctly get a list of users from this config setting, you need to call the 4321 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php. 4322 * 4323 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4324 */ 4325 class admin_setting_users_with_capability extends admin_setting_configmultiselect { 4326 /** @var string The capabilities name */ 4327 protected $capability; 4328 /** @var int include admin users too */ 4329 protected $includeadmins; 4330 4331 /** 4332 * Constructor. 4333 * 4334 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 4335 * @param string $visiblename localised name 4336 * @param string $description localised long description 4337 * @param array $defaultsetting array of usernames 4338 * @param string $capability string capability name. 4339 * @param bool $includeadmins include administrators 4340 */ 4341 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) { 4342 $this->capability = $capability; 4343 $this->includeadmins = $includeadmins; 4344 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL); 4345 } 4346 4347 /** 4348 * Load all of the uses who have the capability into choice array 4349 * 4350 * @return bool Always returns true 4351 */ 4352 function load_choices() { 4353 if (is_array($this->choices)) { 4354 return true; 4355 } 4356 list($sort, $sortparams) = users_order_by_sql('u'); 4357 if (!empty($sortparams)) { 4358 throw new coding_exception('users_order_by_sql returned some query parameters. ' . 4359 'This is unexpected, and a problem because there is no way to pass these ' . 4360 'parameters to get_users_by_capability. See MDL-34657.'); 4361 } 4362 $userfieldsapi = \core_user\fields::for_name(); 4363 $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects; 4364 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort); 4365 $this->choices = array( 4366 '$@NONE@$' => get_string('nobody'), 4367 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)), 4368 ); 4369 if ($this->includeadmins) { 4370 $admins = get_admins(); 4371 foreach ($admins as $user) { 4372 $this->choices[$user->id] = fullname($user); 4373 } 4374 } 4375 if (is_array($users)) { 4376 foreach ($users as $user) { 4377 $this->choices[$user->id] = fullname($user); 4378 } 4379 } 4380 return true; 4381 } 4382 4383 /** 4384 * Returns the default setting for class 4385 * 4386 * @return mixed Array, or string. Empty string if no default 4387 */ 4388 public function get_defaultsetting() { 4389 $this->load_choices(); 4390 $defaultsetting = parent::get_defaultsetting(); 4391 if (empty($defaultsetting)) { 4392 return array('$@NONE@$'); 4393 } else if (array_key_exists($defaultsetting, $this->choices)) { 4394 return $defaultsetting; 4395 } else { 4396 return ''; 4397 } 4398 } 4399 4400 /** 4401 * Returns the current setting 4402 * 4403 * @return mixed array or string 4404 */ 4405 public function get_setting() { 4406 $result = parent::get_setting(); 4407 if ($result === null) { 4408 // this is necessary for settings upgrade 4409 return null; 4410 } 4411 if (empty($result)) { 4412 $result = array('$@NONE@$'); 4413 } 4414 return $result; 4415 } 4416 4417 /** 4418 * Save the chosen setting provided as $data 4419 * 4420 * @param array $data 4421 * @return mixed string or array 4422 */ 4423 public function write_setting($data) { 4424 // If all is selected, remove any explicit options. 4425 if (in_array('$@ALL@$', $data)) { 4426 $data = array('$@ALL@$'); 4427 } 4428 // None never needs to be written to the DB. 4429 if (in_array('$@NONE@$', $data)) { 4430 unset($data[array_search('$@NONE@$', $data)]); 4431 } 4432 return parent::write_setting($data); 4433 } 4434 } 4435 4436 4437 /** 4438 * Special checkbox for calendar - resets SESSION vars. 4439 * 4440 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4441 */ 4442 class admin_setting_special_adminseesall extends admin_setting_configcheckbox { 4443 /** 4444 * Calls the parent::__construct with default values 4445 * 4446 * name => calendar_adminseesall 4447 * visiblename => get_string('adminseesall', 'admin') 4448 * description => get_string('helpadminseesall', 'admin') 4449 * defaultsetting => 0 4450 */ 4451 public function __construct() { 4452 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'), 4453 get_string('helpadminseesall', 'admin'), '0'); 4454 } 4455 4456 /** 4457 * Stores the setting passed in $data 4458 * 4459 * @param mixed gets converted to string for comparison 4460 * @return string empty string or error message 4461 */ 4462 public function write_setting($data) { 4463 global $SESSION; 4464 return parent::write_setting($data); 4465 } 4466 } 4467 4468 /** 4469 * Special select for settings that are altered in setup.php and can not be altered on the fly 4470 * 4471 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4472 */ 4473 class admin_setting_special_selectsetup extends admin_setting_configselect { 4474 /** 4475 * Reads the setting directly from the database 4476 * 4477 * @return mixed 4478 */ 4479 public function get_setting() { 4480 // read directly from db! 4481 return get_config(NULL, $this->name); 4482 } 4483 4484 /** 4485 * Save the setting passed in $data 4486 * 4487 * @param string $data The setting to save 4488 * @return string empty or error message 4489 */ 4490 public function write_setting($data) { 4491 global $CFG; 4492 // do not change active CFG setting! 4493 $current = $CFG->{$this->name}; 4494 $result = parent::write_setting($data); 4495 $CFG->{$this->name} = $current; 4496 return $result; 4497 } 4498 } 4499 4500 4501 /** 4502 * Special select for frontpage - stores data in course table 4503 * 4504 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4505 */ 4506 class admin_setting_sitesetselect extends admin_setting_configselect { 4507 /** 4508 * Returns the site name for the selected site 4509 * 4510 * @see get_site() 4511 * @return string The site name of the selected site 4512 */ 4513 public function get_setting() { 4514 $site = course_get_format(get_site())->get_course(); 4515 return $site->{$this->name}; 4516 } 4517 4518 /** 4519 * Updates the database and save the setting 4520 * 4521 * @param string data 4522 * @return string empty or error message 4523 */ 4524 public function write_setting($data) { 4525 global $DB, $SITE, $COURSE; 4526 if (!in_array($data, array_keys($this->choices))) { 4527 return get_string('errorsetting', 'admin'); 4528 } 4529 $record = new stdClass(); 4530 $record->id = SITEID; 4531 $temp = $this->name; 4532 $record->$temp = $data; 4533 $record->timemodified = time(); 4534 4535 course_get_format($SITE)->update_course_format_options($record); 4536 $DB->update_record('course', $record); 4537 4538 // Reset caches. 4539 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4540 if ($SITE->id == $COURSE->id) { 4541 $COURSE = $SITE; 4542 } 4543 core_courseformat\base::reset_course_cache($SITE->id); 4544 4545 return ''; 4546 4547 } 4548 } 4549 4550 4551 /** 4552 * Select for blog's bloglevel setting: if set to 0, will set blog_menu 4553 * block to hidden. 4554 * 4555 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4556 */ 4557 class admin_setting_bloglevel extends admin_setting_configselect { 4558 /** 4559 * Updates the database and save the setting 4560 * 4561 * @param string data 4562 * @return string empty or error message 4563 */ 4564 public function write_setting($data) { 4565 global $DB, $CFG; 4566 if ($data == 0) { 4567 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1"); 4568 foreach ($blogblocks as $block) { 4569 $DB->set_field('block', 'visible', 0, array('id' => $block->id)); 4570 } 4571 } else { 4572 // reenable all blocks only when switching from disabled blogs 4573 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) { 4574 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0"); 4575 foreach ($blogblocks as $block) { 4576 $DB->set_field('block', 'visible', 1, array('id' => $block->id)); 4577 } 4578 } 4579 } 4580 return parent::write_setting($data); 4581 } 4582 } 4583 4584 4585 /** 4586 * Special select - lists on the frontpage - hacky 4587 * 4588 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4589 */ 4590 class admin_setting_courselist_frontpage extends admin_setting { 4591 /** @var array Array of choices value=>label */ 4592 public $choices; 4593 4594 /** 4595 * Construct override, requires one param 4596 * 4597 * @param bool $loggedin Is the user logged in 4598 */ 4599 public function __construct($loggedin) { 4600 global $CFG; 4601 require_once($CFG->dirroot.'/course/lib.php'); 4602 $name = 'frontpage'.($loggedin ? 'loggedin' : ''); 4603 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin'); 4604 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin'); 4605 $defaults = array(FRONTPAGEALLCOURSELIST); 4606 parent::__construct($name, $visiblename, $description, $defaults); 4607 } 4608 4609 /** 4610 * Loads the choices available 4611 * 4612 * @return bool always returns true 4613 */ 4614 public function load_choices() { 4615 if (is_array($this->choices)) { 4616 return true; 4617 } 4618 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'), 4619 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'), 4620 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'), 4621 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'), 4622 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'), 4623 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'), 4624 'none' => get_string('none')); 4625 if ($this->name === 'frontpage') { 4626 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]); 4627 } 4628 return true; 4629 } 4630 4631 /** 4632 * Returns the selected settings 4633 * 4634 * @param mixed array or setting or null 4635 */ 4636 public function get_setting() { 4637 $result = $this->config_read($this->name); 4638 if (is_null($result)) { 4639 return NULL; 4640 } 4641 if ($result === '') { 4642 return array(); 4643 } 4644 return explode(',', $result); 4645 } 4646 4647 /** 4648 * Save the selected options 4649 * 4650 * @param array $data 4651 * @return mixed empty string (data is not an array) or bool true=success false=failure 4652 */ 4653 public function write_setting($data) { 4654 if (!is_array($data)) { 4655 return ''; 4656 } 4657 $this->load_choices(); 4658 $save = array(); 4659 foreach($data as $datum) { 4660 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) { 4661 continue; 4662 } 4663 $save[$datum] = $datum; // no duplicates 4664 } 4665 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin')); 4666 } 4667 4668 /** 4669 * Return XHTML select field and wrapping div 4670 * 4671 * @todo Add vartype handling to make sure $data is an array 4672 * @param array $data Array of elements to select by default 4673 * @return string XHTML select field and wrapping div 4674 */ 4675 public function output_html($data, $query='') { 4676 global $OUTPUT; 4677 4678 $this->load_choices(); 4679 $currentsetting = array(); 4680 foreach ($data as $key) { 4681 if ($key != 'none' and array_key_exists($key, $this->choices)) { 4682 $currentsetting[] = $key; // already selected first 4683 } 4684 } 4685 4686 $context = (object) [ 4687 'id' => $this->get_id(), 4688 'name' => $this->get_full_name(), 4689 ]; 4690 4691 $options = $this->choices; 4692 $selects = []; 4693 for ($i = 0; $i < count($this->choices) - 1; $i++) { 4694 if (!array_key_exists($i, $currentsetting)) { 4695 $currentsetting[$i] = 'none'; 4696 } 4697 $selects[] = [ 4698 'key' => $i, 4699 'options' => array_map(function($option) use ($options, $currentsetting, $i) { 4700 return [ 4701 'name' => $options[$option], 4702 'value' => $option, 4703 'selected' => $currentsetting[$i] == $option 4704 ]; 4705 }, array_keys($options)) 4706 ]; 4707 } 4708 $context->selects = $selects; 4709 4710 $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context); 4711 4712 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query); 4713 } 4714 } 4715 4716 4717 /** 4718 * Special checkbox for frontpage - stores data in course table 4719 * 4720 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4721 */ 4722 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox { 4723 /** 4724 * Returns the current sites name 4725 * 4726 * @return string 4727 */ 4728 public function get_setting() { 4729 $site = course_get_format(get_site())->get_course(); 4730 return $site->{$this->name}; 4731 } 4732 4733 /** 4734 * Save the selected setting 4735 * 4736 * @param string $data The selected site 4737 * @return string empty string or error message 4738 */ 4739 public function write_setting($data) { 4740 global $DB, $SITE, $COURSE; 4741 $record = new stdClass(); 4742 $record->id = $SITE->id; 4743 $record->{$this->name} = ($data == '1' ? 1 : 0); 4744 $record->timemodified = time(); 4745 4746 course_get_format($SITE)->update_course_format_options($record); 4747 $DB->update_record('course', $record); 4748 4749 // Reset caches. 4750 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4751 if ($SITE->id == $COURSE->id) { 4752 $COURSE = $SITE; 4753 } 4754 core_courseformat\base::reset_course_cache($SITE->id); 4755 4756 return ''; 4757 } 4758 } 4759 4760 /** 4761 * Special text for frontpage - stores data in course table. 4762 * Empty string means not set here. Manual setting is required. 4763 * 4764 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4765 */ 4766 class admin_setting_sitesettext extends admin_setting_configtext { 4767 4768 /** 4769 * Constructor. 4770 */ 4771 public function __construct() { 4772 call_user_func_array(['parent', '__construct'], func_get_args()); 4773 $this->set_force_ltr(false); 4774 } 4775 4776 /** 4777 * Return the current setting 4778 * 4779 * @return mixed string or null 4780 */ 4781 public function get_setting() { 4782 $site = course_get_format(get_site())->get_course(); 4783 return $site->{$this->name} != '' ? $site->{$this->name} : NULL; 4784 } 4785 4786 /** 4787 * Validate the selected data 4788 * 4789 * @param string $data The selected value to validate 4790 * @return mixed true or message string 4791 */ 4792 public function validate($data) { 4793 global $DB, $SITE; 4794 $cleaned = clean_param($data, PARAM_TEXT); 4795 if ($cleaned === '') { 4796 return get_string('required'); 4797 } 4798 if ($this->name ==='shortname' && 4799 $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) { 4800 return get_string('shortnametaken', 'error', $data); 4801 } 4802 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison 4803 return true; 4804 } else { 4805 return get_string('validateerror', 'admin'); 4806 } 4807 } 4808 4809 /** 4810 * Save the selected setting 4811 * 4812 * @param string $data The selected value 4813 * @return string empty or error message 4814 */ 4815 public function write_setting($data) { 4816 global $DB, $SITE, $COURSE; 4817 $data = trim($data); 4818 $validated = $this->validate($data); 4819 if ($validated !== true) { 4820 return $validated; 4821 } 4822 4823 $record = new stdClass(); 4824 $record->id = $SITE->id; 4825 $record->{$this->name} = $data; 4826 $record->timemodified = time(); 4827 4828 course_get_format($SITE)->update_course_format_options($record); 4829 $DB->update_record('course', $record); 4830 4831 // Reset caches. 4832 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4833 if ($SITE->id == $COURSE->id) { 4834 $COURSE = $SITE; 4835 } 4836 core_courseformat\base::reset_course_cache($SITE->id); 4837 4838 return ''; 4839 } 4840 } 4841 4842 4843 /** 4844 * This type of field should be used for mandatory config settings. 4845 * 4846 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4847 */ 4848 class admin_setting_requiredtext extends admin_setting_configtext { 4849 4850 /** 4851 * Validate data before storage. 4852 * 4853 * @param string $data The string to be validated. 4854 * @return bool|string true for success or error string if invalid. 4855 */ 4856 public function validate($data) { 4857 $cleaned = clean_param($data, PARAM_TEXT); 4858 if ($cleaned === '') { 4859 return get_string('required'); 4860 } 4861 4862 return parent::validate($data); 4863 } 4864 } 4865 4866 /** 4867 * Special text editor for site description. 4868 * 4869 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4870 */ 4871 class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor { 4872 4873 /** 4874 * Calls parent::__construct with specific arguments 4875 */ 4876 public function __construct() { 4877 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null, 4878 PARAM_RAW, 60, 15); 4879 } 4880 4881 /** 4882 * Return the current setting 4883 * @return string The current setting 4884 */ 4885 public function get_setting() { 4886 $site = course_get_format(get_site())->get_course(); 4887 return $site->{$this->name}; 4888 } 4889 4890 /** 4891 * Save the new setting 4892 * 4893 * @param string $data The new value to save 4894 * @return string empty or error message 4895 */ 4896 public function write_setting($data) { 4897 global $DB, $SITE, $COURSE; 4898 $record = new stdClass(); 4899 $record->id = $SITE->id; 4900 $record->{$this->name} = $data; 4901 $record->timemodified = time(); 4902 4903 course_get_format($SITE)->update_course_format_options($record); 4904 $DB->update_record('course', $record); 4905 4906 // Reset caches. 4907 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST); 4908 if ($SITE->id == $COURSE->id) { 4909 $COURSE = $SITE; 4910 } 4911 core_courseformat\base::reset_course_cache($SITE->id); 4912 4913 return ''; 4914 } 4915 } 4916 4917 4918 /** 4919 * Administration interface for emoticon_manager settings. 4920 * 4921 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 4922 */ 4923 class admin_setting_emoticons extends admin_setting { 4924 4925 /** 4926 * Calls parent::__construct with specific args 4927 */ 4928 public function __construct() { 4929 global $CFG; 4930 4931 $manager = get_emoticon_manager(); 4932 $defaults = $this->prepare_form_data($manager->default_emoticons()); 4933 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults); 4934 } 4935 4936 /** 4937 * Return the current setting(s) 4938 * 4939 * @return array Current settings array 4940 */ 4941 public function get_setting() { 4942 global $CFG; 4943 4944 $manager = get_emoticon_manager(); 4945 4946 $config = $this->config_read($this->name); 4947 if (is_null($config)) { 4948 return null; 4949 } 4950 4951 $config = $manager->decode_stored_config($config); 4952 if (is_null($config)) { 4953 return null; 4954 } 4955 4956 return $this->prepare_form_data($config); 4957 } 4958 4959 /** 4960 * Save selected settings 4961 * 4962 * @param array $data Array of settings to save 4963 * @return bool 4964 */ 4965 public function write_setting($data) { 4966 4967 $manager = get_emoticon_manager(); 4968 $emoticons = $this->process_form_data($data); 4969 4970 if ($emoticons === false) { 4971 return false; 4972 } 4973 4974 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) { 4975 return ''; // success 4976 } else { 4977 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br'); 4978 } 4979 } 4980 4981 /** 4982 * Return XHTML field(s) for options 4983 * 4984 * @param array $data Array of options to set in HTML 4985 * @return string XHTML string for the fields and wrapping div(s) 4986 */ 4987 public function output_html($data, $query='') { 4988 global $OUTPUT; 4989 4990 $context = (object) [ 4991 'name' => $this->get_full_name(), 4992 'emoticons' => [], 4993 'forceltr' => true, 4994 ]; 4995 4996 $i = 0; 4997 foreach ($data as $field => $value) { 4998 4999 // When $i == 0: text. 5000 // When $i == 1: imagename. 5001 // When $i == 2: imagecomponent. 5002 // When $i == 3: altidentifier. 5003 // When $i == 4: altcomponent. 5004 $fields[$i] = (object) [ 5005 'field' => $field, 5006 'value' => $value, 5007 'index' => $i 5008 ]; 5009 $i++; 5010 5011 if ($i > 4) { 5012 $icon = null; 5013 if (!empty($fields[1]->value)) { 5014 if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) { 5015 $alt = get_string($fields[3]->value, $fields[4]->value); 5016 } else { 5017 $alt = $fields[0]->value; 5018 } 5019 $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value); 5020 } 5021 $context->emoticons[] = [ 5022 'fields' => $fields, 5023 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null 5024 ]; 5025 $fields = []; 5026 $i = 0; 5027 } 5028 } 5029 5030 $context->reseturl = new moodle_url('/admin/resetemoticons.php'); 5031 $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context); 5032 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query); 5033 } 5034 5035 /** 5036 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data 5037 * 5038 * @see self::process_form_data() 5039 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager} 5040 * @return array of form fields and their values 5041 */ 5042 protected function prepare_form_data(array $emoticons) { 5043 5044 $form = array(); 5045 $i = 0; 5046 foreach ($emoticons as $emoticon) { 5047 $form['text'.$i] = $emoticon->text; 5048 $form['imagename'.$i] = $emoticon->imagename; 5049 $form['imagecomponent'.$i] = $emoticon->imagecomponent; 5050 $form['altidentifier'.$i] = $emoticon->altidentifier; 5051 $form['altcomponent'.$i] = $emoticon->altcomponent; 5052 $i++; 5053 } 5054 // add one more blank field set for new object 5055 $form['text'.$i] = ''; 5056 $form['imagename'.$i] = ''; 5057 $form['imagecomponent'.$i] = ''; 5058 $form['altidentifier'.$i] = ''; 5059 $form['altcomponent'.$i] = ''; 5060 5061 return $form; 5062 } 5063 5064 /** 5065 * Converts the data from admin settings form into an array of emoticon objects 5066 * 5067 * @see self::prepare_form_data() 5068 * @param array $data array of admin form fields and values 5069 * @return false|array of emoticon objects 5070 */ 5071 protected function process_form_data(array $form) { 5072 5073 $count = count($form); // number of form field values 5074 5075 if ($count % 5) { 5076 // we must get five fields per emoticon object 5077 return false; 5078 } 5079 5080 $emoticons = array(); 5081 for ($i = 0; $i < $count / 5; $i++) { 5082 $emoticon = new stdClass(); 5083 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS); 5084 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH); 5085 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT); 5086 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID); 5087 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT); 5088 5089 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) { 5090 // prevent from breaking http://url.addresses by accident 5091 $emoticon->text = ''; 5092 } 5093 5094 if (strlen($emoticon->text) < 2) { 5095 // do not allow single character emoticons 5096 $emoticon->text = ''; 5097 } 5098 5099 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) { 5100 // emoticon text must contain some non-alphanumeric character to prevent 5101 // breaking HTML tags 5102 $emoticon->text = ''; 5103 } 5104 5105 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') { 5106 $emoticons[] = $emoticon; 5107 } 5108 } 5109 return $emoticons; 5110 } 5111 5112 } 5113 5114 5115 /** 5116 * Special setting for limiting of the list of available languages. 5117 * 5118 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5119 */ 5120 class admin_setting_langlist extends admin_setting_configtext { 5121 /** 5122 * Calls parent::__construct with specific arguments 5123 */ 5124 public function __construct() { 5125 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS); 5126 } 5127 5128 /** 5129 * Validate that each language identifier exists on the site 5130 * 5131 * @param string $data 5132 * @return bool|string True if validation successful, otherwise error string 5133 */ 5134 public function validate($data) { 5135 $parentcheck = parent::validate($data); 5136 if ($parentcheck !== true) { 5137 return $parentcheck; 5138 } 5139 5140 if ($data === '') { 5141 return true; 5142 } 5143 5144 // Normalize language identifiers. 5145 $langcodes = array_map('trim', explode(',', $data)); 5146 foreach ($langcodes as $langcode) { 5147 // If the langcode contains optional alias, split it out. 5148 [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2); 5149 5150 if (!get_string_manager()->translation_exists($langcode)) { 5151 return get_string('invalidlanguagecode', 'error', $langcode); 5152 } 5153 } 5154 5155 return true; 5156 } 5157 5158 /** 5159 * Save the new setting 5160 * 5161 * @param string $data The new setting 5162 * @return bool 5163 */ 5164 public function write_setting($data) { 5165 $return = parent::write_setting($data); 5166 get_string_manager()->reset_caches(); 5167 return $return; 5168 } 5169 } 5170 5171 5172 /** 5173 * Allows to specify comma separated list of known country codes. 5174 * 5175 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually 5176 * known codes. 5177 * 5178 * @package core 5179 * @category admin 5180 * @copyright 2020 David Mudrák <david@moodle.com> 5181 * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5182 */ 5183 class admin_setting_countrycodes extends admin_setting_configtext { 5184 5185 /** 5186 * Construct the instance of the setting. 5187 * 5188 * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'. 5189 * @param lang_string|string $visiblename Language string with the field label text. 5190 * @param lang_string|string $description Language string with the field description text. 5191 * @param string $defaultsetting Default value of the setting. 5192 * @param int $size Input text field size. 5193 */ 5194 public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) { 5195 parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size); 5196 } 5197 5198 /** 5199 * Validate the setting value before storing it. 5200 * 5201 * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or 5202 * a comma separated list of such words. 5203 * 5204 * @param string $data Value inserted into the setting field. 5205 * @return bool|string True if the value is OK, error string otherwise. 5206 */ 5207 public function validate($data) { 5208 5209 $parentcheck = parent::validate($data); 5210 5211 if ($parentcheck !== true) { 5212 return $parentcheck; 5213 } 5214 5215 if ($data === '') { 5216 return true; 5217 } 5218 5219 $allcountries = get_string_manager()->get_list_of_countries(true); 5220 5221 foreach (explode(',', $data) as $code) { 5222 if (!isset($allcountries[$code])) { 5223 return get_string('invalidcountrycode', 'core_error', $code); 5224 } 5225 } 5226 5227 return true; 5228 } 5229 } 5230 5231 5232 /** 5233 * Selection of one of the recognised countries using the list 5234 * returned by {@link get_list_of_countries()}. 5235 * 5236 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5237 */ 5238 class admin_settings_country_select extends admin_setting_configselect { 5239 protected $includeall; 5240 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) { 5241 $this->includeall = $includeall; 5242 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 5243 } 5244 5245 /** 5246 * Lazy-load the available choices for the select box 5247 */ 5248 public function load_choices() { 5249 global $CFG; 5250 if (is_array($this->choices)) { 5251 return true; 5252 } 5253 $this->choices = array_merge( 5254 array('0' => get_string('choosedots')), 5255 get_string_manager()->get_list_of_countries($this->includeall)); 5256 return true; 5257 } 5258 } 5259 5260 5261 /** 5262 * admin_setting_configselect for the default number of sections in a course, 5263 * simply so we can lazy-load the choices. 5264 * 5265 * @copyright 2011 The Open University 5266 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5267 */ 5268 class admin_settings_num_course_sections extends admin_setting_configselect { 5269 public function __construct($name, $visiblename, $description, $defaultsetting) { 5270 parent::__construct($name, $visiblename, $description, $defaultsetting, array()); 5271 } 5272 5273 /** Lazy-load the available choices for the select box */ 5274 public function load_choices() { 5275 $max = get_config('moodlecourse', 'maxsections'); 5276 if (!isset($max) || !is_numeric($max)) { 5277 $max = 52; 5278 } 5279 for ($i = 0; $i <= $max; $i++) { 5280 $this->choices[$i] = "$i"; 5281 } 5282 return true; 5283 } 5284 } 5285 5286 5287 /** 5288 * Course category selection 5289 * 5290 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5291 */ 5292 class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete { 5293 /** 5294 * Calls parent::__construct with specific arguments 5295 */ 5296 public function __construct($name, $visiblename, $description, $defaultsetting = 1) { 5297 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null); 5298 } 5299 5300 /** 5301 * Load the available choices for the select box 5302 * 5303 * @return bool 5304 */ 5305 public function load_choices() { 5306 if (is_array($this->choices)) { 5307 return true; 5308 } 5309 $this->choices = core_course_category::make_categories_list('', 0, ' / '); 5310 return true; 5311 } 5312 } 5313 5314 5315 /** 5316 * Special control for selecting days to backup 5317 * 5318 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5319 */ 5320 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 { 5321 /** 5322 * Calls parent::__construct with specific arguments 5323 */ 5324 public function __construct() { 5325 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL); 5326 $this->plugin = 'backup'; 5327 } 5328 5329 /** 5330 * Load the available choices for the select box 5331 * 5332 * @return bool Always returns true 5333 */ 5334 public function load_choices() { 5335 if (is_array($this->choices)) { 5336 return true; 5337 } 5338 $this->choices = array(); 5339 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); 5340 foreach ($days as $day) { 5341 $this->choices[$day] = get_string($day, 'calendar'); 5342 } 5343 return true; 5344 } 5345 } 5346 5347 /** 5348 * Special setting for backup auto destination. 5349 * 5350 * @package core 5351 * @subpackage admin 5352 * @copyright 2014 Frédéric Massart - FMCorz.net 5353 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5354 */ 5355 class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory { 5356 5357 /** 5358 * Calls parent::__construct with specific arguments. 5359 */ 5360 public function __construct() { 5361 parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), ''); 5362 } 5363 5364 /** 5365 * Check if the directory must be set, depending on backup/backup_auto_storage. 5366 * 5367 * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise 5368 * there will be conflicts if this validation happens before the other one. 5369 * 5370 * @param string $data Form data. 5371 * @return string Empty when no errors. 5372 */ 5373 public function write_setting($data) { 5374 $storage = (int) get_config('backup', 'backup_auto_storage'); 5375 if ($storage !== 0) { 5376 if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) { 5377 // The directory must exist and be writable. 5378 return get_string('backuperrorinvaliddestination'); 5379 } 5380 } 5381 return parent::write_setting($data); 5382 } 5383 } 5384 5385 5386 /** 5387 * Special debug setting 5388 * 5389 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5390 */ 5391 class admin_setting_special_debug extends admin_setting_configselect { 5392 /** 5393 * Calls parent::__construct with specific arguments 5394 */ 5395 public function __construct() { 5396 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL); 5397 } 5398 5399 /** 5400 * Load the available choices for the select box 5401 * 5402 * @return bool 5403 */ 5404 public function load_choices() { 5405 if (is_array($this->choices)) { 5406 return true; 5407 } 5408 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'), 5409 DEBUG_MINIMAL => get_string('debugminimal', 'admin'), 5410 DEBUG_NORMAL => get_string('debugnormal', 'admin'), 5411 DEBUG_ALL => get_string('debugall', 'admin'), 5412 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin')); 5413 return true; 5414 } 5415 } 5416 5417 5418 /** 5419 * Special admin control 5420 * 5421 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5422 */ 5423 class admin_setting_special_calendar_weekend extends admin_setting { 5424 /** 5425 * Calls parent::__construct with specific arguments 5426 */ 5427 public function __construct() { 5428 $name = 'calendar_weekend'; 5429 $visiblename = get_string('calendar_weekend', 'admin'); 5430 $description = get_string('helpweekenddays', 'admin'); 5431 $default = array ('0', '6'); // Saturdays and Sundays 5432 parent::__construct($name, $visiblename, $description, $default); 5433 } 5434 5435 /** 5436 * Gets the current settings as an array 5437 * 5438 * @return mixed Null if none, else array of settings 5439 */ 5440 public function get_setting() { 5441 $result = $this->config_read($this->name); 5442 if (is_null($result)) { 5443 return NULL; 5444 } 5445 if ($result === '') { 5446 return array(); 5447 } 5448 $settings = array(); 5449 for ($i=0; $i<7; $i++) { 5450 if ($result & (1 << $i)) { 5451 $settings[] = $i; 5452 } 5453 } 5454 return $settings; 5455 } 5456 5457 /** 5458 * Save the new settings 5459 * 5460 * @param array $data Array of new settings 5461 * @return bool 5462 */ 5463 public function write_setting($data) { 5464 if (!is_array($data)) { 5465 return ''; 5466 } 5467 unset($data['xxxxx']); 5468 $result = 0; 5469 foreach($data as $index) { 5470 $result |= 1 << $index; 5471 } 5472 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin')); 5473 } 5474 5475 /** 5476 * Return XHTML to display the control 5477 * 5478 * @param array $data array of selected days 5479 * @param string $query 5480 * @return string XHTML for display (field + wrapping div(s) 5481 */ 5482 public function output_html($data, $query='') { 5483 global $OUTPUT; 5484 5485 // The order matters very much because of the implied numeric keys. 5486 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); 5487 $context = (object) [ 5488 'name' => $this->get_full_name(), 5489 'id' => $this->get_id(), 5490 'days' => array_map(function($index) use ($days, $data) { 5491 return [ 5492 'index' => $index, 5493 'label' => get_string($days[$index], 'calendar'), 5494 'checked' => in_array($index, $data) 5495 ]; 5496 }, array_keys($days)) 5497 ]; 5498 5499 $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context); 5500 5501 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query); 5502 5503 } 5504 } 5505 5506 5507 /** 5508 * Admin setting that allows a user to pick a behaviour. 5509 * 5510 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5511 */ 5512 class admin_setting_question_behaviour extends admin_setting_configselect { 5513 /** 5514 * @param string $name name of config variable 5515 * @param string $visiblename display name 5516 * @param string $description description 5517 * @param string $default default. 5518 */ 5519 public function __construct($name, $visiblename, $description, $default) { 5520 parent::__construct($name, $visiblename, $description, $default, null); 5521 } 5522 5523 /** 5524 * Load list of behaviours as choices 5525 * @return bool true => success, false => error. 5526 */ 5527 public function load_choices() { 5528 global $CFG; 5529 require_once($CFG->dirroot . '/question/engine/lib.php'); 5530 $this->choices = question_engine::get_behaviour_options(''); 5531 return true; 5532 } 5533 } 5534 5535 5536 /** 5537 * Admin setting that allows a user to pick appropriate roles for something. 5538 * 5539 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5540 */ 5541 class admin_setting_pickroles extends admin_setting_configmulticheckbox { 5542 /** @var array Array of capabilities which identify roles */ 5543 private $types; 5544 5545 /** 5546 * @param string $name Name of config variable 5547 * @param string $visiblename Display name 5548 * @param string $description Description 5549 * @param array $types Array of archetypes which identify 5550 * roles that will be enabled by default. 5551 */ 5552 public function __construct($name, $visiblename, $description, $types) { 5553 parent::__construct($name, $visiblename, $description, NULL, NULL); 5554 $this->types = $types; 5555 } 5556 5557 /** 5558 * Load roles as choices 5559 * 5560 * @return bool true=>success, false=>error 5561 */ 5562 public function load_choices() { 5563 global $CFG, $DB; 5564 if (during_initial_install()) { 5565 return false; 5566 } 5567 if (is_array($this->choices)) { 5568 return true; 5569 } 5570 if ($roles = get_all_roles()) { 5571 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true); 5572 return true; 5573 } else { 5574 return false; 5575 } 5576 } 5577 5578 /** 5579 * Return the default setting for this control 5580 * 5581 * @return array Array of default settings 5582 */ 5583 public function get_defaultsetting() { 5584 global $CFG; 5585 5586 if (during_initial_install()) { 5587 return null; 5588 } 5589 $result = array(); 5590 foreach($this->types as $archetype) { 5591 if ($caproles = get_archetype_roles($archetype)) { 5592 foreach ($caproles as $caprole) { 5593 $result[$caprole->id] = 1; 5594 } 5595 } 5596 } 5597 return $result; 5598 } 5599 } 5600 5601 5602 /** 5603 * Admin setting that is a list of installed filter plugins. 5604 * 5605 * @copyright 2015 The Open University 5606 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5607 */ 5608 class admin_setting_pickfilters extends admin_setting_configmulticheckbox { 5609 5610 /** 5611 * Constructor 5612 * 5613 * @param string $name unique ascii name, either 'mysetting' for settings 5614 * that in config, or 'myplugin/mysetting' for ones in config_plugins. 5615 * @param string $visiblename localised name 5616 * @param string $description localised long description 5617 * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1) 5618 */ 5619 public function __construct($name, $visiblename, $description, $default) { 5620 if (empty($default)) { 5621 $default = array(); 5622 } 5623 $this->load_choices(); 5624 foreach ($default as $plugin) { 5625 if (!isset($this->choices[$plugin])) { 5626 unset($default[$plugin]); 5627 } 5628 } 5629 parent::__construct($name, $visiblename, $description, $default, null); 5630 } 5631 5632 public function load_choices() { 5633 if (is_array($this->choices)) { 5634 return true; 5635 } 5636 $this->choices = array(); 5637 5638 foreach (core_component::get_plugin_list('filter') as $plugin => $unused) { 5639 $this->choices[$plugin] = filter_get_name($plugin); 5640 } 5641 return true; 5642 } 5643 } 5644 5645 5646 /** 5647 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting. 5648 * 5649 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5650 */ 5651 class admin_setting_configtext_with_advanced extends admin_setting_configtext { 5652 /** 5653 * Constructor 5654 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 5655 * @param string $visiblename localised 5656 * @param string $description long localised info 5657 * @param array $defaultsetting ('value'=>string, '__construct'=>bool) 5658 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex 5659 * @param int $size default field size 5660 */ 5661 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) { 5662 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size); 5663 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 5664 } 5665 } 5666 5667 5668 /** 5669 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting. 5670 * 5671 * @copyright 2009 Petr Skoda (http://skodak.org) 5672 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5673 */ 5674 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox { 5675 5676 /** 5677 * Constructor 5678 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 5679 * @param string $visiblename localised 5680 * @param string $description long localised info 5681 * @param array $defaultsetting ('value'=>string, 'adv'=>bool) 5682 * @param string $yes value used when checked 5683 * @param string $no value used when not checked 5684 */ 5685 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') { 5686 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no); 5687 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 5688 } 5689 5690 } 5691 5692 5693 /** 5694 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting. 5695 * 5696 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv 5697 * 5698 * @copyright 2010 Sam Hemelryk 5699 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5700 */ 5701 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox { 5702 /** 5703 * Constructor 5704 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. 5705 * @param string $visiblename localised 5706 * @param string $description long localised info 5707 * @param array $defaultsetting ('value'=>string, 'locked'=>bool) 5708 * @param string $yes value used when checked 5709 * @param string $no value used when not checked 5710 */ 5711 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') { 5712 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no); 5713 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked'])); 5714 } 5715 5716 } 5717 5718 /** 5719 * Autocomplete as you type form element. 5720 * 5721 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5722 */ 5723 class admin_setting_configselect_autocomplete extends admin_setting_configselect { 5724 /** @var boolean $tags Should we allow typing new entries to the field? */ 5725 protected $tags = false; 5726 /** @var string $ajax Name of an AMD module to send/process ajax requests. */ 5727 protected $ajax = ''; 5728 /** @var string $placeholder Placeholder text for an empty list. */ 5729 protected $placeholder = ''; 5730 /** @var bool $casesensitive Whether the search has to be case-sensitive. */ 5731 protected $casesensitive = false; 5732 /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */ 5733 protected $showsuggestions = true; 5734 /** @var string $noselectionstring String that is shown when there are no selections. */ 5735 protected $noselectionstring = ''; 5736 5737 /** 5738 * Returns XHTML select field and wrapping div(s) 5739 * 5740 * @see output_select_html() 5741 * 5742 * @param string $data the option to show as selected 5743 * @param string $query 5744 * @return string XHTML field and wrapping div 5745 */ 5746 public function output_html($data, $query='') { 5747 global $PAGE; 5748 5749 $html = parent::output_html($data, $query); 5750 5751 if ($html === '') { 5752 return $html; 5753 } 5754 5755 $this->placeholder = get_string('search'); 5756 5757 $params = array('#' . $this->get_id(), $this->tags, $this->ajax, 5758 $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring); 5759 5760 // Load autocomplete wrapper for select2 library. 5761 $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params); 5762 5763 return $html; 5764 } 5765 } 5766 5767 /** 5768 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting. 5769 * 5770 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5771 */ 5772 class admin_setting_configselect_with_advanced extends admin_setting_configselect { 5773 /** 5774 * Calls parent::__construct with specific arguments 5775 */ 5776 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 5777 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices); 5778 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv'])); 5779 } 5780 5781 } 5782 5783 /** 5784 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting. 5785 * 5786 * @copyright 2017 Marina Glancy 5787 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5788 */ 5789 class admin_setting_configselect_with_lock extends admin_setting_configselect { 5790 /** 5791 * Constructor 5792 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 5793 * or 'myplugin/mysetting' for ones in config_plugins. 5794 * @param string $visiblename localised 5795 * @param string $description long localised info 5796 * @param array $defaultsetting ('value'=>string, 'locked'=>bool) 5797 * @param array $choices array of $value=>$label for each selection 5798 */ 5799 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 5800 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices); 5801 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked'])); 5802 } 5803 } 5804 5805 5806 /** 5807 * Graded roles in gradebook 5808 * 5809 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5810 */ 5811 class admin_setting_special_gradebookroles extends admin_setting_pickroles { 5812 /** 5813 * Calls parent::__construct with specific arguments 5814 */ 5815 public function __construct() { 5816 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'), 5817 get_string('configgradebookroles', 'admin'), 5818 array('student')); 5819 } 5820 } 5821 5822 5823 /** 5824 * 5825 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5826 */ 5827 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox { 5828 /** 5829 * Saves the new settings passed in $data 5830 * 5831 * @param string $data 5832 * @return mixed string or Array 5833 */ 5834 public function write_setting($data) { 5835 global $CFG, $DB; 5836 5837 $oldvalue = $this->config_read($this->name); 5838 $return = parent::write_setting($data); 5839 $newvalue = $this->config_read($this->name); 5840 5841 if ($oldvalue !== $newvalue) { 5842 // force full regrading 5843 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0)); 5844 } 5845 5846 return $return; 5847 } 5848 } 5849 5850 5851 /** 5852 * Which roles to show on course description page 5853 * 5854 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5855 */ 5856 class admin_setting_special_coursecontact extends admin_setting_pickroles { 5857 /** 5858 * Calls parent::__construct with specific arguments 5859 */ 5860 public function __construct() { 5861 parent::__construct('coursecontact', get_string('coursecontact', 'admin'), 5862 get_string('coursecontact_desc', 'admin'), 5863 array('editingteacher')); 5864 $this->set_updatedcallback(function (){ 5865 cache::make('core', 'coursecontacts')->purge(); 5866 }); 5867 } 5868 } 5869 5870 5871 /** 5872 * 5873 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5874 */ 5875 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox { 5876 /** 5877 * Calls parent::__construct with specific arguments 5878 */ 5879 public function __construct() { 5880 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'), 5881 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0'); 5882 } 5883 5884 /** 5885 * Old syntax of class constructor. Deprecated in PHP7. 5886 * 5887 * @deprecated since Moodle 3.1 5888 */ 5889 public function admin_setting_special_gradelimiting() { 5890 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 5891 self::__construct(); 5892 } 5893 5894 /** 5895 * Force site regrading 5896 */ 5897 function regrade_all() { 5898 global $CFG; 5899 require_once("$CFG->libdir/gradelib.php"); 5900 grade_force_site_regrading(); 5901 } 5902 5903 /** 5904 * Saves the new settings 5905 * 5906 * @param mixed $data 5907 * @return string empty string or error message 5908 */ 5909 function write_setting($data) { 5910 $previous = $this->get_setting(); 5911 5912 if ($previous === null) { 5913 if ($data) { 5914 $this->regrade_all(); 5915 } 5916 } else { 5917 if ($data != $previous) { 5918 $this->regrade_all(); 5919 } 5920 } 5921 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 5922 } 5923 5924 } 5925 5926 /** 5927 * Special setting for $CFG->grade_minmaxtouse. 5928 * 5929 * @package core 5930 * @copyright 2015 Frédéric Massart - FMCorz.net 5931 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5932 */ 5933 class admin_setting_special_grademinmaxtouse extends admin_setting_configselect { 5934 5935 /** 5936 * Constructor. 5937 */ 5938 public function __construct() { 5939 parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'), 5940 new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM, 5941 array( 5942 GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'), 5943 GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades') 5944 ) 5945 ); 5946 } 5947 5948 /** 5949 * Saves the new setting. 5950 * 5951 * @param mixed $data 5952 * @return string empty string or error message 5953 */ 5954 function write_setting($data) { 5955 global $CFG; 5956 5957 $previous = $this->get_setting(); 5958 $result = parent::write_setting($data); 5959 5960 // If saved and the value has changed. 5961 if (empty($result) && $previous != $data) { 5962 require_once($CFG->libdir . '/gradelib.php'); 5963 grade_force_site_regrading(); 5964 } 5965 5966 return $result; 5967 } 5968 5969 } 5970 5971 5972 /** 5973 * Primary grade export plugin - has state tracking. 5974 * 5975 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 5976 */ 5977 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox { 5978 /** 5979 * Calls parent::__construct with specific arguments 5980 */ 5981 public function __construct() { 5982 parent::__construct('gradeexport', get_string('gradeexport', 'admin'), 5983 get_string('configgradeexport', 'admin'), array(), NULL); 5984 } 5985 5986 /** 5987 * Load the available choices for the multicheckbox 5988 * 5989 * @return bool always returns true 5990 */ 5991 public function load_choices() { 5992 if (is_array($this->choices)) { 5993 return true; 5994 } 5995 $this->choices = array(); 5996 5997 if ($plugins = core_component::get_plugin_list('gradeexport')) { 5998 foreach($plugins as $plugin => $unused) { 5999 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin); 6000 } 6001 } 6002 return true; 6003 } 6004 } 6005 6006 6007 /** 6008 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax. 6009 * 6010 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6011 */ 6012 class admin_setting_special_gradepointdefault extends admin_setting_configtext { 6013 /** 6014 * Config gradepointmax constructor 6015 * 6016 * @param string $name Overidden by "gradepointmax" 6017 * @param string $visiblename Overridden by "gradepointmax" language string. 6018 * @param string $description Overridden by "gradepointmax_help" language string. 6019 * @param string $defaultsetting Not used, overridden by 100. 6020 * @param mixed $paramtype Overridden by PARAM_INT. 6021 * @param int $size Overridden by 5. 6022 */ 6023 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) { 6024 $name = 'gradepointdefault'; 6025 $visiblename = get_string('gradepointdefault', 'grades'); 6026 $description = get_string('gradepointdefault_help', 'grades'); 6027 $defaultsetting = 100; 6028 $paramtype = PARAM_INT; 6029 $size = 5; 6030 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); 6031 } 6032 6033 /** 6034 * Validate data before storage 6035 * @param string $data The submitted data 6036 * @return bool|string true if ok, string if error found 6037 */ 6038 public function validate($data) { 6039 global $CFG; 6040 if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) { 6041 return true; 6042 } else { 6043 return get_string('gradepointdefault_validateerror', 'grades'); 6044 } 6045 } 6046 } 6047 6048 6049 /** 6050 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000. 6051 * 6052 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6053 */ 6054 class admin_setting_special_gradepointmax extends admin_setting_configtext { 6055 6056 /** 6057 * Config gradepointmax constructor 6058 * 6059 * @param string $name Overidden by "gradepointmax" 6060 * @param string $visiblename Overridden by "gradepointmax" language string. 6061 * @param string $description Overridden by "gradepointmax_help" language string. 6062 * @param string $defaultsetting Not used, overridden by 100. 6063 * @param mixed $paramtype Overridden by PARAM_INT. 6064 * @param int $size Overridden by 5. 6065 */ 6066 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) { 6067 $name = 'gradepointmax'; 6068 $visiblename = get_string('gradepointmax', 'grades'); 6069 $description = get_string('gradepointmax_help', 'grades'); 6070 $defaultsetting = 100; 6071 $paramtype = PARAM_INT; 6072 $size = 5; 6073 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size); 6074 } 6075 6076 /** 6077 * Save the selected setting 6078 * 6079 * @param string $data The selected site 6080 * @return string empty string or error message 6081 */ 6082 public function write_setting($data) { 6083 if ($data === '') { 6084 $data = (int)$this->defaultsetting; 6085 } else { 6086 $data = $data; 6087 } 6088 return parent::write_setting($data); 6089 } 6090 6091 /** 6092 * Validate data before storage 6093 * @param string $data The submitted data 6094 * @return bool|string true if ok, string if error found 6095 */ 6096 public function validate($data) { 6097 if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) { 6098 return true; 6099 } else { 6100 return get_string('gradepointmax_validateerror', 'grades'); 6101 } 6102 } 6103 6104 /** 6105 * Return an XHTML string for the setting 6106 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx 6107 * @param string $query search query to be highlighted 6108 * @return string XHTML to display control 6109 */ 6110 public function output_html($data, $query = '') { 6111 global $OUTPUT; 6112 6113 $default = $this->get_defaultsetting(); 6114 $context = (object) [ 6115 'size' => $this->size, 6116 'id' => $this->get_id(), 6117 'name' => $this->get_full_name(), 6118 'value' => $data, 6119 'attributes' => [ 6120 'maxlength' => 5 6121 ], 6122 'forceltr' => $this->get_force_ltr() 6123 ]; 6124 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context); 6125 6126 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 6127 } 6128 } 6129 6130 6131 /** 6132 * Grade category settings 6133 * 6134 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6135 */ 6136 class admin_setting_gradecat_combo extends admin_setting { 6137 /** @var array Array of choices */ 6138 public $choices; 6139 6140 /** 6141 * Sets choices and calls parent::__construct with passed arguments 6142 * @param string $name 6143 * @param string $visiblename 6144 * @param string $description 6145 * @param mixed $defaultsetting string or array depending on implementation 6146 * @param array $choices An array of choices for the control 6147 */ 6148 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) { 6149 $this->choices = $choices; 6150 parent::__construct($name, $visiblename, $description, $defaultsetting); 6151 } 6152 6153 /** 6154 * Return the current setting(s) array 6155 * 6156 * @return array Array of value=>xx, forced=>xx, adv=>xx 6157 */ 6158 public function get_setting() { 6159 global $CFG; 6160 6161 $value = $this->config_read($this->name); 6162 $flag = $this->config_read($this->name.'_flag'); 6163 6164 if (is_null($value) or is_null($flag)) { 6165 return NULL; 6166 } 6167 6168 $flag = (int)$flag; 6169 $forced = (boolean)(1 & $flag); // first bit 6170 $adv = (boolean)(2 & $flag); // second bit 6171 6172 return array('value' => $value, 'forced' => $forced, 'adv' => $adv); 6173 } 6174 6175 /** 6176 * Save the new settings passed in $data 6177 * 6178 * @todo Add vartype handling to ensure $data is array 6179 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx 6180 * @return string empty or error message 6181 */ 6182 public function write_setting($data) { 6183 global $CFG; 6184 6185 $value = $data['value']; 6186 $forced = empty($data['forced']) ? 0 : 1; 6187 $adv = empty($data['adv']) ? 0 : 2; 6188 $flag = ($forced | $adv); //bitwise or 6189 6190 if (!in_array($value, array_keys($this->choices))) { 6191 return 'Error setting '; 6192 } 6193 6194 $oldvalue = $this->config_read($this->name); 6195 $oldflag = (int)$this->config_read($this->name.'_flag'); 6196 $oldforced = (1 & $oldflag); // first bit 6197 6198 $result1 = $this->config_write($this->name, $value); 6199 $result2 = $this->config_write($this->name.'_flag', $flag); 6200 6201 // force regrade if needed 6202 if ($oldforced != $forced or ($forced and $value != $oldvalue)) { 6203 require_once($CFG->libdir.'/gradelib.php'); 6204 grade_category::updated_forced_settings(); 6205 } 6206 6207 if ($result1 and $result2) { 6208 return ''; 6209 } else { 6210 return get_string('errorsetting', 'admin'); 6211 } 6212 } 6213 6214 /** 6215 * Return XHTML to display the field and wrapping div 6216 * 6217 * @todo Add vartype handling to ensure $data is array 6218 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx 6219 * @param string $query 6220 * @return string XHTML to display control 6221 */ 6222 public function output_html($data, $query='') { 6223 global $OUTPUT; 6224 6225 $value = $data['value']; 6226 6227 $default = $this->get_defaultsetting(); 6228 if (!is_null($default)) { 6229 $defaultinfo = array(); 6230 if (isset($this->choices[$default['value']])) { 6231 $defaultinfo[] = $this->choices[$default['value']]; 6232 } 6233 if (!empty($default['forced'])) { 6234 $defaultinfo[] = get_string('force'); 6235 } 6236 if (!empty($default['adv'])) { 6237 $defaultinfo[] = get_string('advanced'); 6238 } 6239 $defaultinfo = implode(', ', $defaultinfo); 6240 6241 } else { 6242 $defaultinfo = NULL; 6243 } 6244 6245 $options = $this->choices; 6246 $context = (object) [ 6247 'id' => $this->get_id(), 6248 'name' => $this->get_full_name(), 6249 'forced' => !empty($data['forced']), 6250 'advanced' => !empty($data['adv']), 6251 'options' => array_map(function($option) use ($options, $value) { 6252 return [ 6253 'value' => $option, 6254 'name' => $options[$option], 6255 'selected' => $option == $value 6256 ]; 6257 }, array_keys($options)), 6258 ]; 6259 6260 $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context); 6261 6262 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query); 6263 } 6264 } 6265 6266 6267 /** 6268 * Selection of grade report in user profiles 6269 * 6270 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6271 */ 6272 class admin_setting_grade_profilereport extends admin_setting_configselect { 6273 /** 6274 * Calls parent::__construct with specific arguments 6275 */ 6276 public function __construct() { 6277 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null); 6278 } 6279 6280 /** 6281 * Loads an array of choices for the configselect control 6282 * 6283 * @return bool always return true 6284 */ 6285 public function load_choices() { 6286 if (is_array($this->choices)) { 6287 return true; 6288 } 6289 $this->choices = array(); 6290 6291 global $CFG; 6292 require_once($CFG->libdir.'/gradelib.php'); 6293 6294 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) { 6295 if (file_exists($plugindir.'/lib.php')) { 6296 require_once($plugindir.'/lib.php'); 6297 $functionname = 'grade_report_'.$plugin.'_profilereport'; 6298 if (function_exists($functionname)) { 6299 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin); 6300 } 6301 } 6302 } 6303 return true; 6304 } 6305 } 6306 6307 /** 6308 * Provides a selection of grade reports to be used for "grades". 6309 * 6310 * @copyright 2015 Adrian Greeve <adrian@moodle.com> 6311 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6312 */ 6313 class admin_setting_my_grades_report extends admin_setting_configselect { 6314 6315 /** 6316 * Calls parent::__construct with specific arguments. 6317 */ 6318 public function __construct() { 6319 parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'), 6320 new lang_string('mygrades_desc', 'grades'), 'overview', null); 6321 } 6322 6323 /** 6324 * Loads an array of choices for the configselect control. 6325 * 6326 * @return bool always returns true. 6327 */ 6328 public function load_choices() { 6329 global $CFG; // Remove this line and behold the horror of behat test failures! 6330 $this->choices = array(); 6331 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) { 6332 if (file_exists($plugindir . '/lib.php')) { 6333 require_once($plugindir . '/lib.php'); 6334 // Check to see if the class exists. Check the correct plugin convention first. 6335 if (class_exists('gradereport_' . $plugin)) { 6336 $classname = 'gradereport_' . $plugin; 6337 } else if (class_exists('grade_report_' . $plugin)) { 6338 // We are using the old plugin naming convention. 6339 $classname = 'grade_report_' . $plugin; 6340 } else { 6341 continue; 6342 } 6343 if ($classname::supports_mygrades()) { 6344 $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin); 6345 } 6346 } 6347 } 6348 // Add an option to specify an external url. 6349 $this->choices['external'] = get_string('externalurl', 'grades'); 6350 return true; 6351 } 6352 } 6353 6354 /** 6355 * Special class for register auth selection 6356 * 6357 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6358 */ 6359 class admin_setting_special_registerauth extends admin_setting_configselect { 6360 /** 6361 * Calls parent::__construct with specific arguments 6362 */ 6363 public function __construct() { 6364 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null); 6365 } 6366 6367 /** 6368 * Returns the default option 6369 * 6370 * @return string empty or default option 6371 */ 6372 public function get_defaultsetting() { 6373 $this->load_choices(); 6374 $defaultsetting = parent::get_defaultsetting(); 6375 if (array_key_exists($defaultsetting, $this->choices)) { 6376 return $defaultsetting; 6377 } else { 6378 return ''; 6379 } 6380 } 6381 6382 /** 6383 * Loads the possible choices for the array 6384 * 6385 * @return bool always returns true 6386 */ 6387 public function load_choices() { 6388 global $CFG; 6389 6390 if (is_array($this->choices)) { 6391 return true; 6392 } 6393 $this->choices = array(); 6394 $this->choices[''] = get_string('disable'); 6395 6396 $authsenabled = get_enabled_auth_plugins(); 6397 6398 foreach ($authsenabled as $auth) { 6399 $authplugin = get_auth_plugin($auth); 6400 if (!$authplugin->can_signup()) { 6401 continue; 6402 } 6403 // Get the auth title (from core or own auth lang files) 6404 $authtitle = $authplugin->get_title(); 6405 $this->choices[$auth] = $authtitle; 6406 } 6407 return true; 6408 } 6409 } 6410 6411 6412 /** 6413 * General plugins manager 6414 */ 6415 class admin_page_pluginsoverview extends admin_externalpage { 6416 6417 /** 6418 * Sets basic information about the external page 6419 */ 6420 public function __construct() { 6421 global $CFG; 6422 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'), 6423 "$CFG->wwwroot/$CFG->admin/plugins.php"); 6424 } 6425 } 6426 6427 /** 6428 * Module manage page 6429 * 6430 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6431 */ 6432 class admin_page_managemods extends admin_externalpage { 6433 /** 6434 * Calls parent::__construct with specific arguments 6435 */ 6436 public function __construct() { 6437 global $CFG; 6438 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php"); 6439 } 6440 6441 /** 6442 * Try to find the specified module 6443 * 6444 * @param string $query The module to search for 6445 * @return array 6446 */ 6447 public function search($query) { 6448 global $CFG, $DB; 6449 if ($result = parent::search($query)) { 6450 return $result; 6451 } 6452 6453 $found = false; 6454 if ($modules = $DB->get_records('modules')) { 6455 foreach ($modules as $module) { 6456 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) { 6457 continue; 6458 } 6459 if (strpos($module->name, $query) !== false) { 6460 $found = true; 6461 break; 6462 } 6463 $strmodulename = get_string('modulename', $module->name); 6464 if (strpos(core_text::strtolower($strmodulename), $query) !== false) { 6465 $found = true; 6466 break; 6467 } 6468 } 6469 } 6470 if ($found) { 6471 $result = new stdClass(); 6472 $result->page = $this; 6473 $result->settings = array(); 6474 return array($this->name => $result); 6475 } else { 6476 return array(); 6477 } 6478 } 6479 } 6480 6481 6482 /** 6483 * Special class for enrol plugins management. 6484 * 6485 * @copyright 2010 Petr Skoda {@link http://skodak.org} 6486 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6487 */ 6488 class admin_setting_manageenrols extends admin_setting { 6489 /** 6490 * Calls parent::__construct with specific arguments 6491 */ 6492 public function __construct() { 6493 $this->nosave = true; 6494 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', ''); 6495 } 6496 6497 /** 6498 * Always returns true, does nothing 6499 * 6500 * @return true 6501 */ 6502 public function get_setting() { 6503 return true; 6504 } 6505 6506 /** 6507 * Always returns true, does nothing 6508 * 6509 * @return true 6510 */ 6511 public function get_defaultsetting() { 6512 return true; 6513 } 6514 6515 /** 6516 * Always returns '', does not write anything 6517 * 6518 * @return string Always returns '' 6519 */ 6520 public function write_setting($data) { 6521 // do not write any setting 6522 return ''; 6523 } 6524 6525 /** 6526 * Checks if $query is one of the available enrol plugins 6527 * 6528 * @param string $query The string to search for 6529 * @return bool Returns true if found, false if not 6530 */ 6531 public function is_related($query) { 6532 if (parent::is_related($query)) { 6533 return true; 6534 } 6535 6536 $query = core_text::strtolower($query); 6537 $enrols = enrol_get_plugins(false); 6538 foreach ($enrols as $name=>$enrol) { 6539 $localised = get_string('pluginname', 'enrol_'.$name); 6540 if (strpos(core_text::strtolower($name), $query) !== false) { 6541 return true; 6542 } 6543 if (strpos(core_text::strtolower($localised), $query) !== false) { 6544 return true; 6545 } 6546 } 6547 return false; 6548 } 6549 6550 /** 6551 * Builds the XHTML to display the control 6552 * 6553 * @param string $data Unused 6554 * @param string $query 6555 * @return string 6556 */ 6557 public function output_html($data, $query='') { 6558 global $CFG, $OUTPUT, $DB, $PAGE; 6559 6560 // Display strings. 6561 $strup = get_string('up'); 6562 $strdown = get_string('down'); 6563 $strsettings = get_string('settings'); 6564 $strenable = get_string('enable'); 6565 $strdisable = get_string('disable'); 6566 $struninstall = get_string('uninstallplugin', 'core_admin'); 6567 $strusage = get_string('enrolusage', 'enrol'); 6568 $strversion = get_string('version'); 6569 $strtest = get_string('testsettings', 'core_enrol'); 6570 6571 $pluginmanager = core_plugin_manager::instance(); 6572 6573 $enrols_available = enrol_get_plugins(false); 6574 $active_enrols = enrol_get_plugins(true); 6575 6576 $allenrols = array(); 6577 foreach ($active_enrols as $key=>$enrol) { 6578 $allenrols[$key] = true; 6579 } 6580 foreach ($enrols_available as $key=>$enrol) { 6581 $allenrols[$key] = true; 6582 } 6583 // Now find all borked plugins and at least allow then to uninstall. 6584 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}"); 6585 foreach ($condidates as $candidate) { 6586 if (empty($allenrols[$candidate])) { 6587 $allenrols[$candidate] = true; 6588 } 6589 } 6590 6591 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true); 6592 $return .= $OUTPUT->box_start('generalbox enrolsui'); 6593 6594 $table = new html_table(); 6595 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall); 6596 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 6597 $table->id = 'courseenrolmentplugins'; 6598 $table->attributes['class'] = 'admintable generaltable'; 6599 $table->data = array(); 6600 6601 // Iterate through enrol plugins and add to the display table. 6602 $updowncount = 1; 6603 $enrolcount = count($active_enrols); 6604 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey())); 6605 $printed = array(); 6606 foreach($allenrols as $enrol => $unused) { 6607 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol); 6608 $version = get_config('enrol_'.$enrol, 'version'); 6609 if ($version === false) { 6610 $version = ''; 6611 } 6612 6613 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) { 6614 $name = get_string('pluginname', 'enrol_'.$enrol); 6615 } else { 6616 $name = $enrol; 6617 } 6618 // Usage. 6619 $ci = $DB->count_records('enrol', array('enrol'=>$enrol)); 6620 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol)); 6621 $usage = "$ci / $cp"; 6622 6623 // Hide/show links. 6624 $class = ''; 6625 if (isset($active_enrols[$enrol])) { 6626 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol)); 6627 $hideshow = "<a href=\"$aurl\">"; 6628 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>'; 6629 $enabled = true; 6630 $displayname = $name; 6631 } else if (isset($enrols_available[$enrol])) { 6632 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol)); 6633 $hideshow = "<a href=\"$aurl\">"; 6634 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>'; 6635 $enabled = false; 6636 $displayname = $name; 6637 $class = 'dimmed_text'; 6638 } else { 6639 $hideshow = ''; 6640 $enabled = false; 6641 $displayname = '<span class="notifyproblem">'.$name.'</span>'; 6642 } 6643 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) { 6644 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon')); 6645 } else { 6646 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon')); 6647 } 6648 6649 // Up/down link (only if enrol is enabled). 6650 $updown = ''; 6651 if ($enabled) { 6652 if ($updowncount > 1) { 6653 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol)); 6654 $updown .= "<a href=\"$aurl\">"; 6655 $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a> '; 6656 } else { 6657 $updown .= $OUTPUT->spacer() . ' '; 6658 } 6659 if ($updowncount < $enrolcount) { 6660 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol)); 6661 $updown .= "<a href=\"$aurl\">"; 6662 $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a> '; 6663 } else { 6664 $updown .= $OUTPUT->spacer() . ' '; 6665 } 6666 ++$updowncount; 6667 } 6668 6669 // Add settings link. 6670 if (!$version) { 6671 $settings = ''; 6672 } else if ($surl = $plugininfo->get_settings_url()) { 6673 $settings = html_writer::link($surl, $strsettings); 6674 } else { 6675 $settings = ''; 6676 } 6677 6678 // Add uninstall info. 6679 $uninstall = ''; 6680 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) { 6681 $uninstall = html_writer::link($uninstallurl, $struninstall); 6682 } 6683 6684 $test = ''; 6685 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) { 6686 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey())); 6687 $test = html_writer::link($testsettingsurl, $strtest); 6688 } 6689 6690 // Add a row to the table. 6691 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall)); 6692 if ($class) { 6693 $row->attributes['class'] = $class; 6694 } 6695 $table->data[] = $row; 6696 6697 $printed[$enrol] = true; 6698 } 6699 6700 $return .= html_writer::table($table); 6701 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin'); 6702 $return .= $OUTPUT->box_end(); 6703 return highlight($query, $return); 6704 } 6705 } 6706 6707 6708 /** 6709 * Blocks manage page 6710 * 6711 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6712 */ 6713 class admin_page_manageblocks extends admin_externalpage { 6714 /** 6715 * Calls parent::__construct with specific arguments 6716 */ 6717 public function __construct() { 6718 global $CFG; 6719 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php"); 6720 } 6721 6722 /** 6723 * Search for a specific block 6724 * 6725 * @param string $query The string to search for 6726 * @return array 6727 */ 6728 public function search($query) { 6729 global $CFG, $DB; 6730 if ($result = parent::search($query)) { 6731 return $result; 6732 } 6733 6734 $found = false; 6735 if ($blocks = $DB->get_records('block')) { 6736 foreach ($blocks as $block) { 6737 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) { 6738 continue; 6739 } 6740 if (strpos($block->name, $query) !== false) { 6741 $found = true; 6742 break; 6743 } 6744 $strblockname = get_string('pluginname', 'block_'.$block->name); 6745 if (strpos(core_text::strtolower($strblockname), $query) !== false) { 6746 $found = true; 6747 break; 6748 } 6749 } 6750 } 6751 if ($found) { 6752 $result = new stdClass(); 6753 $result->page = $this; 6754 $result->settings = array(); 6755 return array($this->name => $result); 6756 } else { 6757 return array(); 6758 } 6759 } 6760 } 6761 6762 /** 6763 * Message outputs configuration 6764 * 6765 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6766 */ 6767 class admin_page_managemessageoutputs extends admin_externalpage { 6768 /** 6769 * Calls parent::__construct with specific arguments 6770 */ 6771 public function __construct() { 6772 global $CFG; 6773 parent::__construct('managemessageoutputs', 6774 get_string('defaultmessageoutputs', 'message'), 6775 new moodle_url('/admin/message.php') 6776 ); 6777 } 6778 6779 /** 6780 * Search for a specific message processor 6781 * 6782 * @param string $query The string to search for 6783 * @return array 6784 */ 6785 public function search($query) { 6786 global $CFG, $DB; 6787 if ($result = parent::search($query)) { 6788 return $result; 6789 } 6790 6791 $found = false; 6792 if ($processors = get_message_processors()) { 6793 foreach ($processors as $processor) { 6794 if (!$processor->available) { 6795 continue; 6796 } 6797 if (strpos($processor->name, $query) !== false) { 6798 $found = true; 6799 break; 6800 } 6801 $strprocessorname = get_string('pluginname', 'message_'.$processor->name); 6802 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) { 6803 $found = true; 6804 break; 6805 } 6806 } 6807 } 6808 if ($found) { 6809 $result = new stdClass(); 6810 $result->page = $this; 6811 $result->settings = array(); 6812 return array($this->name => $result); 6813 } else { 6814 return array(); 6815 } 6816 } 6817 } 6818 6819 /** 6820 * Manage question behaviours page 6821 * 6822 * @copyright 2011 The Open University 6823 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6824 */ 6825 class admin_page_manageqbehaviours extends admin_externalpage { 6826 /** 6827 * Constructor 6828 */ 6829 public function __construct() { 6830 global $CFG; 6831 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'), 6832 new moodle_url('/admin/qbehaviours.php')); 6833 } 6834 6835 /** 6836 * Search question behaviours for the specified string 6837 * 6838 * @param string $query The string to search for in question behaviours 6839 * @return array 6840 */ 6841 public function search($query) { 6842 global $CFG; 6843 if ($result = parent::search($query)) { 6844 return $result; 6845 } 6846 6847 $found = false; 6848 require_once($CFG->dirroot . '/question/engine/lib.php'); 6849 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) { 6850 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)), 6851 $query) !== false) { 6852 $found = true; 6853 break; 6854 } 6855 } 6856 if ($found) { 6857 $result = new stdClass(); 6858 $result->page = $this; 6859 $result->settings = array(); 6860 return array($this->name => $result); 6861 } else { 6862 return array(); 6863 } 6864 } 6865 } 6866 6867 6868 /** 6869 * Question type manage page 6870 * 6871 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6872 */ 6873 class admin_page_manageqtypes extends admin_externalpage { 6874 /** 6875 * Calls parent::__construct with specific arguments 6876 */ 6877 public function __construct() { 6878 global $CFG; 6879 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'), 6880 new moodle_url('/admin/qtypes.php')); 6881 } 6882 6883 /** 6884 * Search question types for the specified string 6885 * 6886 * @param string $query The string to search for in question types 6887 * @return array 6888 */ 6889 public function search($query) { 6890 global $CFG; 6891 if ($result = parent::search($query)) { 6892 return $result; 6893 } 6894 6895 $found = false; 6896 require_once($CFG->dirroot . '/question/engine/bank.php'); 6897 foreach (question_bank::get_all_qtypes() as $qtype) { 6898 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) { 6899 $found = true; 6900 break; 6901 } 6902 } 6903 if ($found) { 6904 $result = new stdClass(); 6905 $result->page = $this; 6906 $result->settings = array(); 6907 return array($this->name => $result); 6908 } else { 6909 return array(); 6910 } 6911 } 6912 } 6913 6914 6915 class admin_page_manageportfolios extends admin_externalpage { 6916 /** 6917 * Calls parent::__construct with specific arguments 6918 */ 6919 public function __construct() { 6920 global $CFG; 6921 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'), 6922 "$CFG->wwwroot/$CFG->admin/portfolio.php"); 6923 } 6924 6925 /** 6926 * Searches page for the specified string. 6927 * @param string $query The string to search for 6928 * @return bool True if it is found on this page 6929 */ 6930 public function search($query) { 6931 global $CFG; 6932 if ($result = parent::search($query)) { 6933 return $result; 6934 } 6935 6936 $found = false; 6937 $portfolios = core_component::get_plugin_list('portfolio'); 6938 foreach ($portfolios as $p => $dir) { 6939 if (strpos($p, $query) !== false) { 6940 $found = true; 6941 break; 6942 } 6943 } 6944 if (!$found) { 6945 foreach (portfolio_instances(false, false) as $instance) { 6946 $title = $instance->get('name'); 6947 if (strpos(core_text::strtolower($title), $query) !== false) { 6948 $found = true; 6949 break; 6950 } 6951 } 6952 } 6953 6954 if ($found) { 6955 $result = new stdClass(); 6956 $result->page = $this; 6957 $result->settings = array(); 6958 return array($this->name => $result); 6959 } else { 6960 return array(); 6961 } 6962 } 6963 } 6964 6965 6966 class admin_page_managerepositories extends admin_externalpage { 6967 /** 6968 * Calls parent::__construct with specific arguments 6969 */ 6970 public function __construct() { 6971 global $CFG; 6972 parent::__construct('managerepositories', get_string('manage', 6973 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php"); 6974 } 6975 6976 /** 6977 * Searches page for the specified string. 6978 * @param string $query The string to search for 6979 * @return bool True if it is found on this page 6980 */ 6981 public function search($query) { 6982 global $CFG; 6983 if ($result = parent::search($query)) { 6984 return $result; 6985 } 6986 6987 $found = false; 6988 $repositories= core_component::get_plugin_list('repository'); 6989 foreach ($repositories as $p => $dir) { 6990 if (strpos($p, $query) !== false) { 6991 $found = true; 6992 break; 6993 } 6994 } 6995 if (!$found) { 6996 foreach (repository::get_types() as $instance) { 6997 $title = $instance->get_typename(); 6998 if (strpos(core_text::strtolower($title), $query) !== false) { 6999 $found = true; 7000 break; 7001 } 7002 } 7003 } 7004 7005 if ($found) { 7006 $result = new stdClass(); 7007 $result->page = $this; 7008 $result->settings = array(); 7009 return array($this->name => $result); 7010 } else { 7011 return array(); 7012 } 7013 } 7014 } 7015 7016 7017 /** 7018 * Special class for authentication administration. 7019 * 7020 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7021 */ 7022 class admin_setting_manageauths extends admin_setting { 7023 /** 7024 * Calls parent::__construct with specific arguments 7025 */ 7026 public function __construct() { 7027 $this->nosave = true; 7028 parent::__construct('authsui', get_string('authsettings', 'admin'), '', ''); 7029 } 7030 7031 /** 7032 * Always returns true 7033 * 7034 * @return true 7035 */ 7036 public function get_setting() { 7037 return true; 7038 } 7039 7040 /** 7041 * Always returns true 7042 * 7043 * @return true 7044 */ 7045 public function get_defaultsetting() { 7046 return true; 7047 } 7048 7049 /** 7050 * Always returns '' and doesn't write anything 7051 * 7052 * @return string Always returns '' 7053 */ 7054 public function write_setting($data) { 7055 // do not write any setting 7056 return ''; 7057 } 7058 7059 /** 7060 * Search to find if Query is related to auth plugin 7061 * 7062 * @param string $query The string to search for 7063 * @return bool true for related false for not 7064 */ 7065 public function is_related($query) { 7066 if (parent::is_related($query)) { 7067 return true; 7068 } 7069 7070 $authsavailable = core_component::get_plugin_list('auth'); 7071 foreach ($authsavailable as $auth => $dir) { 7072 if (strpos($auth, $query) !== false) { 7073 return true; 7074 } 7075 $authplugin = get_auth_plugin($auth); 7076 $authtitle = $authplugin->get_title(); 7077 if (strpos(core_text::strtolower($authtitle), $query) !== false) { 7078 return true; 7079 } 7080 } 7081 return false; 7082 } 7083 7084 /** 7085 * Return XHTML to display control 7086 * 7087 * @param mixed $data Unused 7088 * @param string $query 7089 * @return string highlight 7090 */ 7091 public function output_html($data, $query='') { 7092 global $CFG, $OUTPUT, $DB; 7093 7094 // display strings 7095 $txt = get_strings(array('authenticationplugins', 'users', 'administration', 7096 'settings', 'edit', 'name', 'enable', 'disable', 7097 'up', 'down', 'none', 'users')); 7098 $txt->updown = "$txt->up/$txt->down"; 7099 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7100 $txt->testsettings = get_string('testsettings', 'core_auth'); 7101 7102 $authsavailable = core_component::get_plugin_list('auth'); 7103 get_enabled_auth_plugins(true); // fix the list of enabled auths 7104 if (empty($CFG->auth)) { 7105 $authsenabled = array(); 7106 } else { 7107 $authsenabled = explode(',', $CFG->auth); 7108 } 7109 7110 // construct the display array, with enabled auth plugins at the top, in order 7111 $displayauths = array(); 7112 $registrationauths = array(); 7113 $registrationauths[''] = $txt->disable; 7114 $authplugins = array(); 7115 foreach ($authsenabled as $auth) { 7116 $authplugin = get_auth_plugin($auth); 7117 $authplugins[$auth] = $authplugin; 7118 /// Get the auth title (from core or own auth lang files) 7119 $authtitle = $authplugin->get_title(); 7120 /// Apply titles 7121 $displayauths[$auth] = $authtitle; 7122 if ($authplugin->can_signup()) { 7123 $registrationauths[$auth] = $authtitle; 7124 } 7125 } 7126 7127 foreach ($authsavailable as $auth => $dir) { 7128 if (array_key_exists($auth, $displayauths)) { 7129 continue; //already in the list 7130 } 7131 $authplugin = get_auth_plugin($auth); 7132 $authplugins[$auth] = $authplugin; 7133 /// Get the auth title (from core or own auth lang files) 7134 $authtitle = $authplugin->get_title(); 7135 /// Apply titles 7136 $displayauths[$auth] = $authtitle; 7137 if ($authplugin->can_signup()) { 7138 $registrationauths[$auth] = $authtitle; 7139 } 7140 } 7141 7142 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main'); 7143 if (in_array('mnet', $authsenabled)) { 7144 $notify = new \core\output\notification(get_string('xmlrpcmnetauthenticationenabled', 'admin'), 7145 \core\output\notification::NOTIFY_WARNING); 7146 $return .= $OUTPUT->render($notify); 7147 } 7148 $return .= $OUTPUT->box_start('generalbox authsui'); 7149 7150 $table = new html_table(); 7151 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall); 7152 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 7153 $table->data = array(); 7154 $table->attributes['class'] = 'admintable generaltable'; 7155 $table->id = 'manageauthtable'; 7156 7157 //add always enabled plugins first 7158 $displayname = $displayauths['manual']; 7159 $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>"; 7160 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0)); 7161 $table->data[] = array($displayname, $usercount, '', '', $settings, '', ''); 7162 $displayname = $displayauths['nologin']; 7163 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0)); 7164 $table->data[] = array($displayname, $usercount, '', '', '', '', ''); 7165 7166 7167 // iterate through auth plugins and add to the display table 7168 $updowncount = 1; 7169 $authcount = count($authsenabled); 7170 $url = "auth.php?sesskey=" . sesskey(); 7171 foreach ($displayauths as $auth => $name) { 7172 if ($auth == 'manual' or $auth == 'nologin') { 7173 continue; 7174 } 7175 $class = ''; 7176 // hide/show link 7177 if (in_array($auth, $authsenabled)) { 7178 $hideshow = "<a href=\"$url&action=disable&auth=$auth\">"; 7179 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>'; 7180 $enabled = true; 7181 $displayname = $name; 7182 } 7183 else { 7184 $hideshow = "<a href=\"$url&action=enable&auth=$auth\">"; 7185 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>'; 7186 $enabled = false; 7187 $displayname = $name; 7188 $class = 'dimmed_text'; 7189 } 7190 7191 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0)); 7192 7193 // up/down link (only if auth is enabled) 7194 $updown = ''; 7195 if ($enabled) { 7196 if ($updowncount > 1) { 7197 $updown .= "<a href=\"$url&action=up&auth=$auth\">"; 7198 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a> '; 7199 } 7200 else { 7201 $updown .= $OUTPUT->spacer() . ' '; 7202 } 7203 if ($updowncount < $authcount) { 7204 $updown .= "<a href=\"$url&action=down&auth=$auth\">"; 7205 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a> '; 7206 } 7207 else { 7208 $updown .= $OUTPUT->spacer() . ' '; 7209 } 7210 ++ $updowncount; 7211 } 7212 7213 // settings link 7214 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) { 7215 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>"; 7216 } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) { 7217 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>"; 7218 } else { 7219 $settings = ''; 7220 } 7221 7222 // Uninstall link. 7223 $uninstall = ''; 7224 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) { 7225 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7226 } 7227 7228 $test = ''; 7229 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) { 7230 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey())); 7231 $test = html_writer::link($testurl, $txt->testsettings); 7232 } 7233 7234 // Add a row to the table. 7235 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall)); 7236 if ($class) { 7237 $row->attributes['class'] = $class; 7238 } 7239 $table->data[] = $row; 7240 } 7241 $return .= html_writer::table($table); 7242 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters'); 7243 $return .= $OUTPUT->box_end(); 7244 return highlight($query, $return); 7245 } 7246 } 7247 7248 7249 /** 7250 * Special class for authentication administration. 7251 * 7252 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7253 */ 7254 class admin_setting_manageeditors extends admin_setting { 7255 /** 7256 * Calls parent::__construct with specific arguments 7257 */ 7258 public function __construct() { 7259 $this->nosave = true; 7260 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', ''); 7261 } 7262 7263 /** 7264 * Always returns true, does nothing 7265 * 7266 * @return true 7267 */ 7268 public function get_setting() { 7269 return true; 7270 } 7271 7272 /** 7273 * Always returns true, does nothing 7274 * 7275 * @return true 7276 */ 7277 public function get_defaultsetting() { 7278 return true; 7279 } 7280 7281 /** 7282 * Always returns '', does not write anything 7283 * 7284 * @return string Always returns '' 7285 */ 7286 public function write_setting($data) { 7287 // do not write any setting 7288 return ''; 7289 } 7290 7291 /** 7292 * Checks if $query is one of the available editors 7293 * 7294 * @param string $query The string to search for 7295 * @return bool Returns true if found, false if not 7296 */ 7297 public function is_related($query) { 7298 if (parent::is_related($query)) { 7299 return true; 7300 } 7301 7302 $editors_available = editors_get_available(); 7303 foreach ($editors_available as $editor=>$editorstr) { 7304 if (strpos($editor, $query) !== false) { 7305 return true; 7306 } 7307 if (strpos(core_text::strtolower($editorstr), $query) !== false) { 7308 return true; 7309 } 7310 } 7311 return false; 7312 } 7313 7314 /** 7315 * Builds the XHTML to display the control 7316 * 7317 * @param string $data Unused 7318 * @param string $query 7319 * @return string 7320 */ 7321 public function output_html($data, $query='') { 7322 global $CFG, $OUTPUT; 7323 7324 // display strings 7325 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable', 7326 'up', 'down', 'none')); 7327 $struninstall = get_string('uninstallplugin', 'core_admin'); 7328 7329 $txt->updown = "$txt->up/$txt->down"; 7330 7331 $editors_available = editors_get_available(); 7332 $active_editors = explode(',', $CFG->texteditors); 7333 7334 $active_editors = array_reverse($active_editors); 7335 foreach ($active_editors as $key=>$editor) { 7336 if (empty($editors_available[$editor])) { 7337 unset($active_editors[$key]); 7338 } else { 7339 $name = $editors_available[$editor]; 7340 unset($editors_available[$editor]); 7341 $editors_available[$editor] = $name; 7342 } 7343 } 7344 if (empty($active_editors)) { 7345 //$active_editors = array('textarea'); 7346 } 7347 $editors_available = array_reverse($editors_available, true); 7348 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true); 7349 $return .= $OUTPUT->box_start('generalbox editorsui'); 7350 7351 $table = new html_table(); 7352 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall); 7353 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 7354 $table->id = 'editormanagement'; 7355 $table->attributes['class'] = 'admintable generaltable'; 7356 $table->data = array(); 7357 7358 // iterate through auth plugins and add to the display table 7359 $updowncount = 1; 7360 $editorcount = count($active_editors); 7361 $url = "editors.php?sesskey=" . sesskey(); 7362 foreach ($editors_available as $editor => $name) { 7363 // hide/show link 7364 $class = ''; 7365 if (in_array($editor, $active_editors)) { 7366 $hideshow = "<a href=\"$url&action=disable&editor=$editor\">"; 7367 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>'; 7368 $enabled = true; 7369 $displayname = $name; 7370 } 7371 else { 7372 $hideshow = "<a href=\"$url&action=enable&editor=$editor\">"; 7373 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>'; 7374 $enabled = false; 7375 $displayname = $name; 7376 $class = 'dimmed_text'; 7377 } 7378 7379 // up/down link (only if auth is enabled) 7380 $updown = ''; 7381 if ($enabled) { 7382 if ($updowncount > 1) { 7383 $updown .= "<a href=\"$url&action=up&editor=$editor\">"; 7384 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a> '; 7385 } 7386 else { 7387 $updown .= $OUTPUT->spacer() . ' '; 7388 } 7389 if ($updowncount < $editorcount) { 7390 $updown .= "<a href=\"$url&action=down&editor=$editor\">"; 7391 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a> '; 7392 } 7393 else { 7394 $updown .= $OUTPUT->spacer() . ' '; 7395 } 7396 ++ $updowncount; 7397 } 7398 7399 // settings link 7400 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) { 7401 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor)); 7402 $settings = "<a href='$eurl'>{$txt->settings}</a>"; 7403 } else { 7404 $settings = ''; 7405 } 7406 7407 $uninstall = ''; 7408 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) { 7409 $uninstall = html_writer::link($uninstallurl, $struninstall); 7410 } 7411 7412 // Add a row to the table. 7413 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall)); 7414 if ($class) { 7415 $row->attributes['class'] = $class; 7416 } 7417 $table->data[] = $row; 7418 } 7419 $return .= html_writer::table($table); 7420 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin'); 7421 $return .= $OUTPUT->box_end(); 7422 return highlight($query, $return); 7423 } 7424 } 7425 7426 /** 7427 * Special class for antiviruses administration. 7428 * 7429 * @copyright 2015 Ruslan Kabalin, Lancaster University. 7430 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7431 */ 7432 class admin_setting_manageantiviruses extends admin_setting { 7433 /** 7434 * Calls parent::__construct with specific arguments 7435 */ 7436 public function __construct() { 7437 $this->nosave = true; 7438 parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', ''); 7439 } 7440 7441 /** 7442 * Always returns true, does nothing 7443 * 7444 * @return true 7445 */ 7446 public function get_setting() { 7447 return true; 7448 } 7449 7450 /** 7451 * Always returns true, does nothing 7452 * 7453 * @return true 7454 */ 7455 public function get_defaultsetting() { 7456 return true; 7457 } 7458 7459 /** 7460 * Always returns '', does not write anything 7461 * 7462 * @param string $data Unused 7463 * @return string Always returns '' 7464 */ 7465 public function write_setting($data) { 7466 // Do not write any setting. 7467 return ''; 7468 } 7469 7470 /** 7471 * Checks if $query is one of the available editors 7472 * 7473 * @param string $query The string to search for 7474 * @return bool Returns true if found, false if not 7475 */ 7476 public function is_related($query) { 7477 if (parent::is_related($query)) { 7478 return true; 7479 } 7480 7481 $antivirusesavailable = \core\antivirus\manager::get_available(); 7482 foreach ($antivirusesavailable as $antivirus => $antivirusstr) { 7483 if (strpos($antivirus, $query) !== false) { 7484 return true; 7485 } 7486 if (strpos(core_text::strtolower($antivirusstr), $query) !== false) { 7487 return true; 7488 } 7489 } 7490 return false; 7491 } 7492 7493 /** 7494 * Builds the XHTML to display the control 7495 * 7496 * @param string $data Unused 7497 * @param string $query 7498 * @return string 7499 */ 7500 public function output_html($data, $query='') { 7501 global $CFG, $OUTPUT; 7502 7503 // Display strings. 7504 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable', 7505 'up', 'down', 'none')); 7506 $struninstall = get_string('uninstallplugin', 'core_admin'); 7507 7508 $txt->updown = "$txt->up/$txt->down"; 7509 7510 $antivirusesavailable = \core\antivirus\manager::get_available(); 7511 $activeantiviruses = explode(',', $CFG->antiviruses); 7512 7513 $activeantiviruses = array_reverse($activeantiviruses); 7514 foreach ($activeantiviruses as $key => $antivirus) { 7515 if (empty($antivirusesavailable[$antivirus])) { 7516 unset($activeantiviruses[$key]); 7517 } else { 7518 $name = $antivirusesavailable[$antivirus]; 7519 unset($antivirusesavailable[$antivirus]); 7520 $antivirusesavailable[$antivirus] = $name; 7521 } 7522 } 7523 $antivirusesavailable = array_reverse($antivirusesavailable, true); 7524 $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true); 7525 $return .= $OUTPUT->box_start('generalbox antivirusesui'); 7526 7527 $table = new html_table(); 7528 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall); 7529 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 7530 $table->id = 'antivirusmanagement'; 7531 $table->attributes['class'] = 'admintable generaltable'; 7532 $table->data = array(); 7533 7534 // Iterate through auth plugins and add to the display table. 7535 $updowncount = 1; 7536 $antiviruscount = count($activeantiviruses); 7537 $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey())); 7538 foreach ($antivirusesavailable as $antivirus => $name) { 7539 // Hide/show link. 7540 $class = ''; 7541 if (in_array($antivirus, $activeantiviruses)) { 7542 $hideshowurl = $baseurl; 7543 $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus)); 7544 $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable')); 7545 $hideshow = html_writer::link($hideshowurl, $hideshowimg); 7546 $enabled = true; 7547 $displayname = $name; 7548 } else { 7549 $hideshowurl = $baseurl; 7550 $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus)); 7551 $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable')); 7552 $hideshow = html_writer::link($hideshowurl, $hideshowimg); 7553 $enabled = false; 7554 $displayname = $name; 7555 $class = 'dimmed_text'; 7556 } 7557 7558 // Up/down link. 7559 $updown = ''; 7560 if ($enabled) { 7561 if ($updowncount > 1) { 7562 $updownurl = $baseurl; 7563 $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus)); 7564 $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup')); 7565 $updown = html_writer::link($updownurl, $updownimg); 7566 } else { 7567 $updownimg = $OUTPUT->spacer(); 7568 } 7569 if ($updowncount < $antiviruscount) { 7570 $updownurl = $baseurl; 7571 $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus)); 7572 $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown')); 7573 $updown = html_writer::link($updownurl, $updownimg); 7574 } else { 7575 $updownimg = $OUTPUT->spacer(); 7576 } 7577 ++ $updowncount; 7578 } 7579 7580 // Settings link. 7581 if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) { 7582 $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus)); 7583 $settings = html_writer::link($eurl, $txt->settings); 7584 } else { 7585 $settings = ''; 7586 } 7587 7588 $uninstall = ''; 7589 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) { 7590 $uninstall = html_writer::link($uninstallurl, $struninstall); 7591 } 7592 7593 // Add a row to the table. 7594 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall)); 7595 if ($class) { 7596 $row->attributes['class'] = $class; 7597 } 7598 $table->data[] = $row; 7599 } 7600 $return .= html_writer::table($table); 7601 $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin'); 7602 $return .= $OUTPUT->box_end(); 7603 return highlight($query, $return); 7604 } 7605 } 7606 7607 /** 7608 * Special class for license administration. 7609 * 7610 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7611 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead. 7612 * @todo MDL-45184 This class will be deleted in Moodle 4.1. 7613 */ 7614 class admin_setting_managelicenses extends admin_setting { 7615 /** 7616 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead. 7617 * @todo MDL-45184 This class will be deleted in Moodle 4.1 7618 */ 7619 public function __construct() { 7620 global $ADMIN; 7621 7622 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', 7623 DEBUG_DEVELOPER); 7624 7625 // Replace admin setting load with new external page load for tool_licensemanager, if not loaded already. 7626 if (!is_null($ADMIN->locate('licensemanager'))) { 7627 $temp = new admin_externalpage('licensemanager', 7628 get_string('licensemanager', 'tool_licensemanager'), 7629 \tool_licensemanager\helper::get_licensemanager_url()); 7630 7631 $ADMIN->add('license', $temp); 7632 } 7633 } 7634 7635 /** 7636 * Always returns true, does nothing 7637 * 7638 * @deprecated since Moodle 3.9 MDL-45184. 7639 * @todo MDL-45184 This method will be deleted in Moodle 4.1 7640 * 7641 * @return true 7642 */ 7643 public function get_setting() { 7644 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', 7645 DEBUG_DEVELOPER); 7646 7647 return true; 7648 } 7649 7650 /** 7651 * Always returns true, does nothing 7652 * 7653 * @deprecated since Moodle 3.9 MDL-45184. 7654 * @todo MDL-45184 This method will be deleted in Moodle 4.1 7655 * 7656 * @return true 7657 */ 7658 public function get_defaultsetting() { 7659 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', 7660 DEBUG_DEVELOPER); 7661 7662 return true; 7663 } 7664 7665 /** 7666 * Always returns '', does not write anything 7667 * 7668 * @deprecated since Moodle 3.9 MDL-45184. 7669 * @todo MDL-45184 This method will be deleted in Moodle 4.1 7670 * 7671 * @return string Always returns '' 7672 */ 7673 public function write_setting($data) { 7674 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', 7675 DEBUG_DEVELOPER); 7676 7677 // do not write any setting 7678 return ''; 7679 } 7680 7681 /** 7682 * Builds the XHTML to display the control 7683 * 7684 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead. 7685 * @todo MDL-45184 This method will be deleted in Moodle 4.1 7686 * 7687 * @param string $data Unused 7688 * @param string $query 7689 * @return string 7690 */ 7691 public function output_html($data, $query='') { 7692 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', 7693 DEBUG_DEVELOPER); 7694 7695 redirect(\tool_licensemanager\helper::get_licensemanager_url()); 7696 } 7697 } 7698 7699 /** 7700 * Course formats manager. Allows to enable/disable formats and jump to settings 7701 */ 7702 class admin_setting_manageformats extends admin_setting { 7703 7704 /** 7705 * Calls parent::__construct with specific arguments 7706 */ 7707 public function __construct() { 7708 $this->nosave = true; 7709 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', ''); 7710 } 7711 7712 /** 7713 * Always returns true 7714 * 7715 * @return true 7716 */ 7717 public function get_setting() { 7718 return true; 7719 } 7720 7721 /** 7722 * Always returns true 7723 * 7724 * @return true 7725 */ 7726 public function get_defaultsetting() { 7727 return true; 7728 } 7729 7730 /** 7731 * Always returns '' and doesn't write anything 7732 * 7733 * @param mixed $data string or array, must not be NULL 7734 * @return string Always returns '' 7735 */ 7736 public function write_setting($data) { 7737 // do not write any setting 7738 return ''; 7739 } 7740 7741 /** 7742 * Search to find if Query is related to format plugin 7743 * 7744 * @param string $query The string to search for 7745 * @return bool true for related false for not 7746 */ 7747 public function is_related($query) { 7748 if (parent::is_related($query)) { 7749 return true; 7750 } 7751 $formats = core_plugin_manager::instance()->get_plugins_of_type('format'); 7752 foreach ($formats as $format) { 7753 if (strpos($format->component, $query) !== false || 7754 strpos(core_text::strtolower($format->displayname), $query) !== false) { 7755 return true; 7756 } 7757 } 7758 return false; 7759 } 7760 7761 /** 7762 * Return XHTML to display control 7763 * 7764 * @param mixed $data Unused 7765 * @param string $query 7766 * @return string highlight 7767 */ 7768 public function output_html($data, $query='') { 7769 global $CFG, $OUTPUT; 7770 $return = ''; 7771 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main'); 7772 $return .= $OUTPUT->box_start('generalbox formatsui'); 7773 7774 $formats = core_plugin_manager::instance()->get_plugins_of_type('format'); 7775 7776 // display strings 7777 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default')); 7778 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7779 $txt->updown = "$txt->up/$txt->down"; 7780 7781 $table = new html_table(); 7782 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings); 7783 $table->align = array('left', 'center', 'center', 'center', 'center'); 7784 $table->attributes['class'] = 'manageformattable generaltable admintable'; 7785 $table->data = array(); 7786 7787 $cnt = 0; 7788 $defaultformat = get_config('moodlecourse', 'format'); 7789 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 7790 foreach ($formats as $format) { 7791 $url = new moodle_url('/admin/courseformats.php', 7792 array('sesskey' => sesskey(), 'format' => $format->name)); 7793 $isdefault = ''; 7794 $class = ''; 7795 if ($format->is_enabled()) { 7796 $strformatname = $format->displayname; 7797 if ($defaultformat === $format->name) { 7798 $hideshow = $txt->default; 7799 } else { 7800 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 7801 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 7802 } 7803 } else { 7804 $strformatname = $format->displayname; 7805 $class = 'dimmed_text'; 7806 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 7807 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 7808 } 7809 $updown = ''; 7810 if ($cnt) { 7811 $updown .= html_writer::link($url->out(false, array('action' => 'up')), 7812 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). ''; 7813 } else { 7814 $updown .= $spacer; 7815 } 7816 if ($cnt < count($formats) - 1) { 7817 $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')), 7818 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall'))); 7819 } else { 7820 $updown .= $spacer; 7821 } 7822 $cnt++; 7823 $settings = ''; 7824 if ($format->get_settings_url()) { 7825 $settings = html_writer::link($format->get_settings_url(), $txt->settings); 7826 } 7827 $uninstall = ''; 7828 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) { 7829 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7830 } 7831 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings)); 7832 if ($class) { 7833 $row->attributes['class'] = $class; 7834 } 7835 $table->data[] = $row; 7836 } 7837 $return .= html_writer::table($table); 7838 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings')); 7839 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link)); 7840 $return .= $OUTPUT->box_end(); 7841 return highlight($query, $return); 7842 } 7843 } 7844 7845 /** 7846 * Custom fields manager. Allows to enable/disable custom fields and jump to settings. 7847 * 7848 * @package core 7849 * @copyright 2018 Toni Barbera 7850 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7851 */ 7852 class admin_setting_managecustomfields extends admin_setting { 7853 7854 /** 7855 * Calls parent::__construct with specific arguments 7856 */ 7857 public function __construct() { 7858 $this->nosave = true; 7859 parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', ''); 7860 } 7861 7862 /** 7863 * Always returns true 7864 * 7865 * @return true 7866 */ 7867 public function get_setting() { 7868 return true; 7869 } 7870 7871 /** 7872 * Always returns true 7873 * 7874 * @return true 7875 */ 7876 public function get_defaultsetting() { 7877 return true; 7878 } 7879 7880 /** 7881 * Always returns '' and doesn't write anything 7882 * 7883 * @param mixed $data string or array, must not be NULL 7884 * @return string Always returns '' 7885 */ 7886 public function write_setting($data) { 7887 // Do not write any setting. 7888 return ''; 7889 } 7890 7891 /** 7892 * Search to find if Query is related to format plugin 7893 * 7894 * @param string $query The string to search for 7895 * @return bool true for related false for not 7896 */ 7897 public function is_related($query) { 7898 if (parent::is_related($query)) { 7899 return true; 7900 } 7901 $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield'); 7902 foreach ($formats as $format) { 7903 if (strpos($format->component, $query) !== false || 7904 strpos(core_text::strtolower($format->displayname), $query) !== false) { 7905 return true; 7906 } 7907 } 7908 return false; 7909 } 7910 7911 /** 7912 * Return XHTML to display control 7913 * 7914 * @param mixed $data Unused 7915 * @param string $query 7916 * @return string highlight 7917 */ 7918 public function output_html($data, $query='') { 7919 global $CFG, $OUTPUT; 7920 $return = ''; 7921 $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main'); 7922 $return .= $OUTPUT->box_start('generalbox customfieldsui'); 7923 7924 $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield'); 7925 7926 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down')); 7927 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 7928 $txt->updown = "$txt->up/$txt->down"; 7929 7930 $table = new html_table(); 7931 $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings); 7932 $table->align = array('left', 'center', 'center', 'center'); 7933 $table->attributes['class'] = 'managecustomfieldtable generaltable admintable'; 7934 $table->data = array(); 7935 7936 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 7937 foreach ($fields as $field) { 7938 $url = new moodle_url('/admin/customfields.php', 7939 array('sesskey' => sesskey(), 'field' => $field->name)); 7940 7941 if ($field->is_enabled()) { 7942 $strfieldname = $field->displayname; 7943 $class = ''; 7944 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 7945 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 7946 } else { 7947 $strfieldname = $field->displayname; 7948 $class = 'dimmed_text'; 7949 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 7950 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 7951 } 7952 $settings = ''; 7953 if ($field->get_settings_url()) { 7954 $settings = html_writer::link($field->get_settings_url(), $txt->settings); 7955 } 7956 $uninstall = ''; 7957 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) { 7958 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 7959 } 7960 $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings)); 7961 $row->attributes['class'] = $class; 7962 $table->data[] = $row; 7963 } 7964 $return .= html_writer::table($table); 7965 $return .= $OUTPUT->box_end(); 7966 return highlight($query, $return); 7967 } 7968 } 7969 7970 /** 7971 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings 7972 * 7973 * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net) 7974 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7975 */ 7976 class admin_setting_managedataformats extends admin_setting { 7977 7978 /** 7979 * Calls parent::__construct with specific arguments 7980 */ 7981 public function __construct() { 7982 $this->nosave = true; 7983 parent::__construct('managedataformats', new lang_string('managedataformats'), '', ''); 7984 } 7985 7986 /** 7987 * Always returns true 7988 * 7989 * @return true 7990 */ 7991 public function get_setting() { 7992 return true; 7993 } 7994 7995 /** 7996 * Always returns true 7997 * 7998 * @return true 7999 */ 8000 public function get_defaultsetting() { 8001 return true; 8002 } 8003 8004 /** 8005 * Always returns '' and doesn't write anything 8006 * 8007 * @param mixed $data string or array, must not be NULL 8008 * @return string Always returns '' 8009 */ 8010 public function write_setting($data) { 8011 // Do not write any setting. 8012 return ''; 8013 } 8014 8015 /** 8016 * Search to find if Query is related to format plugin 8017 * 8018 * @param string $query The string to search for 8019 * @return bool true for related false for not 8020 */ 8021 public function is_related($query) { 8022 if (parent::is_related($query)) { 8023 return true; 8024 } 8025 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat'); 8026 foreach ($formats as $format) { 8027 if (strpos($format->component, $query) !== false || 8028 strpos(core_text::strtolower($format->displayname), $query) !== false) { 8029 return true; 8030 } 8031 } 8032 return false; 8033 } 8034 8035 /** 8036 * Return XHTML to display control 8037 * 8038 * @param mixed $data Unused 8039 * @param string $query 8040 * @return string highlight 8041 */ 8042 public function output_html($data, $query='') { 8043 global $CFG, $OUTPUT; 8044 $return = ''; 8045 8046 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat'); 8047 8048 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default')); 8049 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 8050 $txt->updown = "$txt->up/$txt->down"; 8051 8052 $table = new html_table(); 8053 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings); 8054 $table->align = array('left', 'center', 'center', 'center', 'center'); 8055 $table->attributes['class'] = 'manageformattable generaltable admintable'; 8056 $table->data = array(); 8057 8058 $cnt = 0; 8059 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 8060 $totalenabled = 0; 8061 foreach ($formats as $format) { 8062 if ($format->is_enabled() && $format->is_installed_and_upgraded()) { 8063 $totalenabled++; 8064 } 8065 } 8066 foreach ($formats as $format) { 8067 $status = $format->get_status(); 8068 $url = new moodle_url('/admin/dataformats.php', 8069 array('sesskey' => sesskey(), 'name' => $format->name)); 8070 8071 $class = ''; 8072 if ($format->is_enabled()) { 8073 $strformatname = $format->displayname; 8074 if ($totalenabled == 1&& $format->is_enabled()) { 8075 $hideshow = ''; 8076 } else { 8077 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 8078 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 8079 } 8080 } else { 8081 $class = 'dimmed_text'; 8082 $strformatname = $format->displayname; 8083 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 8084 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 8085 } 8086 8087 $updown = ''; 8088 if ($cnt) { 8089 $updown .= html_writer::link($url->out(false, array('action' => 'up')), 8090 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). ''; 8091 } else { 8092 $updown .= $spacer; 8093 } 8094 if ($cnt < count($formats) - 1) { 8095 $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')), 8096 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall'))); 8097 } else { 8098 $updown .= $spacer; 8099 } 8100 8101 $uninstall = ''; 8102 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) { 8103 $uninstall = get_string('status_missing', 'core_plugin'); 8104 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) { 8105 $uninstall = get_string('status_new', 'core_plugin'); 8106 } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) { 8107 if ($totalenabled != 1 || !$format->is_enabled()) { 8108 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 8109 } 8110 } 8111 8112 $settings = ''; 8113 if ($format->get_settings_url()) { 8114 $settings = html_writer::link($format->get_settings_url(), $txt->settings); 8115 } 8116 8117 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings)); 8118 if ($class) { 8119 $row->attributes['class'] = $class; 8120 } 8121 $table->data[] = $row; 8122 $cnt++; 8123 } 8124 $return .= html_writer::table($table); 8125 return highlight($query, $return); 8126 } 8127 } 8128 8129 /** 8130 * Special class for filter administration. 8131 * 8132 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8133 */ 8134 class admin_page_managefilters extends admin_externalpage { 8135 /** 8136 * Calls parent::__construct with specific arguments 8137 */ 8138 public function __construct() { 8139 global $CFG; 8140 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php"); 8141 } 8142 8143 /** 8144 * Searches all installed filters for specified filter 8145 * 8146 * @param string $query The filter(string) to search for 8147 * @param string $query 8148 */ 8149 public function search($query) { 8150 global $CFG; 8151 if ($result = parent::search($query)) { 8152 return $result; 8153 } 8154 8155 $found = false; 8156 $filternames = filter_get_all_installed(); 8157 foreach ($filternames as $path => $strfiltername) { 8158 if (strpos(core_text::strtolower($strfiltername), $query) !== false) { 8159 $found = true; 8160 break; 8161 } 8162 if (strpos($path, $query) !== false) { 8163 $found = true; 8164 break; 8165 } 8166 } 8167 8168 if ($found) { 8169 $result = new stdClass; 8170 $result->page = $this; 8171 $result->settings = array(); 8172 return array($this->name => $result); 8173 } else { 8174 return array(); 8175 } 8176 } 8177 } 8178 8179 /** 8180 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin. 8181 * Requires a get_rank method on the plugininfo class for sorting. 8182 * 8183 * @copyright 2017 Damyon Wiese 8184 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8185 */ 8186 abstract class admin_setting_manage_plugins extends admin_setting { 8187 8188 /** 8189 * Get the admin settings section name (just a unique string) 8190 * 8191 * @return string 8192 */ 8193 public function get_section_name() { 8194 return 'manage' . $this->get_plugin_type() . 'plugins'; 8195 } 8196 8197 /** 8198 * Get the admin settings section title (use get_string). 8199 * 8200 * @return string 8201 */ 8202 abstract public function get_section_title(); 8203 8204 /** 8205 * Get the type of plugin to manage. 8206 * 8207 * @return string 8208 */ 8209 abstract public function get_plugin_type(); 8210 8211 /** 8212 * Get the name of the second column. 8213 * 8214 * @return string 8215 */ 8216 public function get_info_column_name() { 8217 return ''; 8218 } 8219 8220 /** 8221 * Get the type of plugin to manage. 8222 * 8223 * @param plugininfo The plugin info class. 8224 * @return string 8225 */ 8226 abstract public function get_info_column($plugininfo); 8227 8228 /** 8229 * Calls parent::__construct with specific arguments 8230 */ 8231 public function __construct() { 8232 $this->nosave = true; 8233 parent::__construct($this->get_section_name(), $this->get_section_title(), '', ''); 8234 } 8235 8236 /** 8237 * Always returns true, does nothing 8238 * 8239 * @return true 8240 */ 8241 public function get_setting() { 8242 return true; 8243 } 8244 8245 /** 8246 * Always returns true, does nothing 8247 * 8248 * @return true 8249 */ 8250 public function get_defaultsetting() { 8251 return true; 8252 } 8253 8254 /** 8255 * Always returns '', does not write anything 8256 * 8257 * @param mixed $data 8258 * @return string Always returns '' 8259 */ 8260 public function write_setting($data) { 8261 // Do not write any setting. 8262 return ''; 8263 } 8264 8265 /** 8266 * Checks if $query is one of the available plugins of this type 8267 * 8268 * @param string $query The string to search for 8269 * @return bool Returns true if found, false if not 8270 */ 8271 public function is_related($query) { 8272 if (parent::is_related($query)) { 8273 return true; 8274 } 8275 8276 $query = core_text::strtolower($query); 8277 $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type()); 8278 foreach ($plugins as $name => $plugin) { 8279 $localised = $plugin->displayname; 8280 if (strpos(core_text::strtolower($name), $query) !== false) { 8281 return true; 8282 } 8283 if (strpos(core_text::strtolower($localised), $query) !== false) { 8284 return true; 8285 } 8286 } 8287 return false; 8288 } 8289 8290 /** 8291 * The URL for the management page for this plugintype. 8292 * 8293 * @return moodle_url 8294 */ 8295 protected function get_manage_url() { 8296 return new moodle_url('/admin/updatesetting.php'); 8297 } 8298 8299 /** 8300 * Builds the HTML to display the control. 8301 * 8302 * @param string $data Unused 8303 * @param string $query 8304 * @return string 8305 */ 8306 public function output_html($data, $query = '') { 8307 global $CFG, $OUTPUT, $DB, $PAGE; 8308 8309 $context = (object) [ 8310 'manageurl' => new moodle_url($this->get_manage_url(), [ 8311 'type' => $this->get_plugin_type(), 8312 'sesskey' => sesskey(), 8313 ]), 8314 'infocolumnname' => $this->get_info_column_name(), 8315 'plugins' => [], 8316 ]; 8317 8318 $pluginmanager = core_plugin_manager::instance(); 8319 $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type()); 8320 $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type()); 8321 $plugins = array_merge($enabled, $allplugins); 8322 foreach ($plugins as $key => $plugin) { 8323 $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]); 8324 8325 $pluginkey = (object) [ 8326 'plugin' => $plugin->displayname, 8327 'enabled' => $plugin->is_enabled(), 8328 'togglelink' => '', 8329 'moveuplink' => '', 8330 'movedownlink' => '', 8331 'settingslink' => $plugin->get_settings_url(), 8332 'uninstalllink' => '', 8333 'info' => '', 8334 ]; 8335 8336 // Enable/Disable link. 8337 $togglelink = new moodle_url($pluginlink); 8338 if ($plugin->is_enabled()) { 8339 $toggletarget = false; 8340 $togglelink->param('action', 'disable'); 8341 8342 if (count($context->plugins)) { 8343 // This is not the first plugin. 8344 $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']); 8345 } 8346 8347 if (count($enabled) > count($context->plugins) + 1) { 8348 // This is not the last plugin. 8349 $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']); 8350 } 8351 8352 $pluginkey->info = $this->get_info_column($plugin); 8353 } else { 8354 $toggletarget = true; 8355 $togglelink->param('action', 'enable'); 8356 } 8357 8358 $pluginkey->toggletarget = $toggletarget; 8359 $pluginkey->togglelink = $togglelink; 8360 8361 $frankenstyle = $plugin->type . '_' . $plugin->name; 8362 if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) { 8363 // This plugin supports uninstallation. 8364 $pluginkey->uninstalllink = $uninstalllink; 8365 } 8366 8367 if (!empty($this->get_info_column_name())) { 8368 // This plugintype has an info column. 8369 $pluginkey->info = $this->get_info_column($plugin); 8370 } 8371 8372 $context->plugins[] = $pluginkey; 8373 } 8374 8375 $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context); 8376 return highlight($query, $str); 8377 } 8378 } 8379 8380 /** 8381 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin. 8382 * Requires a get_rank method on the plugininfo class for sorting. 8383 * 8384 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk> 8385 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8386 */ 8387 class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins { 8388 public function get_section_title() { 8389 return get_string('type_fileconverter_plural', 'plugin'); 8390 } 8391 8392 public function get_plugin_type() { 8393 return 'fileconverter'; 8394 } 8395 8396 public function get_info_column_name() { 8397 return get_string('supportedconversions', 'plugin'); 8398 } 8399 8400 public function get_info_column($plugininfo) { 8401 return $plugininfo->get_supported_conversions(); 8402 } 8403 } 8404 8405 /** 8406 * Special class for media player plugins management. 8407 * 8408 * @copyright 2016 Marina Glancy 8409 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8410 */ 8411 class admin_setting_managemediaplayers extends admin_setting { 8412 /** 8413 * Calls parent::__construct with specific arguments 8414 */ 8415 public function __construct() { 8416 $this->nosave = true; 8417 parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', ''); 8418 } 8419 8420 /** 8421 * Always returns true, does nothing 8422 * 8423 * @return true 8424 */ 8425 public function get_setting() { 8426 return true; 8427 } 8428 8429 /** 8430 * Always returns true, does nothing 8431 * 8432 * @return true 8433 */ 8434 public function get_defaultsetting() { 8435 return true; 8436 } 8437 8438 /** 8439 * Always returns '', does not write anything 8440 * 8441 * @param mixed $data 8442 * @return string Always returns '' 8443 */ 8444 public function write_setting($data) { 8445 // Do not write any setting. 8446 return ''; 8447 } 8448 8449 /** 8450 * Checks if $query is one of the available enrol plugins 8451 * 8452 * @param string $query The string to search for 8453 * @return bool Returns true if found, false if not 8454 */ 8455 public function is_related($query) { 8456 if (parent::is_related($query)) { 8457 return true; 8458 } 8459 8460 $query = core_text::strtolower($query); 8461 $plugins = core_plugin_manager::instance()->get_plugins_of_type('media'); 8462 foreach ($plugins as $name => $plugin) { 8463 $localised = $plugin->displayname; 8464 if (strpos(core_text::strtolower($name), $query) !== false) { 8465 return true; 8466 } 8467 if (strpos(core_text::strtolower($localised), $query) !== false) { 8468 return true; 8469 } 8470 } 8471 return false; 8472 } 8473 8474 /** 8475 * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank. 8476 * @return \core\plugininfo\media[] 8477 */ 8478 protected function get_sorted_plugins() { 8479 $pluginmanager = core_plugin_manager::instance(); 8480 8481 $plugins = $pluginmanager->get_plugins_of_type('media'); 8482 $enabledplugins = $pluginmanager->get_enabled_plugins('media'); 8483 8484 // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank. 8485 \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC); 8486 8487 $order = array_values($enabledplugins); 8488 $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order)); 8489 8490 $sortedplugins = array(); 8491 foreach ($order as $name) { 8492 $sortedplugins[$name] = $plugins[$name]; 8493 } 8494 8495 return $sortedplugins; 8496 } 8497 8498 /** 8499 * Builds the XHTML to display the control 8500 * 8501 * @param string $data Unused 8502 * @param string $query 8503 * @return string 8504 */ 8505 public function output_html($data, $query='') { 8506 global $CFG, $OUTPUT, $DB, $PAGE; 8507 8508 // Display strings. 8509 $strup = get_string('up'); 8510 $strdown = get_string('down'); 8511 $strsettings = get_string('settings'); 8512 $strenable = get_string('enable'); 8513 $strdisable = get_string('disable'); 8514 $struninstall = get_string('uninstallplugin', 'core_admin'); 8515 $strversion = get_string('version'); 8516 $strname = get_string('name'); 8517 $strsupports = get_string('supports', 'core_media'); 8518 8519 $pluginmanager = core_plugin_manager::instance(); 8520 8521 $plugins = $this->get_sorted_plugins(); 8522 $enabledplugins = $pluginmanager->get_enabled_plugins('media'); 8523 8524 $return = $OUTPUT->box_start('generalbox mediaplayersui'); 8525 8526 $table = new html_table(); 8527 $table->head = array($strname, $strsupports, $strversion, 8528 $strenable, $strup.'/'.$strdown, $strsettings, $struninstall); 8529 $table->colclasses = array('leftalign', 'leftalign', 'centeralign', 8530 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 8531 $table->id = 'mediaplayerplugins'; 8532 $table->attributes['class'] = 'admintable generaltable'; 8533 $table->data = array(); 8534 8535 // Iterate through media plugins and add to the display table. 8536 $updowncount = 1; 8537 $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey())); 8538 $printed = array(); 8539 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 8540 8541 $usedextensions = []; 8542 foreach ($plugins as $name => $plugin) { 8543 $url->param('media', $name); 8544 $plugininfo = $pluginmanager->get_plugin_info('media_'.$name); 8545 $version = $plugininfo->versiondb; 8546 $supports = $plugininfo->supports($usedextensions); 8547 8548 // Hide/show links. 8549 $class = ''; 8550 if (!$plugininfo->is_installed_and_upgraded()) { 8551 $hideshow = ''; 8552 $enabled = false; 8553 $displayname = '<span class="notifyproblem">'.$name.'</span>'; 8554 } else { 8555 $enabled = $plugininfo->is_enabled(); 8556 if ($enabled) { 8557 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')), 8558 $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall'))); 8559 } else { 8560 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')), 8561 $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall'))); 8562 $class = 'dimmed_text'; 8563 } 8564 $displayname = $plugin->displayname; 8565 if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) { 8566 $displayname .= ' ' . $OUTPUT->help_icon('pluginname', 'media_' . $name); 8567 } 8568 } 8569 if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) { 8570 $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon')); 8571 } else { 8572 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon')); 8573 } 8574 8575 // Up/down link (only if enrol is enabled). 8576 $updown = ''; 8577 if ($enabled) { 8578 if ($updowncount > 1) { 8579 $updown = html_writer::link(new moodle_url($url, array('action' => 'up')), 8580 $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall'))); 8581 } else { 8582 $updown = $spacer; 8583 } 8584 if ($updowncount < count($enabledplugins)) { 8585 $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')), 8586 $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall'))); 8587 } else { 8588 $updown .= $spacer; 8589 } 8590 ++$updowncount; 8591 } 8592 8593 $uninstall = ''; 8594 $status = $plugininfo->get_status(); 8595 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) { 8596 $uninstall = get_string('status_missing', 'core_plugin') . '<br/>'; 8597 } 8598 if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) { 8599 $uninstall = get_string('status_new', 'core_plugin'); 8600 } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) { 8601 $uninstall .= html_writer::link($uninstallurl, $struninstall); 8602 } 8603 8604 $settings = ''; 8605 if ($plugininfo->get_settings_url()) { 8606 $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings); 8607 } 8608 8609 // Add a row to the table. 8610 $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall)); 8611 if ($class) { 8612 $row->attributes['class'] = $class; 8613 } 8614 $table->data[] = $row; 8615 8616 $printed[$name] = true; 8617 } 8618 8619 $return .= html_writer::table($table); 8620 $return .= $OUTPUT->box_end(); 8621 return highlight($query, $return); 8622 } 8623 } 8624 8625 8626 /** 8627 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings 8628 * 8629 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com> 8630 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 8631 */ 8632 class admin_setting_managecontentbankcontenttypes extends admin_setting { 8633 8634 /** 8635 * Calls parent::__construct with specific arguments 8636 */ 8637 public function __construct() { 8638 $this->nosave = true; 8639 parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', ''); 8640 } 8641 8642 /** 8643 * Always returns true 8644 * 8645 * @return true 8646 */ 8647 public function get_setting() { 8648 return true; 8649 } 8650 8651 /** 8652 * Always returns true 8653 * 8654 * @return true 8655 */ 8656 public function get_defaultsetting() { 8657 return true; 8658 } 8659 8660 /** 8661 * Always returns '' and doesn't write anything 8662 * 8663 * @param mixed $data string or array, must not be NULL 8664 * @return string Always returns '' 8665 */ 8666 public function write_setting($data) { 8667 // Do not write any setting. 8668 return ''; 8669 } 8670 8671 /** 8672 * Search to find if Query is related to content bank plugin 8673 * 8674 * @param string $query The string to search for 8675 * @return bool true for related false for not 8676 */ 8677 public function is_related($query) { 8678 if (parent::is_related($query)) { 8679 return true; 8680 } 8681 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype'); 8682 foreach ($types as $type) { 8683 if (strpos($type->component, $query) !== false || 8684 strpos(core_text::strtolower($type->displayname), $query) !== false) { 8685 return true; 8686 } 8687 } 8688 return false; 8689 } 8690 8691 /** 8692 * Return XHTML to display control 8693 * 8694 * @param mixed $data Unused 8695 * @param string $query 8696 * @return string highlight 8697 */ 8698 public function output_html($data, $query='') { 8699 global $CFG, $OUTPUT; 8700 $return = ''; 8701 8702 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype'); 8703 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default')); 8704 $txt->uninstall = get_string('uninstallplugin', 'core_admin'); 8705 8706 $table = new html_table(); 8707 $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall); 8708 $table->align = array('left', 'center', 'center', 'center', 'center'); 8709 $table->attributes['class'] = 'managecontentbanktable generaltable admintable'; 8710 $table->data = array(); 8711 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); 8712 8713 $totalenabled = 0; 8714 $count = 0; 8715 foreach ($types as $type) { 8716 if ($type->is_enabled() && $type->is_installed_and_upgraded()) { 8717 $totalenabled++; 8718 } 8719 } 8720 8721 foreach ($types as $type) { 8722 $url = new moodle_url('/admin/contentbank.php', 8723 array('sesskey' => sesskey(), 'name' => $type->name)); 8724 8725 $class = ''; 8726 $strtypename = $type->displayname; 8727 if ($type->is_enabled()) { 8728 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')), 8729 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall'))); 8730 } else { 8731 $class = 'dimmed_text'; 8732 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')), 8733 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall'))); 8734 } 8735 8736 $updown = ''; 8737 if ($count) { 8738 $updown .= html_writer::link($url->out(false, array('action' => 'up')), 8739 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). ''; 8740 } else { 8741 $updown .= $spacer; 8742 } 8743 if ($count < count($types) - 1) { 8744 $updown .= ' '.html_writer::link($url->out(false, array('action' => 'down')), 8745 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall'))); 8746 } else { 8747 $updown .= $spacer; 8748 } 8749 8750 $settings = ''; 8751 if ($type->get_settings_url()) { 8752 $settings = html_writer::link($type->get_settings_url(), $txt->settings); 8753 } 8754 8755 $uninstall = ''; 8756 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) { 8757 $uninstall = html_writer::link($uninstallurl, $txt->uninstall); 8758 } 8759 8760 $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall)); 8761 if ($class) { 8762 $row->attributes['class'] = $class; 8763 } 8764 $table->data[] = $row; 8765 $count++; 8766 } 8767 $return .= html_writer::table($table); 8768 return highlight($query, $return); 8769 } 8770 } 8771 8772 /** 8773 * Initialise admin page - this function does require login and permission 8774 * checks specified in page definition. 8775 * 8776 * This function must be called on each admin page before other code. 8777 * 8778 * @global moodle_page $PAGE 8779 * 8780 * @param string $section name of page 8781 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button. 8782 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be 8783 * added to the turn blocks editing on/off form, so this page reloads correctly. 8784 * @param string $actualurl if the actual page being viewed is not the normal one for this 8785 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here. 8786 * @param array $options Additional options that can be specified for page setup. 8787 * pagelayout - This option can be used to set a specific pagelyaout, admin is default. 8788 */ 8789 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) { 8790 global $CFG, $PAGE, $USER, $SITE, $OUTPUT; 8791 8792 $PAGE->set_context(null); // hack - set context to something, by default to system context 8793 8794 $site = get_site(); 8795 require_login(null, false); 8796 8797 if (!empty($options['pagelayout'])) { 8798 // A specific page layout has been requested. 8799 $PAGE->set_pagelayout($options['pagelayout']); 8800 } else if ($section === 'upgradesettings') { 8801 $PAGE->set_pagelayout('maintenance'); 8802 } else { 8803 $PAGE->set_pagelayout('admin'); 8804 } 8805 8806 $adminroot = admin_get_root(false, false); // settings not required for external pages 8807 $extpage = $adminroot->locate($section, true); 8808 8809 $hassiteconfig = has_capability('moodle/site:config', context_system::instance()); 8810 if (empty($extpage) or !($extpage instanceof admin_externalpage)) { 8811 // The requested section isn't in the admin tree 8812 // It could be because the user has inadequate capapbilities or because the section doesn't exist 8813 if (!$hassiteconfig) { 8814 // The requested section could depend on a different capability 8815 // but most likely the user has inadequate capabilities 8816 print_error('accessdenied', 'admin'); 8817 } else { 8818 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/"); 8819 } 8820 } 8821 8822 // this eliminates our need to authenticate on the actual pages 8823 if (!$extpage->check_access()) { 8824 print_error('accessdenied', 'admin'); 8825 die; 8826 } 8827 8828 navigation_node::require_admin_tree(); 8829 8830 // $PAGE->set_extra_button($extrabutton); TODO 8831 8832 if (!$actualurl) { 8833 $actualurl = $extpage->url; 8834 } 8835 8836 $PAGE->set_url($actualurl, $extraurlparams); 8837 if (strpos($PAGE->pagetype, 'admin-') !== 0) { 8838 $PAGE->set_pagetype('admin-' . $PAGE->pagetype); 8839 } 8840 8841 if (empty($SITE->fullname) || empty($SITE->shortname)) { 8842 // During initial install. 8843 $strinstallation = get_string('installation', 'install'); 8844 $strsettings = get_string('settings'); 8845 $PAGE->navbar->add($strsettings); 8846 $PAGE->set_title($strinstallation); 8847 $PAGE->set_heading($strinstallation); 8848 $PAGE->set_cacheable(false); 8849 return; 8850 } 8851 8852 // Locate the current item on the navigation and make it active when found. 8853 $path = $extpage->path; 8854 $node = $PAGE->settingsnav; 8855 while ($node && count($path) > 0) { 8856 $node = $node->get(array_pop($path)); 8857 } 8858 if ($node) { 8859 $node->make_active(); 8860 } 8861 8862 // Normal case. 8863 $adminediting = optional_param('adminedit', -1, PARAM_BOOL); 8864 if ($PAGE->user_allowed_editing() && $adminediting != -1) { 8865 $USER->editing = $adminediting; 8866 } 8867 8868 $visiblepathtosection = array_reverse($extpage->visiblepath); 8869 8870 if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) { 8871 if ($PAGE->user_is_editing()) { 8872 $caption = get_string('blockseditoff'); 8873 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey())); 8874 } else { 8875 $caption = get_string('blocksediton'); 8876 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey())); 8877 } 8878 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get')); 8879 } 8880 8881 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection)); 8882 $PAGE->set_heading($SITE->fullname); 8883 8884 if ($hassiteconfig) { 8885 $PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [ 8886 'action' => new moodle_url('/admin/search.php'), 8887 'query' => $PAGE->url->get_param('query'), 8888 ])); 8889 } 8890 8891 // prevent caching in nav block 8892 $PAGE->navigation->clear_cache(); 8893 } 8894 8895 /** 8896 * Returns the reference to admin tree root 8897 * 8898 * @return object admin_root object 8899 */ 8900 function admin_get_root($reload=false, $requirefulltree=true) { 8901 global $CFG, $DB, $OUTPUT, $ADMIN; 8902 8903 if (is_null($ADMIN)) { 8904 // create the admin tree! 8905 $ADMIN = new admin_root($requirefulltree); 8906 } 8907 8908 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) { 8909 $ADMIN->purge_children($requirefulltree); 8910 } 8911 8912 if (!$ADMIN->loaded) { 8913 // we process this file first to create categories first and in correct order 8914 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php'); 8915 8916 // now we process all other files in admin/settings to build the admin tree 8917 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) { 8918 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') { 8919 continue; 8920 } 8921 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') { 8922 // plugins are loaded last - they may insert pages anywhere 8923 continue; 8924 } 8925 require($file); 8926 } 8927 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php'); 8928 8929 $ADMIN->loaded = true; 8930 } 8931 8932 return $ADMIN; 8933 } 8934 8935 /// settings utility functions 8936 8937 /** 8938 * This function applies default settings. 8939 * Because setting the defaults of some settings can enable other settings, 8940 * this function is called recursively until no more new settings are found. 8941 * 8942 * @param object $node, NULL means complete tree, null by default 8943 * @param bool $unconditional if true overrides all values with defaults, true by default 8944 * @param array $admindefaultsettings default admin settings to apply. Used recursively 8945 * @param array $settingsoutput The names and values of the changed settings. Used recursively 8946 * @return array $settingsoutput The names and values of the changed settings 8947 */ 8948 function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) { 8949 $counter = 0; 8950 8951 if (is_null($node)) { 8952 core_plugin_manager::reset_caches(); 8953 $node = admin_get_root(true, true); 8954 $counter = count($settingsoutput); 8955 } 8956 8957 if ($node instanceof admin_category) { 8958 $entries = array_keys($node->children); 8959 foreach ($entries as $entry) { 8960 $settingsoutput = admin_apply_default_settings( 8961 $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput 8962 ); 8963 } 8964 8965 } else if ($node instanceof admin_settingpage) { 8966 foreach ($node->settings as $setting) { 8967 if (!$unconditional && !is_null($setting->get_setting())) { 8968 // Do not override existing defaults. 8969 continue; 8970 } 8971 $defaultsetting = $setting->get_defaultsetting(); 8972 if (is_null($defaultsetting)) { 8973 // No value yet - default maybe applied after admin user creation or in upgradesettings. 8974 continue; 8975 } 8976 8977 $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting. 8978 8979 if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed. 8980 $admindefaultsettings[$settingname] = $settingname; 8981 $settingsoutput[$settingname] = $defaultsetting; 8982 8983 // Set the default for this setting. 8984 $setting->write_setting($defaultsetting); 8985 $setting->write_setting_flags(null); 8986 } else { 8987 unset($admindefaultsettings[$settingname]); // Remove processed settings. 8988 } 8989 } 8990 } 8991 8992 // Call this function recursively until all settings are processed. 8993 if (($node instanceof admin_root) && ($counter != count($settingsoutput))) { 8994 $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput); 8995 } 8996 // Just in case somebody modifies the list of active plugins directly. 8997 core_plugin_manager::reset_caches(); 8998 8999 return $settingsoutput; 9000 } 9001 9002 /** 9003 * Store changed settings, this function updates the errors variable in $ADMIN 9004 * 9005 * @param object $formdata from form 9006 * @return int number of changed settings 9007 */ 9008 function admin_write_settings($formdata) { 9009 global $CFG, $SITE, $DB; 9010 9011 $olddbsessions = !empty($CFG->dbsessions); 9012 $formdata = (array)$formdata; 9013 9014 $data = array(); 9015 foreach ($formdata as $fullname=>$value) { 9016 if (strpos($fullname, 's_') !== 0) { 9017 continue; // not a config value 9018 } 9019 $data[$fullname] = $value; 9020 } 9021 9022 $adminroot = admin_get_root(); 9023 $settings = admin_find_write_settings($adminroot, $data); 9024 9025 $count = 0; 9026 foreach ($settings as $fullname=>$setting) { 9027 /** @var $setting admin_setting */ 9028 $original = $setting->get_setting(); 9029 $error = $setting->write_setting($data[$fullname]); 9030 if ($error !== '') { 9031 $adminroot->errors[$fullname] = new stdClass(); 9032 $adminroot->errors[$fullname]->data = $data[$fullname]; 9033 $adminroot->errors[$fullname]->id = $setting->get_id(); 9034 $adminroot->errors[$fullname]->error = $error; 9035 } else { 9036 $setting->write_setting_flags($data); 9037 } 9038 if ($setting->post_write_settings($original)) { 9039 $count++; 9040 } 9041 } 9042 9043 if ($olddbsessions != !empty($CFG->dbsessions)) { 9044 require_logout(); 9045 } 9046 9047 // Now update $SITE - just update the fields, in case other people have a 9048 // a reference to it (e.g. $PAGE, $COURSE). 9049 $newsite = $DB->get_record('course', array('id'=>$SITE->id)); 9050 foreach (get_object_vars($newsite) as $field => $value) { 9051 $SITE->$field = $value; 9052 } 9053 9054 // now reload all settings - some of them might depend on the changed 9055 admin_get_root(true); 9056 return $count; 9057 } 9058 9059 /** 9060 * Internal recursive function - finds all settings from submitted form 9061 * 9062 * @param object $node Instance of admin_category, or admin_settingpage 9063 * @param array $data 9064 * @return array 9065 */ 9066 function admin_find_write_settings($node, $data) { 9067 $return = array(); 9068 9069 if (empty($data)) { 9070 return $return; 9071 } 9072 9073 if ($node instanceof admin_category) { 9074 if ($node->check_access()) { 9075 $entries = array_keys($node->children); 9076 foreach ($entries as $entry) { 9077 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data)); 9078 } 9079 } 9080 9081 } else if ($node instanceof admin_settingpage) { 9082 if ($node->check_access()) { 9083 foreach ($node->settings as $setting) { 9084 $fullname = $setting->get_full_name(); 9085 if (array_key_exists($fullname, $data)) { 9086 $return[$fullname] = $setting; 9087 } 9088 } 9089 } 9090 9091 } 9092 9093 return $return; 9094 } 9095 9096 /** 9097 * Internal function - prints the search results 9098 * 9099 * @param string $query String to search for 9100 * @return string empty or XHTML 9101 */ 9102 function admin_search_settings_html($query) { 9103 global $CFG, $OUTPUT, $PAGE; 9104 9105 if (core_text::strlen($query) < 2) { 9106 return ''; 9107 } 9108 $query = core_text::strtolower($query); 9109 9110 $adminroot = admin_get_root(); 9111 $findings = $adminroot->search($query); 9112 $savebutton = false; 9113 9114 $tpldata = (object) [ 9115 'actionurl' => $PAGE->url->out(false), 9116 'results' => [], 9117 'sesskey' => sesskey(), 9118 ]; 9119 9120 foreach ($findings as $found) { 9121 $page = $found->page; 9122 $settings = $found->settings; 9123 if ($page->is_hidden()) { 9124 // hidden pages are not displayed in search results 9125 continue; 9126 } 9127 9128 $heading = highlight($query, $page->visiblename); 9129 $headingurl = null; 9130 if ($page instanceof admin_externalpage) { 9131 $headingurl = new moodle_url($page->url); 9132 } else if ($page instanceof admin_settingpage) { 9133 $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]); 9134 } else { 9135 continue; 9136 } 9137 9138 // Locate the page in the admin root and populate its visiblepath attribute. 9139 $path = array(); 9140 $located = $adminroot->locate($page->name, true); 9141 if ($located) { 9142 foreach ($located->visiblepath as $pathitem) { 9143 array_unshift($path, (string) $pathitem); 9144 } 9145 } 9146 9147 $sectionsettings = []; 9148 if (!empty($settings)) { 9149 foreach ($settings as $setting) { 9150 if (empty($setting->nosave)) { 9151 $savebutton = true; 9152 } 9153 $fullname = $setting->get_full_name(); 9154 if (array_key_exists($fullname, $adminroot->errors)) { 9155 $data = $adminroot->errors[$fullname]->data; 9156 } else { 9157 $data = $setting->get_setting(); 9158 // do not use defaults if settings not available - upgradesettings handles the defaults! 9159 } 9160 $sectionsettings[] = $setting->output_html($data, $query); 9161 } 9162 } 9163 9164 $tpldata->results[] = (object) [ 9165 'title' => $heading, 9166 'path' => $path, 9167 'url' => $headingurl->out(false), 9168 'settings' => $sectionsettings 9169 ]; 9170 } 9171 9172 $tpldata->showsave = $savebutton; 9173 $tpldata->hasresults = !empty($tpldata->results); 9174 9175 return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata); 9176 } 9177 9178 /** 9179 * Internal function - returns arrays of html pages with uninitialised settings 9180 * 9181 * @param object $node Instance of admin_category or admin_settingpage 9182 * @return array 9183 */ 9184 function admin_output_new_settings_by_page($node) { 9185 global $OUTPUT; 9186 $return = array(); 9187 9188 if ($node instanceof admin_category) { 9189 $entries = array_keys($node->children); 9190 foreach ($entries as $entry) { 9191 $return += admin_output_new_settings_by_page($node->children[$entry]); 9192 } 9193 9194 } else if ($node instanceof admin_settingpage) { 9195 $newsettings = array(); 9196 foreach ($node->settings as $setting) { 9197 if (is_null($setting->get_setting())) { 9198 $newsettings[] = $setting; 9199 } 9200 } 9201 if (count($newsettings) > 0) { 9202 $adminroot = admin_get_root(); 9203 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main'); 9204 $page .= '<fieldset class="adminsettings">'."\n"; 9205 foreach ($newsettings as $setting) { 9206 $fullname = $setting->get_full_name(); 9207 if (array_key_exists($fullname, $adminroot->errors)) { 9208 $data = $adminroot->errors[$fullname]->data; 9209 } else { 9210 $data = $setting->get_setting(); 9211 if (is_null($data)) { 9212 $data = $setting->get_defaultsetting(); 9213 } 9214 } 9215 $page .= '<div class="clearer"><!-- --></div>'."\n"; 9216 $page .= $setting->output_html($data); 9217 } 9218 $page .= '</fieldset>'; 9219 $return[$node->name] = $page; 9220 } 9221 } 9222 9223 return $return; 9224 } 9225 9226 /** 9227 * Format admin settings 9228 * 9229 * @param object $setting 9230 * @param string $title label element 9231 * @param string $form form fragment, html code - not highlighted automatically 9232 * @param string $description 9233 * @param mixed $label link label to id, true by default or string being the label to connect it to 9234 * @param string $warning warning text 9235 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null 9236 * @param string $query search query to be highlighted 9237 * @return string XHTML 9238 */ 9239 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') { 9240 global $CFG, $OUTPUT; 9241 9242 $context = (object) [ 9243 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name", 9244 'fullname' => $setting->get_full_name(), 9245 ]; 9246 9247 // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate. 9248 if ($label === true) { 9249 $context->labelfor = $setting->get_id(); 9250 } else if ($label === false) { 9251 $context->labelfor = ''; 9252 } else { 9253 $context->labelfor = $label; 9254 } 9255 9256 $form .= $setting->output_setting_flags(); 9257 9258 $context->warning = $warning; 9259 $context->override = ''; 9260 if (empty($setting->plugin)) { 9261 if (array_key_exists($setting->name, $CFG->config_php_settings)) { 9262 $context->override = get_string('configoverride', 'admin'); 9263 } 9264 } else { 9265 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) { 9266 $context->override = get_string('configoverride', 'admin'); 9267 } 9268 } 9269 9270 $defaults = array(); 9271 if (!is_null($defaultinfo)) { 9272 if ($defaultinfo === '') { 9273 $defaultinfo = get_string('emptysettingvalue', 'admin'); 9274 } 9275 $defaults[] = $defaultinfo; 9276 } 9277 9278 $context->default = null; 9279 $setting->get_setting_flag_defaults($defaults); 9280 if (!empty($defaults)) { 9281 $defaultinfo = implode(', ', $defaults); 9282 $defaultinfo = highlight($query, nl2br(s($defaultinfo))); 9283 $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo); 9284 } 9285 9286 9287 $context->error = ''; 9288 $adminroot = admin_get_root(); 9289 if (array_key_exists($context->fullname, $adminroot->errors)) { 9290 $context->error = $adminroot->errors[$context->fullname]->error; 9291 } 9292 9293 if ($dependenton = $setting->get_dependent_on()) { 9294 $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton)); 9295 } 9296 9297 $context->id = 'admin-' . $setting->name; 9298 $context->title = highlightfast($query, $title); 9299 $context->name = highlightfast($query, $context->name); 9300 $context->description = highlight($query, markdown_to_html($description)); 9301 $context->element = $form; 9302 $context->forceltr = $setting->get_force_ltr(); 9303 $context->customcontrol = $setting->has_custom_form_control(); 9304 9305 return $OUTPUT->render_from_template('core_admin/setting', $context); 9306 } 9307 9308 /** 9309 * Based on find_new_settings{@link ()} in upgradesettings.php 9310 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any. 9311 * 9312 * @param object $node Instance of admin_category, or admin_settingpage 9313 * @return boolean true if any settings haven't been initialised, false if they all have 9314 */ 9315 function any_new_admin_settings($node) { 9316 9317 if ($node instanceof admin_category) { 9318 $entries = array_keys($node->children); 9319 foreach ($entries as $entry) { 9320 if (any_new_admin_settings($node->children[$entry])) { 9321 return true; 9322 } 9323 } 9324 9325 } else if ($node instanceof admin_settingpage) { 9326 foreach ($node->settings as $setting) { 9327 if ($setting->get_setting() === NULL) { 9328 return true; 9329 } 9330 } 9331 } 9332 9333 return false; 9334 } 9335 9336 /** 9337 * Given a table and optionally a column name should replaces be done? 9338 * 9339 * @param string $table name 9340 * @param string $column name 9341 * @return bool success or fail 9342 */ 9343 function db_should_replace($table, $column = '', $additionalskiptables = ''): bool { 9344 9345 // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing... 9346 $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions', 9347 'events_queue', 'repository_instance_config', 'block_instances', 'files']; 9348 9349 // Additional skip tables. 9350 if (!empty($additionalskiptables)) { 9351 $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '', $additionalskiptables))); 9352 } 9353 9354 // Don't process these. 9355 if (in_array($table, $skiptables)) { 9356 return false; 9357 } 9358 9359 // To be safe never replace inside a table that looks related to logging. 9360 if (preg_match('/(^|_)logs?($|_)/', $table)) { 9361 return false; 9362 } 9363 9364 // Do column based exclusions. 9365 if (!empty($column)) { 9366 // Don't touch anything that looks like a hash. 9367 if (preg_match('/hash$/', $column)) { 9368 return false; 9369 } 9370 } 9371 9372 return true; 9373 } 9374 9375 /** 9376 * Moved from admin/replace.php so that we can use this in cron 9377 * 9378 * @param string $search string to look for 9379 * @param string $replace string to replace 9380 * @return bool success or fail 9381 */ 9382 function db_replace($search, $replace, $additionalskiptables = '') { 9383 global $DB, $CFG, $OUTPUT; 9384 9385 // Turn off time limits, sometimes upgrades can be slow. 9386 core_php_time_limit::raise(); 9387 9388 if (!$tables = $DB->get_tables() ) { // No tables yet at all. 9389 return false; 9390 } 9391 foreach ($tables as $table) { 9392 9393 if (!db_should_replace($table, '', $additionalskiptables)) { 9394 continue; 9395 } 9396 9397 if ($columns = $DB->get_columns($table)) { 9398 $DB->set_debug(true); 9399 foreach ($columns as $column) { 9400 if (!db_should_replace($table, $column->name)) { 9401 continue; 9402 } 9403 $DB->replace_all_text($table, $column, $search, $replace); 9404 } 9405 $DB->set_debug(false); 9406 } 9407 } 9408 9409 // delete modinfo caches 9410 rebuild_course_cache(0, true); 9411 9412 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks... 9413 $blocks = core_component::get_plugin_list('block'); 9414 foreach ($blocks as $blockname=>$fullblock) { 9415 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it 9416 continue; 9417 } 9418 9419 if (!is_readable($fullblock.'/lib.php')) { 9420 continue; 9421 } 9422 9423 $function = 'block_'.$blockname.'_global_db_replace'; 9424 include_once($fullblock.'/lib.php'); 9425 if (!function_exists($function)) { 9426 continue; 9427 } 9428 9429 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess'); 9430 $function($search, $replace); 9431 echo $OUTPUT->notification("...finished", 'notifysuccess'); 9432 } 9433 9434 // Trigger an event. 9435 $eventargs = [ 9436 'context' => context_system::instance(), 9437 'other' => [ 9438 'search' => $search, 9439 'replace' => $replace 9440 ] 9441 ]; 9442 $event = \core\event\database_text_field_content_replaced::create($eventargs); 9443 $event->trigger(); 9444 9445 purge_all_caches(); 9446 9447 return true; 9448 } 9449 9450 /** 9451 * Manage repository settings 9452 * 9453 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 9454 */ 9455 class admin_setting_managerepository extends admin_setting { 9456 /** @var string */ 9457 private $baseurl; 9458 9459 /** 9460 * calls parent::__construct with specific arguments 9461 */ 9462 public function __construct() { 9463 global $CFG; 9464 parent::__construct('managerepository', get_string('manage', 'repository'), '', ''); 9465 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey(); 9466 } 9467 9468 /** 9469 * Always returns true, does nothing 9470 * 9471 * @return true 9472 */ 9473 public function get_setting() { 9474 return true; 9475 } 9476 9477 /** 9478 * Always returns true does nothing 9479 * 9480 * @return true 9481 */ 9482 public function get_defaultsetting() { 9483 return true; 9484 } 9485 9486 /** 9487 * Always returns s_managerepository 9488 * 9489 * @return string Always return 's_managerepository' 9490 */ 9491 public function get_full_name() { 9492 return 's_managerepository'; 9493 } 9494 9495 /** 9496 * Always returns '' doesn't do anything 9497 */ 9498 public function write_setting($data) { 9499 $url = $this->baseurl . '&new=' . $data; 9500 return ''; 9501 // TODO 9502 // Should not use redirect and exit here 9503 // Find a better way to do this. 9504 // redirect($url); 9505 // exit; 9506 } 9507 9508 /** 9509 * Searches repository plugins for one that matches $query 9510 * 9511 * @param string $query The string to search for 9512 * @return bool true if found, false if not 9513 */ 9514 public function is_related($query) { 9515 if (parent::is_related($query)) { 9516 return true; 9517 } 9518 9519 $repositories= core_component::get_plugin_list('repository'); 9520 foreach ($repositories as $p => $dir) { 9521 if (strpos($p, $query) !== false) { 9522 return true; 9523 } 9524 } 9525 foreach (repository::get_types() as $instance) { 9526 $title = $instance->get_typename(); 9527 if (strpos(core_text::strtolower($title), $query) !== false) { 9528 return true; 9529 } 9530 } 9531 return false; 9532 } 9533 9534 /** 9535 * Helper function that generates a moodle_url object 9536 * relevant to the repository 9537 */ 9538 9539 function repository_action_url($repository) { 9540 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository)); 9541 } 9542 9543 /** 9544 * Builds XHTML to display the control 9545 * 9546 * @param string $data Unused 9547 * @param string $query 9548 * @return string XHTML 9549 */ 9550 public function output_html($data, $query='') { 9551 global $CFG, $USER, $OUTPUT; 9552 9553 // Get strings that are used 9554 $strshow = get_string('on', 'repository'); 9555 $strhide = get_string('off', 'repository'); 9556 $strdelete = get_string('disabled', 'repository'); 9557 9558 $actionchoicesforexisting = array( 9559 'show' => $strshow, 9560 'hide' => $strhide, 9561 'delete' => $strdelete 9562 ); 9563 9564 $actionchoicesfornew = array( 9565 'newon' => $strshow, 9566 'newoff' => $strhide, 9567 'delete' => $strdelete 9568 ); 9569 9570 $return = ''; 9571 $return .= $OUTPUT->box_start('generalbox'); 9572 9573 // Set strings that are used multiple times 9574 $settingsstr = get_string('settings'); 9575 $disablestr = get_string('disable'); 9576 9577 // Table to list plug-ins 9578 $table = new html_table(); 9579 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr); 9580 $table->align = array('left', 'center', 'center', 'center', 'center'); 9581 $table->data = array(); 9582 9583 // Get list of used plug-ins 9584 $repositorytypes = repository::get_types(); 9585 if (!empty($repositorytypes)) { 9586 // Array to store plugins being used 9587 $alreadyplugins = array(); 9588 $totalrepositorytypes = count($repositorytypes); 9589 $updowncount = 1; 9590 foreach ($repositorytypes as $i) { 9591 $settings = ''; 9592 $typename = $i->get_typename(); 9593 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config) 9594 $typeoptionnames = repository::static_function($typename, 'get_type_option_names'); 9595 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names'); 9596 9597 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) { 9598 // Calculate number of instances in order to display them for the Moodle administrator 9599 if (!empty($instanceoptionnames)) { 9600 $params = array(); 9601 $params['context'] = array(context_system::instance()); 9602 $params['onlyvisible'] = false; 9603 $params['type'] = $typename; 9604 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params)); 9605 // site instances 9606 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber); 9607 $params['context'] = array(); 9608 $instances = repository::static_function($typename, 'get_instances', $params); 9609 $courseinstances = array(); 9610 $userinstances = array(); 9611 9612 foreach ($instances as $instance) { 9613 $repocontext = context::instance_by_id($instance->instance->contextid); 9614 if ($repocontext->contextlevel == CONTEXT_COURSE) { 9615 $courseinstances[] = $instance; 9616 } else if ($repocontext->contextlevel == CONTEXT_USER) { 9617 $userinstances[] = $instance; 9618 } 9619 } 9620 // course instances 9621 $instancenumber = count($courseinstances); 9622 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber); 9623 9624 // user private instances 9625 $instancenumber = count($userinstances); 9626 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber); 9627 } else { 9628 $admininstancenumbertext = ""; 9629 $courseinstancenumbertext = ""; 9630 $userinstancenumbertext = ""; 9631 } 9632 9633 $settings .= '<a href="' . $this->baseurl . '&action=edit&repos=' . $typename . '">' . $settingsstr .'</a>'; 9634 9635 $settings .= $OUTPUT->container_start('mdl-left'); 9636 $settings .= '<br/>'; 9637 $settings .= $admininstancenumbertext; 9638 $settings .= '<br/>'; 9639 $settings .= $courseinstancenumbertext; 9640 $settings .= '<br/>'; 9641 $settings .= $userinstancenumbertext; 9642 $settings .= $OUTPUT->container_end(); 9643 } 9644 // Get the current visibility 9645 if ($i->get_visible()) { 9646 $currentaction = 'show'; 9647 } else { 9648 $currentaction = 'hide'; 9649 } 9650 9651 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename)); 9652 9653 // Display up/down link 9654 $updown = ''; 9655 // Should be done with CSS instead. 9656 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon')); 9657 9658 if ($updowncount > 1) { 9659 $updown .= "<a href=\"$this->baseurl&action=moveup&repos=".$typename."\">"; 9660 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a> '; 9661 } 9662 else { 9663 $updown .= $spacer; 9664 } 9665 if ($updowncount < $totalrepositorytypes) { 9666 $updown .= "<a href=\"$this->baseurl&action=movedown&repos=".$typename."\">"; 9667 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a> '; 9668 } 9669 else { 9670 $updown .= $spacer; 9671 } 9672 9673 $updowncount++; 9674 9675 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings); 9676 9677 if (!in_array($typename, $alreadyplugins)) { 9678 $alreadyplugins[] = $typename; 9679 } 9680 } 9681 } 9682 9683 // Get all the plugins that exist on disk 9684 $plugins = core_component::get_plugin_list('repository'); 9685 if (!empty($plugins)) { 9686 foreach ($plugins as $plugin => $dir) { 9687 // Check that it has not already been listed 9688 if (!in_array($plugin, $alreadyplugins)) { 9689 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin)); 9690 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', ''); 9691 } 9692 } 9693 } 9694 9695 $return .= html_writer::table($table); 9696 $return .= $OUTPUT->box_end(); 9697 return highlight($query, $return); 9698 } 9699 } 9700 9701 /** 9702 * Special checkbox for enable mobile web service 9703 * If enable then we store the service id of the mobile service into config table 9704 * If disable then we unstore the service id from the config table 9705 */ 9706 class admin_setting_enablemobileservice extends admin_setting_configcheckbox { 9707 9708 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */ 9709 private $restuse; 9710 9711 /** 9712 * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false. 9713 * 9714 * @return boolean 9715 */ 9716 private function is_protocol_cap_allowed() { 9717 global $DB, $CFG; 9718 9719 // If the $this->restuse variable is not set, it needs to be set. 9720 if (empty($this->restuse) and $this->restuse!==false) { 9721 $params = array(); 9722 $params['permission'] = CAP_ALLOW; 9723 $params['roleid'] = $CFG->defaultuserroleid; 9724 $params['capability'] = 'webservice/rest:use'; 9725 $this->restuse = $DB->record_exists('role_capabilities', $params); 9726 } 9727 9728 return $this->restuse; 9729 } 9730 9731 /** 9732 * Set the 'webservice/rest:use' to the Authenticated user role (allow or not) 9733 * @param type $status true to allow, false to not set 9734 */ 9735 private function set_protocol_cap($status) { 9736 global $CFG; 9737 if ($status and !$this->is_protocol_cap_allowed()) { 9738 //need to allow the cap 9739 $permission = CAP_ALLOW; 9740 $assign = true; 9741 } else if (!$status and $this->is_protocol_cap_allowed()){ 9742 //need to disallow the cap 9743 $permission = CAP_INHERIT; 9744 $assign = true; 9745 } 9746 if (!empty($assign)) { 9747 $systemcontext = context_system::instance(); 9748 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true); 9749 } 9750 } 9751 9752 /** 9753 * Builds XHTML to display the control. 9754 * The main purpose of this overloading is to display a warning when https 9755 * is not supported by the server 9756 * @param string $data Unused 9757 * @param string $query 9758 * @return string XHTML 9759 */ 9760 public function output_html($data, $query='') { 9761 global $OUTPUT; 9762 $html = parent::output_html($data, $query); 9763 9764 if ((string)$data === $this->yes) { 9765 $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here. 9766 foreach ($notifications as $notification) { 9767 $message = get_string($notification[0], $notification[1]); 9768 $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING); 9769 } 9770 } 9771 9772 return $html; 9773 } 9774 9775 /** 9776 * Retrieves the current setting using the objects name 9777 * 9778 * @return string 9779 */ 9780 public function get_setting() { 9781 global $CFG; 9782 9783 // First check if is not set. 9784 $result = $this->config_read($this->name); 9785 if (is_null($result)) { 9786 return null; 9787 } 9788 9789 // For install cli script, $CFG->defaultuserroleid is not set so return 0 9790 // Or if web services aren't enabled this can't be, 9791 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) { 9792 return 0; 9793 } 9794 9795 require_once($CFG->dirroot . '/webservice/lib.php'); 9796 $webservicemanager = new webservice(); 9797 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 9798 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) { 9799 return $result; 9800 } else { 9801 return 0; 9802 } 9803 } 9804 9805 /** 9806 * Save the selected setting 9807 * 9808 * @param string $data The selected site 9809 * @return string empty string or error message 9810 */ 9811 public function write_setting($data) { 9812 global $DB, $CFG; 9813 9814 //for install cli script, $CFG->defaultuserroleid is not set so do nothing 9815 if (empty($CFG->defaultuserroleid)) { 9816 return ''; 9817 } 9818 9819 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE; 9820 9821 require_once($CFG->dirroot . '/webservice/lib.php'); 9822 $webservicemanager = new webservice(); 9823 9824 $updateprotocol = false; 9825 if ((string)$data === $this->yes) { 9826 //code run when enable mobile web service 9827 //enable web service systeme if necessary 9828 set_config('enablewebservices', true); 9829 9830 //enable mobile service 9831 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 9832 $mobileservice->enabled = 1; 9833 $webservicemanager->update_external_service($mobileservice); 9834 9835 // Enable REST server. 9836 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols); 9837 9838 if (!in_array('rest', $activeprotocols)) { 9839 $activeprotocols[] = 'rest'; 9840 $updateprotocol = true; 9841 } 9842 9843 if ($updateprotocol) { 9844 set_config('webserviceprotocols', implode(',', $activeprotocols)); 9845 } 9846 9847 // Allow rest:use capability for authenticated user. 9848 $this->set_protocol_cap(true); 9849 } else { 9850 // Disable the mobile service. 9851 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE); 9852 $mobileservice->enabled = 0; 9853 $webservicemanager->update_external_service($mobileservice); 9854 } 9855 9856 return (parent::write_setting($data)); 9857 } 9858 } 9859 9860 /** 9861 * Special class for management of external services 9862 * 9863 * @author Petr Skoda (skodak) 9864 */ 9865 class admin_setting_manageexternalservices extends admin_setting { 9866 /** 9867 * Calls parent::__construct with specific arguments 9868 */ 9869 public function __construct() { 9870 $this->nosave = true; 9871 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', ''); 9872 } 9873 9874 /** 9875 * Always returns true, does nothing 9876 * 9877 * @return true 9878 */ 9879 public function get_setting() { 9880 return true; 9881 } 9882 9883 /** 9884 * Always returns true, does nothing 9885 * 9886 * @return true 9887 */ 9888 public function get_defaultsetting() { 9889 return true; 9890 } 9891 9892 /** 9893 * Always returns '', does not write anything 9894 * 9895 * @return string Always returns '' 9896 */ 9897 public function write_setting($data) { 9898 // do not write any setting 9899 return ''; 9900 } 9901 9902 /** 9903 * Checks if $query is one of the available external services 9904 * 9905 * @param string $query The string to search for 9906 * @return bool Returns true if found, false if not 9907 */ 9908 public function is_related($query) { 9909 global $DB; 9910 9911 if (parent::is_related($query)) { 9912 return true; 9913 } 9914 9915 $services = $DB->get_records('external_services', array(), 'id, name'); 9916 foreach ($services as $service) { 9917 if (strpos(core_text::strtolower($service->name), $query) !== false) { 9918 return true; 9919 } 9920 } 9921 return false; 9922 } 9923 9924 /** 9925 * Builds the XHTML to display the control 9926 * 9927 * @param string $data Unused 9928 * @param string $query 9929 * @return string 9930 */ 9931 public function output_html($data, $query='') { 9932 global $CFG, $OUTPUT, $DB; 9933 9934 // display strings 9935 $stradministration = get_string('administration'); 9936 $stredit = get_string('edit'); 9937 $strservice = get_string('externalservice', 'webservice'); 9938 $strdelete = get_string('delete'); 9939 $strplugin = get_string('plugin', 'admin'); 9940 $stradd = get_string('add'); 9941 $strfunctions = get_string('functions', 'webservice'); 9942 $strusers = get_string('users'); 9943 $strserviceusers = get_string('serviceusers', 'webservice'); 9944 9945 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php"; 9946 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php"; 9947 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php"; 9948 9949 // built in services 9950 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name'); 9951 $return = ""; 9952 if (!empty($services)) { 9953 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main'); 9954 9955 9956 9957 $table = new html_table(); 9958 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit); 9959 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign '); 9960 $table->id = 'builtinservices'; 9961 $table->attributes['class'] = 'admintable externalservices generaltable'; 9962 $table->data = array(); 9963 9964 // iterate through auth plugins and add to the display table 9965 foreach ($services as $service) { 9966 $name = $service->name; 9967 9968 // hide/show link 9969 if ($service->enabled) { 9970 $displayname = "<span>$name</span>"; 9971 } else { 9972 $displayname = "<span class=\"dimmed_text\">$name</span>"; 9973 } 9974 9975 $plugin = $service->component; 9976 9977 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>"; 9978 9979 if ($service->restrictedusers) { 9980 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>"; 9981 } else { 9982 $users = get_string('allusers', 'webservice'); 9983 } 9984 9985 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>"; 9986 9987 // add a row to the table 9988 $table->data[] = array($displayname, $plugin, $functions, $users, $edit); 9989 } 9990 $return .= html_writer::table($table); 9991 } 9992 9993 // Custom services 9994 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main'); 9995 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name'); 9996 9997 $table = new html_table(); 9998 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit); 9999 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign '); 10000 $table->id = 'customservices'; 10001 $table->attributes['class'] = 'admintable externalservices generaltable'; 10002 $table->data = array(); 10003 10004 // iterate through auth plugins and add to the display table 10005 foreach ($services as $service) { 10006 $name = $service->name; 10007 10008 // hide/show link 10009 if ($service->enabled) { 10010 $displayname = "<span>$name</span>"; 10011 } else { 10012 $displayname = "<span class=\"dimmed_text\">$name</span>"; 10013 } 10014 10015 // delete link 10016 $delete = "<a href=\"$esurl?action=delete&sesskey=".sesskey()."&id=$service->id\">$strdelete</a>"; 10017 10018 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>"; 10019 10020 if ($service->restrictedusers) { 10021 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>"; 10022 } else { 10023 $users = get_string('allusers', 'webservice'); 10024 } 10025 10026 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>"; 10027 10028 // add a row to the table 10029 $table->data[] = array($displayname, $delete, $functions, $users, $edit); 10030 } 10031 // add new custom service option 10032 $return .= html_writer::table($table); 10033 10034 $return .= '<br />'; 10035 // add a token to the table 10036 $return .= "<a href=\"$esurl?id=0\">$stradd</a>"; 10037 10038 return highlight($query, $return); 10039 } 10040 } 10041 10042 /** 10043 * Special class for overview of external services 10044 * 10045 * @author Jerome Mouneyrac 10046 */ 10047 class admin_setting_webservicesoverview extends admin_setting { 10048 10049 /** 10050 * Calls parent::__construct with specific arguments 10051 */ 10052 public function __construct() { 10053 $this->nosave = true; 10054 parent::__construct('webservicesoverviewui', 10055 get_string('webservicesoverview', 'webservice'), '', ''); 10056 } 10057 10058 /** 10059 * Always returns true, does nothing 10060 * 10061 * @return true 10062 */ 10063 public function get_setting() { 10064 return true; 10065 } 10066 10067 /** 10068 * Always returns true, does nothing 10069 * 10070 * @return true 10071 */ 10072 public function get_defaultsetting() { 10073 return true; 10074 } 10075 10076 /** 10077 * Always returns '', does not write anything 10078 * 10079 * @return string Always returns '' 10080 */ 10081 public function write_setting($data) { 10082 // do not write any setting 10083 return ''; 10084 } 10085 10086 /** 10087 * Builds the XHTML to display the control 10088 * 10089 * @param string $data Unused 10090 * @param string $query 10091 * @return string 10092 */ 10093 public function output_html($data, $query='') { 10094 global $CFG, $OUTPUT; 10095 10096 $return = ""; 10097 $brtag = html_writer::empty_tag('br'); 10098 10099 /// One system controlling Moodle with Token 10100 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main'); 10101 $table = new html_table(); 10102 $table->head = array(get_string('step', 'webservice'), get_string('status'), 10103 get_string('description')); 10104 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description'); 10105 $table->id = 'onesystemcontrol'; 10106 $table->attributes['class'] = 'admintable wsoverview generaltable'; 10107 $table->data = array(); 10108 10109 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice') 10110 . $brtag . $brtag; 10111 10112 /// 1. Enable Web Services 10113 $row = array(); 10114 $url = new moodle_url("/admin/search.php?query=enablewebservices"); 10115 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'), 10116 array('href' => $url)); 10117 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 10118 if ($CFG->enablewebservices) { 10119 $status = get_string('yes'); 10120 } 10121 $row[1] = $status; 10122 $row[2] = get_string('enablewsdescription', 'webservice'); 10123 $table->data[] = $row; 10124 10125 /// 2. Enable protocols 10126 $row = array(); 10127 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols"); 10128 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'), 10129 array('href' => $url)); 10130 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger')); 10131 //retrieve activated protocol 10132 $active_protocols = empty($CFG->webserviceprotocols) ? 10133 array() : explode(',', $CFG->webserviceprotocols); 10134 if (!empty($active_protocols)) { 10135 $status = ""; 10136 foreach ($active_protocols as $protocol) { 10137 $status .= $protocol . $brtag; 10138 } 10139 } 10140 $row[1] = $status; 10141 $row[2] = get_string('enableprotocolsdescription', 'webservice'); 10142 $table->data[] = $row; 10143 10144 /// 3. Create user account 10145 $row = array(); 10146 $url = new moodle_url("/user/editadvanced.php?id=-1"); 10147 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'), 10148 array('href' => $url)); 10149 $row[1] = ""; 10150 $row[2] = get_string('createuserdescription', 'webservice'); 10151 $table->data[] = $row; 10152 10153 /// 4. Add capability to users 10154 $row = array(); 10155 $url = new moodle_url("/admin/roles/check.php?contextid=1"); 10156 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'), 10157 array('href' => $url)); 10158 $row[1] = ""; 10159 $row[2] = get_string('checkusercapabilitydescription', 'webservice'); 10160 $table->data[] = $row; 10161 10162 /// 5. Select a web service 10163 $row = array(); 10164 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10165 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'), 10166 array('href' => $url)); 10167 $row[1] = ""; 10168 $row[2] = get_string('createservicedescription', 'webservice'); 10169 $table->data[] = $row; 10170 10171 /// 6. Add functions 10172 $row = array(); 10173 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10174 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'), 10175 array('href' => $url)); 10176 $row[1] = ""; 10177 $row[2] = get_string('addfunctionsdescription', 'webservice'); 10178 $table->data[] = $row; 10179 10180 /// 7. Add the specific user 10181 $row = array(); 10182 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10183 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'), 10184 array('href' => $url)); 10185 $row[1] = ""; 10186 $row[2] = get_string('selectspecificuserdescription', 'webservice'); 10187 $table->data[] = $row; 10188 10189 /// 8. Create token for the specific user 10190 $row = array(); 10191 $url = new moodle_url('/admin/webservice/tokens.php', ['action' => 'create']); 10192 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'), 10193 array('href' => $url)); 10194 $row[1] = ""; 10195 $row[2] = get_string('createtokenforuserdescription', 'webservice'); 10196 $table->data[] = $row; 10197 10198 /// 9. Enable the documentation 10199 $row = array(); 10200 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation"); 10201 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'), 10202 array('href' => $url)); 10203 $status = '<span class="warning">' . get_string('no') . '</span>'; 10204 if ($CFG->enablewsdocumentation) { 10205 $status = get_string('yes'); 10206 } 10207 $row[1] = $status; 10208 $row[2] = get_string('enabledocumentationdescription', 'webservice'); 10209 $table->data[] = $row; 10210 10211 /// 10. Test the service 10212 $row = array(); 10213 $url = new moodle_url("/admin/webservice/testclient.php"); 10214 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'), 10215 array('href' => $url)); 10216 $row[1] = ""; 10217 $row[2] = get_string('testwithtestclientdescription', 'webservice'); 10218 $table->data[] = $row; 10219 10220 $return .= html_writer::table($table); 10221 10222 /// Users as clients with token 10223 $return .= $brtag . $brtag . $brtag; 10224 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main'); 10225 $table = new html_table(); 10226 $table->head = array(get_string('step', 'webservice'), get_string('status'), 10227 get_string('description')); 10228 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description'); 10229 $table->id = 'userasclients'; 10230 $table->attributes['class'] = 'admintable wsoverview generaltable'; 10231 $table->data = array(); 10232 10233 $return .= $brtag . get_string('userasclientsdescription', 'webservice') . 10234 $brtag . $brtag; 10235 10236 /// 1. Enable Web Services 10237 $row = array(); 10238 $url = new moodle_url("/admin/search.php?query=enablewebservices"); 10239 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'), 10240 array('href' => $url)); 10241 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 10242 if ($CFG->enablewebservices) { 10243 $status = get_string('yes'); 10244 } 10245 $row[1] = $status; 10246 $row[2] = get_string('enablewsdescription', 'webservice'); 10247 $table->data[] = $row; 10248 10249 /// 2. Enable protocols 10250 $row = array(); 10251 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols"); 10252 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'), 10253 array('href' => $url)); 10254 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger')); 10255 //retrieve activated protocol 10256 $active_protocols = empty($CFG->webserviceprotocols) ? 10257 array() : explode(',', $CFG->webserviceprotocols); 10258 if (!empty($active_protocols)) { 10259 $status = ""; 10260 foreach ($active_protocols as $protocol) { 10261 $status .= $protocol . $brtag; 10262 } 10263 } 10264 $row[1] = $status; 10265 $row[2] = get_string('enableprotocolsdescription', 'webservice'); 10266 $table->data[] = $row; 10267 10268 10269 /// 3. Select a web service 10270 $row = array(); 10271 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10272 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'), 10273 array('href' => $url)); 10274 $row[1] = ""; 10275 $row[2] = get_string('createserviceforusersdescription', 'webservice'); 10276 $table->data[] = $row; 10277 10278 /// 4. Add functions 10279 $row = array(); 10280 $url = new moodle_url("/admin/settings.php?section=externalservices"); 10281 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'), 10282 array('href' => $url)); 10283 $row[1] = ""; 10284 $row[2] = get_string('addfunctionsdescription', 'webservice'); 10285 $table->data[] = $row; 10286 10287 /// 5. Add capability to users 10288 $row = array(); 10289 $url = new moodle_url("/admin/roles/check.php?contextid=1"); 10290 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'), 10291 array('href' => $url)); 10292 $row[1] = ""; 10293 $row[2] = get_string('addcapabilitytousersdescription', 'webservice'); 10294 $table->data[] = $row; 10295 10296 /// 6. Test the service 10297 $row = array(); 10298 $url = new moodle_url("/admin/webservice/testclient.php"); 10299 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'), 10300 array('href' => $url)); 10301 $row[1] = ""; 10302 $row[2] = get_string('testauserwithtestclientdescription', 'webservice'); 10303 $table->data[] = $row; 10304 10305 $return .= html_writer::table($table); 10306 10307 return highlight($query, $return); 10308 } 10309 10310 } 10311 10312 10313 /** 10314 * Special class for web service protocol administration. 10315 * 10316 * @author Petr Skoda (skodak) 10317 */ 10318 class admin_setting_managewebserviceprotocols extends admin_setting { 10319 10320 /** 10321 * Calls parent::__construct with specific arguments 10322 */ 10323 public function __construct() { 10324 $this->nosave = true; 10325 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', ''); 10326 } 10327 10328 /** 10329 * Always returns true, does nothing 10330 * 10331 * @return true 10332 */ 10333 public function get_setting() { 10334 return true; 10335 } 10336 10337 /** 10338 * Always returns true, does nothing 10339 * 10340 * @return true 10341 */ 10342 public function get_defaultsetting() { 10343 return true; 10344 } 10345 10346 /** 10347 * Always returns '', does not write anything 10348 * 10349 * @return string Always returns '' 10350 */ 10351 public function write_setting($data) { 10352 // do not write any setting 10353 return ''; 10354 } 10355 10356 /** 10357 * Checks if $query is one of the available webservices 10358 * 10359 * @param string $query The string to search for 10360 * @return bool Returns true if found, false if not 10361 */ 10362 public function is_related($query) { 10363 if (parent::is_related($query)) { 10364 return true; 10365 } 10366 10367 $protocols = core_component::get_plugin_list('webservice'); 10368 foreach ($protocols as $protocol=>$location) { 10369 if (strpos($protocol, $query) !== false) { 10370 return true; 10371 } 10372 $protocolstr = get_string('pluginname', 'webservice_'.$protocol); 10373 if (strpos(core_text::strtolower($protocolstr), $query) !== false) { 10374 return true; 10375 } 10376 } 10377 return false; 10378 } 10379 10380 /** 10381 * Builds the XHTML to display the control 10382 * 10383 * @param string $data Unused 10384 * @param string $query 10385 * @return string 10386 */ 10387 public function output_html($data, $query='') { 10388 global $CFG, $OUTPUT; 10389 10390 // display strings 10391 $stradministration = get_string('administration'); 10392 $strsettings = get_string('settings'); 10393 $stredit = get_string('edit'); 10394 $strprotocol = get_string('protocol', 'webservice'); 10395 $strenable = get_string('enable'); 10396 $strdisable = get_string('disable'); 10397 $strversion = get_string('version'); 10398 10399 $protocols_available = core_component::get_plugin_list('webservice'); 10400 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols); 10401 ksort($protocols_available); 10402 10403 foreach ($activeprotocols as $key => $protocol) { 10404 if (empty($protocols_available[$protocol])) { 10405 unset($activeprotocols[$key]); 10406 } 10407 } 10408 10409 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main'); 10410 if (in_array('xmlrpc', $activeprotocols)) { 10411 $notify = new \core\output\notification(get_string('xmlrpcwebserviceenabled', 'admin'), 10412 \core\output\notification::NOTIFY_WARNING); 10413 $return .= $OUTPUT->render($notify); 10414 } 10415 $return .= $OUTPUT->box_start('generalbox webservicesui'); 10416 10417 $table = new html_table(); 10418 $table->head = array($strprotocol, $strversion, $strenable, $strsettings); 10419 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign'); 10420 $table->id = 'webserviceprotocols'; 10421 $table->attributes['class'] = 'admintable generaltable'; 10422 $table->data = array(); 10423 10424 // iterate through auth plugins and add to the display table 10425 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey(); 10426 foreach ($protocols_available as $protocol => $location) { 10427 $name = get_string('pluginname', 'webservice_'.$protocol); 10428 10429 $plugin = new stdClass(); 10430 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) { 10431 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php'); 10432 } 10433 $version = isset($plugin->version) ? $plugin->version : ''; 10434 10435 // hide/show link 10436 if (in_array($protocol, $activeprotocols)) { 10437 $hideshow = "<a href=\"$url&action=disable&webservice=$protocol\">"; 10438 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>'; 10439 $displayname = "<span>$name</span>"; 10440 } else { 10441 $hideshow = "<a href=\"$url&action=enable&webservice=$protocol\">"; 10442 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>'; 10443 $displayname = "<span class=\"dimmed_text\">$name</span>"; 10444 } 10445 10446 // settings link 10447 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) { 10448 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>"; 10449 } else { 10450 $settings = ''; 10451 } 10452 10453 // add a row to the table 10454 $table->data[] = array($displayname, $version, $hideshow, $settings); 10455 } 10456 $return .= html_writer::table($table); 10457 $return .= get_string('configwebserviceplugins', 'webservice'); 10458 $return .= $OUTPUT->box_end(); 10459 10460 return highlight($query, $return); 10461 } 10462 } 10463 10464 /** 10465 * Colour picker 10466 * 10467 * @copyright 2010 Sam Hemelryk 10468 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10469 */ 10470 class admin_setting_configcolourpicker extends admin_setting { 10471 10472 /** 10473 * Information for previewing the colour 10474 * 10475 * @var array|null 10476 */ 10477 protected $previewconfig = null; 10478 10479 /** 10480 * Use default when empty. 10481 */ 10482 protected $usedefaultwhenempty = true; 10483 10484 /** 10485 * 10486 * @param string $name 10487 * @param string $visiblename 10488 * @param string $description 10489 * @param string $defaultsetting 10490 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor'); 10491 */ 10492 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null, 10493 $usedefaultwhenempty = true) { 10494 $this->previewconfig = $previewconfig; 10495 $this->usedefaultwhenempty = $usedefaultwhenempty; 10496 parent::__construct($name, $visiblename, $description, $defaultsetting); 10497 $this->set_force_ltr(true); 10498 } 10499 10500 /** 10501 * Return the setting 10502 * 10503 * @return mixed returns config if successful else null 10504 */ 10505 public function get_setting() { 10506 return $this->config_read($this->name); 10507 } 10508 10509 /** 10510 * Saves the setting 10511 * 10512 * @param string $data 10513 * @return bool 10514 */ 10515 public function write_setting($data) { 10516 $data = $this->validate($data); 10517 if ($data === false) { 10518 return get_string('validateerror', 'admin'); 10519 } 10520 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 10521 } 10522 10523 /** 10524 * Validates the colour that was entered by the user 10525 * 10526 * @param string $data 10527 * @return string|false 10528 */ 10529 protected function validate($data) { 10530 /** 10531 * List of valid HTML colour names 10532 * 10533 * @var array 10534 */ 10535 $colornames = array( 10536 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 10537 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 10538 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 10539 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 10540 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 10541 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta', 10542 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 10543 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 10544 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 10545 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 10546 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 10547 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green', 10548 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo', 10549 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 10550 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 10551 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen', 10552 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 10553 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 10554 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 10555 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 10556 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 10557 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 10558 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 10559 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 10560 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 10561 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red', 10562 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 10563 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 10564 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 10565 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 10566 'whitesmoke', 'yellow', 'yellowgreen' 10567 ); 10568 10569 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) { 10570 if (strpos($data, '#')!==0) { 10571 $data = '#'.$data; 10572 } 10573 return $data; 10574 } else if (in_array(strtolower($data), $colornames)) { 10575 return $data; 10576 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) { 10577 return $data; 10578 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) { 10579 return $data; 10580 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) { 10581 return $data; 10582 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) { 10583 return $data; 10584 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) { 10585 return $data; 10586 } else if (empty($data)) { 10587 if ($this->usedefaultwhenempty){ 10588 return $this->defaultsetting; 10589 } else { 10590 return ''; 10591 } 10592 } else { 10593 return false; 10594 } 10595 } 10596 10597 /** 10598 * Generates the HTML for the setting 10599 * 10600 * @global moodle_page $PAGE 10601 * @global core_renderer $OUTPUT 10602 * @param string $data 10603 * @param string $query 10604 */ 10605 public function output_html($data, $query = '') { 10606 global $PAGE, $OUTPUT; 10607 10608 $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']); 10609 $context = (object) [ 10610 'id' => $this->get_id(), 10611 'name' => $this->get_full_name(), 10612 'value' => $data, 10613 'icon' => $icon->export_for_template($OUTPUT), 10614 'haspreviewconfig' => !empty($this->previewconfig), 10615 'forceltr' => $this->get_force_ltr(), 10616 'readonly' => $this->is_readonly(), 10617 ]; 10618 10619 $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context); 10620 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig)); 10621 10622 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', 10623 $this->get_defaultsetting(), $query); 10624 } 10625 10626 } 10627 10628 10629 /** 10630 * Class used for uploading of one file into file storage, 10631 * the file name is stored in config table. 10632 * 10633 * Please note you need to implement your own '_pluginfile' callback function, 10634 * this setting only stores the file, it does not deal with file serving. 10635 * 10636 * @copyright 2013 Petr Skoda {@link http://skodak.org} 10637 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10638 */ 10639 class admin_setting_configstoredfile extends admin_setting { 10640 /** @var array file area options - should be one file only */ 10641 protected $options; 10642 /** @var string name of the file area */ 10643 protected $filearea; 10644 /** @var int intemid */ 10645 protected $itemid; 10646 /** @var string used for detection of changes */ 10647 protected $oldhashes; 10648 10649 /** 10650 * Create new stored file setting. 10651 * 10652 * @param string $name low level setting name 10653 * @param string $visiblename human readable setting name 10654 * @param string $description description of setting 10655 * @param mixed $filearea file area for file storage 10656 * @param int $itemid itemid for file storage 10657 * @param array $options file area options 10658 */ 10659 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) { 10660 parent::__construct($name, $visiblename, $description, ''); 10661 $this->filearea = $filearea; 10662 $this->itemid = $itemid; 10663 $this->options = (array)$options; 10664 $this->customcontrol = true; 10665 } 10666 10667 /** 10668 * Applies defaults and returns all options. 10669 * @return array 10670 */ 10671 protected function get_options() { 10672 global $CFG; 10673 10674 require_once("$CFG->libdir/filelib.php"); 10675 require_once("$CFG->dirroot/repository/lib.php"); 10676 $defaults = array( 10677 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1, 10678 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED, 10679 'context' => context_system::instance()); 10680 foreach($this->options as $k => $v) { 10681 $defaults[$k] = $v; 10682 } 10683 10684 return $defaults; 10685 } 10686 10687 public function get_setting() { 10688 return $this->config_read($this->name); 10689 } 10690 10691 public function write_setting($data) { 10692 global $USER; 10693 10694 // Let's not deal with validation here, this is for admins only. 10695 $current = $this->get_setting(); 10696 if (empty($data) && $current === null) { 10697 // This will be the case when applying default settings (installation). 10698 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin')); 10699 } else if (!is_number($data)) { 10700 // Draft item id is expected here! 10701 return get_string('errorsetting', 'admin'); 10702 } 10703 10704 $options = $this->get_options(); 10705 $fs = get_file_storage(); 10706 $component = is_null($this->plugin) ? 'core' : $this->plugin; 10707 10708 $this->oldhashes = null; 10709 if ($current) { 10710 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current); 10711 if ($file = $fs->get_file_by_hash($hash)) { 10712 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash(); 10713 } 10714 unset($file); 10715 } 10716 10717 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) { 10718 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime. 10719 // But we can safely ignore that if the destination area is empty, so that the user is not prompt 10720 // with an error because the draft area does not exist, as he did not use it. 10721 $usercontext = context_user::instance($USER->id); 10722 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') { 10723 return get_string('errorsetting', 'admin'); 10724 } 10725 } 10726 10727 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options); 10728 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false); 10729 10730 $filepath = ''; 10731 if ($files) { 10732 /** @var stored_file $file */ 10733 $file = reset($files); 10734 $filepath = $file->get_filepath().$file->get_filename(); 10735 } 10736 10737 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin')); 10738 } 10739 10740 public function post_write_settings($original) { 10741 $options = $this->get_options(); 10742 $fs = get_file_storage(); 10743 $component = is_null($this->plugin) ? 'core' : $this->plugin; 10744 10745 $current = $this->get_setting(); 10746 $newhashes = null; 10747 if ($current) { 10748 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current); 10749 if ($file = $fs->get_file_by_hash($hash)) { 10750 $newhashes = $file->get_contenthash().$file->get_pathnamehash(); 10751 } 10752 unset($file); 10753 } 10754 10755 if ($this->oldhashes === $newhashes) { 10756 $this->oldhashes = null; 10757 return false; 10758 } 10759 $this->oldhashes = null; 10760 10761 $callbackfunction = $this->updatedcallback; 10762 if (!empty($callbackfunction) and function_exists($callbackfunction)) { 10763 $callbackfunction($this->get_full_name()); 10764 } 10765 return true; 10766 } 10767 10768 public function output_html($data, $query = '') { 10769 global $PAGE, $CFG; 10770 10771 $options = $this->get_options(); 10772 $id = $this->get_id(); 10773 $elname = $this->get_full_name(); 10774 $draftitemid = file_get_submitted_draft_itemid($elname); 10775 $component = is_null($this->plugin) ? 'core' : $this->plugin; 10776 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options); 10777 10778 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it... 10779 require_once("$CFG->dirroot/lib/form/filemanager.php"); 10780 10781 $fmoptions = new stdClass(); 10782 $fmoptions->mainfile = $options['mainfile']; 10783 $fmoptions->maxbytes = $options['maxbytes']; 10784 $fmoptions->maxfiles = $options['maxfiles']; 10785 $fmoptions->client_id = uniqid(); 10786 $fmoptions->itemid = $draftitemid; 10787 $fmoptions->subdirs = $options['subdirs']; 10788 $fmoptions->target = $id; 10789 $fmoptions->accepted_types = $options['accepted_types']; 10790 $fmoptions->return_types = $options['return_types']; 10791 $fmoptions->context = $options['context']; 10792 $fmoptions->areamaxbytes = $options['areamaxbytes']; 10793 10794 $fm = new form_filemanager($fmoptions); 10795 $output = $PAGE->get_renderer('core', 'files'); 10796 $html = $output->render($fm); 10797 10798 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />'; 10799 $html .= '<input value="" id="'.$id.'" type="hidden" />'; 10800 10801 return format_admin_setting($this, $this->visiblename, 10802 '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>', 10803 $this->description, true, '', '', $query); 10804 } 10805 } 10806 10807 10808 /** 10809 * Administration interface for user specified regular expressions for device detection. 10810 * 10811 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10812 */ 10813 class admin_setting_devicedetectregex extends admin_setting { 10814 10815 /** 10816 * Calls parent::__construct with specific args 10817 * 10818 * @param string $name 10819 * @param string $visiblename 10820 * @param string $description 10821 * @param mixed $defaultsetting 10822 */ 10823 public function __construct($name, $visiblename, $description, $defaultsetting = '') { 10824 global $CFG; 10825 parent::__construct($name, $visiblename, $description, $defaultsetting); 10826 } 10827 10828 /** 10829 * Return the current setting(s) 10830 * 10831 * @return array Current settings array 10832 */ 10833 public function get_setting() { 10834 global $CFG; 10835 10836 $config = $this->config_read($this->name); 10837 if (is_null($config)) { 10838 return null; 10839 } 10840 10841 return $this->prepare_form_data($config); 10842 } 10843 10844 /** 10845 * Save selected settings 10846 * 10847 * @param array $data Array of settings to save 10848 * @return bool 10849 */ 10850 public function write_setting($data) { 10851 if (empty($data)) { 10852 $data = array(); 10853 } 10854 10855 if ($this->config_write($this->name, $this->process_form_data($data))) { 10856 return ''; // success 10857 } else { 10858 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br'); 10859 } 10860 } 10861 10862 /** 10863 * Return XHTML field(s) for regexes 10864 * 10865 * @param array $data Array of options to set in HTML 10866 * @return string XHTML string for the fields and wrapping div(s) 10867 */ 10868 public function output_html($data, $query='') { 10869 global $OUTPUT; 10870 10871 $context = (object) [ 10872 'expressions' => [], 10873 'name' => $this->get_full_name() 10874 ]; 10875 10876 if (empty($data)) { 10877 $looplimit = 1; 10878 } else { 10879 $looplimit = (count($data)/2)+1; 10880 } 10881 10882 for ($i=0; $i<$looplimit; $i++) { 10883 10884 $expressionname = 'expression'.$i; 10885 10886 if (!empty($data[$expressionname])){ 10887 $expression = $data[$expressionname]; 10888 } else { 10889 $expression = ''; 10890 } 10891 10892 $valuename = 'value'.$i; 10893 10894 if (!empty($data[$valuename])){ 10895 $value = $data[$valuename]; 10896 } else { 10897 $value= ''; 10898 } 10899 10900 $context->expressions[] = [ 10901 'index' => $i, 10902 'expression' => $expression, 10903 'value' => $value 10904 ]; 10905 } 10906 10907 $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context); 10908 10909 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query); 10910 } 10911 10912 /** 10913 * Converts the string of regexes 10914 * 10915 * @see self::process_form_data() 10916 * @param $regexes string of regexes 10917 * @return array of form fields and their values 10918 */ 10919 protected function prepare_form_data($regexes) { 10920 10921 $regexes = json_decode($regexes); 10922 10923 $form = array(); 10924 10925 $i = 0; 10926 10927 foreach ($regexes as $value => $regex) { 10928 $expressionname = 'expression'.$i; 10929 $valuename = 'value'.$i; 10930 10931 $form[$expressionname] = $regex; 10932 $form[$valuename] = $value; 10933 $i++; 10934 } 10935 10936 return $form; 10937 } 10938 10939 /** 10940 * Converts the data from admin settings form into a string of regexes 10941 * 10942 * @see self::prepare_form_data() 10943 * @param array $data array of admin form fields and values 10944 * @return false|string of regexes 10945 */ 10946 protected function process_form_data(array $form) { 10947 10948 $count = count($form); // number of form field values 10949 10950 if ($count % 2) { 10951 // we must get five fields per expression 10952 return false; 10953 } 10954 10955 $regexes = array(); 10956 for ($i = 0; $i < $count / 2; $i++) { 10957 $expressionname = "expression".$i; 10958 $valuename = "value".$i; 10959 10960 $expression = trim($form['expression'.$i]); 10961 $value = trim($form['value'.$i]); 10962 10963 if (empty($expression)){ 10964 continue; 10965 } 10966 10967 $regexes[$value] = $expression; 10968 } 10969 10970 $regexes = json_encode($regexes); 10971 10972 return $regexes; 10973 } 10974 10975 } 10976 10977 /** 10978 * Multiselect for current modules 10979 * 10980 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 10981 */ 10982 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect { 10983 private $excludesystem; 10984 10985 /** 10986 * Calls parent::__construct - note array $choices is not required 10987 * 10988 * @param string $name setting name 10989 * @param string $visiblename localised setting name 10990 * @param string $description setting description 10991 * @param array $defaultsetting a plain array of default module ids 10992 * @param bool $excludesystem If true, excludes modules with 'system' archetype 10993 */ 10994 public function __construct($name, $visiblename, $description, $defaultsetting = array(), 10995 $excludesystem = true) { 10996 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 10997 $this->excludesystem = $excludesystem; 10998 } 10999 11000 /** 11001 * Loads an array of current module choices 11002 * 11003 * @return bool always return true 11004 */ 11005 public function load_choices() { 11006 if (is_array($this->choices)) { 11007 return true; 11008 } 11009 $this->choices = array(); 11010 11011 global $CFG, $DB; 11012 $records = $DB->get_records('modules', array('visible'=>1), 'name'); 11013 foreach ($records as $record) { 11014 // Exclude modules if the code doesn't exist 11015 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) { 11016 // Also exclude system modules (if specified) 11017 if (!($this->excludesystem && 11018 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) === 11019 MOD_ARCHETYPE_SYSTEM)) { 11020 $this->choices[$record->id] = $record->name; 11021 } 11022 } 11023 } 11024 return true; 11025 } 11026 } 11027 11028 /** 11029 * Admin setting to show if a php extension is enabled or not. 11030 * 11031 * @copyright 2013 Damyon Wiese 11032 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11033 */ 11034 class admin_setting_php_extension_enabled extends admin_setting { 11035 11036 /** @var string The name of the extension to check for */ 11037 private $extension; 11038 11039 /** 11040 * Calls parent::__construct with specific arguments 11041 */ 11042 public function __construct($name, $visiblename, $description, $extension) { 11043 $this->extension = $extension; 11044 $this->nosave = true; 11045 parent::__construct($name, $visiblename, $description, ''); 11046 } 11047 11048 /** 11049 * Always returns true, does nothing 11050 * 11051 * @return true 11052 */ 11053 public function get_setting() { 11054 return true; 11055 } 11056 11057 /** 11058 * Always returns true, does nothing 11059 * 11060 * @return true 11061 */ 11062 public function get_defaultsetting() { 11063 return true; 11064 } 11065 11066 /** 11067 * Always returns '', does not write anything 11068 * 11069 * @return string Always returns '' 11070 */ 11071 public function write_setting($data) { 11072 // Do not write any setting. 11073 return ''; 11074 } 11075 11076 /** 11077 * Outputs the html for this setting. 11078 * @return string Returns an XHTML string 11079 */ 11080 public function output_html($data, $query='') { 11081 global $OUTPUT; 11082 11083 $o = ''; 11084 if (!extension_loaded($this->extension)) { 11085 $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description; 11086 11087 $o .= format_admin_setting($this, $this->visiblename, $warning); 11088 } 11089 return $o; 11090 } 11091 } 11092 11093 /** 11094 * Server timezone setting. 11095 * 11096 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} 11097 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11098 * @author Petr Skoda <petr.skoda@totaralms.com> 11099 */ 11100 class admin_setting_servertimezone extends admin_setting_configselect { 11101 /** 11102 * Constructor. 11103 */ 11104 public function __construct() { 11105 $default = core_date::get_default_php_timezone(); 11106 if ($default === 'UTC') { 11107 // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most. 11108 $default = 'Europe/London'; 11109 } 11110 11111 parent::__construct('timezone', 11112 new lang_string('timezone', 'core_admin'), 11113 new lang_string('configtimezone', 'core_admin'), $default, null); 11114 } 11115 11116 /** 11117 * Lazy load timezone options. 11118 * @return bool true if loaded, false if error 11119 */ 11120 public function load_choices() { 11121 global $CFG; 11122 if (is_array($this->choices)) { 11123 return true; 11124 } 11125 11126 $current = isset($CFG->timezone) ? $CFG->timezone : null; 11127 $this->choices = core_date::get_list_of_timezones($current, false); 11128 if ($current == 99) { 11129 // Do not show 99 unless it is current value, we want to get rid of it over time. 11130 $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin', 11131 core_date::get_default_php_timezone()); 11132 } 11133 11134 return true; 11135 } 11136 } 11137 11138 /** 11139 * Forced user timezone setting. 11140 * 11141 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/} 11142 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11143 * @author Petr Skoda <petr.skoda@totaralms.com> 11144 */ 11145 class admin_setting_forcetimezone extends admin_setting_configselect { 11146 /** 11147 * Constructor. 11148 */ 11149 public function __construct() { 11150 parent::__construct('forcetimezone', 11151 new lang_string('forcetimezone', 'core_admin'), 11152 new lang_string('helpforcetimezone', 'core_admin'), '99', null); 11153 } 11154 11155 /** 11156 * Lazy load timezone options. 11157 * @return bool true if loaded, false if error 11158 */ 11159 public function load_choices() { 11160 global $CFG; 11161 if (is_array($this->choices)) { 11162 return true; 11163 } 11164 11165 $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null; 11166 $this->choices = core_date::get_list_of_timezones($current, true); 11167 $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin'); 11168 11169 return true; 11170 } 11171 } 11172 11173 11174 /** 11175 * Search setup steps info. 11176 * 11177 * @package core 11178 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com} 11179 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11180 */ 11181 class admin_setting_searchsetupinfo extends admin_setting { 11182 11183 /** 11184 * Calls parent::__construct with specific arguments 11185 */ 11186 public function __construct() { 11187 $this->nosave = true; 11188 parent::__construct('searchsetupinfo', '', '', ''); 11189 } 11190 11191 /** 11192 * Always returns true, does nothing 11193 * 11194 * @return true 11195 */ 11196 public function get_setting() { 11197 return true; 11198 } 11199 11200 /** 11201 * Always returns true, does nothing 11202 * 11203 * @return true 11204 */ 11205 public function get_defaultsetting() { 11206 return true; 11207 } 11208 11209 /** 11210 * Always returns '', does not write anything 11211 * 11212 * @param array $data 11213 * @return string Always returns '' 11214 */ 11215 public function write_setting($data) { 11216 // Do not write any setting. 11217 return ''; 11218 } 11219 11220 /** 11221 * Builds the HTML to display the control 11222 * 11223 * @param string $data Unused 11224 * @param string $query 11225 * @return string 11226 */ 11227 public function output_html($data, $query='') { 11228 global $CFG, $OUTPUT, $ADMIN; 11229 11230 $return = ''; 11231 $brtag = html_writer::empty_tag('br'); 11232 11233 $searchareas = \core_search\manager::get_search_areas_list(); 11234 $anyenabled = !empty(\core_search\manager::get_search_areas_list(true)); 11235 $anyindexed = false; 11236 foreach ($searchareas as $areaid => $searcharea) { 11237 list($componentname, $varname) = $searcharea->get_config_var_name(); 11238 if (get_config($componentname, $varname . '_indexingstart')) { 11239 $anyindexed = true; 11240 break; 11241 } 11242 } 11243 11244 $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main'); 11245 11246 $table = new html_table(); 11247 $table->head = array(get_string('step', 'search'), get_string('status')); 11248 $table->colclasses = array('leftalign step', 'leftalign status'); 11249 $table->id = 'searchsetup'; 11250 $table->attributes['class'] = 'admintable generaltable'; 11251 $table->data = array(); 11252 11253 $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag; 11254 11255 // Select a search engine. 11256 $row = array(); 11257 $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine'); 11258 $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'), 11259 array('href' => $url)); 11260 11261 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11262 if (!empty($CFG->searchengine)) { 11263 $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine), 11264 array('class' => 'badge badge-success')); 11265 11266 } 11267 $row[1] = $status; 11268 $table->data[] = $row; 11269 11270 // Available areas. 11271 $row = array(); 11272 $url = new moodle_url('/admin/searchareas.php'); 11273 $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'), 11274 array('href' => $url)); 11275 11276 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11277 if ($anyenabled) { 11278 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11279 11280 } 11281 $row[1] = $status; 11282 $table->data[] = $row; 11283 11284 // Setup search engine. 11285 $row = array(); 11286 if (empty($CFG->searchengine)) { 11287 $row[0] = '3. ' . get_string('setupsearchengine', 'admin'); 11288 $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11289 } else { 11290 if ($ADMIN->locate('search' . $CFG->searchengine)) { 11291 $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine); 11292 $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin')); 11293 } else { 11294 $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin'); 11295 } 11296 11297 // Check the engine status. 11298 $searchengine = \core_search\manager::search_engine_instance(); 11299 try { 11300 $serverstatus = $searchengine->is_server_ready(); 11301 } catch (\moodle_exception $e) { 11302 $serverstatus = $e->getMessage(); 11303 } 11304 if ($serverstatus === true) { 11305 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11306 } else { 11307 $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger')); 11308 } 11309 $row[1] = $status; 11310 } 11311 $table->data[] = $row; 11312 11313 // Indexed data. 11314 $row = array(); 11315 $url = new moodle_url('/admin/searchareas.php'); 11316 $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url)); 11317 if ($anyindexed) { 11318 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11319 } else { 11320 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11321 } 11322 $row[1] = $status; 11323 $table->data[] = $row; 11324 11325 // Enable global search. 11326 $row = array(); 11327 $url = new moodle_url("/admin/search.php?query=enableglobalsearch"); 11328 $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'), 11329 array('href' => $url)); 11330 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11331 if (\core_search\manager::is_global_search_enabled()) { 11332 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11333 } 11334 $row[1] = $status; 11335 $table->data[] = $row; 11336 11337 // Replace front page search. 11338 $row = array(); 11339 $url = new moodle_url("/admin/search.php?query=searchincludeallcourses"); 11340 $row[0] = '6. ' . html_writer::tag('a', get_string('replacefrontsearch', 'admin'), 11341 array('href' => $url)); 11342 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger')); 11343 if (\core_search\manager::can_replace_course_search()) { 11344 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success')); 11345 } 11346 $row[1] = $status; 11347 $table->data[] = $row; 11348 11349 $return .= html_writer::table($table); 11350 11351 return highlight($query, $return); 11352 } 11353 11354 } 11355 11356 /** 11357 * Used to validate the contents of SCSS code and ensuring they are parsable. 11358 * 11359 * It does not attempt to detect undefined SCSS variables because it is designed 11360 * to be used without knowledge of other config/scss included. 11361 * 11362 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11363 * @copyright 2016 Dan Poltawski <dan@moodle.com> 11364 */ 11365 class admin_setting_scsscode extends admin_setting_configtextarea { 11366 11367 /** 11368 * Validate the contents of the SCSS to ensure its parsable. Does not 11369 * attempt to detect undefined scss variables. 11370 * 11371 * @param string $data The scss code from text field. 11372 * @return mixed bool true for success or string:error on failure. 11373 */ 11374 public function validate($data) { 11375 if (empty($data)) { 11376 return true; 11377 } 11378 11379 $scss = new core_scss(); 11380 try { 11381 $scss->compile($data); 11382 } catch (ScssPhp\ScssPhp\Exception\ParserException $e) { 11383 return get_string('scssinvalid', 'admin', $e->getMessage()); 11384 } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) { 11385 // Silently ignore this - it could be a scss variable defined from somewhere 11386 // else which we are not examining here. 11387 return true; 11388 } 11389 11390 return true; 11391 } 11392 } 11393 11394 11395 /** 11396 * Administration setting to define a list of file types. 11397 * 11398 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au> 11399 * @copyright 2017 David Mudrák <david@moodle.com> 11400 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11401 */ 11402 class admin_setting_filetypes extends admin_setting_configtext { 11403 11404 /** @var array Allow selection from these file types only. */ 11405 protected $onlytypes = []; 11406 11407 /** @var bool Allow selection of 'All file types' (will be stored as '*'). */ 11408 protected $allowall = true; 11409 11410 /** @var core_form\filetypes_util instance to use as a helper. */ 11411 protected $util = null; 11412 11413 /** 11414 * Constructor. 11415 * 11416 * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting' 11417 * @param string $visiblename Localised label of the setting 11418 * @param string $description Localised description of the setting 11419 * @param string $defaultsetting Default setting value. 11420 * @param array $options Setting widget options, an array with optional keys: 11421 * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']]. 11422 * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set. 11423 */ 11424 public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) { 11425 11426 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW); 11427 11428 if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) { 11429 $this->onlytypes = $options['onlytypes']; 11430 } 11431 11432 if (!$this->onlytypes && array_key_exists('allowall', $options)) { 11433 $this->allowall = (bool)$options['allowall']; 11434 } 11435 11436 $this->util = new \core_form\filetypes_util(); 11437 } 11438 11439 /** 11440 * Normalize the user's input and write it to the database as comma separated list. 11441 * 11442 * Comma separated list as a text representation of the array was chosen to 11443 * make this compatible with how the $CFG->courseoverviewfilesext values are stored. 11444 * 11445 * @param string $data Value submitted by the admin. 11446 * @return string Epty string if all good, error message otherwise. 11447 */ 11448 public function write_setting($data) { 11449 return parent::write_setting(implode(',', $this->util->normalize_file_types($data))); 11450 } 11451 11452 /** 11453 * Validate data before storage 11454 * 11455 * @param string $data The setting values provided by the admin 11456 * @return bool|string True if ok, the string if error found 11457 */ 11458 public function validate($data) { 11459 $parentcheck = parent::validate($data); 11460 if ($parentcheck !== true) { 11461 return $parentcheck; 11462 } 11463 11464 // Check for unknown file types. 11465 if ($unknown = $this->util->get_unknown_file_types($data)) { 11466 return get_string('filetypesunknown', 'core_form', implode(', ', $unknown)); 11467 } 11468 11469 // Check for disallowed file types. 11470 if ($notlisted = $this->util->get_not_listed($data, $this->onlytypes)) { 11471 return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted)); 11472 } 11473 11474 return true; 11475 } 11476 11477 /** 11478 * Return an HTML string for the setting element. 11479 * 11480 * @param string $data The current setting value 11481 * @param string $query Admin search query to be highlighted 11482 * @return string HTML to be displayed 11483 */ 11484 public function output_html($data, $query='') { 11485 global $OUTPUT, $PAGE; 11486 11487 $default = $this->get_defaultsetting(); 11488 $context = (object) [ 11489 'id' => $this->get_id(), 11490 'name' => $this->get_full_name(), 11491 'value' => $data, 11492 'descriptions' => $this->util->describe_file_types($data), 11493 ]; 11494 $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context); 11495 11496 $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [ 11497 $this->get_id(), 11498 $this->visiblename->out(), 11499 $this->onlytypes, 11500 $this->allowall, 11501 ]); 11502 11503 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); 11504 } 11505 11506 /** 11507 * Should the values be always displayed in LTR mode? 11508 * 11509 * We always return true here because these values are not RTL compatible. 11510 * 11511 * @return bool True because these values are not RTL compatible. 11512 */ 11513 public function get_force_ltr() { 11514 return true; 11515 } 11516 } 11517 11518 /** 11519 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable. 11520 * 11521 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11522 * @copyright 2018 Mihail Geshoski <mihail@moodle.com> 11523 */ 11524 class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea { 11525 11526 /** 11527 * Constructor. 11528 * 11529 * @param string $name 11530 * @param string $visiblename 11531 * @param string $description 11532 * @param mixed $defaultsetting string or array 11533 * @param mixed $paramtype 11534 * @param string $cols 11535 * @param string $rows 11536 */ 11537 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW, 11538 $cols = '60', $rows = '8') { 11539 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows); 11540 // Pre-set force LTR to false. 11541 $this->set_force_ltr(false); 11542 } 11543 11544 /** 11545 * Validate the content and format of the age of digital consent map to ensure it is parsable. 11546 * 11547 * @param string $data The age of digital consent map from text field. 11548 * @return mixed bool true for success or string:error on failure. 11549 */ 11550 public function validate($data) { 11551 if (empty($data)) { 11552 return true; 11553 } 11554 11555 try { 11556 \core_auth\digital_consent::parse_age_digital_consent_map($data); 11557 } catch (\moodle_exception $e) { 11558 return get_string('invalidagedigitalconsent', 'admin', $e->getMessage()); 11559 } 11560 11561 return true; 11562 } 11563 } 11564 11565 /** 11566 * Selection of plugins that can work as site policy handlers 11567 * 11568 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11569 * @copyright 2018 Marina Glancy 11570 */ 11571 class admin_settings_sitepolicy_handler_select extends admin_setting_configselect { 11572 11573 /** 11574 * Constructor 11575 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' 11576 * for ones in config_plugins. 11577 * @param string $visiblename localised 11578 * @param string $description long localised info 11579 * @param string $defaultsetting 11580 */ 11581 public function __construct($name, $visiblename, $description, $defaultsetting = '') { 11582 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 11583 } 11584 11585 /** 11586 * Lazy-load the available choices for the select box 11587 */ 11588 public function load_choices() { 11589 if (during_initial_install()) { 11590 return false; 11591 } 11592 if (is_array($this->choices)) { 11593 return true; 11594 } 11595 11596 $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')]; 11597 $manager = new \core_privacy\local\sitepolicy\manager(); 11598 $plugins = $manager->get_all_handlers(); 11599 foreach ($plugins as $pname => $unused) { 11600 $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin', 11601 ['name' => new lang_string('pluginname', $pname), 'component' => $pname]); 11602 } 11603 11604 return true; 11605 } 11606 } 11607 11608 /** 11609 * Used to validate theme presets code and ensuring they compile well. 11610 * 11611 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11612 * @copyright 2019 Bas Brands <bas@moodle.com> 11613 */ 11614 class admin_setting_configthemepreset extends admin_setting_configselect { 11615 11616 /** @var string The name of the theme to check for */ 11617 private $themename; 11618 11619 /** 11620 * Constructor 11621 * @param string $name unique ascii name, either 'mysetting' for settings that in config, 11622 * or 'myplugin/mysetting' for ones in config_plugins. 11623 * @param string $visiblename localised 11624 * @param string $description long localised info 11625 * @param string|int $defaultsetting 11626 * @param array $choices array of $value=>$label for each selection 11627 * @param string $themename name of theme to check presets for. 11628 */ 11629 public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) { 11630 $this->themename = $themename; 11631 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices); 11632 } 11633 11634 /** 11635 * Write settings if validated 11636 * 11637 * @param string $data 11638 * @return string 11639 */ 11640 public function write_setting($data) { 11641 $validated = $this->validate($data); 11642 if ($validated !== true) { 11643 return $validated; 11644 } 11645 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin')); 11646 } 11647 11648 /** 11649 * Validate the preset file to ensure its parsable. 11650 * 11651 * @param string $data The preset file chosen. 11652 * @return mixed bool true for success or string:error on failure. 11653 */ 11654 public function validate($data) { 11655 11656 if (in_array($data, ['default.scss', 'plain.scss'])) { 11657 return true; 11658 } 11659 11660 $fs = get_file_storage(); 11661 $theme = theme_config::load($this->themename); 11662 $context = context_system::instance(); 11663 11664 // If the preset has not changed there is no need to validate it. 11665 if ($theme->settings->preset == $data) { 11666 return true; 11667 } 11668 11669 if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) { 11670 // This operation uses a lot of resources. 11671 raise_memory_limit(MEMORY_EXTRA); 11672 core_php_time_limit::raise(300); 11673 11674 // TODO: MDL-62757 When changing anything in this method please do not forget to check 11675 // if the get_css_content_from_scss() method in class theme_config needs updating too. 11676 11677 $compiler = new core_scss(); 11678 $compiler->prepend_raw_scss($theme->get_pre_scss_code()); 11679 $compiler->append_raw_scss($presetfile->get_content()); 11680 if ($scssproperties = $theme->get_scss_property()) { 11681 $compiler->setImportPaths($scssproperties[0]); 11682 } 11683 $compiler->append_raw_scss($theme->get_extra_scss_code()); 11684 11685 try { 11686 $compiler->to_css(); 11687 } catch (Exception $e) { 11688 return get_string('invalidthemepreset', 'admin', $e->getMessage()); 11689 } 11690 11691 // Try to save memory. 11692 $compiler = null; 11693 unset($compiler); 11694 } 11695 11696 return true; 11697 } 11698 } 11699 11700 /** 11701 * Selection of plugins that can work as H5P libraries handlers 11702 * 11703 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 11704 * @copyright 2020 Sara Arjona <sara@moodle.com> 11705 */ 11706 class admin_settings_h5plib_handler_select extends admin_setting_configselect { 11707 11708 /** 11709 * Constructor 11710 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' 11711 * for ones in config_plugins. 11712 * @param string $visiblename localised 11713 * @param string $description long localised info 11714 * @param string $defaultsetting 11715 */ 11716 public function __construct($name, $visiblename, $description, $defaultsetting = '') { 11717 parent::__construct($name, $visiblename, $description, $defaultsetting, null); 11718 } 11719 11720 /** 11721 * Lazy-load the available choices for the select box 11722 */ 11723 public function load_choices() { 11724 if (during_initial_install()) { 11725 return false; 11726 } 11727 if (is_array($this->choices)) { 11728 return true; 11729 } 11730 11731 $this->choices = \core_h5p\local\library\autoloader::get_all_handlers(); 11732 foreach ($this->choices as $name => $class) { 11733 $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin', 11734 ['name' => new lang_string('pluginname', $name), 'component' => $name]); 11735 } 11736 11737 return true; 11738 } 11739 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body