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