Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
/lib/ -> adminlib.php (source)

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Functions and classes used during installation, upgrades and for admin settings.
  19   *
  20   *  ADMIN SETTINGS TREE INTRODUCTION
  21   *
  22   *  This file performs the following tasks:
  23   *   -it defines the necessary objects and interfaces to build the Moodle
  24   *    admin hierarchy
  25   *   -it defines the admin_externalpage_setup()
  26   *
  27   *  ADMIN_SETTING OBJECTS
  28   *
  29   *  Moodle settings are represented by objects that inherit from the admin_setting
  30   *  class. These objects encapsulate how to read a setting, how to write a new value
  31   *  to a setting, and how to appropriately display the HTML to modify the setting.
  32   *
  33   *  ADMIN_SETTINGPAGE OBJECTS
  34   *
  35   *  The admin_setting objects are then grouped into admin_settingpages. The latter
  36   *  appear in the Moodle admin tree block. All interaction with admin_settingpage
  37   *  objects is handled by the admin/settings.php file.
  38   *
  39   *  ADMIN_EXTERNALPAGE OBJECTS
  40   *
  41   *  There are some settings in Moodle that are too complex to (efficiently) handle
  42   *  with admin_settingpages. (Consider, for example, user management and displaying
  43   *  lists of users.) In this case, we use the admin_externalpage object. This object
  44   *  places a link to an external PHP file in the admin tree block.
  45   *
  46   *  If you're using an admin_externalpage object for some settings, you can take
  47   *  advantage of the admin_externalpage_* functions. For example, suppose you wanted
  48   *  to add a foo.php file into admin. First off, you add the following line to
  49   *  admin/settings/first.php (at the end of the file) or to some other file in
  50   *  admin/settings:
  51   * <code>
  52   *     $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
  53   *         $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
  54   * </code>
  55   *
  56   *  Next, in foo.php, your file structure would resemble the following:
  57   * <code>
  58   *         require(__DIR__.'/../../config.php');
  59   *         require_once($CFG->libdir.'/adminlib.php');
  60   *         admin_externalpage_setup('foo');
  61   *         // functionality like processing form submissions goes here
  62   *         echo $OUTPUT->header();
  63   *         // your HTML goes here
  64   *         echo $OUTPUT->footer();
  65   * </code>
  66   *
  67   *  The admin_externalpage_setup() function call ensures the user is logged in,
  68   *  and makes sure that they have the proper role permission to access the page.
  69   *  It also configures all $PAGE properties needed for navigation.
  70   *
  71   *  ADMIN_CATEGORY OBJECTS
  72   *
  73   *  Above and beyond all this, we have admin_category objects. These objects
  74   *  appear as folders in the admin tree block. They contain admin_settingpage's,
  75   *  admin_externalpage's, and other admin_category's.
  76   *
  77   *  OTHER NOTES
  78   *
  79   *  admin_settingpage's, admin_externalpage's, and admin_category's all inherit
  80   *  from part_of_admin_tree (a pseudointerface). This interface insists that
  81   *  a class has a check_access method for access permissions, a locate method
  82   *  used to find a specific node in the admin tree and find parent path.
  83   *
  84   *  admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
  85   *  interface ensures that the class implements a recursive add function which
  86   *  accepts a part_of_admin_tree object and searches for the proper place to
  87   *  put it. parentable_part_of_admin_tree implies part_of_admin_tree.
  88   *
  89   *  Please note that the $this->name field of any part_of_admin_tree must be
  90   *  UNIQUE throughout the ENTIRE admin tree.
  91   *
  92   *  The $this->name field of an admin_setting object (which is *not* part_of_
  93   *  admin_tree) must be unique on the respective admin_settingpage where it is
  94   *  used.
  95   *
  96   * Original author: Vincenzo K. Marcovecchio
  97   * Maintainer:      Petr Skoda
  98   *
  99   * @package    core
 100   * @subpackage admin
 101   * @copyright  1999 onwards Martin Dougiamas  http://dougiamas.com
 102   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 103   */
 104  
 105  defined('MOODLE_INTERNAL') || die();
 106  
 107  /// Add libraries
 108  require_once($CFG->libdir.'/ddllib.php');
 109  require_once($CFG->libdir.'/xmlize.php');
 110  require_once($CFG->libdir.'/messagelib.php');
 111  
 112  define('INSECURE_DATAROOT_WARNING', 1);
 113  define('INSECURE_DATAROOT_ERROR', 2);
 114  
 115  /**
 116   * Automatically clean-up all plugin data and remove the plugin DB tables
 117   *
 118   * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
 119   *
 120   * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
 121   * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
 122   * @uses global $OUTPUT to produce notices and other messages
 123   * @return void
 124   */
 125  function uninstall_plugin($type, $name) {
 126      global $CFG, $DB, $OUTPUT;
 127  
 128      // This may take a long time.
 129      core_php_time_limit::raise();
 130  
 131      // Recursively uninstall all subplugins first.
 132      $subplugintypes = core_component::get_plugin_types_with_subplugins();
 133      if (isset($subplugintypes[$type])) {
 134          $base = core_component::get_plugin_directory($type, $name);
 135  
 136          $subpluginsfile = "{$base}/db/subplugins.json";
 137          if (file_exists($subpluginsfile)) {
 138              $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
 139          } else if (file_exists("{$base}/db/subplugins.php")) {
 140              debugging('Use of subplugins.php has been deprecated. ' .
 141                      'Please update your plugin to provide a subplugins.json file instead.',
 142                      DEBUG_DEVELOPER);
 143              $subplugins = [];
 144              include("{$base}/db/subplugins.php");
 145          }
 146  
 147          if (!empty($subplugins)) {
 148              foreach (array_keys($subplugins) as $subplugintype) {
 149                  $instances = core_component::get_plugin_list($subplugintype);
 150                  foreach ($instances as $subpluginname => $notusedpluginpath) {
 151                      uninstall_plugin($subplugintype, $subpluginname);
 152                  }
 153              }
 154          }
 155      }
 156  
 157      $component = $type . '_' . $name;  // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
 158  
 159      if ($type === 'mod') {
 160          $pluginname = $name;  // eg. 'forum'
 161          if (get_string_manager()->string_exists('modulename', $component)) {
 162              $strpluginname = get_string('modulename', $component);
 163          } else {
 164              $strpluginname = $component;
 165          }
 166  
 167      } else {
 168          $pluginname = $component;
 169          if (get_string_manager()->string_exists('pluginname', $component)) {
 170              $strpluginname = get_string('pluginname', $component);
 171          } else {
 172              $strpluginname = $component;
 173          }
 174      }
 175  
 176      echo $OUTPUT->heading($pluginname);
 177  
 178      // Delete all tag areas, collections and instances associated with this plugin.
 179      core_tag_area::uninstall($component);
 180  
 181      // Custom plugin uninstall.
 182      $plugindirectory = core_component::get_plugin_directory($type, $name);
 183      $uninstalllib = $plugindirectory . '/db/uninstall.php';
 184      if (file_exists($uninstalllib)) {
 185          require_once($uninstalllib);
 186          $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall';    // eg. 'xmldb_workshop_uninstall()'
 187          if (function_exists($uninstallfunction)) {
 188              // Do not verify result, let plugin complain if necessary.
 189              $uninstallfunction();
 190          }
 191      }
 192  
 193      // Specific plugin type cleanup.
 194      $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
 195      if ($plugininfo) {
 196          $plugininfo->uninstall_cleanup();
 197          core_plugin_manager::reset_caches();
 198      }
 199      $plugininfo = null;
 200  
 201      // perform clean-up task common for all the plugin/subplugin types
 202  
 203      //delete the web service functions and pre-built services
 204      require_once($CFG->dirroot.'/lib/externallib.php');
 205      external_delete_descriptions($component);
 206  
 207      // delete calendar events
 208      $DB->delete_records('event', array('modulename' => $pluginname));
 209      $DB->delete_records('event', ['component' => $component]);
 210  
 211      // Delete scheduled tasks.
 212      $DB->delete_records('task_scheduled', array('component' => $component));
 213  
 214      // Delete Inbound Message datakeys.
 215      $DB->delete_records_select('messageinbound_datakeys',
 216              'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
 217  
 218      // Delete Inbound Message handlers.
 219      $DB->delete_records('messageinbound_handlers', array('component' => $component));
 220  
 221      // delete all the logs
 222      $DB->delete_records('log', array('module' => $pluginname));
 223  
 224      // delete log_display information
 225      $DB->delete_records('log_display', array('component' => $component));
 226  
 227      // delete the module configuration records
 228      unset_all_config_for_plugin($component);
 229      if ($type === 'mod') {
 230          unset_all_config_for_plugin($pluginname);
 231      }
 232  
 233      // delete message provider
 234      message_provider_uninstall($component);
 235  
 236      // delete the plugin tables
 237      $xmldbfilepath = $plugindirectory . '/db/install.xml';
 238      drop_plugin_tables($component, $xmldbfilepath, false);
 239      if ($type === 'mod' or $type === 'block') {
 240          // non-frankenstyle table prefixes
 241          drop_plugin_tables($name, $xmldbfilepath, false);
 242      }
 243  
 244      // delete the capabilities that were defined by this module
 245      capabilities_cleanup($component);
 246  
 247      // Delete all remaining files in the filepool owned by the component.
 248      $fs = get_file_storage();
 249      $fs->delete_component_files($component);
 250  
 251      // Finally purge all caches.
 252      purge_all_caches();
 253  
 254      // Invalidate the hash used for upgrade detections.
 255      set_config('allversionshash', '');
 256  
 257      echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
 258  }
 259  
 260  /**
 261   * Returns the version of installed component
 262   *
 263   * @param string $component component name
 264   * @param string $source either 'disk' or 'installed' - where to get the version information from
 265   * @return string|bool version number or false if the component is not found
 266   */
 267  function get_component_version($component, $source='installed') {
 268      global $CFG, $DB;
 269  
 270      list($type, $name) = core_component::normalize_component($component);
 271  
 272      // moodle core or a core subsystem
 273      if ($type === 'core') {
 274          if ($source === 'installed') {
 275              if (empty($CFG->version)) {
 276                  return false;
 277              } else {
 278                  return $CFG->version;
 279              }
 280          } else {
 281              if (!is_readable($CFG->dirroot.'/version.php')) {
 282                  return false;
 283              } else {
 284                  $version = null; //initialize variable for IDEs
 285                  include($CFG->dirroot.'/version.php');
 286                  return $version;
 287              }
 288          }
 289      }
 290  
 291      // activity module
 292      if ($type === 'mod') {
 293          if ($source === 'installed') {
 294              if ($CFG->version < 2013092001.02) {
 295                  return $DB->get_field('modules', 'version', array('name'=>$name));
 296              } else {
 297                  return get_config('mod_'.$name, 'version');
 298              }
 299  
 300          } else {
 301              $mods = core_component::get_plugin_list('mod');
 302              if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
 303                  return false;
 304              } else {
 305                  $plugin = new stdClass();
 306                  $plugin->version = null;
 307                  $module = $plugin;
 308                  include($mods[$name].'/version.php');
 309                  return $plugin->version;
 310              }
 311          }
 312      }
 313  
 314      // block
 315      if ($type === 'block') {
 316          if ($source === 'installed') {
 317              if ($CFG->version < 2013092001.02) {
 318                  return $DB->get_field('block', 'version', array('name'=>$name));
 319              } else {
 320                  return get_config('block_'.$name, 'version');
 321              }
 322          } else {
 323              $blocks = core_component::get_plugin_list('block');
 324              if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
 325                  return false;
 326              } else {
 327                  $plugin = new stdclass();
 328                  include($blocks[$name].'/version.php');
 329                  return $plugin->version;
 330              }
 331          }
 332      }
 333  
 334      // all other plugin types
 335      if ($source === 'installed') {
 336          return get_config($type.'_'.$name, 'version');
 337      } else {
 338          $plugins = core_component::get_plugin_list($type);
 339          if (empty($plugins[$name])) {
 340              return false;
 341          } else {
 342              $plugin = new stdclass();
 343              include($plugins[$name].'/version.php');
 344              return $plugin->version;
 345          }
 346      }
 347  }
 348  
 349  /**
 350   * Delete all plugin tables
 351   *
 352   * @param string $name Name of plugin, used as table prefix
 353   * @param string $file Path to install.xml file
 354   * @param bool $feedback defaults to true
 355   * @return bool Always returns true
 356   */
 357  function drop_plugin_tables($name, $file, $feedback=true) {
 358      global $CFG, $DB;
 359  
 360      // first try normal delete
 361      if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
 362          return true;
 363      }
 364  
 365      // then try to find all tables that start with name and are not in any xml file
 366      $used_tables = get_used_table_names();
 367  
 368      $tables = $DB->get_tables();
 369  
 370      /// Iterate over, fixing id fields as necessary
 371      foreach ($tables as $table) {
 372          if (in_array($table, $used_tables)) {
 373              continue;
 374          }
 375  
 376          if (strpos($table, $name) !== 0) {
 377              continue;
 378          }
 379  
 380          // found orphan table --> delete it
 381          if ($DB->get_manager()->table_exists($table)) {
 382              $xmldb_table = new xmldb_table($table);
 383              $DB->get_manager()->drop_table($xmldb_table);
 384          }
 385      }
 386  
 387      return true;
 388  }
 389  
 390  /**
 391   * Returns names of all known tables == tables that moodle knows about.
 392   *
 393   * @return array Array of lowercase table names
 394   */
 395  function get_used_table_names() {
 396      $table_names = array();
 397      $dbdirs = get_db_directories();
 398  
 399      foreach ($dbdirs as $dbdir) {
 400          $file = $dbdir.'/install.xml';
 401  
 402          $xmldb_file = new xmldb_file($file);
 403  
 404          if (!$xmldb_file->fileExists()) {
 405              continue;
 406          }
 407  
 408          $loaded    = $xmldb_file->loadXMLStructure();
 409          $structure = $xmldb_file->getStructure();
 410  
 411          if ($loaded and $tables = $structure->getTables()) {
 412              foreach($tables as $table) {
 413                  $table_names[] = strtolower($table->getName());
 414              }
 415          }
 416      }
 417  
 418      return $table_names;
 419  }
 420  
 421  /**
 422   * Returns list of all directories where we expect install.xml files
 423   * @return array Array of paths
 424   */
 425  function get_db_directories() {
 426      global $CFG;
 427  
 428      $dbdirs = array();
 429  
 430      /// First, the main one (lib/db)
 431      $dbdirs[] = $CFG->libdir.'/db';
 432  
 433      /// Then, all the ones defined by core_component::get_plugin_types()
 434      $plugintypes = core_component::get_plugin_types();
 435      foreach ($plugintypes as $plugintype => $pluginbasedir) {
 436          if ($plugins = core_component::get_plugin_list($plugintype)) {
 437              foreach ($plugins as $plugin => $plugindir) {
 438                  $dbdirs[] = $plugindir.'/db';
 439              }
 440          }
 441      }
 442  
 443      return $dbdirs;
 444  }
 445  
 446  /**
 447   * Try to obtain or release the cron lock.
 448   * @param string  $name  name of lock
 449   * @param int  $until timestamp when this lock considered stale, null means remove lock unconditionally
 450   * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
 451   * @return bool true if lock obtained
 452   */
 453  function set_cron_lock($name, $until, $ignorecurrent=false) {
 454      global $DB;
 455      if (empty($name)) {
 456          debugging("Tried to get a cron lock for a null fieldname");
 457          return false;
 458      }
 459  
 460      // remove lock by force == remove from config table
 461      if (is_null($until)) {
 462          set_config($name, null);
 463          return true;
 464      }
 465  
 466      if (!$ignorecurrent) {
 467          // read value from db - other processes might have changed it
 468          $value = $DB->get_field('config', 'value', array('name'=>$name));
 469  
 470          if ($value and $value > time()) {
 471              //lock active
 472              return false;
 473          }
 474      }
 475  
 476      set_config($name, $until);
 477      return true;
 478  }
 479  
 480  /**
 481   * Test if and critical warnings are present
 482   * @return bool
 483   */
 484  function admin_critical_warnings_present() {
 485      global $SESSION;
 486  
 487      if (!has_capability('moodle/site:config', context_system::instance())) {
 488          return 0;
 489      }
 490  
 491      if (!isset($SESSION->admin_critical_warning)) {
 492          $SESSION->admin_critical_warning = 0;
 493          if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
 494              $SESSION->admin_critical_warning = 1;
 495          }
 496      }
 497  
 498      return $SESSION->admin_critical_warning;
 499  }
 500  
 501  /**
 502   * Detects if float supports at least 10 decimal digits
 503   *
 504   * Detects if float supports at least 10 decimal digits
 505   * and also if float-->string conversion works as expected.
 506   *
 507   * @return bool true if problem found
 508   */
 509  function is_float_problem() {
 510      $num1 = 2009010200.01;
 511      $num2 = 2009010200.02;
 512  
 513      return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
 514  }
 515  
 516  /**
 517   * Try to verify that dataroot is not accessible from web.
 518   *
 519   * Try to verify that dataroot is not accessible from web.
 520   * It is not 100% correct but might help to reduce number of vulnerable sites.
 521   * Protection from httpd.conf and .htaccess is not detected properly.
 522   *
 523   * @uses INSECURE_DATAROOT_WARNING
 524   * @uses INSECURE_DATAROOT_ERROR
 525   * @param bool $fetchtest try to test public access by fetching file, default false
 526   * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
 527   */
 528  function is_dataroot_insecure($fetchtest=false) {
 529      global $CFG;
 530  
 531      $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
 532  
 533      $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
 534      $rp = strrev(trim($rp, '/'));
 535      $rp = explode('/', $rp);
 536      foreach($rp as $r) {
 537          if (strpos($siteroot, '/'.$r.'/') === 0) {
 538              $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
 539          } else {
 540              break; // probably alias root
 541          }
 542      }
 543  
 544      $siteroot = strrev($siteroot);
 545      $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
 546  
 547      if (strpos($dataroot, $siteroot) !== 0) {
 548          return false;
 549      }
 550  
 551      if (!$fetchtest) {
 552          return INSECURE_DATAROOT_WARNING;
 553      }
 554  
 555      // now try all methods to fetch a test file using http protocol
 556  
 557      $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
 558      preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
 559      $httpdocroot = $matches[1];
 560      $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
 561      make_upload_directory('diag');
 562      $testfile = $CFG->dataroot.'/diag/public.txt';
 563      if (!file_exists($testfile)) {
 564          file_put_contents($testfile, 'test file, do not delete');
 565          @chmod($testfile, $CFG->filepermissions);
 566      }
 567      $teststr = trim(file_get_contents($testfile));
 568      if (empty($teststr)) {
 569      // hmm, strange
 570          return INSECURE_DATAROOT_WARNING;
 571      }
 572  
 573      $testurl = $datarooturl.'/diag/public.txt';
 574      if (extension_loaded('curl') and
 575          !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
 576          !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
 577          ($ch = @curl_init($testurl)) !== false) {
 578          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 579          curl_setopt($ch, CURLOPT_HEADER, false);
 580          $data = curl_exec($ch);
 581          if (!curl_errno($ch)) {
 582              $data = trim($data);
 583              if ($data === $teststr) {
 584                  curl_close($ch);
 585                  return INSECURE_DATAROOT_ERROR;
 586              }
 587          }
 588          curl_close($ch);
 589      }
 590  
 591      if ($data = @file_get_contents($testurl)) {
 592          $data = trim($data);
 593          if ($data === $teststr) {
 594              return INSECURE_DATAROOT_ERROR;
 595          }
 596      }
 597  
 598      preg_match('|https?://([^/]+)|i', $testurl, $matches);
 599      $sitename = $matches[1];
 600      $error = 0;
 601      if ($fp = @fsockopen($sitename, 80, $error)) {
 602          preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
 603          $localurl = $matches[1];
 604          $out = "GET $localurl HTTP/1.1\r\n";
 605          $out .= "Host: $sitename\r\n";
 606          $out .= "Connection: Close\r\n\r\n";
 607          fwrite($fp, $out);
 608          $data = '';
 609          $incoming = false;
 610          while (!feof($fp)) {
 611              if ($incoming) {
 612                  $data .= fgets($fp, 1024);
 613              } else if (@fgets($fp, 1024) === "\r\n") {
 614                      $incoming = true;
 615                  }
 616          }
 617          fclose($fp);
 618          $data = trim($data);
 619          if ($data === $teststr) {
 620              return INSECURE_DATAROOT_ERROR;
 621          }
 622      }
 623  
 624      return INSECURE_DATAROOT_WARNING;
 625  }
 626  
 627  /**
 628   * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
 629   */
 630  function enable_cli_maintenance_mode() {
 631      global $CFG;
 632  
 633      if (file_exists("$CFG->dataroot/climaintenance.html")) {
 634          unlink("$CFG->dataroot/climaintenance.html");
 635      }
 636  
 637      if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
 638          $data = $CFG->maintenance_message;
 639          $data = bootstrap_renderer::early_error_content($data, null, null, null);
 640          $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
 641  
 642      } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
 643          $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
 644  
 645      } else {
 646          $data = get_string('sitemaintenance', 'admin');
 647          $data = bootstrap_renderer::early_error_content($data, null, null, null);
 648          $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
 649      }
 650  
 651      file_put_contents("$CFG->dataroot/climaintenance.html", $data);
 652      chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
 653  }
 654  
 655  /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
 656  
 657  
 658  /**
 659   * Interface for anything appearing in the admin tree
 660   *
 661   * The interface that is implemented by anything that appears in the admin tree
 662   * block. It forces inheriting classes to define a method for checking user permissions
 663   * and methods for finding something in the admin tree.
 664   *
 665   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 666   */
 667  interface part_of_admin_tree {
 668  
 669  /**
 670   * Finds a named part_of_admin_tree.
 671   *
 672   * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
 673   * and not parentable_part_of_admin_tree, then this function should only check if
 674   * $this->name matches $name. If it does, it should return a reference to $this,
 675   * otherwise, it should return a reference to NULL.
 676   *
 677   * If a class inherits parentable_part_of_admin_tree, this method should be called
 678   * recursively on all child objects (assuming, of course, the parent object's name
 679   * doesn't match the search criterion).
 680   *
 681   * @param string $name The internal name of the part_of_admin_tree we're searching for.
 682   * @return mixed An object reference or a NULL reference.
 683   */
 684      public function locate($name);
 685  
 686      /**
 687       * Removes named part_of_admin_tree.
 688       *
 689       * @param string $name The internal name of the part_of_admin_tree we want to remove.
 690       * @return bool success.
 691       */
 692      public function prune($name);
 693  
 694      /**
 695       * Search using query
 696       * @param string $query
 697       * @return mixed array-object structure of found settings and pages
 698       */
 699      public function search($query);
 700  
 701      /**
 702       * Verifies current user's access to this part_of_admin_tree.
 703       *
 704       * Used to check if the current user has access to this part of the admin tree or
 705       * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
 706       * then this method is usually just a call to has_capability() in the site context.
 707       *
 708       * If a class inherits parentable_part_of_admin_tree, this method should return the
 709       * logical OR of the return of check_access() on all child objects.
 710       *
 711       * @return bool True if the user has access, false if she doesn't.
 712       */
 713      public function check_access();
 714  
 715      /**
 716       * Mostly useful for removing of some parts of the tree in admin tree block.
 717       *
 718       * @return True is hidden from normal list view
 719       */
 720      public function is_hidden();
 721  
 722      /**
 723       * Show we display Save button at the page bottom?
 724       * @return bool
 725       */
 726      public function show_save();
 727  }
 728  
 729  
 730  /**
 731   * Interface implemented by any part_of_admin_tree that has children.
 732   *
 733   * The interface implemented by any part_of_admin_tree that can be a parent
 734   * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
 735   * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
 736   * include an add method for adding other part_of_admin_tree objects as children.
 737   *
 738   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 739   */
 740  interface parentable_part_of_admin_tree extends part_of_admin_tree {
 741  
 742  /**
 743   * Adds a part_of_admin_tree object to the admin tree.
 744   *
 745   * Used to add a part_of_admin_tree object to this object or a child of this
 746   * object. $something should only be added if $destinationname matches
 747   * $this->name. If it doesn't, add should be called on child objects that are
 748   * also parentable_part_of_admin_tree's.
 749   *
 750   * $something should be appended as the last child in the $destinationname. If the
 751   * $beforesibling is specified, $something should be prepended to it. If the given
 752   * sibling is not found, $something should be appended to the end of $destinationname
 753   * and a developer debugging message should be displayed.
 754   *
 755   * @param string $destinationname The internal name of the new parent for $something.
 756   * @param part_of_admin_tree $something The object to be added.
 757   * @return bool True on success, false on failure.
 758   */
 759      public function add($destinationname, $something, $beforesibling = null);
 760  
 761  }
 762  
 763  
 764  /**
 765   * The object used to represent folders (a.k.a. categories) in the admin tree block.
 766   *
 767   * Each admin_category object contains a number of part_of_admin_tree objects.
 768   *
 769   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 770   */
 771  class admin_category implements parentable_part_of_admin_tree {
 772  
 773      /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
 774      protected $children;
 775      /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
 776      public $name;
 777      /** @var string The displayed name for this category. Usually obtained through get_string() */
 778      public $visiblename;
 779      /** @var bool Should this category be hidden in admin tree block? */
 780      public $hidden;
 781      /** @var mixed Either a string or an array or strings */
 782      public $path;
 783      /** @var mixed Either a string or an array or strings */
 784      public $visiblepath;
 785  
 786      /** @var array fast lookup category cache, all categories of one tree point to one cache */
 787      protected $category_cache;
 788  
 789      /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
 790      protected $sort = false;
 791      /** @var bool If set to true children will be sorted in ascending order. */
 792      protected $sortasc = true;
 793      /** @var bool If set to true sub categories and pages will be split and then sorted.. */
 794      protected $sortsplit = true;
 795      /** @var bool $sorted True if the children have been sorted and don't need resorting */
 796      protected $sorted = false;
 797  
 798      /**
 799       * Constructor for an empty admin category
 800       *
 801       * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
 802       * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
 803       * @param bool $hidden hide category in admin tree block, defaults to false
 804       */
 805      public function __construct($name, $visiblename, $hidden=false) {
 806          $this->children    = array();
 807          $this->name        = $name;
 808          $this->visiblename = $visiblename;
 809          $this->hidden      = $hidden;
 810      }
 811  
 812      /**
 813       * Returns a reference to the part_of_admin_tree object with internal name $name.
 814       *
 815       * @param string $name The internal name of the object we want.
 816       * @param bool $findpath initialize path and visiblepath arrays
 817       * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
 818       *                  defaults to false
 819       */
 820      public function locate($name, $findpath=false) {
 821          if (!isset($this->category_cache[$this->name])) {
 822              // somebody much have purged the cache
 823              $this->category_cache[$this->name] = $this;
 824          }
 825  
 826          if ($this->name == $name) {
 827              if ($findpath) {
 828                  $this->visiblepath[] = $this->visiblename;
 829                  $this->path[]        = $this->name;
 830              }
 831              return $this;
 832          }
 833  
 834          // quick category lookup
 835          if (!$findpath and isset($this->category_cache[$name])) {
 836              return $this->category_cache[$name];
 837          }
 838  
 839          $return = NULL;
 840          foreach($this->children as $childid=>$unused) {
 841              if ($return = $this->children[$childid]->locate($name, $findpath)) {
 842                  break;
 843              }
 844          }
 845  
 846          if (!is_null($return) and $findpath) {
 847              $return->visiblepath[] = $this->visiblename;
 848              $return->path[]        = $this->name;
 849          }
 850  
 851          return $return;
 852      }
 853  
 854      /**
 855       * Search using query
 856       *
 857       * @param string query
 858       * @return mixed array-object structure of found settings and pages
 859       */
 860      public function search($query) {
 861          $result = array();
 862          foreach ($this->get_children() as $child) {
 863              $subsearch = $child->search($query);
 864              if (!is_array($subsearch)) {
 865                  debugging('Incorrect search result from '.$child->name);
 866                  continue;
 867              }
 868              $result = array_merge($result, $subsearch);
 869          }
 870          return $result;
 871      }
 872  
 873      /**
 874       * Removes part_of_admin_tree object with internal name $name.
 875       *
 876       * @param string $name The internal name of the object we want to remove.
 877       * @return bool success
 878       */
 879      public function prune($name) {
 880  
 881          if ($this->name == $name) {
 882              return false;  //can not remove itself
 883          }
 884  
 885          foreach($this->children as $precedence => $child) {
 886              if ($child->name == $name) {
 887                  // clear cache and delete self
 888                  while($this->category_cache) {
 889                      // delete the cache, but keep the original array address
 890                      array_pop($this->category_cache);
 891                  }
 892                  unset($this->children[$precedence]);
 893                  return true;
 894              } else if ($this->children[$precedence]->prune($name)) {
 895                  return true;
 896              }
 897          }
 898          return false;
 899      }
 900  
 901      /**
 902       * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
 903       *
 904       * By default the new part of the tree is appended as the last child of the parent. You
 905       * can specify a sibling node that the new part should be prepended to. If the given
 906       * sibling is not found, the part is appended to the end (as it would be by default) and
 907       * a developer debugging message is displayed.
 908       *
 909       * @throws coding_exception if the $beforesibling is empty string or is not string at all.
 910       * @param string $destinationame The internal name of the immediate parent that we want for $something.
 911       * @param mixed $something A part_of_admin_tree or setting instance to be added.
 912       * @param string $beforesibling The name of the parent's child the $something should be prepended to.
 913       * @return bool True if successfully added, false if $something can not be added.
 914       */
 915      public function add($parentname, $something, $beforesibling = null) {
 916          global $CFG;
 917  
 918          $parent = $this->locate($parentname);
 919          if (is_null($parent)) {
 920              debugging('parent does not exist!');
 921              return false;
 922          }
 923  
 924          if ($something instanceof part_of_admin_tree) {
 925              if (!($parent instanceof parentable_part_of_admin_tree)) {
 926                  debugging('error - parts of tree can be inserted only into parentable parts');
 927                  return false;
 928              }
 929              if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
 930                  // The name of the node is already used, simply warn the developer that this should not happen.
 931                  // It is intentional to check for the debug level before performing the check.
 932                  debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
 933              }
 934              if (is_null($beforesibling)) {
 935                  // Append $something as the parent's last child.
 936                  $parent->children[] = $something;
 937              } else {
 938                  if (!is_string($beforesibling) or trim($beforesibling) === '') {
 939                      throw new coding_exception('Unexpected value of the beforesibling parameter');
 940                  }
 941                  // Try to find the position of the sibling.
 942                  $siblingposition = null;
 943                  foreach ($parent->children as $childposition => $child) {
 944                      if ($child->name === $beforesibling) {
 945                          $siblingposition = $childposition;
 946                          break;
 947                      }
 948                  }
 949                  if (is_null($siblingposition)) {
 950                      debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
 951                      $parent->children[] = $something;
 952                  } else {
 953                      $parent->children = array_merge(
 954                          array_slice($parent->children, 0, $siblingposition),
 955                          array($something),
 956                          array_slice($parent->children, $siblingposition)
 957                      );
 958                  }
 959              }
 960              if ($something instanceof admin_category) {
 961                  if (isset($this->category_cache[$something->name])) {
 962                      debugging('Duplicate admin category name: '.$something->name);
 963                  } else {
 964                      $this->category_cache[$something->name] = $something;
 965                      $something->category_cache =& $this->category_cache;
 966                      foreach ($something->children as $child) {
 967                          // just in case somebody already added subcategories
 968                          if ($child instanceof admin_category) {
 969                              if (isset($this->category_cache[$child->name])) {
 970                                  debugging('Duplicate admin category name: '.$child->name);
 971                              } else {
 972                                  $this->category_cache[$child->name] = $child;
 973                                  $child->category_cache =& $this->category_cache;
 974                              }
 975                          }
 976                      }
 977                  }
 978              }
 979              return true;
 980  
 981          } else {
 982              debugging('error - can not add this element');
 983              return false;
 984          }
 985  
 986      }
 987  
 988      /**
 989       * Checks if the user has access to anything in this category.
 990       *
 991       * @return bool True if the user has access to at least one child in this category, false otherwise.
 992       */
 993      public function check_access() {
 994          foreach ($this->children as $child) {
 995              if ($child->check_access()) {
 996                  return true;
 997              }
 998          }
 999          return false;
1000      }
1001  
1002      /**
1003       * Is this category hidden in admin tree block?
1004       *
1005       * @return bool True if hidden
1006       */
1007      public function is_hidden() {
1008          return $this->hidden;
1009      }
1010  
1011      /**
1012       * Show we display Save button at the page bottom?
1013       * @return bool
1014       */
1015      public function show_save() {
1016          foreach ($this->children as $child) {
1017              if ($child->show_save()) {
1018                  return true;
1019              }
1020          }
1021          return false;
1022      }
1023  
1024      /**
1025       * Sets sorting on this category.
1026       *
1027       * Please note this function doesn't actually do the sorting.
1028       * It can be called anytime.
1029       * Sorting occurs when the user calls get_children.
1030       * Code using the children array directly won't see the sorted results.
1031       *
1032       * @param bool $sort If set to true children will be sorted, if false they won't be.
1033       * @param bool $asc If true sorting will be ascending, otherwise descending.
1034       * @param bool $split If true we sort pages and sub categories separately.
1035       */
1036      public function set_sorting($sort, $asc = true, $split = true) {
1037          $this->sort = (bool)$sort;
1038          $this->sortasc = (bool)$asc;
1039          $this->sortsplit = (bool)$split;
1040      }
1041  
1042      /**
1043       * Returns the children associated with this category.
1044       *
1045       * @return part_of_admin_tree[]
1046       */
1047      public function get_children() {
1048          // If we should sort and it hasn't already been sorted.
1049          if ($this->sort && !$this->sorted) {
1050              if ($this->sortsplit) {
1051                  $categories = array();
1052                  $pages = array();
1053                  foreach ($this->children as $child) {
1054                      if ($child instanceof admin_category) {
1055                          $categories[] = $child;
1056                      } else {
1057                          $pages[] = $child;
1058                      }
1059                  }
1060                  core_collator::asort_objects_by_property($categories, 'visiblename');
1061                  core_collator::asort_objects_by_property($pages, 'visiblename');
1062                  if (!$this->sortasc) {
1063                      $categories = array_reverse($categories);
1064                      $pages = array_reverse($pages);
1065                  }
1066                  $this->children = array_merge($pages, $categories);
1067              } else {
1068                  core_collator::asort_objects_by_property($this->children, 'visiblename');
1069                  if (!$this->sortasc) {
1070                      $this->children = array_reverse($this->children);
1071                  }
1072              }
1073              $this->sorted = true;
1074          }
1075          return $this->children;
1076      }
1077  
1078      /**
1079       * Magically gets a property from this object.
1080       *
1081       * @param $property
1082       * @return part_of_admin_tree[]
1083       * @throws coding_exception
1084       */
1085      public function __get($property) {
1086          if ($property === 'children') {
1087              return $this->get_children();
1088          }
1089          throw new coding_exception('Invalid property requested.');
1090      }
1091  
1092      /**
1093       * Magically sets a property against this object.
1094       *
1095       * @param string $property
1096       * @param mixed $value
1097       * @throws coding_exception
1098       */
1099      public function __set($property, $value) {
1100          if ($property === 'children') {
1101              $this->sorted = false;
1102              $this->children = $value;
1103          } else {
1104              throw new coding_exception('Invalid property requested.');
1105          }
1106      }
1107  
1108      /**
1109       * Checks if an inaccessible property is set.
1110       *
1111       * @param string $property
1112       * @return bool
1113       * @throws coding_exception
1114       */
1115      public function __isset($property) {
1116          if ($property === 'children') {
1117              return isset($this->children);
1118          }
1119          throw new coding_exception('Invalid property requested.');
1120      }
1121  }
1122  
1123  
1124  /**
1125   * Root of admin settings tree, does not have any parent.
1126   *
1127   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1128   */
1129  class admin_root extends admin_category {
1130  /** @var array List of errors */
1131      public $errors;
1132      /** @var string search query */
1133      public $search;
1134      /** @var bool full tree flag - true means all settings required, false only pages required */
1135      public $fulltree;
1136      /** @var bool flag indicating loaded tree */
1137      public $loaded;
1138      /** @var mixed site custom defaults overriding defaults in settings files*/
1139      public $custom_defaults;
1140  
1141      /**
1142       * @param bool $fulltree true means all settings required,
1143       *                            false only pages required
1144       */
1145      public function __construct($fulltree) {
1146          global $CFG;
1147  
1148          parent::__construct('root', get_string('administration'), false);
1149          $this->errors   = array();
1150          $this->search   = '';
1151          $this->fulltree = $fulltree;
1152          $this->loaded   = false;
1153  
1154          $this->category_cache = array();
1155  
1156          // load custom defaults if found
1157          $this->custom_defaults = null;
1158          $defaultsfile = "$CFG->dirroot/local/defaults.php";
1159          if (is_readable($defaultsfile)) {
1160              $defaults = array();
1161              include($defaultsfile);
1162              if (is_array($defaults) and count($defaults)) {
1163                  $this->custom_defaults = $defaults;
1164              }
1165          }
1166      }
1167  
1168      /**
1169       * Empties children array, and sets loaded to false
1170       *
1171       * @param bool $requirefulltree
1172       */
1173      public function purge_children($requirefulltree) {
1174          $this->children = array();
1175          $this->fulltree = ($requirefulltree || $this->fulltree);
1176          $this->loaded   = false;
1177          //break circular dependencies - this helps PHP 5.2
1178          while($this->category_cache) {
1179              array_pop($this->category_cache);
1180          }
1181          $this->category_cache = array();
1182      }
1183  }
1184  
1185  
1186  /**
1187   * Links external PHP pages into the admin tree.
1188   *
1189   * See detailed usage example at the top of this document (adminlib.php)
1190   *
1191   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1192   */
1193  class admin_externalpage implements part_of_admin_tree {
1194  
1195      /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1196      public $name;
1197  
1198      /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1199      public $visiblename;
1200  
1201      /** @var string The external URL that we should link to when someone requests this external page. */
1202      public $url;
1203  
1204      /** @var array The role capability/permission a user must have to access this external page. */
1205      public $req_capability;
1206  
1207      /** @var object The context in which capability/permission should be checked, default is site context. */
1208      public $context;
1209  
1210      /** @var bool hidden in admin tree block. */
1211      public $hidden;
1212  
1213      /** @var mixed either string or array of string */
1214      public $path;
1215  
1216      /** @var array list of visible names of page parents */
1217      public $visiblepath;
1218  
1219      /**
1220       * Constructor for adding an external page into the admin tree.
1221       *
1222       * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1223       * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1224       * @param string $url The external URL that we should link to when someone requests this external page.
1225       * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1226       * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1227       * @param stdClass $context The context the page relates to. Not sure what happens
1228       *      if you specify something other than system or front page. Defaults to system.
1229       */
1230      public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1231          $this->name        = $name;
1232          $this->visiblename = $visiblename;
1233          $this->url         = $url;
1234          if (is_array($req_capability)) {
1235              $this->req_capability = $req_capability;
1236          } else {
1237              $this->req_capability = array($req_capability);
1238          }
1239          $this->hidden = $hidden;
1240          $this->context = $context;
1241      }
1242  
1243      /**
1244       * Returns a reference to the part_of_admin_tree object with internal name $name.
1245       *
1246       * @param string $name The internal name of the object we want.
1247       * @param bool $findpath defaults to false
1248       * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1249       */
1250      public function locate($name, $findpath=false) {
1251          if ($this->name == $name) {
1252              if ($findpath) {
1253                  $this->visiblepath = array($this->visiblename);
1254                  $this->path        = array($this->name);
1255              }
1256              return $this;
1257          } else {
1258              $return = NULL;
1259              return $return;
1260          }
1261      }
1262  
1263      /**
1264       * This function always returns false, required function by interface
1265       *
1266       * @param string $name
1267       * @return false
1268       */
1269      public function prune($name) {
1270          return false;
1271      }
1272  
1273      /**
1274       * Search using query
1275       *
1276       * @param string $query
1277       * @return mixed array-object structure of found settings and pages
1278       */
1279      public function search($query) {
1280          $found = false;
1281          if (strpos(strtolower($this->name), $query) !== false) {
1282              $found = true;
1283          } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1284                  $found = true;
1285              }
1286          if ($found) {
1287              $result = new stdClass();
1288              $result->page     = $this;
1289              $result->settings = array();
1290              return array($this->name => $result);
1291          } else {
1292              return array();
1293          }
1294      }
1295  
1296      /**
1297       * Determines if the current user has access to this external page based on $this->req_capability.
1298       *
1299       * @return bool True if user has access, false otherwise.
1300       */
1301      public function check_access() {
1302          global $CFG;
1303          $context = empty($this->context) ? context_system::instance() : $this->context;
1304          foreach($this->req_capability as $cap) {
1305              if (has_capability($cap, $context)) {
1306                  return true;
1307              }
1308          }
1309          return false;
1310      }
1311  
1312      /**
1313       * Is this external page hidden in admin tree block?
1314       *
1315       * @return bool True if hidden
1316       */
1317      public function is_hidden() {
1318          return $this->hidden;
1319      }
1320  
1321      /**
1322       * Show we display Save button at the page bottom?
1323       * @return bool
1324       */
1325      public function show_save() {
1326          return false;
1327      }
1328  }
1329  
1330  /**
1331   * Used to store details of the dependency between two settings elements.
1332   *
1333   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1334   * @copyright 2017 Davo Smith, Synergy Learning
1335   */
1336  class admin_settingdependency {
1337      /** @var string the name of the setting to be shown/hidden */
1338      public $settingname;
1339      /** @var string the setting this is dependent on */
1340      public $dependenton;
1341      /** @var string the condition to show/hide the element */
1342      public $condition;
1343      /** @var string the value to compare against */
1344      public $value;
1345  
1346      /** @var string[] list of valid conditions */
1347      private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1348  
1349      /**
1350       * admin_settingdependency constructor.
1351       * @param string $settingname
1352       * @param string $dependenton
1353       * @param string $condition
1354       * @param string $value
1355       * @throws \coding_exception
1356       */
1357      public function __construct($settingname, $dependenton, $condition, $value) {
1358          $this->settingname = $this->parse_name($settingname);
1359          $this->dependenton = $this->parse_name($dependenton);
1360          $this->condition = $condition;
1361          $this->value = $value;
1362  
1363          if (!in_array($this->condition, self::$validconditions)) {
1364              throw new coding_exception("Invalid condition '$condition'");
1365          }
1366      }
1367  
1368      /**
1369       * Convert the setting name into the form field name.
1370       * @param string $name
1371       * @return string
1372       */
1373      private function parse_name($name) {
1374          $bits = explode('/', $name);
1375          $name = array_pop($bits);
1376          $plugin = '';
1377          if ($bits) {
1378              $plugin = array_pop($bits);
1379              if ($plugin === 'moodle') {
1380                  $plugin = '';
1381              }
1382          }
1383          return 's_'.$plugin.'_'.$name;
1384      }
1385  
1386      /**
1387       * Gather together all the dependencies in a format suitable for initialising javascript
1388       * @param admin_settingdependency[] $dependencies
1389       * @return array
1390       */
1391      public static function prepare_for_javascript($dependencies) {
1392          $result = [];
1393          foreach ($dependencies as $d) {
1394              if (!isset($result[$d->dependenton])) {
1395                  $result[$d->dependenton] = [];
1396              }
1397              if (!isset($result[$d->dependenton][$d->condition])) {
1398                  $result[$d->dependenton][$d->condition] = [];
1399              }
1400              if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1401                  $result[$d->dependenton][$d->condition][$d->value] = [];
1402              }
1403              $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1404          }
1405          return $result;
1406      }
1407  }
1408  
1409  /**
1410   * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1411   *
1412   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1413   */
1414  class admin_settingpage implements part_of_admin_tree {
1415  
1416      /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1417      public $name;
1418  
1419      /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1420      public $visiblename;
1421  
1422      /** @var mixed An array of admin_setting objects that are part of this setting page. */
1423      public $settings;
1424  
1425      /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1426      protected $dependencies = [];
1427  
1428      /** @var array The role capability/permission a user must have to access this external page. */
1429      public $req_capability;
1430  
1431      /** @var object The context in which capability/permission should be checked, default is site context. */
1432      public $context;
1433  
1434      /** @var bool hidden in admin tree block. */
1435      public $hidden;
1436  
1437      /** @var mixed string of paths or array of strings of paths */
1438      public $path;
1439  
1440      /** @var array list of visible names of page parents */
1441      public $visiblepath;
1442  
1443      /**
1444       * see admin_settingpage for details of this function
1445       *
1446       * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1447       * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1448       * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1449       * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1450       * @param stdClass $context The context the page relates to. Not sure what happens
1451       *      if you specify something other than system or front page. Defaults to system.
1452       */
1453      public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1454          $this->settings    = new stdClass();
1455          $this->name        = $name;
1456          $this->visiblename = $visiblename;
1457          if (is_array($req_capability)) {
1458              $this->req_capability = $req_capability;
1459          } else {
1460              $this->req_capability = array($req_capability);
1461          }
1462          $this->hidden      = $hidden;
1463          $this->context     = $context;
1464      }
1465  
1466      /**
1467       * see admin_category
1468       *
1469       * @param string $name
1470       * @param bool $findpath
1471       * @return mixed Object (this) if name ==  this->name, else returns null
1472       */
1473      public function locate($name, $findpath=false) {
1474          if ($this->name == $name) {
1475              if ($findpath) {
1476                  $this->visiblepath = array($this->visiblename);
1477                  $this->path        = array($this->name);
1478              }
1479              return $this;
1480          } else {
1481              $return = NULL;
1482              return $return;
1483          }
1484      }
1485  
1486      /**
1487       * Search string in settings page.
1488       *
1489       * @param string $query
1490       * @return array
1491       */
1492      public function search($query) {
1493          $found = array();
1494  
1495          foreach ($this->settings as $setting) {
1496              if ($setting->is_related($query)) {
1497                  $found[] = $setting;
1498              }
1499          }
1500  
1501          if ($found) {
1502              $result = new stdClass();
1503              $result->page     = $this;
1504              $result->settings = $found;
1505              return array($this->name => $result);
1506          }
1507  
1508          $found = false;
1509          if (strpos(strtolower($this->name), $query) !== false) {
1510              $found = true;
1511          } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1512                  $found = true;
1513              }
1514          if ($found) {
1515              $result = new stdClass();
1516              $result->page     = $this;
1517              $result->settings = array();
1518              return array($this->name => $result);
1519          } else {
1520              return array();
1521          }
1522      }
1523  
1524      /**
1525       * This function always returns false, required by interface
1526       *
1527       * @param string $name
1528       * @return bool Always false
1529       */
1530      public function prune($name) {
1531          return false;
1532      }
1533  
1534      /**
1535       * adds an admin_setting to this admin_settingpage
1536       *
1537       * not the same as add for admin_category. adds an admin_setting to this admin_settingpage. settings appear (on the settingpage) in the order in which they're added
1538       * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1539       *
1540       * @param object $setting is the admin_setting object you want to add
1541       * @return bool true if successful, false if not
1542       */
1543      public function add($setting) {
1544          if (!($setting instanceof admin_setting)) {
1545              debugging('error - not a setting instance');
1546              return false;
1547          }
1548  
1549          $name = $setting->name;
1550          if ($setting->plugin) {
1551              $name = $setting->plugin . $name;
1552          }
1553          $this->settings->{$name} = $setting;
1554          return true;
1555      }
1556  
1557      /**
1558       * Hide the named setting if the specified condition is matched.
1559       *
1560       * @param string $settingname
1561       * @param string $dependenton
1562       * @param string $condition
1563       * @param string $value
1564       */
1565      public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1566          $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1567  
1568          // Reformat the dependency name to the plugin | name format used in the display.
1569          $dependenton = str_replace('/', ' | ', $dependenton);
1570  
1571          // Let the setting know, so it can be displayed underneath.
1572          $findname = str_replace('/', '', $settingname);
1573          foreach ($this->settings as $name => $setting) {
1574              if ($name === $findname) {
1575                  $setting->add_dependent_on($dependenton);
1576              }
1577          }
1578      }
1579  
1580      /**
1581       * see admin_externalpage
1582       *
1583       * @return bool Returns true for yes false for no
1584       */
1585      public function check_access() {
1586          global $CFG;
1587          $context = empty($this->context) ? context_system::instance() : $this->context;
1588          foreach($this->req_capability as $cap) {
1589              if (has_capability($cap, $context)) {
1590                  return true;
1591              }
1592          }
1593          return false;
1594      }
1595  
1596      /**
1597       * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1598       * @return string Returns an XHTML string
1599       */
1600      public function output_html() {
1601          $adminroot = admin_get_root();
1602          $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1603          foreach($this->settings as $setting) {
1604              $fullname = $setting->get_full_name();
1605              if (array_key_exists($fullname, $adminroot->errors)) {
1606                  $data = $adminroot->errors[$fullname]->data;
1607              } else {
1608                  $data = $setting->get_setting();
1609                  // do not use defaults if settings not available - upgrade settings handles the defaults!
1610              }
1611              $return .= $setting->output_html($data);
1612          }
1613          $return .= '</fieldset>';
1614          return $return;
1615      }
1616  
1617      /**
1618       * Is this settings page hidden in admin tree block?
1619       *
1620       * @return bool True if hidden
1621       */
1622      public function is_hidden() {
1623          return $this->hidden;
1624      }
1625  
1626      /**
1627       * Show we display Save button at the page bottom?
1628       * @return bool
1629       */
1630      public function show_save() {
1631          foreach($this->settings as $setting) {
1632              if (empty($setting->nosave)) {
1633                  return true;
1634              }
1635          }
1636          return false;
1637      }
1638  
1639      /**
1640       * Should any of the settings on this page be shown / hidden based on conditions?
1641       * @return bool
1642       */
1643      public function has_dependencies() {
1644          return (bool)$this->dependencies;
1645      }
1646  
1647      /**
1648       * Format the setting show/hide conditions ready to initialise the page javascript
1649       * @return array
1650       */
1651      public function get_dependencies_for_javascript() {
1652          if (!$this->has_dependencies()) {
1653              return [];
1654          }
1655          return admin_settingdependency::prepare_for_javascript($this->dependencies);
1656      }
1657  }
1658  
1659  
1660  /**
1661   * Admin settings class. Only exists on setting pages.
1662   * Read & write happens at this level; no authentication.
1663   *
1664   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1665   */
1666  abstract class admin_setting {
1667      /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1668      public $name;
1669      /** @var string localised name */
1670      public $visiblename;
1671      /** @var string localised long description in Markdown format */
1672      public $description;
1673      /** @var mixed Can be string or array of string */
1674      public $defaultsetting;
1675      /** @var string */
1676      public $updatedcallback;
1677      /** @var mixed can be String or Null.  Null means main config table */
1678      public $plugin; // null means main config table
1679      /** @var bool true indicates this setting does not actually save anything, just information */
1680      public $nosave = false;
1681      /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1682      public $affectsmodinfo = false;
1683      /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1684      private $flags = array();
1685      /** @var bool Whether this field must be forced LTR. */
1686      private $forceltr = null;
1687      /** @var array list of other settings that may cause this setting to be hidden */
1688      private $dependenton = [];
1689      /** @var bool Whether this setting uses a custom form control */
1690      protected $customcontrol = false;
1691  
1692      /**
1693       * Constructor
1694       * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1695       *                     or 'myplugin/mysetting' for ones in config_plugins.
1696       * @param string $visiblename localised name
1697       * @param string $description localised long description
1698       * @param mixed $defaultsetting string or array depending on implementation
1699       */
1700      public function __construct($name, $visiblename, $description, $defaultsetting) {
1701          $this->parse_setting_name($name);
1702          $this->visiblename    = $visiblename;
1703          $this->description    = $description;
1704          $this->defaultsetting = $defaultsetting;
1705      }
1706  
1707      /**
1708       * Generic function to add a flag to this admin setting.
1709       *
1710       * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1711       * @param bool $default - The default for the flag
1712       * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1713       * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1714       */
1715      protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1716          if (empty($this->flags[$shortname])) {
1717              $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1718          } else {
1719              $this->flags[$shortname]->set_options($enabled, $default);
1720          }
1721      }
1722  
1723      /**
1724       * Set the enabled options flag on this admin setting.
1725       *
1726       * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1727       * @param bool $default - The default for the flag
1728       */
1729      public function set_enabled_flag_options($enabled, $default) {
1730          $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1731      }
1732  
1733      /**
1734       * Set the advanced options flag on this admin setting.
1735       *
1736       * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1737       * @param bool $default - The default for the flag
1738       */
1739      public function set_advanced_flag_options($enabled, $default) {
1740          $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1741      }
1742  
1743  
1744      /**
1745       * Set the locked options flag on this admin setting.
1746       *
1747       * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1748       * @param bool $default - The default for the flag
1749       */
1750      public function set_locked_flag_options($enabled, $default) {
1751          $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1752      }
1753  
1754      /**
1755       * Set the required options flag on this admin setting.
1756       *
1757       * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1758       * @param bool $default - The default for the flag.
1759       */
1760      public function set_required_flag_options($enabled, $default) {
1761          $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1762      }
1763  
1764      /**
1765       * Is this option forced in config.php?
1766       *
1767       * @return bool
1768       */
1769      public function is_readonly(): bool {
1770          global $CFG;
1771  
1772          if (empty($this->plugin)) {
1773              if (array_key_exists($this->name, $CFG->config_php_settings)) {
1774                  return true;
1775              }
1776          } else {
1777              if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
1778                  and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
1779                  return true;
1780              }
1781          }
1782          return false;
1783      }
1784  
1785      /**
1786       * Get the currently saved value for a setting flag
1787       *
1788       * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1789       * @return bool
1790       */
1791      public function get_setting_flag_value(admin_setting_flag $flag) {
1792          $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1793          if (!isset($value)) {
1794              $value = $flag->get_default();
1795          }
1796  
1797          return !empty($value);
1798      }
1799  
1800      /**
1801       * Get the list of defaults for the flags on this setting.
1802       *
1803       * @param array of strings describing the defaults for this setting. This is appended to by this function.
1804       */
1805      public function get_setting_flag_defaults(& $defaults) {
1806          foreach ($this->flags as $flag) {
1807              if ($flag->is_enabled() && $flag->get_default()) {
1808                  $defaults[] = $flag->get_displayname();
1809              }
1810          }
1811      }
1812  
1813      /**
1814       * Output the input fields for the advanced and locked flags on this setting.
1815       *
1816       * @param bool $adv - The current value of the advanced flag.
1817       * @param bool $locked - The current value of the locked flag.
1818       * @return string $output - The html for the flags.
1819       */
1820      public function output_setting_flags() {
1821          $output = '';
1822  
1823          foreach ($this->flags as $flag) {
1824              if ($flag->is_enabled()) {
1825                  $output .= $flag->output_setting_flag($this);
1826              }
1827          }
1828  
1829          if (!empty($output)) {
1830              return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1831          }
1832          return $output;
1833      }
1834  
1835      /**
1836       * Write the values of the flags for this admin setting.
1837       *
1838       * @param array $data - The data submitted from the form or null to set the default value for new installs.
1839       * @return bool - true if successful.
1840       */
1841      public function write_setting_flags($data) {
1842          $result = true;
1843          foreach ($this->flags as $flag) {
1844              $result = $result && $flag->write_setting_flag($this, $data);
1845          }
1846          return $result;
1847      }
1848  
1849      /**
1850       * Set up $this->name and potentially $this->plugin
1851       *
1852       * Set up $this->name and possibly $this->plugin based on whether $name looks
1853       * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1854       * on the names, that is, output a developer debug warning if the name
1855       * contains anything other than [a-zA-Z0-9_]+.
1856       *
1857       * @param string $name the setting name passed in to the constructor.
1858       */
1859      private function parse_setting_name($name) {
1860          $bits = explode('/', $name);
1861          if (count($bits) > 2) {
1862              throw new moodle_exception('invalidadminsettingname', '', '', $name);
1863          }
1864          $this->name = array_pop($bits);
1865          if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1866              throw new moodle_exception('invalidadminsettingname', '', '', $name);
1867          }
1868          if (!empty($bits)) {
1869              $this->plugin = array_pop($bits);
1870              if ($this->plugin === 'moodle') {
1871                  $this->plugin = null;
1872              } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1873                      throw new moodle_exception('invalidadminsettingname', '', '', $name);
1874                  }
1875          }
1876      }
1877  
1878      /**
1879       * Returns the fullname prefixed by the plugin
1880       * @return string
1881       */
1882      public function get_full_name() {
1883          return 's_'.$this->plugin.'_'.$this->name;
1884      }
1885  
1886      /**
1887       * Returns the ID string based on plugin and name
1888       * @return string
1889       */
1890      public function get_id() {
1891          return 'id_s_'.$this->plugin.'_'.$this->name;
1892      }
1893  
1894      /**
1895       * @param bool $affectsmodinfo If true, changes to this setting will
1896       *   cause the course cache to be rebuilt
1897       */
1898      public function set_affects_modinfo($affectsmodinfo) {
1899          $this->affectsmodinfo = $affectsmodinfo;
1900      }
1901  
1902      /**
1903       * Returns the config if possible
1904       *
1905       * @return mixed returns config if successful else null
1906       */
1907      public function config_read($name) {
1908          global $CFG;
1909          if (!empty($this->plugin)) {
1910              $value = get_config($this->plugin, $name);
1911              return $value === false ? NULL : $value;
1912  
1913          } else {
1914              if (isset($CFG->$name)) {
1915                  return $CFG->$name;
1916              } else {
1917                  return NULL;
1918              }
1919          }
1920      }
1921  
1922      /**
1923       * Used to set a config pair and log change
1924       *
1925       * @param string $name
1926       * @param mixed $value Gets converted to string if not null
1927       * @return bool Write setting to config table
1928       */
1929      public function config_write($name, $value) {
1930          global $DB, $USER, $CFG;
1931  
1932          if ($this->nosave) {
1933              return true;
1934          }
1935  
1936          // make sure it is a real change
1937          $oldvalue = get_config($this->plugin, $name);
1938          $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1939          $value = is_null($value) ? null : (string)$value;
1940  
1941          if ($oldvalue === $value) {
1942              return true;
1943          }
1944  
1945          // store change
1946          set_config($name, $value, $this->plugin);
1947  
1948          // Some admin settings affect course modinfo
1949          if ($this->affectsmodinfo) {
1950              // Clear course cache for all courses
1951              rebuild_course_cache(0, true);
1952          }
1953  
1954          $this->add_to_config_log($name, $oldvalue, $value);
1955  
1956          return true; // BC only
1957      }
1958  
1959      /**
1960       * Log config changes if necessary.
1961       * @param string $name
1962       * @param string $oldvalue
1963       * @param string $value
1964       */
1965      protected function add_to_config_log($name, $oldvalue, $value) {
1966          add_to_config_log($name, $oldvalue, $value, $this->plugin);
1967      }
1968  
1969      /**
1970       * Returns current value of this setting
1971       * @return mixed array or string depending on instance, NULL means not set yet
1972       */
1973      public abstract function get_setting();
1974  
1975      /**
1976       * Returns default setting if exists
1977       * @return mixed array or string depending on instance; NULL means no default, user must supply
1978       */
1979      public function get_defaultsetting() {
1980          $adminroot =  admin_get_root(false, false);
1981          if (!empty($adminroot->custom_defaults)) {
1982              $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1983              if (isset($adminroot->custom_defaults[$plugin])) {
1984                  if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1985                      return $adminroot->custom_defaults[$plugin][$this->name];
1986                  }
1987              }
1988          }
1989          return $this->defaultsetting;
1990      }
1991  
1992      /**
1993       * Store new setting
1994       *
1995       * @param mixed $data string or array, must not be NULL
1996       * @return string empty string if ok, string error message otherwise
1997       */
1998      public abstract function write_setting($data);
1999  
2000      /**
2001       * Return part of form with setting
2002       * This function should always be overwritten
2003       *
2004       * @param mixed $data array or string depending on setting
2005       * @param string $query
2006       * @return string
2007       */
2008      public function output_html($data, $query='') {
2009      // should be overridden
2010          return;
2011      }
2012  
2013      /**
2014       * Function called if setting updated - cleanup, cache reset, etc.
2015       * @param string $functionname Sets the function name
2016       * @return void
2017       */
2018      public function set_updatedcallback($functionname) {
2019          $this->updatedcallback = $functionname;
2020      }
2021  
2022      /**
2023       * Execute postupdatecallback if necessary.
2024       * @param mixed $original original value before write_setting()
2025       * @return bool true if changed, false if not.
2026       */
2027      public function post_write_settings($original) {
2028          // Comparison must work for arrays too.
2029          if (serialize($original) === serialize($this->get_setting())) {
2030              return false;
2031          }
2032  
2033          $callbackfunction = $this->updatedcallback;
2034          if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2035              $callbackfunction($this->get_full_name());
2036          }
2037          return true;
2038      }
2039  
2040      /**
2041       * Is setting related to query text - used when searching
2042       * @param string $query
2043       * @return bool
2044       */
2045      public function is_related($query) {
2046          if (strpos(strtolower($this->name), $query) !== false) {
2047              return true;
2048          }
2049          if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2050              return true;
2051          }
2052          if (strpos(core_text::strtolower($this->description), $query) !== false) {
2053              return true;
2054          }
2055          $current = $this->get_setting();
2056          if (!is_null($current)) {
2057              if (is_string($current)) {
2058                  if (strpos(core_text::strtolower($current), $query) !== false) {
2059                      return true;
2060                  }
2061              }
2062          }
2063          $default = $this->get_defaultsetting();
2064          if (!is_null($default)) {
2065              if (is_string($default)) {
2066                  if (strpos(core_text::strtolower($default), $query) !== false) {
2067                      return true;
2068                  }
2069              }
2070          }
2071          return false;
2072      }
2073  
2074      /**
2075       * Get whether this should be displayed in LTR mode.
2076       *
2077       * @return bool|null
2078       */
2079      public function get_force_ltr() {
2080          return $this->forceltr;
2081      }
2082  
2083      /**
2084       * Set whether to force LTR or not.
2085       *
2086       * @param bool $value True when forced, false when not force, null when unknown.
2087       */
2088      public function set_force_ltr($value) {
2089          $this->forceltr = $value;
2090      }
2091  
2092      /**
2093       * Add a setting to the list of those that could cause this one to be hidden
2094       * @param string $dependenton
2095       */
2096      public function add_dependent_on($dependenton) {
2097          $this->dependenton[] = $dependenton;
2098      }
2099  
2100      /**
2101       * Get a list of the settings that could cause this one to be hidden.
2102       * @return array
2103       */
2104      public function get_dependent_on() {
2105          return $this->dependenton;
2106      }
2107  
2108      /**
2109       * Whether this setting uses a custom form control.
2110       * This function is especially useful to decide if we should render a label element for this setting or not.
2111       *
2112       * @return bool
2113       */
2114      public function has_custom_form_control(): bool {
2115          return $this->customcontrol;
2116      }
2117  }
2118  
2119  /**
2120   * An additional option that can be applied to an admin setting.
2121   * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2122   *
2123   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2124   */
2125  class admin_setting_flag {
2126      /** @var bool Flag to indicate if this option can be toggled for this setting */
2127      private $enabled = false;
2128      /** @var bool Flag to indicate if this option defaults to true or false */
2129      private $default = false;
2130      /** @var string Short string used to create setting name - e.g. 'adv' */
2131      private $shortname = '';
2132      /** @var string String used as the label for this flag */
2133      private $displayname = '';
2134      /** @const Checkbox for this flag is displayed in admin page */
2135      const ENABLED = true;
2136      /** @const Checkbox for this flag is not displayed in admin page */
2137      const DISABLED = false;
2138  
2139      /**
2140       * Constructor
2141       *
2142       * @param bool $enabled Can this option can be toggled.
2143       *                      Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2144       * @param bool $default The default checked state for this setting option.
2145       * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2146       * @param string $displayname The displayname of this flag. Used as a label for the flag.
2147       */
2148      public function __construct($enabled, $default, $shortname, $displayname) {
2149          $this->shortname = $shortname;
2150          $this->displayname = $displayname;
2151          $this->set_options($enabled, $default);
2152      }
2153  
2154      /**
2155       * Update the values of this setting options class
2156       *
2157       * @param bool $enabled Can this option can be toggled.
2158       *                      Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2159       * @param bool $default The default checked state for this setting option.
2160       */
2161      public function set_options($enabled, $default) {
2162          $this->enabled = $enabled;
2163          $this->default = $default;
2164      }
2165  
2166      /**
2167       * Should this option appear in the interface and be toggleable?
2168       *
2169       * @return bool Is it enabled?
2170       */
2171      public function is_enabled() {
2172          return $this->enabled;
2173      }
2174  
2175      /**
2176       * Should this option be checked by default?
2177       *
2178       * @return bool Is it on by default?
2179       */
2180      public function get_default() {
2181          return $this->default;
2182      }
2183  
2184      /**
2185       * Return the short name for this flag. e.g. 'adv' or 'locked'
2186       *
2187       * @return string
2188       */
2189      public function get_shortname() {
2190          return $this->shortname;
2191      }
2192  
2193      /**
2194       * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2195       *
2196       * @return string
2197       */
2198      public function get_displayname() {
2199          return $this->displayname;
2200      }
2201  
2202      /**
2203       * Save the submitted data for this flag - or set it to the default if $data is null.
2204       *
2205       * @param admin_setting $setting - The admin setting for this flag
2206       * @param array $data - The data submitted from the form or null to set the default value for new installs.
2207       * @return bool
2208       */
2209      public function write_setting_flag(admin_setting $setting, $data) {
2210          $result = true;
2211          if ($this->is_enabled()) {
2212              if (!isset($data)) {
2213                  $value = $this->get_default();
2214              } else {
2215                  $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2216              }
2217              $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2218          }
2219  
2220          return $result;
2221  
2222      }
2223  
2224      /**
2225       * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2226       *
2227       * @param admin_setting $setting - The admin setting for this flag
2228       * @return string - The html for the checkbox.
2229       */
2230      public function output_setting_flag(admin_setting $setting) {
2231          global $OUTPUT;
2232  
2233          $value = $setting->get_setting_flag_value($this);
2234  
2235          $context = new stdClass();
2236          $context->id = $setting->get_id() . '_' . $this->get_shortname();
2237          $context->name = $setting->get_full_name() .  '_' . $this->get_shortname();
2238          $context->value = 1;
2239          $context->checked = $value ? true : false;
2240          $context->label = $this->get_displayname();
2241  
2242          return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2243      }
2244  }
2245  
2246  
2247  /**
2248   * No setting - just heading and text.
2249   *
2250   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2251   */
2252  class admin_setting_heading extends admin_setting {
2253  
2254      /**
2255       * not a setting, just text
2256       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2257       * @param string $heading heading
2258       * @param string $information text in box
2259       */
2260      public function __construct($name, $heading, $information) {
2261          $this->nosave = true;
2262          parent::__construct($name, $heading, $information, '');
2263      }
2264  
2265      /**
2266       * Always returns true
2267       * @return bool Always returns true
2268       */
2269      public function get_setting() {
2270          return true;
2271      }
2272  
2273      /**
2274       * Always returns true
2275       * @return bool Always returns true
2276       */
2277      public function get_defaultsetting() {
2278          return true;
2279      }
2280  
2281      /**
2282       * Never write settings
2283       * @return string Always returns an empty string
2284       */
2285      public function write_setting($data) {
2286      // do not write any setting
2287          return '';
2288      }
2289  
2290      /**
2291       * Returns an HTML string
2292       * @return string Returns an HTML string
2293       */
2294      public function output_html($data, $query='') {
2295          global $OUTPUT;
2296          $context = new stdClass();
2297          $context->title = $this->visiblename;
2298          $context->description = $this->description;
2299          $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2300          return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2301      }
2302  }
2303  
2304  /**
2305   * No setting - just name and description in same row.
2306   *
2307   * @copyright 2018 onwards Amaia Anabitarte
2308   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2309   */
2310  class admin_setting_description extends admin_setting {
2311  
2312      /**
2313       * Not a setting, just text
2314       *
2315       * @param string $name
2316       * @param string $visiblename
2317       * @param string $description
2318       */
2319      public function __construct($name, $visiblename, $description) {
2320          $this->nosave = true;
2321          parent::__construct($name, $visiblename, $description, '');
2322      }
2323  
2324      /**
2325       * Always returns true
2326       *
2327       * @return bool Always returns true
2328       */
2329      public function get_setting() {
2330          return true;
2331      }
2332  
2333      /**
2334       * Always returns true
2335       *
2336       * @return bool Always returns true
2337       */
2338      public function get_defaultsetting() {
2339          return true;
2340      }
2341  
2342      /**
2343       * Never write settings
2344       *
2345       * @param mixed $data Gets converted to str for comparison against yes value
2346       * @return string Always returns an empty string
2347       */
2348      public function write_setting($data) {
2349          // Do not write any setting.
2350          return '';
2351      }
2352  
2353      /**
2354       * Returns an HTML string
2355       *
2356       * @param string $data
2357       * @param string $query
2358       * @return string Returns an HTML string
2359       */
2360      public function output_html($data, $query='') {
2361          global $OUTPUT;
2362  
2363          $context = new stdClass();
2364          $context->title = $this->visiblename;
2365          $context->description = $this->description;
2366  
2367          return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2368      }
2369  }
2370  
2371  
2372  
2373  /**
2374   * The most flexible setting, the user enters text.
2375   *
2376   * This type of field should be used for config settings which are using
2377   * English words and are not localised (passwords, database name, list of values, ...).
2378   *
2379   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2380   */
2381  class admin_setting_configtext extends admin_setting {
2382  
2383      /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
2384      public $paramtype;
2385      /** @var int default field size */
2386      public $size;
2387  
2388      /**
2389       * Config text constructor
2390       *
2391       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2392       * @param string $visiblename localised
2393       * @param string $description long localised info
2394       * @param string $defaultsetting
2395       * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2396       * @param int $size default field size
2397       */
2398      public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2399          $this->paramtype = $paramtype;
2400          if (!is_null($size)) {
2401              $this->size  = $size;
2402          } else {
2403              $this->size  = ($paramtype === PARAM_INT) ? 5 : 30;
2404          }
2405          parent::__construct($name, $visiblename, $description, $defaultsetting);
2406      }
2407  
2408      /**
2409       * Get whether this should be displayed in LTR mode.
2410       *
2411       * Try to guess from the PARAM type unless specifically set.
2412       */
2413      public function get_force_ltr() {
2414          $forceltr = parent::get_force_ltr();
2415          if ($forceltr === null) {
2416              return !is_rtl_compatible($this->paramtype);
2417          }
2418          return $forceltr;
2419      }
2420  
2421      /**
2422       * Return the setting
2423       *
2424       * @return mixed returns config if successful else null
2425       */
2426      public function get_setting() {
2427          return $this->config_read($this->name);
2428      }
2429  
2430      public function write_setting($data) {
2431          if ($this->paramtype === PARAM_INT and $data === '') {
2432          // do not complain if '' used instead of 0
2433              $data = 0;
2434          }
2435          // $data is a string
2436          $validated = $this->validate($data);
2437          if ($validated !== true) {
2438              return $validated;
2439          }
2440          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2441      }
2442  
2443      /**
2444       * Validate data before storage
2445       * @param string data
2446       * @return mixed true if ok string if error found
2447       */
2448      public function validate($data) {
2449          // allow paramtype to be a custom regex if it is the form of /pattern/
2450          if (preg_match('#^/.*/$#', $this->paramtype)) {
2451              if (preg_match($this->paramtype, $data)) {
2452                  return true;
2453              } else {
2454                  return get_string('validateerror', 'admin');
2455              }
2456  
2457          } else if ($this->paramtype === PARAM_RAW) {
2458              return true;
2459  
2460          } else {
2461              $cleaned = clean_param($data, $this->paramtype);
2462              if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2463                  return true;
2464              } else {
2465                  return get_string('validateerror', 'admin');
2466              }
2467          }
2468      }
2469  
2470      /**
2471       * Return an XHTML string for the setting
2472       * @return string Returns an XHTML string
2473       */
2474      public function output_html($data, $query='') {
2475          global $OUTPUT;
2476  
2477          $default = $this->get_defaultsetting();
2478          $context = (object) [
2479              'size' => $this->size,
2480              'id' => $this->get_id(),
2481              'name' => $this->get_full_name(),
2482              'value' => $data,
2483              'forceltr' => $this->get_force_ltr(),
2484              'readonly' => $this->is_readonly(),
2485          ];
2486          $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2487  
2488          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2489      }
2490  }
2491  
2492  /**
2493   * Text input with a maximum length constraint.
2494   *
2495   * @copyright 2015 onwards Ankit Agarwal
2496   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2497   */
2498  class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2499  
2500      /** @var int maximum number of chars allowed. */
2501      protected $maxlength;
2502  
2503      /**
2504       * Config text constructor
2505       *
2506       * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2507       *                     or 'myplugin/mysetting' for ones in config_plugins.
2508       * @param string $visiblename localised
2509       * @param string $description long localised info
2510       * @param string $defaultsetting
2511       * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2512       * @param int $size default field size
2513       * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2514       */
2515      public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2516                                  $size=null, $maxlength = 0) {
2517          $this->maxlength = $maxlength;
2518          parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2519      }
2520  
2521      /**
2522       * Validate data before storage
2523       *
2524       * @param string $data data
2525       * @return mixed true if ok string if error found
2526       */
2527      public function validate($data) {
2528          $parentvalidation = parent::validate($data);
2529          if ($parentvalidation === true) {
2530              if ($this->maxlength > 0) {
2531                  // Max length check.
2532                  $length = core_text::strlen($data);
2533                  if ($length > $this->maxlength) {
2534                      return get_string('maximumchars', 'moodle',  $this->maxlength);
2535                  }
2536                  return true;
2537              } else {
2538                  return true; // No max length check needed.
2539              }
2540          } else {
2541              return $parentvalidation;
2542          }
2543      }
2544  }
2545  
2546  /**
2547   * General text area without html editor.
2548   *
2549   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2550   */
2551  class admin_setting_configtextarea extends admin_setting_configtext {
2552      private $rows;
2553      private $cols;
2554  
2555      /**
2556       * @param string $name
2557       * @param string $visiblename
2558       * @param string $description
2559       * @param mixed $defaultsetting string or array
2560       * @param mixed $paramtype
2561       * @param string $cols The number of columns to make the editor
2562       * @param string $rows The number of rows to make the editor
2563       */
2564      public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2565          $this->rows = $rows;
2566          $this->cols = $cols;
2567          parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2568      }
2569  
2570      /**
2571       * Returns an XHTML string for the editor
2572       *
2573       * @param string $data
2574       * @param string $query
2575       * @return string XHTML string for the editor
2576       */
2577      public function output_html($data, $query='') {
2578          global $OUTPUT;
2579  
2580          $default = $this->get_defaultsetting();
2581          $defaultinfo = $default;
2582          if (!is_null($default) and $default !== '') {
2583              $defaultinfo = "\n".$default;
2584          }
2585  
2586          $context = (object) [
2587              'cols' => $this->cols,
2588              'rows' => $this->rows,
2589              'id' => $this->get_id(),
2590              'name' => $this->get_full_name(),
2591              'value' => $data,
2592              'forceltr' => $this->get_force_ltr(),
2593              'readonly' => $this->is_readonly(),
2594          ];
2595          $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2596  
2597          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2598      }
2599  }
2600  
2601  /**
2602   * General text area with html editor.
2603   */
2604  class admin_setting_confightmleditor extends admin_setting_configtextarea {
2605  
2606      /**
2607       * @param string $name
2608       * @param string $visiblename
2609       * @param string $description
2610       * @param mixed $defaultsetting string or array
2611       * @param mixed $paramtype
2612       */
2613      public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2614          parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2615          $this->set_force_ltr(false);
2616          editors_head_setup();
2617      }
2618  
2619      /**
2620       * Returns an XHTML string for the editor
2621       *
2622       * @param string $data
2623       * @param string $query
2624       * @return string XHTML string for the editor
2625       */
2626      public function output_html($data, $query='') {
2627          $editor = editors_get_preferred_editor(FORMAT_HTML);
2628          $editor->set_text($data);
2629          $editor->use_editor($this->get_id(), array('noclean'=>true));
2630          return parent::output_html($data, $query);
2631      }
2632  
2633      /**
2634       * Checks if data has empty html.
2635       *
2636       * @param string $data
2637       * @return string Empty when no errors.
2638       */
2639      public function write_setting($data) {
2640          if (trim(html_to_text($data)) === '') {
2641              $data = '';
2642          }
2643          return parent::write_setting($data);
2644      }
2645  }
2646  
2647  
2648  /**
2649   * Password field, allows unmasking of password
2650   *
2651   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2652   */
2653  class admin_setting_configpasswordunmask extends admin_setting_configtext {
2654  
2655      /**
2656       * Constructor
2657       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2658       * @param string $visiblename localised
2659       * @param string $description long localised info
2660       * @param string $defaultsetting default password
2661       */
2662      public function __construct($name, $visiblename, $description, $defaultsetting) {
2663          parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2664      }
2665  
2666      /**
2667       * Log config changes if necessary.
2668       * @param string $name
2669       * @param string $oldvalue
2670       * @param string $value
2671       */
2672      protected function add_to_config_log($name, $oldvalue, $value) {
2673          if ($value !== '') {
2674              $value = '********';
2675          }
2676          if ($oldvalue !== '' and $oldvalue !== null) {
2677              $oldvalue = '********';
2678          }
2679          parent::add_to_config_log($name, $oldvalue, $value);
2680      }
2681  
2682      /**
2683       * Returns HTML for the field.
2684       *
2685       * @param   string  $data       Value for the field
2686       * @param   string  $query      Passed as final argument for format_admin_setting
2687       * @return  string              Rendered HTML
2688       */
2689      public function output_html($data, $query='') {
2690          global $OUTPUT;
2691  
2692          $context = (object) [
2693              'id' => $this->get_id(),
2694              'name' => $this->get_full_name(),
2695              'size' => $this->size,
2696              'value' => $this->is_readonly() ? null : $data,
2697              'forceltr' => $this->get_force_ltr(),
2698              'readonly' => $this->is_readonly(),
2699          ];
2700          $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2701          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2702      }
2703  }
2704  
2705  /**
2706   * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2707   *
2708   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2709   * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2710   */
2711  class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2712  
2713      /**
2714       * Constructor
2715       *
2716       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2717       * @param string $visiblename localised
2718       * @param string $description long localised info
2719       * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2720       */
2721      public function __construct($name, $visiblename, $description, $defaultsetting) {
2722          parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2723          $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2724      }
2725  }
2726  
2727  /**
2728   * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2729   * Note: Only advanced makes sense right now - locked does not.
2730   *
2731   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2732   */
2733  class admin_setting_configempty extends admin_setting_configtext {
2734  
2735      /**
2736       * @param string $name
2737       * @param string $visiblename
2738       * @param string $description
2739       */
2740      public function __construct($name, $visiblename, $description) {
2741          parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2742      }
2743  
2744      /**
2745       * Returns an XHTML string for the hidden field
2746       *
2747       * @param string $data
2748       * @param string $query
2749       * @return string XHTML string for the editor
2750       */
2751      public function output_html($data, $query='') {
2752          global $OUTPUT;
2753  
2754          $context = (object) [
2755              'id' => $this->get_id(),
2756              'name' => $this->get_full_name()
2757          ];
2758          $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2759  
2760          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2761      }
2762  }
2763  
2764  
2765  /**
2766   * Path to directory
2767   *
2768   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2769   */
2770  class admin_setting_configfile extends admin_setting_configtext {
2771      /**
2772       * Constructor
2773       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2774       * @param string $visiblename localised
2775       * @param string $description long localised info
2776       * @param string $defaultdirectory default directory location
2777       */
2778      public function __construct($name, $visiblename, $description, $defaultdirectory) {
2779          parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2780      }
2781  
2782      /**
2783       * Returns XHTML for the field
2784       *
2785       * Returns XHTML for the field and also checks whether the file
2786       * specified in $data exists using file_exists()
2787       *
2788       * @param string $data File name and path to use in value attr
2789       * @param string $query
2790       * @return string XHTML field
2791       */
2792      public function output_html($data, $query='') {
2793          global $CFG, $OUTPUT;
2794  
2795          $default = $this->get_defaultsetting();
2796          $context = (object) [
2797              'id' => $this->get_id(),
2798              'name' => $this->get_full_name(),
2799              'size' => $this->size,
2800              'value' => $data,
2801              'showvalidity' => !empty($data),
2802              'valid' => $data && file_exists($data),
2803              'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
2804              'forceltr' => $this->get_force_ltr(),
2805          ];
2806  
2807          if ($context->readonly) {
2808              $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2809          }
2810  
2811          $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2812  
2813          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2814      }
2815  
2816      /**
2817       * Checks if execpatch has been disabled in config.php
2818       */
2819      public function write_setting($data) {
2820          global $CFG;
2821          if (!empty($CFG->preventexecpath)) {
2822              if ($this->get_setting() === null) {
2823                  // Use default during installation.
2824                  $data = $this->get_defaultsetting();
2825                  if ($data === null) {
2826                      $data = '';
2827                  }
2828              } else {
2829                  return '';
2830              }
2831          }
2832          return parent::write_setting($data);
2833      }
2834  
2835  }
2836  
2837  
2838  /**
2839   * Path to executable file
2840   *
2841   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2842   */
2843  class admin_setting_configexecutable extends admin_setting_configfile {
2844  
2845      /**
2846       * Returns an XHTML field
2847       *
2848       * @param string $data This is the value for the field
2849       * @param string $query
2850       * @return string XHTML field
2851       */
2852      public function output_html($data, $query='') {
2853          global $CFG, $OUTPUT;
2854          $default = $this->get_defaultsetting();
2855          require_once("$CFG->libdir/filelib.php");
2856  
2857          $context = (object) [
2858              'id' => $this->get_id(),
2859              'name' => $this->get_full_name(),
2860              'size' => $this->size,
2861              'value' => $data,
2862              'showvalidity' => !empty($data),
2863              'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
2864              'readonly' => !empty($CFG->preventexecpath),
2865              'forceltr' => $this->get_force_ltr()
2866          ];
2867  
2868          if (!empty($CFG->preventexecpath)) {
2869              $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2870          }
2871  
2872          $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
2873  
2874          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2875      }
2876  }
2877  
2878  
2879  /**
2880   * Path to directory
2881   *
2882   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2883   */
2884  class admin_setting_configdirectory extends admin_setting_configfile {
2885  
2886      /**
2887       * Returns an XHTML field
2888       *
2889       * @param string $data This is the value for the field
2890       * @param string $query
2891       * @return string XHTML
2892       */
2893      public function output_html($data, $query='') {
2894          global $CFG, $OUTPUT;
2895          $default = $this->get_defaultsetting();
2896  
2897          $context = (object) [
2898              'id' => $this->get_id(),
2899              'name' => $this->get_full_name(),
2900              'size' => $this->size,
2901              'value' => $data,
2902              'showvalidity' => !empty($data),
2903              'valid' => $data && file_exists($data) && is_dir($data),
2904              'readonly' => !empty($CFG->preventexecpath),
2905              'forceltr' => $this->get_force_ltr()
2906          ];
2907  
2908          if (!empty($CFG->preventexecpath)) {
2909              $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2910          }
2911  
2912          $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
2913  
2914          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2915      }
2916  }
2917  
2918  
2919  /**
2920   * Checkbox
2921   *
2922   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2923   */
2924  class admin_setting_configcheckbox extends admin_setting {
2925      /** @var string Value used when checked */
2926      public $yes;
2927      /** @var string Value used when not checked */
2928      public $no;
2929  
2930      /**
2931       * Constructor
2932       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2933       * @param string $visiblename localised
2934       * @param string $description long localised info
2935       * @param string $defaultsetting
2936       * @param string $yes value used when checked
2937       * @param string $no value used when not checked
2938       */
2939      public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2940          parent::__construct($name, $visiblename, $description, $defaultsetting);
2941          $this->yes = (string)$yes;
2942          $this->no  = (string)$no;
2943      }
2944  
2945      /**
2946       * Retrieves the current setting using the objects name
2947       *
2948       * @return string
2949       */
2950      public function get_setting() {
2951          return $this->config_read($this->name);
2952      }
2953  
2954      /**
2955       * Sets the value for the setting
2956       *
2957       * Sets the value for the setting to either the yes or no values
2958       * of the object by comparing $data to yes
2959       *
2960       * @param mixed $data Gets converted to str for comparison against yes value
2961       * @return string empty string or error
2962       */
2963      public function write_setting($data) {
2964          if ((string)$data === $this->yes) { // convert to strings before comparison
2965              $data = $this->yes;
2966          } else {
2967              $data = $this->no;
2968          }
2969          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2970      }
2971  
2972      /**
2973       * Returns an XHTML checkbox field
2974       *
2975       * @param string $data If $data matches yes then checkbox is checked
2976       * @param string $query
2977       * @return string XHTML field
2978       */
2979      public function output_html($data, $query='') {
2980          global $OUTPUT;
2981  
2982          $context = (object) [
2983              'id' => $this->get_id(),
2984              'name' => $this->get_full_name(),
2985              'no' => $this->no,
2986              'value' => $this->yes,
2987              'checked' => (string) $data === $this->yes,
2988              'readonly' => $this->is_readonly(),
2989          ];
2990  
2991          $default = $this->get_defaultsetting();
2992          if (!is_null($default)) {
2993              if ((string)$default === $this->yes) {
2994                  $defaultinfo = get_string('checkboxyes', 'admin');
2995              } else {
2996                  $defaultinfo = get_string('checkboxno', 'admin');
2997              }
2998          } else {
2999              $defaultinfo = NULL;
3000          }
3001  
3002          $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
3003  
3004          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3005      }
3006  }
3007  
3008  
3009  /**
3010   * Multiple checkboxes, each represents different value, stored in csv format
3011   *
3012   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3013   */
3014  class admin_setting_configmulticheckbox extends admin_setting {
3015      /** @var array Array of choices value=>label */
3016      public $choices;
3017  
3018      /**
3019       * Constructor: uses parent::__construct
3020       *
3021       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3022       * @param string $visiblename localised
3023       * @param string $description long localised info
3024       * @param array $defaultsetting array of selected
3025       * @param array $choices array of $value=>$label for each checkbox
3026       */
3027      public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3028          $this->choices = $choices;
3029          parent::__construct($name, $visiblename, $description, $defaultsetting);
3030      }
3031  
3032      /**
3033       * This public function may be used in ancestors for lazy loading of choices
3034       *
3035       * @todo Check if this function is still required content commented out only returns true
3036       * @return bool true if loaded, false if error
3037       */
3038      public function load_choices() {
3039          /*
3040          if (is_array($this->choices)) {
3041              return true;
3042          }
3043          .... load choices here
3044          */
3045          return true;
3046      }
3047  
3048      /**
3049       * Is setting related to query text - used when searching
3050       *
3051       * @param string $query
3052       * @return bool true on related, false on not or failure
3053       */
3054      public function is_related($query) {
3055          if (!$this->load_choices() or empty($this->choices)) {
3056              return false;
3057          }
3058          if (parent::is_related($query)) {
3059              return true;
3060          }
3061  
3062          foreach ($this->choices as $desc) {
3063              if (strpos(core_text::strtolower($desc), $query) !== false) {
3064                  return true;
3065              }
3066          }
3067          return false;
3068      }
3069  
3070      /**
3071       * Returns the current setting if it is set
3072       *
3073       * @return mixed null if null, else an array
3074       */
3075      public function get_setting() {
3076          $result = $this->config_read($this->name);
3077  
3078          if (is_null($result)) {
3079              return NULL;
3080          }
3081          if ($result === '') {
3082              return array();
3083          }
3084          $enabled = explode(',', $result);
3085          $setting = array();
3086          foreach ($enabled as $option) {
3087              $setting[$option] = 1;
3088          }
3089          return $setting;
3090      }
3091  
3092      /**
3093       * Saves the setting(s) provided in $data
3094       *
3095       * @param array $data An array of data, if not array returns empty str
3096       * @return mixed empty string on useless data or bool true=success, false=failed
3097       */
3098      public function write_setting($data) {
3099          if (!is_array($data)) {
3100              return ''; // ignore it
3101          }
3102          if (!$this->load_choices() or empty($this->choices)) {
3103              return '';
3104          }
3105          unset($data['xxxxx']);
3106          $result = array();
3107          foreach ($data as $key => $value) {
3108              if ($value and array_key_exists($key, $this->choices)) {
3109                  $result[] = $key;
3110              }
3111          }
3112          return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3113      }
3114  
3115      /**
3116       * Returns XHTML field(s) as required by choices
3117       *
3118       * Relies on data being an array should data ever be another valid vartype with
3119       * acceptable value this may cause a warning/error
3120       * if (!is_array($data)) would fix the problem
3121       *
3122       * @todo Add vartype handling to ensure $data is an array
3123       *
3124       * @param array $data An array of checked values
3125       * @param string $query
3126       * @return string XHTML field
3127       */
3128      public function output_html($data, $query='') {
3129          global $OUTPUT;
3130  
3131          if (!$this->load_choices() or empty($this->choices)) {
3132              return '';
3133          }
3134  
3135          $default = $this->get_defaultsetting();
3136          if (is_null($default)) {
3137              $default = array();
3138          }
3139          if (is_null($data)) {
3140              $data = array();
3141          }
3142  
3143          $context = (object) [
3144              'id' => $this->get_id(),
3145              'name' => $this->get_full_name(),
3146          ];
3147  
3148          $options = array();
3149          $defaults = array();
3150          foreach ($this->choices as $key => $description) {
3151              if (!empty($default[$key])) {
3152                  $defaults[] = $description;
3153              }
3154  
3155              $options[] = [
3156                  'key' => $key,
3157                  'checked' => !empty($data[$key]),
3158                  'label' => highlightfast($query, $description)
3159              ];
3160          }
3161  
3162          if (is_null($default)) {
3163              $defaultinfo = null;
3164          } else if (!empty($defaults)) {
3165              $defaultinfo = implode(', ', $defaults);
3166          } else {
3167              $defaultinfo = get_string('none');
3168          }
3169  
3170          $context->options = $options;
3171          $context->hasoptions = !empty($options);
3172  
3173          $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3174  
3175          return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3176  
3177      }
3178  }
3179  
3180  
3181  /**
3182   * Multiple checkboxes 2, value stored as string 00101011
3183   *
3184   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3185   */
3186  class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3187  
3188      /**
3189       * Returns the setting if set
3190       *
3191       * @return mixed null if not set, else an array of set settings
3192       */
3193      public function get_setting() {
3194          $result = $this->config_read($this->name);
3195          if (is_null($result)) {
3196              return NULL;
3197          }
3198          if (!$this->load_choices()) {
3199              return NULL;
3200          }
3201          $result = str_pad($result, count($this->choices), '0');
3202          $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3203          $setting = array();
3204          foreach ($this->choices as $key=>$unused) {
3205              $value = array_shift($result);
3206              if ($value) {
3207                  $setting[$key] = 1;
3208              }
3209          }
3210          return $setting;
3211      }
3212  
3213      /**
3214       * Save setting(s) provided in $data param
3215       *
3216       * @param array $data An array of settings to save
3217       * @return mixed empty string for bad data or bool true=>success, false=>error
3218       */
3219      public function write_setting($data) {
3220          if (!is_array($data)) {
3221              return ''; // ignore it
3222          }
3223          if (!$this->load_choices() or empty($this->choices)) {
3224              return '';
3225          }
3226          $result = '';
3227          foreach ($this->choices as $key=>$unused) {
3228              if (!empty($data[$key])) {
3229                  $result .= '1';
3230              } else {
3231                  $result .= '0';
3232              }
3233          }
3234          return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3235      }
3236  }
3237  
3238  
3239  /**
3240   * Select one value from list
3241   *
3242   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3243   */
3244  class admin_setting_configselect extends admin_setting {
3245      /** @var array Array of choices value=>label */
3246      public $choices;
3247      /** @var array Array of choices grouped using optgroups */
3248      public $optgroups;
3249  
3250      /**
3251       * Constructor
3252       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3253       * @param string $visiblename localised
3254       * @param string $description long localised info
3255       * @param string|int $defaultsetting
3256       * @param array $choices array of $value=>$label for each selection
3257       */
3258      public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3259          // Look for optgroup and single options.
3260          if (is_array($choices)) {
3261              $this->choices = [];
3262              foreach ($choices as $key => $val) {
3263                  if (is_array($val)) {
3264                      $this->optgroups[$key] = $val;
3265                      $this->choices = array_merge($this->choices, $val);
3266                  } else {
3267                      $this->choices[$key] = $val;
3268                  }
3269              }
3270          }
3271  
3272          parent::__construct($name, $visiblename, $description, $defaultsetting);
3273      }
3274  
3275      /**
3276       * This function may be used in ancestors for lazy loading of choices
3277       *
3278       * Override this method if loading of choices is expensive, such
3279       * as when it requires multiple db requests.
3280       *
3281       * @return bool true if loaded, false if error
3282       */
3283      public function load_choices() {
3284          /*
3285          if (is_array($this->choices)) {
3286              return true;
3287          }
3288          .... load choices here
3289          */
3290          return true;
3291      }
3292  
3293      /**
3294       * Check if this is $query is related to a choice
3295       *
3296       * @param string $query
3297       * @return bool true if related, false if not
3298       */
3299      public function is_related($query) {
3300          if (parent::is_related($query)) {
3301              return true;
3302          }
3303          if (!$this->load_choices()) {
3304              return false;
3305          }
3306          foreach ($this->choices as $key=>$value) {
3307              if (strpos(core_text::strtolower($key), $query) !== false) {
3308                  return true;
3309              }
3310              if (strpos(core_text::strtolower($value), $query) !== false) {
3311                  return true;
3312              }
3313          }
3314          return false;
3315      }
3316  
3317      /**
3318       * Return the setting
3319       *
3320       * @return mixed returns config if successful else null
3321       */
3322      public function get_setting() {
3323          return $this->config_read($this->name);
3324      }
3325  
3326      /**
3327       * Save a setting
3328       *
3329       * @param string $data
3330       * @return string empty of error string
3331       */
3332      public function write_setting($data) {
3333          if (!$this->load_choices() or empty($this->choices)) {
3334              return '';
3335          }
3336          if (!array_key_exists($data, $this->choices)) {
3337              return ''; // ignore it
3338          }
3339  
3340          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3341      }
3342  
3343      /**
3344       * Returns XHTML select field
3345       *
3346       * Ensure the options are loaded, and generate the XHTML for the select
3347       * element and any warning message. Separating this out from output_html
3348       * makes it easier to subclass this class.
3349       *
3350       * @param string $data the option to show as selected.
3351       * @param string $current the currently selected option in the database, null if none.
3352       * @param string $default the default selected option.
3353       * @return array the HTML for the select element, and a warning message.
3354       * @deprecated since Moodle 3.2
3355       */
3356      public function output_select_html($data, $current, $default, $extraname = '') {
3357          debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3358      }
3359  
3360      /**
3361       * Returns XHTML select field and wrapping div(s)
3362       *
3363       * @see output_select_html()
3364       *
3365       * @param string $data the option to show as selected
3366       * @param string $query
3367       * @return string XHTML field and wrapping div
3368       */
3369      public function output_html($data, $query='') {
3370          global $OUTPUT;
3371  
3372          $default = $this->get_defaultsetting();
3373          $current = $this->get_setting();
3374  
3375          if (!$this->load_choices() || empty($this->choices)) {
3376              return '';
3377          }
3378  
3379          $context = (object) [
3380              'id' => $this->get_id(),
3381              'name' => $this->get_full_name(),
3382          ];
3383  
3384          if (!is_null($default) && array_key_exists($default, $this->choices)) {
3385              $defaultinfo = $this->choices[$default];
3386          } else {
3387              $defaultinfo = NULL;
3388          }
3389  
3390          // Warnings.
3391          $warning = '';
3392          if ($current === null) {
3393              // First run.
3394          } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3395              // No warning.
3396          } else if (!array_key_exists($current, $this->choices)) {
3397              $warning = get_string('warningcurrentsetting', 'admin', $current);
3398              if (!is_null($default) && $data == $current) {
3399                  $data = $default; // Use default instead of first value when showing the form.
3400              }
3401          }
3402  
3403          $options = [];
3404          $template = 'core_admin/setting_configselect';
3405  
3406          if (!empty($this->optgroups)) {
3407              $optgroups = [];
3408              foreach ($this->optgroups as $label => $choices) {
3409                  $optgroup = array('label' => $label, 'options' => []);
3410                  foreach ($choices as $value => $name) {
3411                      $optgroup['options'][] = [
3412                          'value' => $value,
3413                          'name' => $name,
3414                          'selected' => (string) $value == $data
3415                      ];
3416                      unset($this->choices[$value]);
3417                  }
3418                  $optgroups[] = $optgroup;
3419              }
3420              $context->options = $options;
3421              $context->optgroups = $optgroups;
3422              $template = 'core_admin/setting_configselect_optgroup';
3423          }
3424  
3425          foreach ($this->choices as $value => $name) {
3426              $options[] = [
3427                  'value' => $value,
3428                  'name' => $name,
3429                  'selected' => (string) $value == $data
3430              ];
3431          }
3432          $context->options = $options;
3433          $context->readonly = $this->is_readonly();
3434  
3435          $element = $OUTPUT->render_from_template($template, $context);
3436  
3437          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3438      }
3439  }
3440  
3441  
3442  /**
3443   * Select multiple items from list
3444   *
3445   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3446   */
3447  class admin_setting_configmultiselect extends admin_setting_configselect {
3448      /**
3449       * Constructor
3450       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3451       * @param string $visiblename localised
3452       * @param string $description long localised info
3453       * @param array $defaultsetting array of selected items
3454       * @param array $choices array of $value=>$label for each list item
3455       */
3456      public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3457          parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3458      }
3459  
3460      /**
3461       * Returns the select setting(s)
3462       *
3463       * @return mixed null or array. Null if no settings else array of setting(s)
3464       */
3465      public function get_setting() {
3466          $result = $this->config_read($this->name);
3467          if (is_null($result)) {
3468              return NULL;
3469          }
3470          if ($result === '') {
3471              return array();
3472          }
3473          return explode(',', $result);
3474      }
3475  
3476      /**
3477       * Saves setting(s) provided through $data
3478       *
3479       * Potential bug in the works should anyone call with this function
3480       * using a vartype that is not an array
3481       *
3482       * @param array $data
3483       */
3484      public function write_setting($data) {
3485          if (!is_array($data)) {
3486              return ''; //ignore it
3487          }
3488          if (!$this->load_choices() or empty($this->choices)) {
3489              return '';
3490          }
3491  
3492          unset($data['xxxxx']);
3493  
3494          $save = array();
3495          foreach ($data as $value) {
3496              if (!array_key_exists($value, $this->choices)) {
3497                  continue; // ignore it
3498              }
3499              $save[] = $value;
3500          }
3501  
3502          return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3503      }
3504  
3505      /**
3506       * Is setting related to query text - used when searching
3507       *
3508       * @param string $query
3509       * @return bool true if related, false if not
3510       */
3511      public function is_related($query) {
3512          if (!$this->load_choices() or empty($this->choices)) {
3513              return false;
3514          }
3515          if (parent::is_related($query)) {
3516              return true;
3517          }
3518  
3519          foreach ($this->choices as $desc) {
3520              if (strpos(core_text::strtolower($desc), $query) !== false) {
3521                  return true;
3522              }
3523          }
3524          return false;
3525      }
3526  
3527      /**
3528       * Returns XHTML multi-select field
3529       *
3530       * @todo Add vartype handling to ensure $data is an array
3531       * @param array $data Array of values to select by default
3532       * @param string $query
3533       * @return string XHTML multi-select field
3534       */
3535      public function output_html($data, $query='') {
3536          global $OUTPUT;
3537  
3538          if (!$this->load_choices() or empty($this->choices)) {
3539              return '';
3540          }
3541  
3542          $default = $this->get_defaultsetting();
3543          if (is_null($default)) {
3544              $default = array();
3545          }
3546          if (is_null($data)) {
3547              $data = array();
3548          }
3549  
3550          $context = (object) [
3551              'id' => $this->get_id(),
3552              'name' => $this->get_full_name(),
3553              'size' => min(10, count($this->choices))
3554          ];
3555  
3556          $defaults = [];
3557          $options = [];
3558          $template = 'core_admin/setting_configmultiselect';
3559  
3560          if (!empty($this->optgroups)) {
3561              $optgroups = [];
3562              foreach ($this->optgroups as $label => $choices) {
3563                  $optgroup = array('label' => $label, 'options' => []);
3564                  foreach ($choices as $value => $name) {
3565                      if (in_array($value, $default)) {
3566                          $defaults[] = $name;
3567                      }
3568                      $optgroup['options'][] = [
3569                          'value' => $value,
3570                          'name' => $name,
3571                          'selected' => in_array($value, $data)
3572                      ];
3573                      unset($this->choices[$value]);
3574                  }
3575                  $optgroups[] = $optgroup;
3576              }
3577              $context->optgroups = $optgroups;
3578              $template = 'core_admin/setting_configmultiselect_optgroup';
3579          }
3580  
3581          foreach ($this->choices as $value => $name) {
3582              if (in_array($value, $default)) {
3583                  $defaults[] = $name;
3584              }
3585              $options[] = [
3586                  'value' => $value,
3587                  'name' => $name,
3588                  'selected' => in_array($value, $data)
3589              ];
3590          }
3591          $context->options = $options;
3592          $context->readonly = $this->is_readonly();
3593  
3594          if (is_null($default)) {
3595              $defaultinfo = NULL;
3596          } if (!empty($defaults)) {
3597              $defaultinfo = implode(', ', $defaults);
3598          } else {
3599              $defaultinfo = get_string('none');
3600          }
3601  
3602          $element = $OUTPUT->render_from_template($template, $context);
3603  
3604          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3605      }
3606  }
3607  
3608  /**
3609   * Time selector
3610   *
3611   * This is a liiitle bit messy. we're using two selects, but we're returning
3612   * them as an array named after $name (so we only use $name2 internally for the setting)
3613   *
3614   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3615   */
3616  class admin_setting_configtime extends admin_setting {
3617      /** @var string Used for setting second select (minutes) */
3618      public $name2;
3619  
3620      /**
3621       * Constructor
3622       * @param string $hoursname setting for hours
3623       * @param string $minutesname setting for hours
3624       * @param string $visiblename localised
3625       * @param string $description long localised info
3626       * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3627       */
3628      public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3629          $this->name2 = $minutesname;
3630          parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3631      }
3632  
3633      /**
3634       * Get the selected time
3635       *
3636       * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3637       */
3638      public function get_setting() {
3639          $result1 = $this->config_read($this->name);
3640          $result2 = $this->config_read($this->name2);
3641          if (is_null($result1) or is_null($result2)) {
3642              return NULL;
3643          }
3644  
3645          return array('h' => $result1, 'm' => $result2);
3646      }
3647  
3648      /**
3649       * Store the time (hours and minutes)
3650       *
3651       * @param array $data Must be form 'h'=>xx, 'm'=>xx
3652       * @return bool true if success, false if not
3653       */
3654      public function write_setting($data) {
3655          if (!is_array($data)) {
3656              return '';
3657          }
3658  
3659          $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3660          return ($result ? '' : get_string('errorsetting', 'admin'));
3661      }
3662  
3663      /**
3664       * Returns XHTML time select fields
3665       *
3666       * @param array $data Must be form 'h'=>xx, 'm'=>xx
3667       * @param string $query
3668       * @return string XHTML time select fields and wrapping div(s)
3669       */
3670      public function output_html($data, $query='') {
3671          global $OUTPUT;
3672  
3673          $default = $this->get_defaultsetting();
3674          if (is_array($default)) {
3675              $defaultinfo = $default['h'].':'.$default['m'];
3676          } else {
3677              $defaultinfo = NULL;
3678          }
3679  
3680          $context = (object) [
3681              'id' => $this->get_id(),
3682              'name' => $this->get_full_name(),
3683              'readonly' => $this->is_readonly(),
3684              'hours' => array_map(function($i) use ($data) {
3685                  return [
3686                      'value' => $i,
3687                      'name' => $i,
3688                      'selected' => $i == $data['h']
3689                  ];
3690              }, range(0, 23)),
3691              'minutes' => array_map(function($i) use ($data) {
3692                  return [
3693                      'value' => $i,
3694                      'name' => $i,
3695                      'selected' => $i == $data['m']
3696                  ];
3697              }, range(0, 59, 5))
3698          ];
3699  
3700          $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3701  
3702          return format_admin_setting($this, $this->visiblename, $element, $this->description,
3703              $this->get_id() . 'h', '', $defaultinfo, $query);
3704      }
3705  
3706  }
3707  
3708  
3709  /**
3710   * Seconds duration setting.
3711   *
3712   * @copyright 2012 Petr Skoda (http://skodak.org)
3713   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3714   */
3715  class admin_setting_configduration extends admin_setting {
3716  
3717      /** @var int default duration unit */
3718      protected $defaultunit;
3719  
3720      /**
3721       * Constructor
3722       * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3723       *                     or 'myplugin/mysetting' for ones in config_plugins.
3724       * @param string $visiblename localised name
3725       * @param string $description localised long description
3726       * @param mixed $defaultsetting string or array depending on implementation
3727       * @param int $defaultunit - day, week, etc. (in seconds)
3728       */
3729      public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3730          if (is_number($defaultsetting)) {
3731              $defaultsetting = self::parse_seconds($defaultsetting);
3732          }
3733          $units = self::get_units();
3734          if (isset($units[$defaultunit])) {
3735              $this->defaultunit = $defaultunit;
3736          } else {
3737              $this->defaultunit = 86400;
3738          }
3739          parent::__construct($name, $visiblename, $description, $defaultsetting);
3740      }
3741  
3742      /**
3743       * Returns selectable units.
3744       * @static
3745       * @return array
3746       */
3747      protected static function get_units() {
3748          return array(
3749              604800 => get_string('weeks'),
3750              86400 => get_string('days'),
3751              3600 => get_string('hours'),
3752              60 => get_string('minutes'),
3753              1 => get_string('seconds'),
3754          );
3755      }
3756  
3757      /**
3758       * Converts seconds to some more user friendly string.
3759       * @static
3760       * @param int $seconds
3761       * @return string
3762       */
3763      protected static function get_duration_text($seconds) {
3764          if (empty($seconds)) {
3765              return get_string('none');
3766          }
3767          $data = self::parse_seconds($seconds);
3768          switch ($data['u']) {
3769              case (60*60*24*7):
3770                  return get_string('numweeks', '', $data['v']);
3771              case (60*60*24):
3772                  return get_string('numdays', '', $data['v']);
3773              case (60*60):
3774                  return get_string('numhours', '', $data['v']);
3775              case (60):
3776                  return get_string('numminutes', '', $data['v']);
3777              default:
3778                  return get_string('numseconds', '', $data['v']*$data['u']);
3779          }
3780      }
3781  
3782      /**
3783       * Finds suitable units for given duration.
3784       * @static
3785       * @param int $seconds
3786       * @return array
3787       */
3788      protected static function parse_seconds($seconds) {
3789          foreach (self::get_units() as $unit => $unused) {
3790              if ($seconds % $unit === 0) {
3791                  return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
3792              }
3793          }
3794          return array('v'=>(int)$seconds, 'u'=>1);
3795      }
3796  
3797      /**
3798       * Get the selected duration as array.
3799       *
3800       * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
3801       */
3802      public function get_setting() {
3803          $seconds = $this->config_read($this->name);
3804          if (is_null($seconds)) {
3805              return null;
3806          }
3807  
3808          return self::parse_seconds($seconds);
3809      }
3810  
3811      /**
3812       * Store the duration as seconds.
3813       *
3814       * @param array $data Must be form 'h'=>xx, 'm'=>xx
3815       * @return bool true if success, false if not
3816       */
3817      public function write_setting($data) {
3818          if (!is_array($data)) {
3819              return '';
3820          }
3821  
3822          $seconds = (int)($data['v']*$data['u']);
3823          if ($seconds < 0) {
3824              return get_string('errorsetting', 'admin');
3825          }
3826  
3827          $result = $this->config_write($this->name, $seconds);
3828          return ($result ? '' : get_string('errorsetting', 'admin'));
3829      }
3830  
3831      /**
3832       * Returns duration text+select fields.
3833       *
3834       * @param array $data Must be form 'v'=>xx, 'u'=>xx
3835       * @param string $query
3836       * @return string duration text+select fields and wrapping div(s)
3837       */
3838      public function output_html($data, $query='') {
3839          global $OUTPUT;
3840  
3841          $default = $this->get_defaultsetting();
3842          if (is_number($default)) {
3843              $defaultinfo = self::get_duration_text($default);
3844          } else if (is_array($default)) {
3845              $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3846          } else {
3847              $defaultinfo = null;
3848          }
3849  
3850          $inputid = $this->get_id() . 'v';
3851          $units = self::get_units();
3852          $defaultunit = $this->defaultunit;
3853  
3854          $context = (object) [
3855              'id' => $this->get_id(),
3856              'name' => $this->get_full_name(),
3857              'value' => $data['v'],
3858              'readonly' => $this->is_readonly(),
3859              'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
3860                  return [
3861                      'value' => $unit,
3862                      'name' => $units[$unit],
3863                      'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
3864                  ];
3865              }, array_keys($units))
3866          ];
3867  
3868          $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
3869  
3870          return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
3871      }
3872  }
3873  
3874  
3875  /**
3876   * Seconds duration setting with an advanced checkbox, that controls a additional
3877   * $name.'_adv' setting.
3878   *
3879   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3880   * @copyright 2014 The Open University
3881   */
3882  class admin_setting_configduration_with_advanced extends admin_setting_configduration {
3883      /**
3884       * Constructor
3885       * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3886       *                     or 'myplugin/mysetting' for ones in config_plugins.
3887       * @param string $visiblename localised name
3888       * @param string $description localised long description
3889       * @param array  $defaultsetting array of int value, and bool whether it is
3890       *                     is advanced by default.
3891       * @param int $defaultunit - day, week, etc. (in seconds)
3892       */
3893      public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3894          parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
3895          $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
3896      }
3897  }
3898  
3899  
3900  /**
3901   * Used to validate a textarea used for ip addresses
3902   *
3903   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3904   * @copyright 2011 Petr Skoda (http://skodak.org)
3905   */
3906  class admin_setting_configiplist extends admin_setting_configtextarea {
3907  
3908      /**
3909       * Validate the contents of the textarea as IP addresses
3910       *
3911       * Used to validate a new line separated list of IP addresses collected from
3912       * a textarea control
3913       *
3914       * @param string $data A list of IP Addresses separated by new lines
3915       * @return mixed bool true for success or string:error on failure
3916       */
3917      public function validate($data) {
3918          if(!empty($data)) {
3919              $lines = explode("\n", $data);
3920          } else {
3921              return true;
3922          }
3923          $result = true;
3924          $badips = array();
3925          foreach ($lines as $line) {
3926              $tokens = explode('#', $line);
3927              $ip = trim($tokens[0]);
3928              if (empty($ip)) {
3929                  continue;
3930              }
3931              if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
3932                  preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
3933                  preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
3934              } else {
3935                  $result = false;
3936                  $badips[] = $ip;
3937              }
3938          }
3939          if($result) {
3940              return true;
3941          } else {
3942              return get_string('validateiperror', 'admin', join(', ', $badips));
3943          }
3944      }
3945  }
3946  
3947  /**
3948   * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
3949   *
3950   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3951   * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
3952   */
3953  class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
3954  
3955      /**
3956       * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
3957       * Used to validate a new line separated list of entries collected from a textarea control.
3958       *
3959       * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
3960       * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
3961       * via the get_setting() method, which has been overriden.
3962       *
3963       * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
3964       * @return mixed bool true for success or string:error on failure
3965       */
3966      public function validate($data) {
3967          if (empty($data)) {
3968              return true;
3969          }
3970          $entries = explode("\n", $data);
3971          $badentries = [];
3972  
3973          foreach ($entries as $key => $entry) {
3974              $entry = trim($entry);
3975              if (empty($entry)) {
3976                  return get_string('validateemptylineerror', 'admin');
3977              }
3978  
3979              // Validate each string entry against the supported formats.
3980              if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
3981                      || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
3982                      || \core\ip_utils::is_domain_matching_pattern($entry)) {
3983                  continue;
3984              }
3985  
3986              // Otherwise, the entry is invalid.
3987              $badentries[] = $entry;
3988          }
3989  
3990          if ($badentries) {
3991              return get_string('validateerrorlist', 'admin', join(', ', $badentries));
3992          }
3993          return true;
3994      }
3995  
3996      /**
3997       * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
3998       *
3999       * @param string $data the setting data, as sent from the web form.
4000       * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
4001       */
4002      protected function ace_encode($data) {
4003          if (empty($data)) {
4004              return $data;
4005          }
4006          $entries = explode("\n", $data);
4007          foreach ($entries as $key => $entry) {
4008              $entry = trim($entry);
4009              // This regex matches any string that has non-ascii character.
4010              if (preg_match('/[^\x00-\x7f]/', $entry)) {
4011                  // If we can convert the unicode string to an idn, do so.
4012                  // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
4013                  $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4014                  $entries[$key] = $val ? $val : $entry;
4015              }
4016          }
4017          return implode("\n", $entries);
4018      }
4019  
4020      /**
4021       * Decode any ascii-encoded domain names back to their utf-8 representation for display.
4022       *
4023       * @param string $data the setting data, as found in the database.
4024       * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
4025       */
4026      protected function ace_decode($data) {
4027          $entries = explode("\n", $data);
4028          foreach ($entries as $key => $entry) {
4029              $entry = trim($entry);
4030              if (strpos($entry, 'xn--') !== false) {
4031                  $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4032              }
4033          }
4034          return implode("\n", $entries);
4035      }
4036  
4037      /**
4038       * Override, providing utf8-decoding for ascii-encoded IDN strings.
4039       *
4040       * @return mixed returns punycode-converted setting string if successful, else null.
4041       */
4042      public function get_setting() {
4043          // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4044          $data = $this->config_read($this->name);
4045          if (function_exists('idn_to_utf8') && !is_null($data)) {
4046              $data = $this->ace_decode($data);
4047          }
4048          return $data;
4049      }
4050  
4051      /**
4052       * Override, providing ascii-encoding for utf8 (native) IDN strings.
4053       *
4054       * @param string $data
4055       * @return string
4056       */
4057      public function write_setting($data) {
4058          if ($this->paramtype === PARAM_INT and $data === '') {
4059              // Do not complain if '' used instead of 0.
4060              $data = 0;
4061          }
4062  
4063          // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4064          if (function_exists('idn_to_ascii')) {
4065              $data = $this->ace_encode($data);
4066          }
4067  
4068          $validated = $this->validate($data);
4069          if ($validated !== true) {
4070              return $validated;
4071          }
4072          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4073      }
4074  }
4075  
4076  /**
4077   * Used to validate a textarea used for port numbers.
4078   *
4079   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4080   * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4081   */
4082  class admin_setting_configportlist extends admin_setting_configtextarea {
4083  
4084      /**
4085       * Validate the contents of the textarea as port numbers.
4086       * Used to validate a new line separated list of ports collected from a textarea control.
4087       *
4088       * @param string $data A list of ports separated by new lines
4089       * @return mixed bool true for success or string:error on failure
4090       */
4091      public function validate($data) {
4092          if (empty($data)) {
4093              return true;
4094          }
4095          $ports = explode("\n", $data);
4096          $badentries = [];
4097          foreach ($ports as $port) {
4098              $port = trim($port);
4099              if (empty($port)) {
4100                  return get_string('validateemptylineerror', 'admin');
4101              }
4102  
4103              // Is the string a valid integer number?
4104              if (strval(intval($port)) !== $port || intval($port) <= 0) {
4105                  $badentries[] = $port;
4106              }
4107          }
4108          if ($badentries) {
4109              return get_string('validateerrorlist', 'admin', $badentries);
4110          }
4111          return true;
4112      }
4113  }
4114  
4115  
4116  /**
4117   * An admin setting for selecting one or more users who have a capability
4118   * in the system context
4119   *
4120   * An admin setting for selecting one or more users, who have a particular capability
4121   * in the system context. Warning, make sure the list will never be too long. There is
4122   * no paging or searching of this list.
4123   *
4124   * To correctly get a list of users from this config setting, you need to call the
4125   * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4126   *
4127   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4128   */
4129  class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4130      /** @var string The capabilities name */
4131      protected $capability;
4132      /** @var int include admin users too */
4133      protected $includeadmins;
4134  
4135      /**
4136       * Constructor.
4137       *
4138       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4139       * @param string $visiblename localised name
4140       * @param string $description localised long description
4141       * @param array $defaultsetting array of usernames
4142       * @param string $capability string capability name.
4143       * @param bool $includeadmins include administrators
4144       */
4145      function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4146          $this->capability    = $capability;
4147          $this->includeadmins = $includeadmins;
4148          parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4149      }
4150  
4151      /**
4152       * Load all of the uses who have the capability into choice array
4153       *
4154       * @return bool Always returns true
4155       */
4156      function load_choices() {
4157          if (is_array($this->choices)) {
4158              return true;
4159          }
4160          list($sort, $sortparams) = users_order_by_sql('u');
4161          if (!empty($sortparams)) {
4162              throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4163                      'This is unexpected, and a problem because there is no way to pass these ' .
4164                      'parameters to get_users_by_capability. See MDL-34657.');
4165          }
4166          $userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
4167          $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4168          $this->choices = array(
4169              '$@NONE@$' => get_string('nobody'),
4170              '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4171          );
4172          if ($this->includeadmins) {
4173              $admins = get_admins();
4174              foreach ($admins as $user) {
4175                  $this->choices[$user->id] = fullname($user);
4176              }
4177          }
4178          if (is_array($users)) {
4179              foreach ($users as $user) {
4180                  $this->choices[$user->id] = fullname($user);
4181              }
4182          }
4183          return true;
4184      }
4185  
4186      /**
4187       * Returns the default setting for class
4188       *
4189       * @return mixed Array, or string. Empty string if no default
4190       */
4191      public function get_defaultsetting() {
4192          $this->load_choices();
4193          $defaultsetting = parent::get_defaultsetting();
4194          if (empty($defaultsetting)) {
4195              return array('$@NONE@$');
4196          } else if (array_key_exists($defaultsetting, $this->choices)) {
4197                  return $defaultsetting;
4198              } else {
4199                  return '';
4200              }
4201      }
4202  
4203      /**
4204       * Returns the current setting
4205       *
4206       * @return mixed array or string
4207       */
4208      public function get_setting() {
4209          $result = parent::get_setting();
4210          if ($result === null) {
4211              // this is necessary for settings upgrade
4212              return null;
4213          }
4214          if (empty($result)) {
4215              $result = array('$@NONE@$');
4216          }
4217          return $result;
4218      }
4219  
4220      /**
4221       * Save the chosen setting provided as $data
4222       *
4223       * @param array $data
4224       * @return mixed string or array
4225       */
4226      public function write_setting($data) {
4227      // If all is selected, remove any explicit options.
4228          if (in_array('$@ALL@$', $data)) {
4229              $data = array('$@ALL@$');
4230          }
4231          // None never needs to be written to the DB.
4232          if (in_array('$@NONE@$', $data)) {
4233              unset($data[array_search('$@NONE@$', $data)]);
4234          }
4235          return parent::write_setting($data);
4236      }
4237  }
4238  
4239  
4240  /**
4241   * Special checkbox for calendar - resets SESSION vars.
4242   *
4243   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4244   */
4245  class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4246      /**
4247       * Calls the parent::__construct with default values
4248       *
4249       * name =>  calendar_adminseesall
4250       * visiblename => get_string('adminseesall', 'admin')
4251       * description => get_string('helpadminseesall', 'admin')
4252       * defaultsetting => 0
4253       */
4254      public function __construct() {
4255          parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4256              get_string('helpadminseesall', 'admin'), '0');
4257      }
4258  
4259      /**
4260       * Stores the setting passed in $data
4261       *
4262       * @param mixed gets converted to string for comparison
4263       * @return string empty string or error message
4264       */
4265      public function write_setting($data) {
4266          global $SESSION;
4267          return parent::write_setting($data);
4268      }
4269  }
4270  
4271  /**
4272   * Special select for settings that are altered in setup.php and can not be altered on the fly
4273   *
4274   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4275   */
4276  class admin_setting_special_selectsetup extends admin_setting_configselect {
4277      /**
4278       * Reads the setting directly from the database
4279       *
4280       * @return mixed
4281       */
4282      public function get_setting() {
4283      // read directly from db!
4284          return get_config(NULL, $this->name);
4285      }
4286  
4287      /**
4288       * Save the setting passed in $data
4289       *
4290       * @param string $data The setting to save
4291       * @return string empty or error message
4292       */
4293      public function write_setting($data) {
4294          global $CFG;
4295          // do not change active CFG setting!
4296          $current = $CFG->{$this->name};
4297          $result = parent::write_setting($data);
4298          $CFG->{$this->name} = $current;
4299          return $result;
4300      }
4301  }
4302  
4303  
4304  /**
4305   * Special select for frontpage - stores data in course table
4306   *
4307   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4308   */
4309  class admin_setting_sitesetselect extends admin_setting_configselect {
4310      /**
4311       * Returns the site name for the selected site
4312       *
4313       * @see get_site()
4314       * @return string The site name of the selected site
4315       */
4316      public function get_setting() {
4317          $site = course_get_format(get_site())->get_course();
4318          return $site->{$this->name};
4319      }
4320  
4321      /**
4322       * Updates the database and save the setting
4323       *
4324       * @param string data
4325       * @return string empty or error message
4326       */
4327      public function write_setting($data) {
4328          global $DB, $SITE, $COURSE;
4329          if (!in_array($data, array_keys($this->choices))) {
4330              return get_string('errorsetting', 'admin');
4331          }
4332          $record = new stdClass();
4333          $record->id           = SITEID;
4334          $temp                 = $this->name;
4335          $record->$temp        = $data;
4336          $record->timemodified = time();
4337  
4338          course_get_format($SITE)->update_course_format_options($record);
4339          $DB->update_record('course', $record);
4340  
4341          // Reset caches.
4342          $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4343          if ($SITE->id == $COURSE->id) {
4344              $COURSE = $SITE;
4345          }
4346          format_base::reset_course_cache($SITE->id);
4347  
4348          return '';
4349  
4350      }
4351  }
4352  
4353  
4354  /**
4355   * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4356   * block to hidden.
4357   *
4358   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4359   */
4360  class admin_setting_bloglevel extends admin_setting_configselect {
4361      /**
4362       * Updates the database and save the setting
4363       *
4364       * @param string data
4365       * @return string empty or error message
4366       */
4367      public function write_setting($data) {
4368          global $DB, $CFG;
4369          if ($data == 0) {
4370              $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4371              foreach ($blogblocks as $block) {
4372                  $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4373              }
4374          } else {
4375              // reenable all blocks only when switching from disabled blogs
4376              if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4377                  $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4378                  foreach ($blogblocks as $block) {
4379                      $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4380                  }
4381              }
4382          }
4383          return parent::write_setting($data);
4384      }
4385  }
4386  
4387  
4388  /**
4389   * Special select - lists on the frontpage - hacky
4390   *
4391   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4392   */
4393  class admin_setting_courselist_frontpage extends admin_setting {
4394      /** @var array Array of choices value=>label */
4395      public $choices;
4396  
4397      /**
4398       * Construct override, requires one param
4399       *
4400       * @param bool $loggedin Is the user logged in
4401       */
4402      public function __construct($loggedin) {
4403          global $CFG;
4404          require_once($CFG->dirroot.'/course/lib.php');
4405          $name        = 'frontpage'.($loggedin ? 'loggedin' : '');
4406          $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4407          $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4408          $defaults    = array(FRONTPAGEALLCOURSELIST);
4409          parent::__construct($name, $visiblename, $description, $defaults);
4410      }
4411  
4412      /**
4413       * Loads the choices available
4414       *
4415       * @return bool always returns true
4416       */
4417      public function load_choices() {
4418          if (is_array($this->choices)) {
4419              return true;
4420          }
4421          $this->choices = array(FRONTPAGENEWS          => get_string('frontpagenews'),
4422              FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4423              FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4424              FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4425              FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4426              FRONTPAGECOURSESEARCH  => get_string('frontpagecoursesearch'),
4427              'none'                 => get_string('none'));
4428          if ($this->name === 'frontpage') {
4429              unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4430          }
4431          return true;
4432      }
4433  
4434      /**
4435       * Returns the selected settings
4436       *
4437       * @param mixed array or setting or null
4438       */
4439      public function get_setting() {
4440          $result = $this->config_read($this->name);
4441          if (is_null($result)) {
4442              return NULL;
4443          }
4444          if ($result === '') {
4445              return array();
4446          }
4447          return explode(',', $result);
4448      }
4449  
4450      /**
4451       * Save the selected options
4452       *
4453       * @param array $data
4454       * @return mixed empty string (data is not an array) or bool true=success false=failure
4455       */
4456      public function write_setting($data) {
4457          if (!is_array($data)) {
4458              return '';
4459          }
4460          $this->load_choices();
4461          $save = array();
4462          foreach($data as $datum) {
4463              if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4464                  continue;
4465              }
4466              $save[$datum] = $datum; // no duplicates
4467          }
4468          return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4469      }
4470  
4471      /**
4472       * Return XHTML select field and wrapping div
4473       *
4474       * @todo Add vartype handling to make sure $data is an array
4475       * @param array $data Array of elements to select by default
4476       * @return string XHTML select field and wrapping div
4477       */
4478      public function output_html($data, $query='') {
4479          global $OUTPUT;
4480  
4481          $this->load_choices();
4482          $currentsetting = array();
4483          foreach ($data as $key) {
4484              if ($key != 'none' and array_key_exists($key, $this->choices)) {
4485                  $currentsetting[] = $key; // already selected first
4486              }
4487          }
4488  
4489          $context = (object) [
4490              'id' => $this->get_id(),
4491              'name' => $this->get_full_name(),
4492          ];
4493  
4494          $options = $this->choices;
4495          $selects = [];
4496          for ($i = 0; $i < count($this->choices) - 1; $i++) {
4497              if (!array_key_exists($i, $currentsetting)) {
4498                  $currentsetting[$i] = 'none';
4499              }
4500              $selects[] = [
4501                  'key' => $i,
4502                  'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4503                      return [
4504                          'name' => $options[$option],
4505                          'value' => $option,
4506                          'selected' => $currentsetting[$i] == $option
4507                      ];
4508                  }, array_keys($options))
4509              ];
4510          }
4511          $context->selects = $selects;
4512  
4513          $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4514  
4515          return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4516      }
4517  }
4518  
4519  
4520  /**
4521   * Special checkbox for frontpage - stores data in course table
4522   *
4523   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4524   */
4525  class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4526      /**
4527       * Returns the current sites name
4528       *
4529       * @return string
4530       */
4531      public function get_setting() {
4532          $site = course_get_format(get_site())->get_course();
4533          return $site->{$this->name};
4534      }
4535  
4536      /**
4537       * Save the selected setting
4538       *
4539       * @param string $data The selected site
4540       * @return string empty string or error message
4541       */
4542      public function write_setting($data) {
4543          global $DB, $SITE, $COURSE;
4544          $record = new stdClass();
4545          $record->id            = $SITE->id;
4546          $record->{$this->name} = ($data == '1' ? 1 : 0);
4547          $record->timemodified  = time();
4548  
4549          course_get_format($SITE)->update_course_format_options($record);
4550          $DB->update_record('course', $record);
4551  
4552          // Reset caches.
4553          $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4554          if ($SITE->id == $COURSE->id) {
4555              $COURSE = $SITE;
4556          }
4557          format_base::reset_course_cache($SITE->id);
4558  
4559          return '';
4560      }
4561  }
4562  
4563  /**
4564   * Special text for frontpage - stores data in course table.
4565   * Empty string means not set here. Manual setting is required.
4566   *
4567   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4568   */
4569  class admin_setting_sitesettext extends admin_setting_configtext {
4570  
4571      /**
4572       * Constructor.
4573       */
4574      public function __construct() {
4575          call_user_func_array(['parent', '__construct'], func_get_args());
4576          $this->set_force_ltr(false);
4577      }
4578  
4579      /**
4580       * Return the current setting
4581       *
4582       * @return mixed string or null
4583       */
4584      public function get_setting() {
4585          $site = course_get_format(get_site())->get_course();
4586          return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4587      }
4588  
4589      /**
4590       * Validate the selected data
4591       *
4592       * @param string $data The selected value to validate
4593       * @return mixed true or message string
4594       */
4595      public function validate($data) {
4596          global $DB, $SITE;
4597          $cleaned = clean_param($data, PARAM_TEXT);
4598          if ($cleaned === '') {
4599              return get_string('required');
4600          }
4601          if ($this->name ==='shortname' &&
4602                  $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4603              return get_string('shortnametaken', 'error', $data);
4604          }
4605          if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4606              return true;
4607          } else {
4608              return get_string('validateerror', 'admin');
4609          }
4610      }
4611  
4612      /**
4613       * Save the selected setting
4614       *
4615       * @param string $data The selected value
4616       * @return string empty or error message
4617       */
4618      public function write_setting($data) {
4619          global $DB, $SITE, $COURSE;
4620          $data = trim($data);
4621          $validated = $this->validate($data);
4622          if ($validated !== true) {
4623              return $validated;
4624          }
4625  
4626          $record = new stdClass();
4627          $record->id            = $SITE->id;
4628          $record->{$this->name} = $data;
4629          $record->timemodified  = time();
4630  
4631          course_get_format($SITE)->update_course_format_options($record);
4632          $DB->update_record('course', $record);
4633  
4634          // Reset caches.
4635          $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4636          if ($SITE->id == $COURSE->id) {
4637              $COURSE = $SITE;
4638          }
4639          format_base::reset_course_cache($SITE->id);
4640  
4641          return '';
4642      }
4643  }
4644  
4645  
4646  /**
4647   * Special text editor for site description.
4648   *
4649   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4650   */
4651  class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
4652  
4653      /**
4654       * Calls parent::__construct with specific arguments
4655       */
4656      public function __construct() {
4657          parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
4658              PARAM_RAW, 60, 15);
4659      }
4660  
4661      /**
4662       * Return the current setting
4663       * @return string The current setting
4664       */
4665      public function get_setting() {
4666          $site = course_get_format(get_site())->get_course();
4667          return $site->{$this->name};
4668      }
4669  
4670      /**
4671       * Save the new setting
4672       *
4673       * @param string $data The new value to save
4674       * @return string empty or error message
4675       */
4676      public function write_setting($data) {
4677          global $DB, $SITE, $COURSE;
4678          $record = new stdClass();
4679          $record->id            = $SITE->id;
4680          $record->{$this->name} = $data;
4681          $record->timemodified  = time();
4682  
4683          course_get_format($SITE)->update_course_format_options($record);
4684          $DB->update_record('course', $record);
4685  
4686          // Reset caches.
4687          $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4688          if ($SITE->id == $COURSE->id) {
4689              $COURSE = $SITE;
4690          }
4691          format_base::reset_course_cache($SITE->id);
4692  
4693          return '';
4694      }
4695  }
4696  
4697  
4698  /**
4699   * Administration interface for emoticon_manager settings.
4700   *
4701   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4702   */
4703  class admin_setting_emoticons extends admin_setting {
4704  
4705      /**
4706       * Calls parent::__construct with specific args
4707       */
4708      public function __construct() {
4709          global $CFG;
4710  
4711          $manager = get_emoticon_manager();
4712          $defaults = $this->prepare_form_data($manager->default_emoticons());
4713          parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
4714      }
4715  
4716      /**
4717       * Return the current setting(s)
4718       *
4719       * @return array Current settings array
4720       */
4721      public function get_setting() {
4722          global $CFG;
4723  
4724          $manager = get_emoticon_manager();
4725  
4726          $config = $this->config_read($this->name);
4727          if (is_null($config)) {
4728              return null;
4729          }
4730  
4731          $config = $manager->decode_stored_config($config);
4732          if (is_null($config)) {
4733              return null;
4734          }
4735  
4736          return $this->prepare_form_data($config);
4737      }
4738  
4739      /**
4740       * Save selected settings
4741       *
4742       * @param array $data Array of settings to save
4743       * @return bool
4744       */
4745      public function write_setting($data) {
4746  
4747          $manager = get_emoticon_manager();
4748          $emoticons = $this->process_form_data($data);
4749  
4750          if ($emoticons === false) {
4751              return false;
4752          }
4753  
4754          if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
4755              return ''; // success
4756          } else {
4757              return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
4758          }
4759      }
4760  
4761      /**
4762       * Return XHTML field(s) for options
4763       *
4764       * @param array $data Array of options to set in HTML
4765       * @return string XHTML string for the fields and wrapping div(s)
4766       */
4767      public function output_html($data, $query='') {
4768          global $OUTPUT;
4769  
4770          $context = (object) [
4771              'name' => $this->get_full_name(),
4772              'emoticons' => [],
4773              'forceltr' => true,
4774          ];
4775  
4776          $i = 0;
4777          foreach ($data as $field => $value) {
4778  
4779              // When $i == 0: text.
4780              // When $i == 1: imagename.
4781              // When $i == 2: imagecomponent.
4782              // When $i == 3: altidentifier.
4783              // When $i == 4: altcomponent.
4784              $fields[$i] = (object) [
4785                  'field' => $field,
4786                  'value' => $value,
4787                  'index' => $i
4788              ];
4789              $i++;
4790  
4791              if ($i > 4) {
4792                  $icon = null;
4793                  if (!empty($fields[1]->value)) {
4794                      if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
4795                          $alt = get_string($fields[3]->value, $fields[4]->value);
4796                      } else {
4797                          $alt = $fields[0]->value;
4798                      }
4799                      $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
4800                  }
4801                  $context->emoticons[] = [
4802                      'fields' => $fields,
4803                      'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
4804                  ];
4805                  $fields = [];
4806                  $i = 0;
4807              }
4808          }
4809  
4810          $context->reseturl = new moodle_url('/admin/resetemoticons.php');
4811          $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
4812          return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
4813      }
4814  
4815      /**
4816       * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
4817       *
4818       * @see self::process_form_data()
4819       * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
4820       * @return array of form fields and their values
4821       */
4822      protected function prepare_form_data(array $emoticons) {
4823  
4824          $form = array();
4825          $i = 0;
4826          foreach ($emoticons as $emoticon) {
4827              $form['text'.$i]            = $emoticon->text;
4828              $form['imagename'.$i]       = $emoticon->imagename;
4829              $form['imagecomponent'.$i]  = $emoticon->imagecomponent;
4830              $form['altidentifier'.$i]   = $emoticon->altidentifier;
4831              $form['altcomponent'.$i]    = $emoticon->altcomponent;
4832              $i++;
4833          }
4834          // add one more blank field set for new object
4835          $form['text'.$i]            = '';
4836          $form['imagename'.$i]       = '';
4837          $form['imagecomponent'.$i]  = '';
4838          $form['altidentifier'.$i]   = '';
4839          $form['altcomponent'.$i]    = '';
4840  
4841          return $form;
4842      }
4843  
4844      /**
4845       * Converts the data from admin settings form into an array of emoticon objects
4846       *
4847       * @see self::prepare_form_data()
4848       * @param array $data array of admin form fields and values
4849       * @return false|array of emoticon objects
4850       */
4851      protected function process_form_data(array $form) {
4852  
4853          $count = count($form); // number of form field values
4854  
4855          if ($count % 5) {
4856              // we must get five fields per emoticon object
4857              return false;
4858          }
4859  
4860          $emoticons = array();
4861          for ($i = 0; $i < $count / 5; $i++) {
4862              $emoticon                   = new stdClass();
4863              $emoticon->text             = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
4864              $emoticon->imagename        = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
4865              $emoticon->imagecomponent   = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
4866              $emoticon->altidentifier    = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
4867              $emoticon->altcomponent     = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
4868  
4869              if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
4870                  // prevent from breaking http://url.addresses by accident
4871                  $emoticon->text = '';
4872              }
4873  
4874              if (strlen($emoticon->text) < 2) {
4875                  // do not allow single character emoticons
4876                  $emoticon->text = '';
4877              }
4878  
4879              if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
4880                  // emoticon text must contain some non-alphanumeric character to prevent
4881                  // breaking HTML tags
4882                  $emoticon->text = '';
4883              }
4884  
4885              if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
4886                  $emoticons[] = $emoticon;
4887              }
4888          }
4889          return $emoticons;
4890      }
4891  
4892  }
4893  
4894  
4895  /**
4896   * Special setting for limiting of the list of available languages.
4897   *
4898   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4899   */
4900  class admin_setting_langlist extends admin_setting_configtext {
4901      /**
4902       * Calls parent::__construct with specific arguments
4903       */
4904      public function __construct() {
4905          parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
4906      }
4907  
4908      /**
4909       * Validate that each language identifier exists on the site
4910       *
4911       * @param string $data
4912       * @return bool|string True if validation successful, otherwise error string
4913       */
4914      public function validate($data) {
4915          $parentcheck = parent::validate($data);
4916          if ($parentcheck !== true) {
4917              return $parentcheck;
4918          }
4919  
4920          if ($data === '') {
4921              return true;
4922          }
4923  
4924          // Normalize language identifiers.
4925          $langcodes = array_map('trim', explode(',', $data));
4926          foreach ($langcodes as $langcode) {
4927              // If the langcode contains optional alias, split it out.
4928              [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
4929  
4930              if (!get_string_manager()->translation_exists($langcode)) {
4931                  return get_string('invalidlanguagecode', 'error', $langcode);
4932              }
4933          }
4934  
4935          return true;
4936      }
4937  
4938      /**
4939       * Save the new setting
4940       *
4941       * @param string $data The new setting
4942       * @return bool
4943       */
4944      public function write_setting($data) {
4945          $return = parent::write_setting($data);
4946          get_string_manager()->reset_caches();
4947          return $return;
4948      }
4949  }
4950  
4951  
4952  /**
4953   * Allows to specify comma separated list of known country codes.
4954   *
4955   * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
4956   * known codes.
4957   *
4958   * @package     core
4959   * @category    admin
4960   * @copyright   2020 David Mudrák <david@moodle.com>
4961   * @license     https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4962   */
4963  class admin_setting_countrycodes extends admin_setting_configtext {
4964  
4965      /**
4966       * Construct the instance of the setting.
4967       *
4968       * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
4969       * @param lang_string|string $visiblename Language string with the field label text.
4970       * @param lang_string|string $description Language string with the field description text.
4971       * @param string $defaultsetting Default value of the setting.
4972       * @param int $size Input text field size.
4973       */
4974      public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
4975          parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
4976      }
4977  
4978      /**
4979       * Validate the setting value before storing it.
4980       *
4981       * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
4982       * a comma separated list of such words.
4983       *
4984       * @param string $data Value inserted into the setting field.
4985       * @return bool|string True if the value is OK, error string otherwise.
4986       */
4987      public function validate($data) {
4988  
4989          $parentcheck = parent::validate($data);
4990  
4991          if ($parentcheck !== true) {
4992              return $parentcheck;
4993          }
4994  
4995          if ($data === '') {
4996              return true;
4997          }
4998  
4999          $allcountries = get_string_manager()->get_list_of_countries(true);
5000  
5001          foreach (explode(',', $data) as $code) {
5002              if (!isset($allcountries[$code])) {
5003                  return get_string('invalidcountrycode', 'core_error', $code);
5004              }
5005          }
5006  
5007          return true;
5008      }
5009  }
5010  
5011  
5012  /**
5013   * Selection of one of the recognised countries using the list
5014   * returned by {@link get_list_of_countries()}.
5015   *
5016   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5017   */
5018  class admin_settings_country_select extends admin_setting_configselect {
5019      protected $includeall;
5020      public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
5021          $this->includeall = $includeall;
5022          parent::__construct($name, $visiblename, $description, $defaultsetting, null);
5023      }
5024  
5025      /**
5026       * Lazy-load the available choices for the select box
5027       */
5028      public function load_choices() {
5029          global $CFG;
5030          if (is_array($this->choices)) {
5031              return true;
5032          }
5033          $this->choices = array_merge(
5034                  array('0' => get_string('choosedots')),
5035                  get_string_manager()->get_list_of_countries($this->includeall));
5036          return true;
5037      }
5038  }
5039  
5040  
5041  /**
5042   * admin_setting_configselect for the default number of sections in a course,
5043   * simply so we can lazy-load the choices.
5044   *
5045   * @copyright 2011 The Open University
5046   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5047   */
5048  class admin_settings_num_course_sections extends admin_setting_configselect {
5049      public function __construct($name, $visiblename, $description, $defaultsetting) {
5050          parent::__construct($name, $visiblename, $description, $defaultsetting, array());
5051      }
5052  
5053      /** Lazy-load the available choices for the select box */
5054      public function load_choices() {
5055          $max = get_config('moodlecourse', 'maxsections');
5056          if (!isset($max) || !is_numeric($max)) {
5057              $max = 52;
5058          }
5059          for ($i = 0; $i <= $max; $i++) {
5060              $this->choices[$i] = "$i";
5061          }
5062          return true;
5063      }
5064  }
5065  
5066  
5067  /**
5068   * Course category selection
5069   *
5070   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5071   */
5072  class admin_settings_coursecat_select extends admin_setting_configselect {
5073      /**
5074       * Calls parent::__construct with specific arguments
5075       */
5076      public function __construct($name, $visiblename, $description, $defaultsetting) {
5077          parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
5078      }
5079  
5080      /**
5081       * Load the available choices for the select box
5082       *
5083       * @return bool
5084       */
5085      public function load_choices() {
5086          global $CFG;
5087          require_once($CFG->dirroot.'/course/lib.php');
5088          if (is_array($this->choices)) {
5089              return true;
5090          }
5091          $this->choices = make_categories_options();
5092          return true;
5093      }
5094  }
5095  
5096  
5097  /**
5098   * Special control for selecting days to backup
5099   *
5100   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5101   */
5102  class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
5103      /**
5104       * Calls parent::__construct with specific arguments
5105       */
5106      public function __construct() {
5107          parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
5108          $this->plugin = 'backup';
5109      }
5110  
5111      /**
5112       * Load the available choices for the select box
5113       *
5114       * @return bool Always returns true
5115       */
5116      public function load_choices() {
5117          if (is_array($this->choices)) {
5118              return true;
5119          }
5120          $this->choices = array();
5121          $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5122          foreach ($days as $day) {
5123              $this->choices[$day] = get_string($day, 'calendar');
5124          }
5125          return true;
5126      }
5127  }
5128  
5129  /**
5130   * Special setting for backup auto destination.
5131   *
5132   * @package    core
5133   * @subpackage admin
5134   * @copyright  2014 Frédéric Massart - FMCorz.net
5135   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5136   */
5137  class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5138  
5139      /**
5140       * Calls parent::__construct with specific arguments.
5141       */
5142      public function __construct() {
5143          parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5144      }
5145  
5146      /**
5147       * Check if the directory must be set, depending on backup/backup_auto_storage.
5148       *
5149       * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5150       * there will be conflicts if this validation happens before the other one.
5151       *
5152       * @param string $data Form data.
5153       * @return string Empty when no errors.
5154       */
5155      public function write_setting($data) {
5156          $storage = (int) get_config('backup', 'backup_auto_storage');
5157          if ($storage !== 0) {
5158              if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5159                  // The directory must exist and be writable.
5160                  return get_string('backuperrorinvaliddestination');
5161              }
5162          }
5163          return parent::write_setting($data);
5164      }
5165  }
5166  
5167  
5168  /**
5169   * Special debug setting
5170   *
5171   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5172   */
5173  class admin_setting_special_debug extends admin_setting_configselect {
5174      /**
5175       * Calls parent::__construct with specific arguments
5176       */
5177      public function __construct() {
5178          parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5179      }
5180  
5181      /**
5182       * Load the available choices for the select box
5183       *
5184       * @return bool
5185       */
5186      public function load_choices() {
5187          if (is_array($this->choices)) {
5188              return true;
5189          }
5190          $this->choices = array(DEBUG_NONE      => get_string('debugnone', 'admin'),
5191              DEBUG_MINIMAL   => get_string('debugminimal', 'admin'),
5192              DEBUG_NORMAL    => get_string('debugnormal', 'admin'),
5193              DEBUG_ALL       => get_string('debugall', 'admin'),
5194              DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5195          return true;
5196      }
5197  }
5198  
5199  
5200  /**
5201   * Special admin control
5202   *
5203   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5204   */
5205  class admin_setting_special_calendar_weekend extends admin_setting {
5206      /**
5207       * Calls parent::__construct with specific arguments
5208       */
5209      public function __construct() {
5210          $name = 'calendar_weekend';
5211          $visiblename = get_string('calendar_weekend', 'admin');
5212          $description = get_string('helpweekenddays', 'admin');
5213          $default = array ('0', '6'); // Saturdays and Sundays
5214          parent::__construct($name, $visiblename, $description, $default);
5215      }
5216  
5217      /**
5218       * Gets the current settings as an array
5219       *
5220       * @return mixed Null if none, else array of settings
5221       */
5222      public function get_setting() {
5223          $result = $this->config_read($this->name);
5224          if (is_null($result)) {
5225              return NULL;
5226          }
5227          if ($result === '') {
5228              return array();
5229          }
5230          $settings = array();
5231          for ($i=0; $i<7; $i++) {
5232              if ($result & (1 << $i)) {
5233                  $settings[] = $i;
5234              }
5235          }
5236          return $settings;
5237      }
5238  
5239      /**
5240       * Save the new settings
5241       *
5242       * @param array $data Array of new settings
5243       * @return bool
5244       */
5245      public function write_setting($data) {
5246          if (!is_array($data)) {
5247              return '';
5248          }
5249          unset($data['xxxxx']);
5250          $result = 0;
5251          foreach($data as $index) {
5252              $result |= 1 << $index;
5253          }
5254          return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5255      }
5256  
5257      /**
5258       * Return XHTML to display the control
5259       *
5260       * @param array $data array of selected days
5261       * @param string $query
5262       * @return string XHTML for display (field + wrapping div(s)
5263       */
5264      public function output_html($data, $query='') {
5265          global $OUTPUT;
5266  
5267          // The order matters very much because of the implied numeric keys.
5268          $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5269          $context = (object) [
5270              'name' => $this->get_full_name(),
5271              'id' => $this->get_id(),
5272              'days' => array_map(function($index) use ($days, $data) {
5273                  return [
5274                      'index' => $index,
5275                      'label' => get_string($days[$index], 'calendar'),
5276                      'checked' => in_array($index, $data)
5277                  ];
5278              }, array_keys($days))
5279          ];
5280  
5281          $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5282  
5283          return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5284  
5285      }
5286  }
5287  
5288  
5289  /**
5290   * Admin setting that allows a user to pick a behaviour.
5291   *
5292   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5293   */
5294  class admin_setting_question_behaviour extends admin_setting_configselect {
5295      /**
5296       * @param string $name name of config variable
5297       * @param string $visiblename display name
5298       * @param string $description description
5299       * @param string $default default.
5300       */
5301      public function __construct($name, $visiblename, $description, $default) {
5302          parent::__construct($name, $visiblename, $description, $default, null);
5303      }
5304  
5305      /**
5306       * Load list of behaviours as choices
5307       * @return bool true => success, false => error.
5308       */
5309      public function load_choices() {
5310          global $CFG;
5311          require_once($CFG->dirroot . '/question/engine/lib.php');
5312          $this->choices = question_engine::get_behaviour_options('');
5313          return true;
5314      }
5315  }
5316  
5317  
5318  /**
5319   * Admin setting that allows a user to pick appropriate roles for something.
5320   *
5321   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5322   */
5323  class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5324      /** @var array Array of capabilities which identify roles */
5325      private $types;
5326  
5327      /**
5328       * @param string $name Name of config variable
5329       * @param string $visiblename Display name
5330       * @param string $description Description
5331       * @param array $types Array of archetypes which identify
5332       *              roles that will be enabled by default.
5333       */
5334      public function __construct($name, $visiblename, $description, $types) {
5335          parent::__construct($name, $visiblename, $description, NULL, NULL);
5336          $this->types = $types;
5337      }
5338  
5339      /**
5340       * Load roles as choices
5341       *
5342       * @return bool true=>success, false=>error
5343       */
5344      public function load_choices() {
5345          global $CFG, $DB;
5346          if (during_initial_install()) {
5347              return false;
5348          }
5349          if (is_array($this->choices)) {
5350              return true;
5351          }
5352          if ($roles = get_all_roles()) {
5353              $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5354              return true;
5355          } else {
5356              return false;
5357          }
5358      }
5359  
5360      /**
5361       * Return the default setting for this control
5362       *
5363       * @return array Array of default settings
5364       */
5365      public function get_defaultsetting() {
5366          global $CFG;
5367  
5368          if (during_initial_install()) {
5369              return null;
5370          }
5371          $result = array();
5372          foreach($this->types as $archetype) {
5373              if ($caproles = get_archetype_roles($archetype)) {
5374                  foreach ($caproles as $caprole) {
5375                      $result[$caprole->id] = 1;
5376                  }
5377              }
5378          }
5379          return $result;
5380      }
5381  }
5382  
5383  
5384  /**
5385   * Admin setting that is a list of installed filter plugins.
5386   *
5387   * @copyright 2015 The Open University
5388   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5389   */
5390  class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5391  
5392      /**
5393       * Constructor
5394       *
5395       * @param string $name unique ascii name, either 'mysetting' for settings
5396       *      that in config, or 'myplugin/mysetting' for ones in config_plugins.
5397       * @param string $visiblename localised name
5398       * @param string $description localised long description
5399       * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5400       */
5401      public function __construct($name, $visiblename, $description, $default) {
5402          if (empty($default)) {
5403              $default = array();
5404          }
5405          $this->load_choices();
5406          foreach ($default as $plugin) {
5407              if (!isset($this->choices[$plugin])) {
5408                  unset($default[$plugin]);
5409              }
5410          }
5411          parent::__construct($name, $visiblename, $description, $default, null);
5412      }
5413  
5414      public function load_choices() {
5415          if (is_array($this->choices)) {
5416              return true;
5417          }
5418          $this->choices = array();
5419  
5420          foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5421              $this->choices[$plugin] = filter_get_name($plugin);
5422          }
5423          return true;
5424      }
5425  }
5426  
5427  
5428  /**
5429   * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5430   *
5431   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5432   */
5433  class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5434      /**
5435       * Constructor
5436       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5437       * @param string $visiblename localised
5438       * @param string $description long localised info
5439       * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5440       * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5441       * @param int $size default field size
5442       */
5443      public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5444          parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5445          $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5446      }
5447  }
5448  
5449  
5450  /**
5451   * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5452   *
5453   * @copyright 2009 Petr Skoda (http://skodak.org)
5454   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5455   */
5456  class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5457  
5458      /**
5459       * Constructor
5460       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5461       * @param string $visiblename localised
5462       * @param string $description long localised info
5463       * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5464       * @param string $yes value used when checked
5465       * @param string $no value used when not checked
5466       */
5467      public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5468          parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5469          $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5470      }
5471  
5472  }
5473  
5474  
5475  /**
5476   * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5477   *
5478   * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5479   *
5480   * @copyright 2010 Sam Hemelryk
5481   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5482   */
5483  class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5484      /**
5485       * Constructor
5486       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5487       * @param string $visiblename localised
5488       * @param string $description long localised info
5489       * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5490       * @param string $yes value used when checked
5491       * @param string $no value used when not checked
5492       */
5493      public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5494          parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5495          $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5496      }
5497  
5498  }
5499  
5500  
5501  /**
5502   * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5503   *
5504   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5505   */
5506  class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5507      /**
5508       * Calls parent::__construct with specific arguments
5509       */
5510      public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5511          parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5512          $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5513      }
5514  
5515  }
5516  
5517  /**
5518   * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5519   *
5520   * @copyright 2017 Marina Glancy
5521   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5522   */
5523  class admin_setting_configselect_with_lock extends admin_setting_configselect {
5524      /**
5525       * Constructor
5526       * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5527       *     or 'myplugin/mysetting' for ones in config_plugins.
5528       * @param string $visiblename localised
5529       * @param string $description long localised info
5530       * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5531       * @param array $choices array of $value=>$label for each selection
5532       */
5533      public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5534          parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5535          $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5536      }
5537  }
5538  
5539  
5540  /**
5541   * Graded roles in gradebook
5542   *
5543   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5544   */
5545  class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5546      /**
5547       * Calls parent::__construct with specific arguments
5548       */
5549      public function __construct() {
5550          parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5551              get_string('configgradebookroles', 'admin'),
5552              array('student'));
5553      }
5554  }
5555  
5556  
5557  /**
5558   *
5559   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5560   */
5561  class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5562      /**
5563       * Saves the new settings passed in $data
5564       *
5565       * @param string $data
5566       * @return mixed string or Array
5567       */
5568      public function write_setting($data) {
5569          global $CFG, $DB;
5570  
5571          $oldvalue  = $this->config_read($this->name);
5572          $return    = parent::write_setting($data);
5573          $newvalue  = $this->config_read($this->name);
5574  
5575          if ($oldvalue !== $newvalue) {
5576          // force full regrading
5577              $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
5578          }
5579  
5580          return $return;
5581      }
5582  }
5583  
5584  
5585  /**
5586   * Which roles to show on course description page
5587   *
5588   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5589   */
5590  class admin_setting_special_coursecontact extends admin_setting_pickroles {
5591      /**
5592       * Calls parent::__construct with specific arguments
5593       */
5594      public function __construct() {
5595          parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
5596              get_string('coursecontact_desc', 'admin'),
5597              array('editingteacher'));
5598          $this->set_updatedcallback(function (){
5599              cache::make('core', 'coursecontacts')->purge();
5600          });
5601      }
5602  }
5603  
5604  
5605  /**
5606   *
5607   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5608   */
5609  class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
5610      /**
5611       * Calls parent::__construct with specific arguments
5612       */
5613      public function __construct() {
5614          parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
5615              get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
5616      }
5617  
5618      /**
5619       * Old syntax of class constructor. Deprecated in PHP7.
5620       *
5621       * @deprecated since Moodle 3.1
5622       */
5623      public function admin_setting_special_gradelimiting() {
5624          debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
5625          self::__construct();
5626      }
5627  
5628      /**
5629       * Force site regrading
5630       */
5631      function regrade_all() {
5632          global $CFG;
5633          require_once("$CFG->libdir/gradelib.php");
5634          grade_force_site_regrading();
5635      }
5636  
5637      /**
5638       * Saves the new settings
5639       *
5640       * @param mixed $data
5641       * @return string empty string or error message
5642       */
5643      function write_setting($data) {
5644          $previous = $this->get_setting();
5645  
5646          if ($previous === null) {
5647              if ($data) {
5648                  $this->regrade_all();
5649              }
5650          } else {
5651              if ($data != $previous) {
5652                  $this->regrade_all();
5653              }
5654          }
5655          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
5656      }
5657  
5658  }
5659  
5660  /**
5661   * Special setting for $CFG->grade_minmaxtouse.
5662   *
5663   * @package    core
5664   * @copyright  2015 Frédéric Massart - FMCorz.net
5665   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5666   */
5667  class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
5668  
5669      /**
5670       * Constructor.
5671       */
5672      public function __construct() {
5673          parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
5674              new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
5675              array(
5676                  GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
5677                  GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
5678              )
5679          );
5680      }
5681  
5682      /**
5683       * Saves the new setting.
5684       *
5685       * @param mixed $data
5686       * @return string empty string or error message
5687       */
5688      function write_setting($data) {
5689          global $CFG;
5690  
5691          $previous = $this->get_setting();
5692          $result = parent::write_setting($data);
5693  
5694          // If saved and the value has changed.
5695          if (empty($result) && $previous != $data) {
5696              require_once($CFG->libdir . '/gradelib.php');
5697              grade_force_site_regrading();
5698          }
5699  
5700          return $result;
5701      }
5702  
5703  }
5704  
5705  
5706  /**
5707   * Primary grade export plugin - has state tracking.
5708   *
5709   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5710   */
5711  class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
5712      /**
5713       * Calls parent::__construct with specific arguments
5714       */
5715      public function __construct() {
5716          parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
5717              get_string('configgradeexport', 'admin'), array(), NULL);
5718      }
5719  
5720      /**
5721       * Load the available choices for the multicheckbox
5722       *
5723       * @return bool always returns true
5724       */
5725      public function load_choices() {
5726          if (is_array($this->choices)) {
5727              return true;
5728          }
5729          $this->choices = array();
5730  
5731          if ($plugins = core_component::get_plugin_list('gradeexport')) {
5732              foreach($plugins as $plugin => $unused) {
5733                  $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
5734              }
5735          }
5736          return true;
5737      }
5738  }
5739  
5740  
5741  /**
5742   * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
5743   *
5744   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5745   */
5746  class admin_setting_special_gradepointdefault extends admin_setting_configtext {
5747      /**
5748       * Config gradepointmax constructor
5749       *
5750       * @param string $name Overidden by "gradepointmax"
5751       * @param string $visiblename Overridden by "gradepointmax" language string.
5752       * @param string $description Overridden by "gradepointmax_help" language string.
5753       * @param string $defaultsetting Not used, overridden by 100.
5754       * @param mixed $paramtype Overridden by PARAM_INT.
5755       * @param int $size Overridden by 5.
5756       */
5757      public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5758          $name = 'gradepointdefault';
5759          $visiblename = get_string('gradepointdefault', 'grades');
5760          $description = get_string('gradepointdefault_help', 'grades');
5761          $defaultsetting = 100;
5762          $paramtype = PARAM_INT;
5763          $size = 5;
5764          parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5765      }
5766  
5767      /**
5768       * Validate data before storage
5769       * @param string $data The submitted data
5770       * @return bool|string true if ok, string if error found
5771       */
5772      public function validate($data) {
5773          global $CFG;
5774          if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
5775              return true;
5776          } else {
5777              return get_string('gradepointdefault_validateerror', 'grades');
5778          }
5779      }
5780  }
5781  
5782  
5783  /**
5784   * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
5785   *
5786   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5787   */
5788  class admin_setting_special_gradepointmax extends admin_setting_configtext {
5789  
5790      /**
5791       * Config gradepointmax constructor
5792       *
5793       * @param string $name Overidden by "gradepointmax"
5794       * @param string $visiblename Overridden by "gradepointmax" language string.
5795       * @param string $description Overridden by "gradepointmax_help" language string.
5796       * @param string $defaultsetting Not used, overridden by 100.
5797       * @param mixed $paramtype Overridden by PARAM_INT.
5798       * @param int $size Overridden by 5.
5799       */
5800      public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5801          $name = 'gradepointmax';
5802          $visiblename = get_string('gradepointmax', 'grades');
5803          $description = get_string('gradepointmax_help', 'grades');
5804          $defaultsetting = 100;
5805          $paramtype = PARAM_INT;
5806          $size = 5;
5807          parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5808      }
5809  
5810      /**
5811       * Save the selected setting
5812       *
5813       * @param string $data The selected site
5814       * @return string empty string or error message
5815       */
5816      public function write_setting($data) {
5817          if ($data === '') {
5818              $data = (int)$this->defaultsetting;
5819          } else {
5820              $data = $data;
5821          }
5822          return parent::write_setting($data);
5823      }
5824  
5825      /**
5826       * Validate data before storage
5827       * @param string $data The submitted data
5828       * @return bool|string true if ok, string if error found
5829       */
5830      public function validate($data) {
5831          if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
5832              return true;
5833          } else {
5834              return get_string('gradepointmax_validateerror', 'grades');
5835          }
5836      }
5837  
5838      /**
5839       * Return an XHTML string for the setting
5840       * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5841       * @param string $query search query to be highlighted
5842       * @return string XHTML to display control
5843       */
5844      public function output_html($data, $query = '') {
5845          global $OUTPUT;
5846  
5847          $default = $this->get_defaultsetting();
5848          $context = (object) [
5849              'size' => $this->size,
5850              'id' => $this->get_id(),
5851              'name' => $this->get_full_name(),
5852              'value' => $data,
5853              'attributes' => [
5854                  'maxlength' => 5
5855              ],
5856              'forceltr' => $this->get_force_ltr()
5857          ];
5858          $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
5859  
5860          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
5861      }
5862  }
5863  
5864  
5865  /**
5866   * Grade category settings
5867   *
5868   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5869   */
5870  class admin_setting_gradecat_combo extends admin_setting {
5871      /** @var array Array of choices */
5872      public $choices;
5873  
5874      /**
5875       * Sets choices and calls parent::__construct with passed arguments
5876       * @param string $name
5877       * @param string $visiblename
5878       * @param string $description
5879       * @param mixed $defaultsetting string or array depending on implementation
5880       * @param array $choices An array of choices for the control
5881       */
5882      public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5883          $this->choices = $choices;
5884          parent::__construct($name, $visiblename, $description, $defaultsetting);
5885      }
5886  
5887      /**
5888       * Return the current setting(s) array
5889       *
5890       * @return array Array of value=>xx, forced=>xx, adv=>xx
5891       */
5892      public function get_setting() {
5893          global $CFG;
5894  
5895          $value = $this->config_read($this->name);
5896          $flag  = $this->config_read($this->name.'_flag');
5897  
5898          if (is_null($value) or is_null($flag)) {
5899              return NULL;
5900          }
5901  
5902          $flag   = (int)$flag;
5903          $forced = (boolean)(1 & $flag); // first bit
5904          $adv    = (boolean)(2 & $flag); // second bit
5905  
5906          return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
5907      }
5908  
5909      /**
5910       * Save the new settings passed in $data
5911       *
5912       * @todo Add vartype handling to ensure $data is array
5913       * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5914       * @return string empty or error message
5915       */
5916      public function write_setting($data) {
5917          global $CFG;
5918  
5919          $value  = $data['value'];
5920          $forced = empty($data['forced']) ? 0 : 1;
5921          $adv    = empty($data['adv'])    ? 0 : 2;
5922          $flag   = ($forced | $adv); //bitwise or
5923  
5924          if (!in_array($value, array_keys($this->choices))) {
5925              return 'Error setting ';
5926          }
5927  
5928          $oldvalue  = $this->config_read($this->name);
5929          $oldflag   = (int)$this->config_read($this->name.'_flag');
5930          $oldforced = (1 & $oldflag); // first bit
5931  
5932          $result1 = $this->config_write($this->name, $value);
5933          $result2 = $this->config_write($this->name.'_flag', $flag);
5934  
5935          // force regrade if needed
5936          if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
5937              require_once($CFG->libdir.'/gradelib.php');
5938              grade_category::updated_forced_settings();
5939          }
5940  
5941          if ($result1 and $result2) {
5942              return '';
5943          } else {
5944              return get_string('errorsetting', 'admin');
5945          }
5946      }
5947  
5948      /**
5949       * Return XHTML to display the field and wrapping div
5950       *
5951       * @todo Add vartype handling to ensure $data is array
5952       * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5953       * @param string $query
5954       * @return string XHTML to display control
5955       */
5956      public function output_html($data, $query='') {
5957          global $OUTPUT;
5958  
5959          $value  = $data['value'];
5960  
5961          $default = $this->get_defaultsetting();
5962          if (!is_null($default)) {
5963              $defaultinfo = array();
5964              if (isset($this->choices[$default['value']])) {
5965                  $defaultinfo[] = $this->choices[$default['value']];
5966              }
5967              if (!empty($default['forced'])) {
5968                  $defaultinfo[] = get_string('force');
5969              }
5970              if (!empty($default['adv'])) {
5971                  $defaultinfo[] = get_string('advanced');
5972              }
5973              $defaultinfo = implode(', ', $defaultinfo);
5974  
5975          } else {
5976              $defaultinfo = NULL;
5977          }
5978  
5979          $options = $this->choices;
5980          $context = (object) [
5981              'id' => $this->get_id(),
5982              'name' => $this->get_full_name(),
5983              'forced' => !empty($data['forced']),
5984              'advanced' => !empty($data['adv']),
5985              'options' => array_map(function($option) use ($options, $value) {
5986                  return [
5987                      'value' => $option,
5988                      'name' => $options[$option],
5989                      'selected' => $option == $value
5990                  ];
5991              }, array_keys($options)),
5992          ];
5993  
5994          $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
5995  
5996          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
5997      }
5998  }
5999  
6000  
6001  /**
6002   * Selection of grade report in user profiles
6003   *
6004   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6005   */
6006  class admin_setting_grade_profilereport extends admin_setting_configselect {
6007      /**
6008       * Calls parent::__construct with specific arguments
6009       */
6010      public function __construct() {
6011          parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
6012      }
6013  
6014      /**
6015       * Loads an array of choices for the configselect control
6016       *
6017       * @return bool always return true
6018       */
6019      public function load_choices() {
6020          if (is_array($this->choices)) {
6021              return true;
6022          }
6023          $this->choices = array();
6024  
6025          global $CFG;
6026          require_once($CFG->libdir.'/gradelib.php');
6027  
6028          foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6029              if (file_exists($plugindir.'/lib.php')) {
6030                  require_once ($plugindir.'/lib.php');
6031                  $functionname = 'grade_report_'.$plugin.'_profilereport';
6032                  if (function_exists($functionname)) {
6033                      $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
6034                  }
6035              }
6036          }
6037          return true;
6038      }
6039  }
6040  
6041  /**
6042   * Provides a selection of grade reports to be used for "grades".
6043   *
6044   * @copyright 2015 Adrian Greeve <adrian@moodle.com>
6045   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6046   */
6047  class admin_setting_my_grades_report extends admin_setting_configselect {
6048  
6049      /**
6050       * Calls parent::__construct with specific arguments.
6051       */
6052      public function __construct() {
6053          parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
6054                  new lang_string('mygrades_desc', 'grades'), 'overview', null);
6055      }
6056  
6057      /**
6058       * Loads an array of choices for the configselect control.
6059       *
6060       * @return bool always returns true.
6061       */
6062      public function load_choices() {
6063          global $CFG; // Remove this line and behold the horror of behat test failures!
6064          $this->choices = array();
6065          foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6066              if (file_exists($plugindir . '/lib.php')) {
6067                  require_once($plugindir . '/lib.php');
6068                  // Check to see if the class exists. Check the correct plugin convention first.
6069                  if (class_exists('gradereport_' . $plugin)) {
6070                      $classname = 'gradereport_' . $plugin;
6071                  } else if (class_exists('grade_report_' . $plugin)) {
6072                      // We are using the old plugin naming convention.
6073                      $classname = 'grade_report_' . $plugin;
6074                  } else {
6075                      continue;
6076                  }
6077                  if ($classname::supports_mygrades()) {
6078                      $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
6079                  }
6080              }
6081          }
6082          // Add an option to specify an external url.
6083          $this->choices['external'] = get_string('externalurl', 'grades');
6084          return true;
6085      }
6086  }
6087  
6088  /**
6089   * Special class for register auth selection
6090   *
6091   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6092   */
6093  class admin_setting_special_registerauth extends admin_setting_configselect {
6094      /**
6095       * Calls parent::__construct with specific arguments
6096       */
6097      public function __construct() {
6098          parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
6099      }
6100  
6101      /**
6102       * Returns the default option
6103       *
6104       * @return string empty or default option
6105       */
6106      public function get_defaultsetting() {
6107          $this->load_choices();
6108          $defaultsetting = parent::get_defaultsetting();
6109          if (array_key_exists($defaultsetting, $this->choices)) {
6110              return $defaultsetting;
6111          } else {
6112              return '';
6113          }
6114      }
6115  
6116      /**
6117       * Loads the possible choices for the array
6118       *
6119       * @return bool always returns true
6120       */
6121      public function load_choices() {
6122          global $CFG;
6123  
6124          if (is_array($this->choices)) {
6125              return true;
6126          }
6127          $this->choices = array();
6128          $this->choices[''] = get_string('disable');
6129  
6130          $authsenabled = get_enabled_auth_plugins();
6131  
6132          foreach ($authsenabled as $auth) {
6133              $authplugin = get_auth_plugin($auth);
6134              if (!$authplugin->can_signup()) {
6135                  continue;
6136              }
6137              // Get the auth title (from core or own auth lang files)
6138              $authtitle = $authplugin->get_title();
6139              $this->choices[$auth] = $authtitle;
6140          }
6141          return true;
6142      }
6143  }
6144  
6145  
6146  /**
6147   * General plugins manager
6148   */
6149  class admin_page_pluginsoverview extends admin_externalpage {
6150  
6151      /**
6152       * Sets basic information about the external page
6153       */
6154      public function __construct() {
6155          global $CFG;
6156          parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6157              "$CFG->wwwroot/$CFG->admin/plugins.php");
6158      }
6159  }
6160  
6161  /**
6162   * Module manage page
6163   *
6164   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6165   */
6166  class admin_page_managemods extends admin_externalpage {
6167      /**
6168       * Calls parent::__construct with specific arguments
6169       */
6170      public function __construct() {
6171          global $CFG;
6172          parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6173      }
6174  
6175      /**
6176       * Try to find the specified module
6177       *
6178       * @param string $query The module to search for
6179       * @return array
6180       */
6181      public function search($query) {
6182          global $CFG, $DB;
6183          if ($result = parent::search($query)) {
6184              return $result;
6185          }
6186  
6187          $found = false;
6188          if ($modules = $DB->get_records('modules')) {
6189              foreach ($modules as $module) {
6190                  if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6191                      continue;
6192                  }
6193                  if (strpos($module->name, $query) !== false) {
6194                      $found = true;
6195                      break;
6196                  }
6197                  $strmodulename = get_string('modulename', $module->name);
6198                  if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6199                      $found = true;
6200                      break;
6201                  }
6202              }
6203          }
6204          if ($found) {
6205              $result = new stdClass();
6206              $result->page     = $this;
6207              $result->settings = array();
6208              return array($this->name => $result);
6209          } else {
6210              return array();
6211          }
6212      }
6213  }
6214  
6215  
6216  /**
6217   * Special class for enrol plugins management.
6218   *
6219   * @copyright 2010 Petr Skoda {@link http://skodak.org}
6220   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6221   */
6222  class admin_setting_manageenrols extends admin_setting {
6223      /**
6224       * Calls parent::__construct with specific arguments
6225       */
6226      public function __construct() {
6227          $this->nosave = true;
6228          parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6229      }
6230  
6231      /**
6232       * Always returns true, does nothing
6233       *
6234       * @return true
6235       */
6236      public function get_setting() {
6237          return true;
6238      }
6239  
6240      /**
6241       * Always returns true, does nothing
6242       *
6243       * @return true
6244       */
6245      public function get_defaultsetting() {
6246          return true;
6247      }
6248  
6249      /**
6250       * Always returns '', does not write anything
6251       *
6252       * @return string Always returns ''
6253       */
6254      public function write_setting($data) {
6255      // do not write any setting
6256          return '';
6257      }
6258  
6259      /**
6260       * Checks if $query is one of the available enrol plugins
6261       *
6262       * @param string $query The string to search for
6263       * @return bool Returns true if found, false if not
6264       */
6265      public function is_related($query) {
6266          if (parent::is_related($query)) {
6267              return true;
6268          }
6269  
6270          $query = core_text::strtolower($query);
6271          $enrols = enrol_get_plugins(false);
6272          foreach ($enrols as $name=>$enrol) {
6273              $localised = get_string('pluginname', 'enrol_'.$name);
6274              if (strpos(core_text::strtolower($name), $query) !== false) {
6275                  return true;
6276              }
6277              if (strpos(core_text::strtolower($localised), $query) !== false) {
6278                  return true;
6279              }
6280          }
6281          return false;
6282      }
6283  
6284      /**
6285       * Builds the XHTML to display the control
6286       *
6287       * @param string $data Unused
6288       * @param string $query
6289       * @return string
6290       */
6291      public function output_html($data, $query='') {
6292          global $CFG, $OUTPUT, $DB, $PAGE;
6293  
6294          // Display strings.
6295          $strup        = get_string('up');
6296          $strdown      = get_string('down');
6297          $strsettings  = get_string('settings');
6298          $strenable    = get_string('enable');
6299          $strdisable   = get_string('disable');
6300          $struninstall = get_string('uninstallplugin', 'core_admin');
6301          $strusage     = get_string('enrolusage', 'enrol');
6302          $strversion   = get_string('version');
6303          $strtest      = get_string('testsettings', 'core_enrol');
6304  
6305          $pluginmanager = core_plugin_manager::instance();
6306  
6307          $enrols_available = enrol_get_plugins(false);
6308          $active_enrols    = enrol_get_plugins(true);
6309  
6310          $allenrols = array();
6311          foreach ($active_enrols as $key=>$enrol) {
6312              $allenrols[$key] = true;
6313          }
6314          foreach ($enrols_available as $key=>$enrol) {
6315              $allenrols[$key] = true;
6316          }
6317          // Now find all borked plugins and at least allow then to uninstall.
6318          $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6319          foreach ($condidates as $candidate) {
6320              if (empty($allenrols[$candidate])) {
6321                  $allenrols[$candidate] = true;
6322              }
6323          }
6324  
6325          $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6326          $return .= $OUTPUT->box_start('generalbox enrolsui');
6327  
6328          $table = new html_table();
6329          $table->head  = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6330          $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6331          $table->id = 'courseenrolmentplugins';
6332          $table->attributes['class'] = 'admintable generaltable';
6333          $table->data  = array();
6334  
6335          // Iterate through enrol plugins and add to the display table.
6336          $updowncount = 1;
6337          $enrolcount = count($active_enrols);
6338          $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6339          $printed = array();
6340          foreach($allenrols as $enrol => $unused) {
6341              $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6342              $version = get_config('enrol_'.$enrol, 'version');
6343              if ($version === false) {
6344                  $version = '';
6345              }
6346  
6347              if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6348                  $name = get_string('pluginname', 'enrol_'.$enrol);
6349              } else {
6350                  $name = $enrol;
6351              }
6352              // Usage.
6353              $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6354              $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6355              $usage = "$ci / $cp";
6356  
6357              // Hide/show links.
6358              $class = '';
6359              if (isset($active_enrols[$enrol])) {
6360                  $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6361                  $hideshow = "<a href=\"$aurl\">";
6362                  $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6363                  $enabled = true;
6364                  $displayname = $name;
6365              } else if (isset($enrols_available[$enrol])) {
6366                  $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6367                  $hideshow = "<a href=\"$aurl\">";
6368                  $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6369                  $enabled = false;
6370                  $displayname = $name;
6371                  $class = 'dimmed_text';
6372              } else {
6373                  $hideshow = '';
6374                  $enabled = false;
6375                  $displayname = '<span class="notifyproblem">'.$name.'</span>';
6376              }
6377              if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6378                  $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6379              } else {
6380                  $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6381              }
6382  
6383              // Up/down link (only if enrol is enabled).
6384              $updown = '';
6385              if ($enabled) {
6386                  if ($updowncount > 1) {
6387                      $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6388                      $updown .= "<a href=\"$aurl\">";
6389                      $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6390                  } else {
6391                      $updown .= $OUTPUT->spacer() . '&nbsp;';
6392                  }
6393                  if ($updowncount < $enrolcount) {
6394                      $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6395                      $updown .= "<a href=\"$aurl\">";
6396                      $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6397                  } else {
6398                      $updown .= $OUTPUT->spacer() . '&nbsp;';
6399                  }
6400                  ++$updowncount;
6401              }
6402  
6403              // Add settings link.
6404              if (!$version) {
6405                  $settings = '';
6406              } else if ($surl = $plugininfo->get_settings_url()) {
6407                  $settings = html_writer::link($surl, $strsettings);
6408              } else {
6409                  $settings = '';
6410              }
6411  
6412              // Add uninstall info.
6413              $uninstall = '';
6414              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6415                  $uninstall = html_writer::link($uninstallurl, $struninstall);
6416              }
6417  
6418              $test = '';
6419              if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6420                  $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
6421                  $test = html_writer::link($testsettingsurl, $strtest);
6422              }
6423  
6424              // Add a row to the table.
6425              $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6426              if ($class) {
6427                  $row->attributes['class'] = $class;
6428              }
6429              $table->data[] = $row;
6430  
6431              $printed[$enrol] = true;
6432          }
6433  
6434          $return .= html_writer::table($table);
6435          $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6436          $return .= $OUTPUT->box_end();
6437          return highlight($query, $return);
6438      }
6439  }
6440  
6441  
6442  /**
6443   * Blocks manage page
6444   *
6445   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6446   */
6447  class admin_page_manageblocks extends admin_externalpage {
6448      /**
6449       * Calls parent::__construct with specific arguments
6450       */
6451      public function __construct() {
6452          global $CFG;
6453          parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6454      }
6455  
6456      /**
6457       * Search for a specific block
6458       *
6459       * @param string $query The string to search for
6460       * @return array
6461       */
6462      public function search($query) {
6463          global $CFG, $DB;
6464          if ($result = parent::search($query)) {
6465              return $result;
6466          }
6467  
6468          $found = false;
6469          if ($blocks = $DB->get_records('block')) {
6470              foreach ($blocks as $block) {
6471                  if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6472                      continue;
6473                  }
6474                  if (strpos($block->name, $query) !== false) {
6475                      $found = true;
6476                      break;
6477                  }
6478                  $strblockname = get_string('pluginname', 'block_'.$block->name);
6479                  if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6480                      $found = true;
6481                      break;
6482                  }
6483              }
6484          }
6485          if ($found) {
6486              $result = new stdClass();
6487              $result->page     = $this;
6488              $result->settings = array();
6489              return array($this->name => $result);
6490          } else {
6491              return array();
6492          }
6493      }
6494  }
6495  
6496  /**
6497   * Message outputs configuration
6498   *
6499   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6500   */
6501  class admin_page_managemessageoutputs extends admin_externalpage {
6502      /**
6503       * Calls parent::__construct with specific arguments
6504       */
6505      public function __construct() {
6506          global $CFG;
6507          parent::__construct('managemessageoutputs',
6508              get_string('defaultmessageoutputs', 'message'),
6509              new moodle_url('/admin/message.php')
6510          );
6511      }
6512  
6513      /**
6514       * Search for a specific message processor
6515       *
6516       * @param string $query The string to search for
6517       * @return array
6518       */
6519      public function search($query) {
6520          global $CFG, $DB;
6521          if ($result = parent::search($query)) {
6522              return $result;
6523          }
6524  
6525          $found = false;
6526          if ($processors = get_message_processors()) {
6527              foreach ($processors as $processor) {
6528                  if (!$processor->available) {
6529                      continue;
6530                  }
6531                  if (strpos($processor->name, $query) !== false) {
6532                      $found = true;
6533                      break;
6534                  }
6535                  $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
6536                  if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
6537                      $found = true;
6538                      break;
6539                  }
6540              }
6541          }
6542          if ($found) {
6543              $result = new stdClass();
6544              $result->page     = $this;
6545              $result->settings = array();
6546              return array($this->name => $result);
6547          } else {
6548              return array();
6549          }
6550      }
6551  }
6552  
6553  /**
6554   * Default message outputs configuration
6555   *
6556   * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
6557   * @todo       MDL-64866 This will be deleted in Moodle 4.1.
6558   *
6559   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6560   */
6561  class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
6562      /**
6563       * Calls parent::__construct with specific arguments
6564       *
6565       * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
6566       * @todo       MDL-64866 This will be deleted in Moodle 4.1.
6567       */
6568      public function __construct() {
6569          global $CFG;
6570  
6571          debugging('admin_page_defaultmessageoutputs class is deprecated. Please use admin_page_managemessageoutputs instead.',
6572              DEBUG_DEVELOPER);
6573  
6574          admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
6575      }
6576  }
6577  
6578  
6579  /**
6580   * Manage question behaviours page
6581   *
6582   * @copyright  2011 The Open University
6583   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6584   */
6585  class admin_page_manageqbehaviours extends admin_externalpage {
6586      /**
6587       * Constructor
6588       */
6589      public function __construct() {
6590          global $CFG;
6591          parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
6592                  new moodle_url('/admin/qbehaviours.php'));
6593      }
6594  
6595      /**
6596       * Search question behaviours for the specified string
6597       *
6598       * @param string $query The string to search for in question behaviours
6599       * @return array
6600       */
6601      public function search($query) {
6602          global $CFG;
6603          if ($result = parent::search($query)) {
6604              return $result;
6605          }
6606  
6607          $found = false;
6608          require_once($CFG->dirroot . '/question/engine/lib.php');
6609          foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
6610              if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
6611                      $query) !== false) {
6612                  $found = true;
6613                  break;
6614              }
6615          }
6616          if ($found) {
6617              $result = new stdClass();
6618              $result->page     = $this;
6619              $result->settings = array();
6620              return array($this->name => $result);
6621          } else {
6622              return array();
6623          }
6624      }
6625  }
6626  
6627  
6628  /**
6629   * Question type manage page
6630   *
6631   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6632   */
6633  class admin_page_manageqtypes extends admin_externalpage {
6634      /**
6635       * Calls parent::__construct with specific arguments
6636       */
6637      public function __construct() {
6638          global $CFG;
6639          parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
6640                  new moodle_url('/admin/qtypes.php'));
6641      }
6642  
6643      /**
6644       * Search question types for the specified string
6645       *
6646       * @param string $query The string to search for in question types
6647       * @return array
6648       */
6649      public function search($query) {
6650          global $CFG;
6651          if ($result = parent::search($query)) {
6652              return $result;
6653          }
6654  
6655          $found = false;
6656          require_once($CFG->dirroot . '/question/engine/bank.php');
6657          foreach (question_bank::get_all_qtypes() as $qtype) {
6658              if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
6659                  $found = true;
6660                  break;
6661              }
6662          }
6663          if ($found) {
6664              $result = new stdClass();
6665              $result->page     = $this;
6666              $result->settings = array();
6667              return array($this->name => $result);
6668          } else {
6669              return array();
6670          }
6671      }
6672  }
6673  
6674  
6675  class admin_page_manageportfolios extends admin_externalpage {
6676      /**
6677       * Calls parent::__construct with specific arguments
6678       */
6679      public function __construct() {
6680          global $CFG;
6681          parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
6682                  "$CFG->wwwroot/$CFG->admin/portfolio.php");
6683      }
6684  
6685      /**
6686       * Searches page for the specified string.
6687       * @param string $query The string to search for
6688       * @return bool True if it is found on this page
6689       */
6690      public function search($query) {
6691          global $CFG;
6692          if ($result = parent::search($query)) {
6693              return $result;
6694          }
6695  
6696          $found = false;
6697          $portfolios = core_component::get_plugin_list('portfolio');
6698          foreach ($portfolios as $p => $dir) {
6699              if (strpos($p, $query) !== false) {
6700                  $found = true;
6701                  break;
6702              }
6703          }
6704          if (!$found) {
6705              foreach (portfolio_instances(false, false) as $instance) {
6706                  $title = $instance->get('name');
6707                  if (strpos(core_text::strtolower($title), $query) !== false) {
6708                      $found = true;
6709                      break;
6710                  }
6711              }
6712          }
6713  
6714          if ($found) {
6715              $result = new stdClass();
6716              $result->page     = $this;
6717              $result->settings = array();
6718              return array($this->name => $result);
6719          } else {
6720              return array();
6721          }
6722      }
6723  }
6724  
6725  
6726  class admin_page_managerepositories extends admin_externalpage {
6727      /**
6728       * Calls parent::__construct with specific arguments
6729       */
6730      public function __construct() {
6731          global $CFG;
6732          parent::__construct('managerepositories', get_string('manage',
6733                  'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
6734      }
6735  
6736      /**
6737       * Searches page for the specified string.
6738       * @param string $query The string to search for
6739       * @return bool True if it is found on this page
6740       */
6741      public function search($query) {
6742          global $CFG;
6743          if ($result = parent::search($query)) {
6744              return $result;
6745          }
6746  
6747          $found = false;
6748          $repositories= core_component::get_plugin_list('repository');
6749          foreach ($repositories as $p => $dir) {
6750              if (strpos($p, $query) !== false) {
6751                  $found = true;
6752                  break;
6753              }
6754          }
6755          if (!$found) {
6756              foreach (repository::get_types() as $instance) {
6757                  $title = $instance->get_typename();
6758                  if (strpos(core_text::strtolower($title), $query) !== false) {
6759                      $found = true;
6760                      break;
6761                  }
6762              }
6763          }
6764  
6765          if ($found) {
6766              $result = new stdClass();
6767              $result->page     = $this;
6768              $result->settings = array();
6769              return array($this->name => $result);
6770          } else {
6771              return array();
6772          }
6773      }
6774  }
6775  
6776  
6777  /**
6778   * Special class for authentication administration.
6779   *
6780   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6781   */
6782  class admin_setting_manageauths extends admin_setting {
6783      /**
6784       * Calls parent::__construct with specific arguments
6785       */
6786      public function __construct() {
6787          $this->nosave = true;
6788          parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
6789      }
6790  
6791      /**
6792       * Always returns true
6793       *
6794       * @return true
6795       */
6796      public function get_setting() {
6797          return true;
6798      }
6799  
6800      /**
6801       * Always returns true
6802       *
6803       * @return true
6804       */
6805      public function get_defaultsetting() {
6806          return true;
6807      }
6808  
6809      /**
6810       * Always returns '' and doesn't write anything
6811       *
6812       * @return string Always returns ''
6813       */
6814      public function write_setting($data) {
6815      // do not write any setting
6816          return '';
6817      }
6818  
6819      /**
6820       * Search to find if Query is related to auth plugin
6821       *
6822       * @param string $query The string to search for
6823       * @return bool true for related false for not
6824       */
6825      public function is_related($query) {
6826          if (parent::is_related($query)) {
6827              return true;
6828          }
6829  
6830          $authsavailable = core_component::get_plugin_list('auth');
6831          foreach ($authsavailable as $auth => $dir) {
6832              if (strpos($auth, $query) !== false) {
6833                  return true;
6834              }
6835              $authplugin = get_auth_plugin($auth);
6836              $authtitle = $authplugin->get_title();
6837              if (strpos(core_text::strtolower($authtitle), $query) !== false) {
6838                  return true;
6839              }
6840          }
6841          return false;
6842      }
6843  
6844      /**
6845       * Return XHTML to display control
6846       *
6847       * @param mixed $data Unused
6848       * @param string $query
6849       * @return string highlight
6850       */
6851      public function output_html($data, $query='') {
6852          global $CFG, $OUTPUT, $DB;
6853  
6854          // display strings
6855          $txt = get_strings(array('authenticationplugins', 'users', 'administration',
6856              'settings', 'edit', 'name', 'enable', 'disable',
6857              'up', 'down', 'none', 'users'));
6858          $txt->updown = "$txt->up/$txt->down";
6859          $txt->uninstall = get_string('uninstallplugin', 'core_admin');
6860          $txt->testsettings = get_string('testsettings', 'core_auth');
6861  
6862          $authsavailable = core_component::get_plugin_list('auth');
6863          get_enabled_auth_plugins(true); // fix the list of enabled auths
6864          if (empty($CFG->auth)) {
6865              $authsenabled = array();
6866          } else {
6867              $authsenabled = explode(',', $CFG->auth);
6868          }
6869  
6870          // construct the display array, with enabled auth plugins at the top, in order
6871          $displayauths = array();
6872          $registrationauths = array();
6873          $registrationauths[''] = $txt->disable;
6874          $authplugins = array();
6875          foreach ($authsenabled as $auth) {
6876              $authplugin = get_auth_plugin($auth);
6877              $authplugins[$auth] = $authplugin;
6878              /// Get the auth title (from core or own auth lang files)
6879              $authtitle = $authplugin->get_title();
6880              /// Apply titles
6881              $displayauths[$auth] = $authtitle;
6882              if ($authplugin->can_signup()) {
6883                  $registrationauths[$auth] = $authtitle;
6884              }
6885          }
6886  
6887          foreach ($authsavailable as $auth => $dir) {
6888              if (array_key_exists($auth, $displayauths)) {
6889                  continue; //already in the list
6890              }
6891              $authplugin = get_auth_plugin($auth);
6892              $authplugins[$auth] = $authplugin;
6893              /// Get the auth title (from core or own auth lang files)
6894              $authtitle = $authplugin->get_title();
6895              /// Apply titles
6896              $displayauths[$auth] = $authtitle;
6897              if ($authplugin->can_signup()) {
6898                  $registrationauths[$auth] = $authtitle;
6899              }
6900          }
6901  
6902          $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
6903          $return .= $OUTPUT->box_start('generalbox authsui');
6904  
6905          $table = new html_table();
6906          $table->head  = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
6907          $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6908          $table->data  = array();
6909          $table->attributes['class'] = 'admintable generaltable';
6910          $table->id = 'manageauthtable';
6911  
6912          //add always enabled plugins first
6913          $displayname = $displayauths['manual'];
6914          $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
6915          $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
6916          $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
6917          $displayname = $displayauths['nologin'];
6918          $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
6919          $table->data[] = array($displayname, $usercount, '', '', '', '', '');
6920  
6921  
6922          // iterate through auth plugins and add to the display table
6923          $updowncount = 1;
6924          $authcount = count($authsenabled);
6925          $url = "auth.php?sesskey=" . sesskey();
6926          foreach ($displayauths as $auth => $name) {
6927              if ($auth == 'manual' or $auth == 'nologin') {
6928                  continue;
6929              }
6930              $class = '';
6931              // hide/show link
6932              if (in_array($auth, $authsenabled)) {
6933                  $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
6934                  $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
6935                  $enabled = true;
6936                  $displayname = $name;
6937              }
6938              else {
6939                  $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
6940                  $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
6941                  $enabled = false;
6942                  $displayname = $name;
6943                  $class = 'dimmed_text';
6944              }
6945  
6946              $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
6947  
6948              // up/down link (only if auth is enabled)
6949              $updown = '';
6950              if ($enabled) {
6951                  if ($updowncount > 1) {
6952                      $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
6953                      $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
6954                  }
6955                  else {
6956                      $updown .= $OUTPUT->spacer() . '&nbsp;';
6957                  }
6958                  if ($updowncount < $authcount) {
6959                      $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
6960                      $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
6961                  }
6962                  else {
6963                      $updown .= $OUTPUT->spacer() . '&nbsp;';
6964                  }
6965                  ++ $updowncount;
6966              }
6967  
6968              // settings link
6969              if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
6970                  $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
6971              } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
6972                  $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
6973              } else {
6974                  $settings = '';
6975              }
6976  
6977              // Uninstall link.
6978              $uninstall = '';
6979              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
6980                  $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
6981              }
6982  
6983              $test = '';
6984              if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
6985                  $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
6986                  $test = html_writer::link($testurl, $txt->testsettings);
6987              }
6988  
6989              // Add a row to the table.
6990              $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
6991              if ($class) {
6992                  $row->attributes['class'] = $class;
6993              }
6994              $table->data[] = $row;
6995          }
6996          $return .= html_writer::table($table);
6997          $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
6998          $return .= $OUTPUT->box_end();
6999          return highlight($query, $return);
7000      }
7001  }
7002  
7003  
7004  /**
7005   * Special class for authentication administration.
7006   *
7007   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7008   */
7009  class admin_setting_manageeditors extends admin_setting {
7010      /**
7011       * Calls parent::__construct with specific arguments
7012       */
7013      public function __construct() {
7014          $this->nosave = true;
7015          parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
7016      }
7017  
7018      /**
7019       * Always returns true, does nothing
7020       *
7021       * @return true
7022       */
7023      public function get_setting() {
7024          return true;
7025      }
7026  
7027      /**
7028       * Always returns true, does nothing
7029       *
7030       * @return true
7031       */
7032      public function get_defaultsetting() {
7033          return true;
7034      }
7035  
7036      /**
7037       * Always returns '', does not write anything
7038       *
7039       * @return string Always returns ''
7040       */
7041      public function write_setting($data) {
7042      // do not write any setting
7043          return '';
7044      }
7045  
7046      /**
7047       * Checks if $query is one of the available editors
7048       *
7049       * @param string $query The string to search for
7050       * @return bool Returns true if found, false if not
7051       */
7052      public function is_related($query) {
7053          if (parent::is_related($query)) {
7054              return true;
7055          }
7056  
7057          $editors_available = editors_get_available();
7058          foreach ($editors_available as $editor=>$editorstr) {
7059              if (strpos($editor, $query) !== false) {
7060                  return true;
7061              }
7062              if (strpos(core_text::strtolower($editorstr), $query) !== false) {
7063                  return true;
7064              }
7065          }
7066          return false;
7067      }
7068  
7069      /**
7070       * Builds the XHTML to display the control
7071       *
7072       * @param string $data Unused
7073       * @param string $query
7074       * @return string
7075       */
7076      public function output_html($data, $query='') {
7077          global $CFG, $OUTPUT;
7078  
7079          // display strings
7080          $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7081              'up', 'down', 'none'));
7082          $struninstall = get_string('uninstallplugin', 'core_admin');
7083  
7084          $txt->updown = "$txt->up/$txt->down";
7085  
7086          $editors_available = editors_get_available();
7087          $active_editors = explode(',', $CFG->texteditors);
7088  
7089          $active_editors = array_reverse($active_editors);
7090          foreach ($active_editors as $key=>$editor) {
7091              if (empty($editors_available[$editor])) {
7092                  unset($active_editors[$key]);
7093              } else {
7094                  $name = $editors_available[$editor];
7095                  unset($editors_available[$editor]);
7096                  $editors_available[$editor] = $name;
7097              }
7098          }
7099          if (empty($active_editors)) {
7100          //$active_editors = array('textarea');
7101          }
7102          $editors_available = array_reverse($editors_available, true);
7103          $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
7104          $return .= $OUTPUT->box_start('generalbox editorsui');
7105  
7106          $table = new html_table();
7107          $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7108          $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7109          $table->id = 'editormanagement';
7110          $table->attributes['class'] = 'admintable generaltable';
7111          $table->data  = array();
7112  
7113          // iterate through auth plugins and add to the display table
7114          $updowncount = 1;
7115          $editorcount = count($active_editors);
7116          $url = "editors.php?sesskey=" . sesskey();
7117          foreach ($editors_available as $editor => $name) {
7118          // hide/show link
7119              $class = '';
7120              if (in_array($editor, $active_editors)) {
7121                  $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
7122                  $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7123                  $enabled = true;
7124                  $displayname = $name;
7125              }
7126              else {
7127                  $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
7128                  $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7129                  $enabled = false;
7130                  $displayname = $name;
7131                  $class = 'dimmed_text';
7132              }
7133  
7134              // up/down link (only if auth is enabled)
7135              $updown = '';
7136              if ($enabled) {
7137                  if ($updowncount > 1) {
7138                      $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
7139                      $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7140                  }
7141                  else {
7142                      $updown .= $OUTPUT->spacer() . '&nbsp;';
7143                  }
7144                  if ($updowncount < $editorcount) {
7145                      $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
7146                      $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7147                  }
7148                  else {
7149                      $updown .= $OUTPUT->spacer() . '&nbsp;';
7150                  }
7151                  ++ $updowncount;
7152              }
7153  
7154              // settings link
7155              if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
7156                  $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
7157                  $settings = "<a href='$eurl'>{$txt->settings}</a>";
7158              } else {
7159                  $settings = '';
7160              }
7161  
7162              $uninstall = '';
7163              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
7164                  $uninstall = html_writer::link($uninstallurl, $struninstall);
7165              }
7166  
7167              // Add a row to the table.
7168              $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7169              if ($class) {
7170                  $row->attributes['class'] = $class;
7171              }
7172              $table->data[] = $row;
7173          }
7174          $return .= html_writer::table($table);
7175          $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
7176          $return .= $OUTPUT->box_end();
7177          return highlight($query, $return);
7178      }
7179  }
7180  
7181  /**
7182   * Special class for antiviruses administration.
7183   *
7184   * @copyright  2015 Ruslan Kabalin, Lancaster University.
7185   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7186   */
7187  class admin_setting_manageantiviruses extends admin_setting {
7188      /**
7189       * Calls parent::__construct with specific arguments
7190       */
7191      public function __construct() {
7192          $this->nosave = true;
7193          parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
7194      }
7195  
7196      /**
7197       * Always returns true, does nothing
7198       *
7199       * @return true
7200       */
7201      public function get_setting() {
7202          return true;
7203      }
7204  
7205      /**
7206       * Always returns true, does nothing
7207       *
7208       * @return true
7209       */
7210      public function get_defaultsetting() {
7211          return true;
7212      }
7213  
7214      /**
7215       * Always returns '', does not write anything
7216       *
7217       * @param string $data Unused
7218       * @return string Always returns ''
7219       */
7220      public function write_setting($data) {
7221          // Do not write any setting.
7222          return '';
7223      }
7224  
7225      /**
7226       * Checks if $query is one of the available editors
7227       *
7228       * @param string $query The string to search for
7229       * @return bool Returns true if found, false if not
7230       */
7231      public function is_related($query) {
7232          if (parent::is_related($query)) {
7233              return true;
7234          }
7235  
7236          $antivirusesavailable = \core\antivirus\manager::get_available();
7237          foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
7238              if (strpos($antivirus, $query) !== false) {
7239                  return true;
7240              }
7241              if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
7242                  return true;
7243              }
7244          }
7245          return false;
7246      }
7247  
7248      /**
7249       * Builds the XHTML to display the control
7250       *
7251       * @param string $data Unused
7252       * @param string $query
7253       * @return string
7254       */
7255      public function output_html($data, $query='') {
7256          global $CFG, $OUTPUT;
7257  
7258          // Display strings.
7259          $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7260              'up', 'down', 'none'));
7261          $struninstall = get_string('uninstallplugin', 'core_admin');
7262  
7263          $txt->updown = "$txt->up/$txt->down";
7264  
7265          $antivirusesavailable = \core\antivirus\manager::get_available();
7266          $activeantiviruses = explode(',', $CFG->antiviruses);
7267  
7268          $activeantiviruses = array_reverse($activeantiviruses);
7269          foreach ($activeantiviruses as $key => $antivirus) {
7270              if (empty($antivirusesavailable[$antivirus])) {
7271                  unset($activeantiviruses[$key]);
7272              } else {
7273                  $name = $antivirusesavailable[$antivirus];
7274                  unset($antivirusesavailable[$antivirus]);
7275                  $antivirusesavailable[$antivirus] = $name;
7276              }
7277          }
7278          $antivirusesavailable = array_reverse($antivirusesavailable, true);
7279          $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
7280          $return .= $OUTPUT->box_start('generalbox antivirusesui');
7281  
7282          $table = new html_table();
7283          $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7284          $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7285          $table->id = 'antivirusmanagement';
7286          $table->attributes['class'] = 'admintable generaltable';
7287          $table->data  = array();
7288  
7289          // Iterate through auth plugins and add to the display table.
7290          $updowncount = 1;
7291          $antiviruscount = count($activeantiviruses);
7292          $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
7293          foreach ($antivirusesavailable as $antivirus => $name) {
7294              // Hide/show link.
7295              $class = '';
7296              if (in_array($antivirus, $activeantiviruses)) {
7297                  $hideshowurl = $baseurl;
7298                  $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
7299                  $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
7300                  $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7301                  $enabled = true;
7302                  $displayname = $name;
7303              } else {
7304                  $hideshowurl = $baseurl;
7305                  $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
7306                  $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
7307                  $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7308                  $enabled = false;
7309                  $displayname = $name;
7310                  $class = 'dimmed_text';
7311              }
7312  
7313              // Up/down link.
7314              $updown = '';
7315              if ($enabled) {
7316                  if ($updowncount > 1) {
7317                      $updownurl = $baseurl;
7318                      $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
7319                      $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
7320                      $updown = html_writer::link($updownurl, $updownimg);
7321                  } else {
7322                      $updownimg = $OUTPUT->spacer();
7323                  }
7324                  if ($updowncount < $antiviruscount) {
7325                      $updownurl = $baseurl;
7326                      $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
7327                      $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
7328                      $updown = html_writer::link($updownurl, $updownimg);
7329                  } else {
7330                      $updownimg = $OUTPUT->spacer();
7331                  }
7332                  ++ $updowncount;
7333              }
7334  
7335              // Settings link.
7336              if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
7337                  $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
7338                  $settings = html_writer::link($eurl, $txt->settings);
7339              } else {
7340                  $settings = '';
7341              }
7342  
7343              $uninstall = '';
7344              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
7345                  $uninstall = html_writer::link($uninstallurl, $struninstall);
7346              }
7347  
7348              // Add a row to the table.
7349              $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7350              if ($class) {
7351                  $row->attributes['class'] = $class;
7352              }
7353              $table->data[] = $row;
7354          }
7355          $return .= html_writer::table($table);
7356          $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
7357          $return .= $OUTPUT->box_end();
7358          return highlight($query, $return);
7359      }
7360  }
7361  
7362  /**
7363   * Special class for license administration.
7364   *
7365   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7366   * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7367   * @todo MDL-45184 This class will be deleted in Moodle 4.3.
7368   */
7369  class admin_setting_managelicenses extends admin_setting {
7370      /**
7371       * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7372       * @todo MDL-45184 This class will be deleted in Moodle 4.3
7373       */
7374      public function __construct() {
7375          global $ADMIN;
7376  
7377          debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7378              DEBUG_DEVELOPER);
7379  
7380          // Replace admin setting load with new external page load for tool_licensemanager, if not loaded already.
7381          if (!is_null($ADMIN->locate('licensemanager'))) {
7382              $temp = new admin_externalpage('licensemanager',
7383                  get_string('licensemanager', 'tool_licensemanager'),
7384                  \tool_licensemanager\helper::get_licensemanager_url());
7385  
7386              $ADMIN->add('license', $temp);
7387          }
7388      }
7389  
7390      /**
7391       * Always returns true, does nothing
7392       *
7393       * @deprecated since Moodle 3.9 MDL-45184.
7394       * @todo MDL-45184 This method will be deleted in Moodle 4.3
7395       *
7396       * @return true
7397       */
7398      public function get_setting() {
7399          debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7400              DEBUG_DEVELOPER);
7401  
7402          return true;
7403      }
7404  
7405      /**
7406       * Always returns true, does nothing
7407       *
7408       * @deprecated since Moodle 3.9 MDL-45184.
7409       * @todo MDL-45184 This method will be deleted in Moodle 4.3
7410       *
7411       * @return true
7412       */
7413      public function get_defaultsetting() {
7414          debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7415              DEBUG_DEVELOPER);
7416  
7417          return true;
7418      }
7419  
7420      /**
7421       * Always returns '', does not write anything
7422       *
7423       * @deprecated since Moodle 3.9 MDL-45184.
7424       * @todo MDL-45184 This method will be deleted in Moodle 4.3
7425       *
7426       * @return string Always returns ''
7427       */
7428      public function write_setting($data) {
7429          debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7430              DEBUG_DEVELOPER);
7431  
7432          // do not write any setting
7433          return '';
7434      }
7435  
7436      /**
7437       * Builds the XHTML to display the control
7438       *
7439       * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7440       * @todo MDL-45184 This method will be deleted in Moodle 4.3
7441       *
7442       * @param string $data Unused
7443       * @param string $query
7444       * @return string
7445       */
7446      public function output_html($data, $query='') {
7447          debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7448              DEBUG_DEVELOPER);
7449  
7450          redirect(\tool_licensemanager\helper::get_licensemanager_url());
7451      }
7452  }
7453  
7454  /**
7455   * Course formats manager. Allows to enable/disable formats and jump to settings
7456   */
7457  class admin_setting_manageformats extends admin_setting {
7458  
7459      /**
7460       * Calls parent::__construct with specific arguments
7461       */
7462      public function __construct() {
7463          $this->nosave = true;
7464          parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7465      }
7466  
7467      /**
7468       * Always returns true
7469       *
7470       * @return true
7471       */
7472      public function get_setting() {
7473          return true;
7474      }
7475  
7476      /**
7477       * Always returns true
7478       *
7479       * @return true
7480       */
7481      public function get_defaultsetting() {
7482          return true;
7483      }
7484  
7485      /**
7486       * Always returns '' and doesn't write anything
7487       *
7488       * @param mixed $data string or array, must not be NULL
7489       * @return string Always returns ''
7490       */
7491      public function write_setting($data) {
7492          // do not write any setting
7493          return '';
7494      }
7495  
7496      /**
7497       * Search to find if Query is related to format plugin
7498       *
7499       * @param string $query The string to search for
7500       * @return bool true for related false for not
7501       */
7502      public function is_related($query) {
7503          if (parent::is_related($query)) {
7504              return true;
7505          }
7506          $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7507          foreach ($formats as $format) {
7508              if (strpos($format->component, $query) !== false ||
7509                      strpos(core_text::strtolower($format->displayname), $query) !== false) {
7510                  return true;
7511              }
7512          }
7513          return false;
7514      }
7515  
7516      /**
7517       * Return XHTML to display control
7518       *
7519       * @param mixed $data Unused
7520       * @param string $query
7521       * @return string highlight
7522       */
7523      public function output_html($data, $query='') {
7524          global $CFG, $OUTPUT;
7525          $return = '';
7526          $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7527          $return .= $OUTPUT->box_start('generalbox formatsui');
7528  
7529          $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7530  
7531          // display strings
7532          $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7533          $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7534          $txt->updown = "$txt->up/$txt->down";
7535  
7536          $table = new html_table();
7537          $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7538          $table->align = array('left', 'center', 'center', 'center', 'center');
7539          $table->attributes['class'] = 'manageformattable generaltable admintable';
7540          $table->data  = array();
7541  
7542          $cnt = 0;
7543          $defaultformat = get_config('moodlecourse', 'format');
7544          $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7545          foreach ($formats as $format) {
7546              $url = new moodle_url('/admin/courseformats.php',
7547                      array('sesskey' => sesskey(), 'format' => $format->name));
7548              $isdefault = '';
7549              $class = '';
7550              if ($format->is_enabled()) {
7551                  $strformatname = $format->displayname;
7552                  if ($defaultformat === $format->name) {
7553                      $hideshow = $txt->default;
7554                  } else {
7555                      $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7556                              $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7557                  }
7558              } else {
7559                  $strformatname = $format->displayname;
7560                  $class = 'dimmed_text';
7561                  $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7562                      $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7563              }
7564              $updown = '';
7565              if ($cnt) {
7566                  $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7567                      $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7568              } else {
7569                  $updown .= $spacer;
7570              }
7571              if ($cnt < count($formats) - 1) {
7572                  $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7573                      $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7574              } else {
7575                  $updown .= $spacer;
7576              }
7577              $cnt++;
7578              $settings = '';
7579              if ($format->get_settings_url()) {
7580                  $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7581              }
7582              $uninstall = '';
7583              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7584                  $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7585              }
7586              $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7587              if ($class) {
7588                  $row->attributes['class'] = $class;
7589              }
7590              $table->data[] = $row;
7591          }
7592          $return .= html_writer::table($table);
7593          $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7594          $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7595          $return .= $OUTPUT->box_end();
7596          return highlight($query, $return);
7597      }
7598  }
7599  
7600  /**
7601   * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7602   *
7603   * @package    core
7604   * @copyright  2018 Toni Barbera
7605   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7606   */
7607  class admin_setting_managecustomfields extends admin_setting {
7608  
7609      /**
7610       * Calls parent::__construct with specific arguments
7611       */
7612      public function __construct() {
7613          $this->nosave = true;
7614          parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7615      }
7616  
7617      /**
7618       * Always returns true
7619       *
7620       * @return true
7621       */
7622      public function get_setting() {
7623          return true;
7624      }
7625  
7626      /**
7627       * Always returns true
7628       *
7629       * @return true
7630       */
7631      public function get_defaultsetting() {
7632          return true;
7633      }
7634  
7635      /**
7636       * Always returns '' and doesn't write anything
7637       *
7638       * @param mixed $data string or array, must not be NULL
7639       * @return string Always returns ''
7640       */
7641      public function write_setting($data) {
7642          // Do not write any setting.
7643          return '';
7644      }
7645  
7646      /**
7647       * Search to find if Query is related to format plugin
7648       *
7649       * @param string $query The string to search for
7650       * @return bool true for related false for not
7651       */
7652      public function is_related($query) {
7653          if (parent::is_related($query)) {
7654              return true;
7655          }
7656          $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7657          foreach ($formats as $format) {
7658              if (strpos($format->component, $query) !== false ||
7659                      strpos(core_text::strtolower($format->displayname), $query) !== false) {
7660                  return true;
7661              }
7662          }
7663          return false;
7664      }
7665  
7666      /**
7667       * Return XHTML to display control
7668       *
7669       * @param mixed $data Unused
7670       * @param string $query
7671       * @return string highlight
7672       */
7673      public function output_html($data, $query='') {
7674          global $CFG, $OUTPUT;
7675          $return = '';
7676          $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7677          $return .= $OUTPUT->box_start('generalbox customfieldsui');
7678  
7679          $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7680  
7681          $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7682          $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7683          $txt->updown = "$txt->up/$txt->down";
7684  
7685          $table = new html_table();
7686          $table->head  = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7687          $table->align = array('left', 'center', 'center', 'center');
7688          $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7689          $table->data  = array();
7690  
7691          $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7692          foreach ($fields as $field) {
7693              $url = new moodle_url('/admin/customfields.php',
7694                      array('sesskey' => sesskey(), 'field' => $field->name));
7695  
7696              if ($field->is_enabled()) {
7697                  $strfieldname = $field->displayname;
7698                  $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7699                          $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7700              } else {
7701                  $strfieldname = $field->displayname;
7702                  $class = 'dimmed_text';
7703                  $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7704                      $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7705              }
7706              $settings = '';
7707              if ($field->get_settings_url()) {
7708                  $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7709              }
7710              $uninstall = '';
7711              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7712                  $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7713              }
7714              $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7715              $table->data[] = $row;
7716          }
7717          $return .= html_writer::table($table);
7718          $return .= $OUTPUT->box_end();
7719          return highlight($query, $return);
7720      }
7721  }
7722  
7723  /**
7724   * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7725   *
7726   * @copyright  2016 Brendan Heywood (brendan@catalyst-au.net)
7727   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7728   */
7729  class admin_setting_managedataformats extends admin_setting {
7730  
7731      /**
7732       * Calls parent::__construct with specific arguments
7733       */
7734      public function __construct() {
7735          $this->nosave = true;
7736          parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7737      }
7738  
7739      /**
7740       * Always returns true
7741       *
7742       * @return true
7743       */
7744      public function get_setting() {
7745          return true;
7746      }
7747  
7748      /**
7749       * Always returns true
7750       *
7751       * @return true
7752       */
7753      public function get_defaultsetting() {
7754          return true;
7755      }
7756  
7757      /**
7758       * Always returns '' and doesn't write anything
7759       *
7760       * @param mixed $data string or array, must not be NULL
7761       * @return string Always returns ''
7762       */
7763      public function write_setting($data) {
7764          // Do not write any setting.
7765          return '';
7766      }
7767  
7768      /**
7769       * Search to find if Query is related to format plugin
7770       *
7771       * @param string $query The string to search for
7772       * @return bool true for related false for not
7773       */
7774      public function is_related($query) {
7775          if (parent::is_related($query)) {
7776              return true;
7777          }
7778          $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7779          foreach ($formats as $format) {
7780              if (strpos($format->component, $query) !== false ||
7781                      strpos(core_text::strtolower($format->displayname), $query) !== false) {
7782                  return true;
7783              }
7784          }
7785          return false;
7786      }
7787  
7788      /**
7789       * Return XHTML to display control
7790       *
7791       * @param mixed $data Unused
7792       * @param string $query
7793       * @return string highlight
7794       */
7795      public function output_html($data, $query='') {
7796          global $CFG, $OUTPUT;
7797          $return = '';
7798  
7799          $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7800  
7801          $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7802          $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7803          $txt->updown = "$txt->up/$txt->down";
7804  
7805          $table = new html_table();
7806          $table->head  = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7807          $table->align = array('left', 'center', 'center', 'center', 'center');
7808          $table->attributes['class'] = 'manageformattable generaltable admintable';
7809          $table->data  = array();
7810  
7811          $cnt = 0;
7812          $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7813          $totalenabled = 0;
7814          foreach ($formats as $format) {
7815              if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7816                  $totalenabled++;
7817              }
7818          }
7819          foreach ($formats as $format) {
7820              $status = $format->get_status();
7821              $url = new moodle_url('/admin/dataformats.php',
7822                      array('sesskey' => sesskey(), 'name' => $format->name));
7823  
7824              $class = '';
7825              if ($format->is_enabled()) {
7826                  $strformatname = $format->displayname;
7827                  if ($totalenabled == 1&& $format->is_enabled()) {
7828                      $hideshow = '';
7829                  } else {
7830                      $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7831                          $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7832                  }
7833              } else {
7834                  $class = 'dimmed_text';
7835                  $strformatname = $format->displayname;
7836                  $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7837                      $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7838              }
7839  
7840              $updown = '';
7841              if ($cnt) {
7842                  $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7843                      $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7844              } else {
7845                  $updown .= $spacer;
7846              }
7847              if ($cnt < count($formats) - 1) {
7848                  $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7849                      $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7850              } else {
7851                  $updown .= $spacer;
7852              }
7853  
7854              $uninstall = '';
7855              if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
7856                  $uninstall = get_string('status_missing', 'core_plugin');
7857              } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
7858                  $uninstall = get_string('status_new', 'core_plugin');
7859              } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
7860                  if ($totalenabled != 1 || !$format->is_enabled()) {
7861                      $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7862                  }
7863              }
7864  
7865              $settings = '';
7866              if ($format->get_settings_url()) {
7867                  $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7868              }
7869  
7870              $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7871              if ($class) {
7872                  $row->attributes['class'] = $class;
7873              }
7874              $table->data[] = $row;
7875              $cnt++;
7876          }
7877          $return .= html_writer::table($table);
7878          return highlight($query, $return);
7879      }
7880  }
7881  
7882  /**
7883   * Special class for filter administration.
7884   *
7885   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7886   */
7887  class admin_page_managefilters extends admin_externalpage {
7888      /**
7889       * Calls parent::__construct with specific arguments
7890       */
7891      public function __construct() {
7892          global $CFG;
7893          parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
7894      }
7895  
7896      /**
7897       * Searches all installed filters for specified filter
7898       *
7899       * @param string $query The filter(string) to search for
7900       * @param string $query
7901       */
7902      public function search($query) {
7903          global $CFG;
7904          if ($result = parent::search($query)) {
7905              return $result;
7906          }
7907  
7908          $found = false;
7909          $filternames = filter_get_all_installed();
7910          foreach ($filternames as $path => $strfiltername) {
7911              if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
7912                  $found = true;
7913                  break;
7914              }
7915              if (strpos($path, $query) !== false) {
7916                  $found = true;
7917                  break;
7918              }
7919          }
7920  
7921          if ($found) {
7922              $result = new stdClass;
7923              $result->page = $this;
7924              $result->settings = array();
7925              return array($this->name => $result);
7926          } else {
7927              return array();
7928          }
7929      }
7930  }
7931  
7932  /**
7933   * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
7934   * Requires a get_rank method on the plugininfo class for sorting.
7935   *
7936   * @copyright 2017 Damyon Wiese
7937   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7938   */
7939  abstract class admin_setting_manage_plugins extends admin_setting {
7940  
7941      /**
7942       * Get the admin settings section name (just a unique string)
7943       *
7944       * @return string
7945       */
7946      public function get_section_name() {
7947          return 'manage' . $this->get_plugin_type() . 'plugins';
7948      }
7949  
7950      /**
7951       * Get the admin settings section title (use get_string).
7952       *
7953       * @return string
7954       */
7955      abstract public function get_section_title();
7956  
7957      /**
7958       * Get the type of plugin to manage.
7959       *
7960       * @return string
7961       */
7962      abstract public function get_plugin_type();
7963  
7964      /**
7965       * Get the name of the second column.
7966       *
7967       * @return string
7968       */
7969      public function get_info_column_name() {
7970          return '';
7971      }
7972  
7973      /**
7974       * Get the type of plugin to manage.
7975       *
7976       * @param plugininfo The plugin info class.
7977       * @return string
7978       */
7979      abstract public function get_info_column($plugininfo);
7980  
7981      /**
7982       * Calls parent::__construct with specific arguments
7983       */
7984      public function __construct() {
7985          $this->nosave = true;
7986          parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
7987      }
7988  
7989      /**
7990       * Always returns true, does nothing
7991       *
7992       * @return true
7993       */
7994      public function get_setting() {
7995          return true;
7996      }
7997  
7998      /**
7999       * Always returns true, does nothing
8000       *
8001       * @return true
8002       */
8003      public function get_defaultsetting() {
8004          return true;
8005      }
8006  
8007      /**
8008       * Always returns '', does not write anything
8009       *
8010       * @param mixed $data
8011       * @return string Always returns ''
8012       */
8013      public function write_setting($data) {
8014          // Do not write any setting.
8015          return '';
8016      }
8017  
8018      /**
8019       * Checks if $query is one of the available plugins of this type
8020       *
8021       * @param string $query The string to search for
8022       * @return bool Returns true if found, false if not
8023       */
8024      public function is_related($query) {
8025          if (parent::is_related($query)) {
8026              return true;
8027          }
8028  
8029          $query = core_text::strtolower($query);
8030          $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
8031          foreach ($plugins as $name => $plugin) {
8032              $localised = $plugin->displayname;
8033              if (strpos(core_text::strtolower($name), $query) !== false) {
8034                  return true;
8035              }
8036              if (strpos(core_text::strtolower($localised), $query) !== false) {
8037                  return true;
8038              }
8039          }
8040          return false;
8041      }
8042  
8043      /**
8044       * The URL for the management page for this plugintype.
8045       *
8046       * @return moodle_url
8047       */
8048      protected function get_manage_url() {
8049          return new moodle_url('/admin/updatesetting.php');
8050      }
8051  
8052      /**
8053       * Builds the HTML to display the control.
8054       *
8055       * @param string $data Unused
8056       * @param string $query
8057       * @return string
8058       */
8059      public function output_html($data, $query = '') {
8060          global $CFG, $OUTPUT, $DB, $PAGE;
8061  
8062          $context = (object) [
8063              'manageurl' => new moodle_url($this->get_manage_url(), [
8064                      'type' => $this->get_plugin_type(),
8065                      'sesskey' => sesskey(),
8066                  ]),
8067              'infocolumnname' => $this->get_info_column_name(),
8068              'plugins' => [],
8069          ];
8070  
8071          $pluginmanager = core_plugin_manager::instance();
8072          $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
8073          $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
8074          $plugins = array_merge($enabled, $allplugins);
8075          foreach ($plugins as $key => $plugin) {
8076              $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
8077  
8078              $pluginkey = (object) [
8079                  'plugin' => $plugin->displayname,
8080                  'enabled' => $plugin->is_enabled(),
8081                  'togglelink' => '',
8082                  'moveuplink' => '',
8083                  'movedownlink' => '',
8084                  'settingslink' => $plugin->get_settings_url(),
8085                  'uninstalllink' => '',
8086                  'info' => '',
8087              ];
8088  
8089              // Enable/Disable link.
8090              $togglelink = new moodle_url($pluginlink);
8091              if ($plugin->is_enabled()) {
8092                  $toggletarget = false;
8093                  $togglelink->param('action', 'disable');
8094  
8095                  if (count($context->plugins)) {
8096                      // This is not the first plugin.
8097                      $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
8098                  }
8099  
8100                  if (count($enabled) > count($context->plugins) + 1) {
8101                      // This is not the last plugin.
8102                      $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
8103                  }
8104  
8105                  $pluginkey->info = $this->get_info_column($plugin);
8106              } else {
8107                  $toggletarget = true;
8108                  $togglelink->param('action', 'enable');
8109              }
8110  
8111              $pluginkey->toggletarget = $toggletarget;
8112              $pluginkey->togglelink = $togglelink;
8113  
8114              $frankenstyle = $plugin->type . '_' . $plugin->name;
8115              if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
8116                  // This plugin supports uninstallation.
8117                  $pluginkey->uninstalllink = $uninstalllink;
8118              }
8119  
8120              if (!empty($this->get_info_column_name())) {
8121                  // This plugintype has an info column.
8122                  $pluginkey->info = $this->get_info_column($plugin);
8123              }
8124  
8125              $context->plugins[] = $pluginkey;
8126          }
8127  
8128          $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8129          return highlight($query, $str);
8130      }
8131  }
8132  
8133  /**
8134   * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8135   * Requires a get_rank method on the plugininfo class for sorting.
8136   *
8137   * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8138  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8139   */
8140  class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8141      public function get_section_title() {
8142          return get_string('type_fileconverter_plural', 'plugin');
8143      }
8144  
8145      public function get_plugin_type() {
8146          return 'fileconverter';
8147      }
8148  
8149      public function get_info_column_name() {
8150          return get_string('supportedconversions', 'plugin');
8151      }
8152  
8153      public function get_info_column($plugininfo) {
8154          return $plugininfo->get_supported_conversions();
8155      }
8156  }
8157  
8158  /**
8159   * Special class for media player plugins management.
8160   *
8161   * @copyright 2016 Marina Glancy
8162   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8163   */
8164  class admin_setting_managemediaplayers extends admin_setting {
8165      /**
8166       * Calls parent::__construct with specific arguments
8167       */
8168      public function __construct() {
8169          $this->nosave = true;
8170          parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8171      }
8172  
8173      /**
8174       * Always returns true, does nothing
8175       *
8176       * @return true
8177       */
8178      public function get_setting() {
8179          return true;
8180      }
8181  
8182      /**
8183       * Always returns true, does nothing
8184       *
8185       * @return true
8186       */
8187      public function get_defaultsetting() {
8188          return true;
8189      }
8190  
8191      /**
8192       * Always returns '', does not write anything
8193       *
8194       * @param mixed $data
8195       * @return string Always returns ''
8196       */
8197      public function write_setting($data) {
8198          // Do not write any setting.
8199          return '';
8200      }
8201  
8202      /**
8203       * Checks if $query is one of the available enrol plugins
8204       *
8205       * @param string $query The string to search for
8206       * @return bool Returns true if found, false if not
8207       */
8208      public function is_related($query) {
8209          if (parent::is_related($query)) {
8210              return true;
8211          }
8212  
8213          $query = core_text::strtolower($query);
8214          $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8215          foreach ($plugins as $name => $plugin) {
8216              $localised = $plugin->displayname;
8217              if (strpos(core_text::strtolower($name), $query) !== false) {
8218                  return true;
8219              }
8220              if (strpos(core_text::strtolower($localised), $query) !== false) {
8221                  return true;
8222              }
8223          }
8224          return false;
8225      }
8226  
8227      /**
8228       * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8229       * @return \core\plugininfo\media[]
8230       */
8231      protected function get_sorted_plugins() {
8232          $pluginmanager = core_plugin_manager::instance();
8233  
8234          $plugins = $pluginmanager->get_plugins_of_type('media');
8235          $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8236  
8237          // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8238          \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8239  
8240          $order = array_values($enabledplugins);
8241          $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8242  
8243          $sortedplugins = array();
8244          foreach ($order as $name) {
8245              $sortedplugins[$name] = $plugins[$name];
8246          }
8247  
8248          return $sortedplugins;
8249      }
8250  
8251      /**
8252       * Builds the XHTML to display the control
8253       *
8254       * @param string $data Unused
8255       * @param string $query
8256       * @return string
8257       */
8258      public function output_html($data, $query='') {
8259          global $CFG, $OUTPUT, $DB, $PAGE;
8260  
8261          // Display strings.
8262          $strup        = get_string('up');
8263          $strdown      = get_string('down');
8264          $strsettings  = get_string('settings');
8265          $strenable    = get_string('enable');
8266          $strdisable   = get_string('disable');
8267          $struninstall = get_string('uninstallplugin', 'core_admin');
8268          $strversion   = get_string('version');
8269          $strname      = get_string('name');
8270          $strsupports  = get_string('supports', 'core_media');
8271  
8272          $pluginmanager = core_plugin_manager::instance();
8273  
8274          $plugins = $this->get_sorted_plugins();
8275          $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8276  
8277          $return = $OUTPUT->box_start('generalbox mediaplayersui');
8278  
8279          $table = new html_table();
8280          $table->head  = array($strname, $strsupports, $strversion,
8281              $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8282          $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8283              'centeralign', 'centeralign', 'centeralign', 'centeralign');
8284          $table->id = 'mediaplayerplugins';
8285          $table->attributes['class'] = 'admintable generaltable';
8286          $table->data  = array();
8287  
8288          // Iterate through media plugins and add to the display table.
8289          $updowncount = 1;
8290          $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8291          $printed = array();
8292          $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8293  
8294          $usedextensions = [];
8295          foreach ($plugins as $name => $plugin) {
8296              $url->param('media', $name);
8297              $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8298              $version = $plugininfo->versiondb;
8299              $supports = $plugininfo->supports($usedextensions);
8300  
8301              // Hide/show links.
8302              $class = '';
8303              if (!$plugininfo->is_installed_and_upgraded()) {
8304                  $hideshow = '';
8305                  $enabled = false;
8306                  $displayname = '<span class="notifyproblem">'.$name.'</span>';
8307              } else {
8308                  $enabled = $plugininfo->is_enabled();
8309                  if ($enabled) {
8310                      $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8311                          $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8312                  } else {
8313                      $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8314                          $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8315                      $class = 'dimmed_text';
8316                  }
8317                  $displayname = $plugin->displayname;
8318                  if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8319                      $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8320                  }
8321              }
8322              if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8323                  $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8324              } else {
8325                  $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8326              }
8327  
8328              // Up/down link (only if enrol is enabled).
8329              $updown = '';
8330              if ($enabled) {
8331                  if ($updowncount > 1) {
8332                      $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8333                          $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8334                  } else {
8335                      $updown = $spacer;
8336                  }
8337                  if ($updowncount < count($enabledplugins)) {
8338                      $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8339                          $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8340                  } else {
8341                      $updown .= $spacer;
8342                  }
8343                  ++$updowncount;
8344              }
8345  
8346              $uninstall = '';
8347              $status = $plugininfo->get_status();
8348              if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8349                  $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8350              }
8351              if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8352                  $uninstall = get_string('status_new', 'core_plugin');
8353              } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8354                  $uninstall .= html_writer::link($uninstallurl, $struninstall);
8355              }
8356  
8357              $settings = '';
8358              if ($plugininfo->get_settings_url()) {
8359                  $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8360              }
8361  
8362              // Add a row to the table.
8363              $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8364              if ($class) {
8365                  $row->attributes['class'] = $class;
8366              }
8367              $table->data[] = $row;
8368  
8369              $printed[$name] = true;
8370          }
8371  
8372          $return .= html_writer::table($table);
8373          $return .= $OUTPUT->box_end();
8374          return highlight($query, $return);
8375      }
8376  }
8377  
8378  
8379  /**
8380   * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8381   *
8382   * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
8383   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8384   */
8385  class admin_setting_managecontentbankcontenttypes extends admin_setting {
8386  
8387      /**
8388       * Calls parent::__construct with specific arguments
8389       */
8390      public function __construct() {
8391          $this->nosave = true;
8392          parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8393      }
8394  
8395      /**
8396       * Always returns true
8397       *
8398       * @return true
8399       */
8400      public function get_setting() {
8401          return true;
8402      }
8403  
8404      /**
8405       * Always returns true
8406       *
8407       * @return true
8408       */
8409      public function get_defaultsetting() {
8410          return true;
8411      }
8412  
8413      /**
8414       * Always returns '' and doesn't write anything
8415       *
8416       * @param mixed $data string or array, must not be NULL
8417       * @return string Always returns ''
8418       */
8419      public function write_setting($data) {
8420          // Do not write any setting.
8421          return '';
8422      }
8423  
8424      /**
8425       * Search to find if Query is related to content bank plugin
8426       *
8427       * @param string $query The string to search for
8428       * @return bool true for related false for not
8429       */
8430      public function is_related($query) {
8431          if (parent::is_related($query)) {
8432              return true;
8433          }
8434          $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8435          foreach ($types as $type) {
8436              if (strpos($type->component, $query) !== false ||
8437                  strpos(core_text::strtolower($type->displayname), $query) !== false) {
8438                  return true;
8439              }
8440          }
8441          return false;
8442      }
8443  
8444      /**
8445       * Return XHTML to display control
8446       *
8447       * @param mixed $data Unused
8448       * @param string $query
8449       * @return string highlight
8450       */
8451      public function output_html($data, $query='') {
8452          global $CFG, $OUTPUT;
8453          $return = '';
8454  
8455          $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8456          $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8457          $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8458  
8459          $table = new html_table();
8460          $table->head  = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8461          $table->align = array('left', 'center', 'center', 'center', 'center');
8462          $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8463          $table->data  = array();
8464          $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8465  
8466          $totalenabled = 0;
8467          $count = 0;
8468          foreach ($types as $type) {
8469              if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8470                  $totalenabled++;
8471              }
8472          }
8473  
8474          foreach ($types as $type) {
8475              $url = new moodle_url('/admin/contentbank.php',
8476                  array('sesskey' => sesskey(), 'name' => $type->name));
8477  
8478              $class = '';
8479              $strtypename = $type->displayname;
8480              if ($type->is_enabled()) {
8481                  $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8482                      $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8483              } else {
8484                  $class = 'dimmed_text';
8485                  $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8486                      $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8487              }
8488  
8489              $updown = '';
8490              if ($count) {
8491                  $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8492                          $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8493              } else {
8494                  $updown .= $spacer;
8495              }
8496              if ($count < count($types) - 1) {
8497                  $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8498                          $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8499              } else {
8500                  $updown .= $spacer;
8501              }
8502  
8503              $settings = '';
8504              if ($type->get_settings_url()) {
8505                  $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8506              }
8507  
8508              $uninstall = '';
8509              if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8510                  $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8511              }
8512  
8513              $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8514              if ($class) {
8515                  $row->attributes['class'] = $class;
8516              }
8517              $table->data[] = $row;
8518              $count++;
8519          }
8520          $return .= html_writer::table($table);
8521          return highlight($query, $return);
8522      }
8523  }
8524  
8525  /**
8526   * Initialise admin page - this function does require login and permission
8527   * checks specified in page definition.
8528   *
8529   * This function must be called on each admin page before other code.
8530   *
8531   * @global moodle_page $PAGE
8532   *
8533   * @param string $section name of page
8534   * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8535   * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8536   *      added to the turn blocks editing on/off form, so this page reloads correctly.
8537   * @param string $actualurl if the actual page being viewed is not the normal one for this
8538   *      page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8539   * @param array $options Additional options that can be specified for page setup.
8540   *      pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8541   */
8542  function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
8543      global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8544  
8545      $PAGE->set_context(null); // hack - set context to something, by default to system context
8546  
8547      $site = get_site();
8548      require_login(null, false);
8549  
8550      if (!empty($options['pagelayout'])) {
8551          // A specific page layout has been requested.
8552          $PAGE->set_pagelayout($options['pagelayout']);
8553      } else if ($section === 'upgradesettings') {
8554          $PAGE->set_pagelayout('maintenance');
8555      } else {
8556          $PAGE->set_pagelayout('admin');
8557      }
8558  
8559      $adminroot = admin_get_root(false, false); // settings not required for external pages
8560      $extpage = $adminroot->locate($section, true);
8561  
8562      if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8563          // The requested section isn't in the admin tree
8564          // It could be because the user has inadequate capapbilities or because the section doesn't exist
8565          if (!has_capability('moodle/site:config', context_system::instance())) {
8566              // The requested section could depend on a different capability
8567              // but most likely the user has inadequate capabilities
8568              print_error('accessdenied', 'admin');
8569          } else {
8570              print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8571          }
8572      }
8573  
8574      // this eliminates our need to authenticate on the actual pages
8575      if (!$extpage->check_access()) {
8576          print_error('accessdenied', 'admin');
8577          die;
8578      }
8579  
8580      navigation_node::require_admin_tree();
8581  
8582      // $PAGE->set_extra_button($extrabutton); TODO
8583  
8584      if (!$actualurl) {
8585          $actualurl = $extpage->url;
8586      }
8587  
8588      $PAGE->set_url($actualurl, $extraurlparams);
8589      if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8590          $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8591      }
8592  
8593      if (empty($SITE->fullname) || empty($SITE->shortname)) {
8594          // During initial install.
8595          $strinstallation = get_string('installation', 'install');
8596          $strsettings = get_string('settings');
8597          $PAGE->navbar->add($strsettings);
8598          $PAGE->set_title($strinstallation);
8599          $PAGE->set_heading($strinstallation);
8600          $PAGE->set_cacheable(false);
8601          return;
8602      }
8603  
8604      // Locate the current item on the navigation and make it active when found.
8605      $path = $extpage->path;
8606      $node = $PAGE->settingsnav;
8607      while ($node && count($path) > 0) {
8608          $node = $node->get(array_pop($path));
8609      }
8610      if ($node) {
8611          $node->make_active();
8612      }
8613  
8614      // Normal case.
8615      $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8616      if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8617          $USER->editing = $adminediting;
8618      }
8619  
8620      $visiblepathtosection = array_reverse($extpage->visiblepath);
8621  
8622      if ($PAGE->user_allowed_editing()) {
8623          if ($PAGE->user_is_editing()) {
8624              $caption = get_string('blockseditoff');
8625              $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8626          } else {
8627              $caption = get_string('blocksediton');
8628              $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8629          }
8630          $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8631      }
8632  
8633      $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
8634      $PAGE->set_heading($SITE->fullname);
8635  
8636      // prevent caching in nav block
8637      $PAGE->navigation->clear_cache();
8638  }
8639  
8640  /**
8641   * Returns the reference to admin tree root
8642   *
8643   * @return object admin_root object
8644   */
8645  function admin_get_root($reload=false, $requirefulltree=true) {
8646      global $CFG, $DB, $OUTPUT, $ADMIN;
8647  
8648      if (is_null($ADMIN)) {
8649      // create the admin tree!
8650          $ADMIN = new admin_root($requirefulltree);
8651      }
8652  
8653      if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8654          $ADMIN->purge_children($requirefulltree);
8655      }
8656  
8657      if (!$ADMIN->loaded) {
8658      // we process this file first to create categories first and in correct order
8659          require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8660  
8661          // now we process all other files in admin/settings to build the admin tree
8662          foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8663              if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8664                  continue;
8665              }
8666              if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8667              // plugins are loaded last - they may insert pages anywhere
8668                  continue;
8669              }
8670              require($file);
8671          }
8672          require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8673  
8674          $ADMIN->loaded = true;
8675      }
8676  
8677      return $ADMIN;
8678  }
8679  
8680  /// settings utility functions
8681  
8682  /**
8683   * This function applies default settings.
8684   * Because setting the defaults of some settings can enable other settings,
8685   * this function is called recursively until no more new settings are found.
8686   *
8687   * @param object $node, NULL means complete tree, null by default
8688   * @param bool $unconditional if true overrides all values with defaults, true by default
8689   * @param array $admindefaultsettings default admin settings to apply. Used recursively
8690   * @param array $settingsoutput The names and values of the changed settings. Used recursively
8691   * @return array $settingsoutput The names and values of the changed settings
8692   */
8693  function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
8694      $counter = 0;
8695  
8696      if (is_null($node)) {
8697          core_plugin_manager::reset_caches();
8698          $node = admin_get_root(true, true);
8699          $counter = count($settingsoutput);
8700      }
8701  
8702      if ($node instanceof admin_category) {
8703          $entries = array_keys($node->children);
8704          foreach ($entries as $entry) {
8705              $settingsoutput = admin_apply_default_settings(
8706                      $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
8707                      );
8708          }
8709  
8710      } else if ($node instanceof admin_settingpage) {
8711          foreach ($node->settings as $setting) {
8712              if (!$unconditional && !is_null($setting->get_setting())) {
8713                  // Do not override existing defaults.
8714                  continue;
8715              }
8716              $defaultsetting = $setting->get_defaultsetting();
8717              if (is_null($defaultsetting)) {
8718                  // No value yet - default maybe applied after admin user creation or in upgradesettings.
8719                  continue;
8720              }
8721  
8722              $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
8723  
8724              if (!array_key_exists($settingname, $admindefaultsettings)) {  // Only update a setting if not already processed.
8725                  $admindefaultsettings[$settingname] = $settingname;
8726                  $settingsoutput[$settingname] = $defaultsetting;
8727  
8728                  // Set the default for this setting.
8729                  $setting->write_setting($defaultsetting);
8730                  $setting->write_setting_flags(null);
8731              } else {
8732                  unset($admindefaultsettings[$settingname]); // Remove processed settings.
8733              }
8734          }
8735      }
8736  
8737      // Call this function recursively until all settings are processed.
8738      if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
8739          $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
8740      }
8741      // Just in case somebody modifies the list of active plugins directly.
8742      core_plugin_manager::reset_caches();
8743  
8744      return $settingsoutput;
8745  }
8746  
8747  /**
8748   * Store changed settings, this function updates the errors variable in $ADMIN
8749   *
8750   * @param object $formdata from form
8751   * @return int number of changed settings
8752   */
8753  function admin_write_settings($formdata) {
8754      global $CFG, $SITE, $DB;
8755  
8756      $olddbsessions = !empty($CFG->dbsessions);
8757      $formdata = (array)$formdata;
8758  
8759      $data = array();
8760      foreach ($formdata as $fullname=>$value) {
8761          if (strpos($fullname, 's_') !== 0) {
8762              continue; // not a config value
8763          }
8764          $data[$fullname] = $value;
8765      }
8766  
8767      $adminroot = admin_get_root();
8768      $settings = admin_find_write_settings($adminroot, $data);
8769  
8770      $count = 0;
8771      foreach ($settings as $fullname=>$setting) {
8772          /** @var $setting admin_setting */
8773          $original = $setting->get_setting();
8774          $error = $setting->write_setting($data[$fullname]);
8775          if ($error !== '') {
8776              $adminroot->errors[$fullname] = new stdClass();
8777              $adminroot->errors[$fullname]->data  = $data[$fullname];
8778              $adminroot->errors[$fullname]->id    = $setting->get_id();
8779              $adminroot->errors[$fullname]->error = $error;
8780          } else {
8781              $setting->write_setting_flags($data);
8782          }
8783          if ($setting->post_write_settings($original)) {
8784              $count++;
8785          }
8786      }
8787  
8788      if ($olddbsessions != !empty($CFG->dbsessions)) {
8789          require_logout();
8790      }
8791  
8792      // Now update $SITE - just update the fields, in case other people have a
8793      // a reference to it (e.g. $PAGE, $COURSE).
8794      $newsite = $DB->get_record('course', array('id'=>$SITE->id));
8795      foreach (get_object_vars($newsite) as $field => $value) {
8796          $SITE->$field = $value;
8797      }
8798  
8799      // now reload all settings - some of them might depend on the changed
8800      admin_get_root(true);
8801      return $count;
8802  }
8803  
8804  /**
8805   * Internal recursive function - finds all settings from submitted form
8806   *
8807   * @param object $node Instance of admin_category, or admin_settingpage
8808   * @param array $data
8809   * @return array
8810   */
8811  function admin_find_write_settings($node, $data) {
8812      $return = array();
8813  
8814      if (empty($data)) {
8815          return $return;
8816      }
8817  
8818      if ($node instanceof admin_category) {
8819          if ($node->check_access()) {
8820              $entries = array_keys($node->children);
8821              foreach ($entries as $entry) {
8822                  $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
8823              }
8824          }
8825  
8826      } else if ($node instanceof admin_settingpage) {
8827          if ($node->check_access()) {
8828              foreach ($node->settings as $setting) {
8829                  $fullname = $setting->get_full_name();
8830                  if (array_key_exists($fullname, $data)) {
8831                      $return[$fullname] = $setting;
8832                  }
8833              }
8834          }
8835  
8836      }
8837  
8838      return $return;
8839  }
8840  
8841  /**
8842   * Internal function - prints the search results
8843   *
8844   * @param string $query String to search for
8845   * @return string empty or XHTML
8846   */
8847  function admin_search_settings_html($query) {
8848      global $CFG, $OUTPUT, $PAGE;
8849  
8850      if (core_text::strlen($query) < 2) {
8851          return '';
8852      }
8853      $query = core_text::strtolower($query);
8854  
8855      $adminroot = admin_get_root();
8856      $findings = $adminroot->search($query);
8857      $savebutton = false;
8858  
8859      $tpldata = (object) [
8860          'actionurl' => $PAGE->url->out(false),
8861          'results' => [],
8862          'sesskey' => sesskey(),
8863      ];
8864  
8865      foreach ($findings as $found) {
8866          $page     = $found->page;
8867          $settings = $found->settings;
8868          if ($page->is_hidden()) {
8869          // hidden pages are not displayed in search results
8870              continue;
8871          }
8872  
8873          $heading = highlight($query, $page->visiblename);
8874          $headingurl = null;
8875          if ($page instanceof admin_externalpage) {
8876              $headingurl = new moodle_url($page->url);
8877          } else if ($page instanceof admin_settingpage) {
8878              $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
8879          } else {
8880              continue;
8881          }
8882  
8883          // Locate the page in the admin root and populate its visiblepath attribute.
8884          $path = array();
8885          $located = $adminroot->locate($page->name, true);
8886          if ($located) {
8887              foreach ($located->visiblepath as $pathitem) {
8888                  array_unshift($path, (string) $pathitem);
8889              }
8890          }
8891  
8892          $sectionsettings = [];
8893          if (!empty($settings)) {
8894              foreach ($settings as $setting) {
8895                  if (empty($setting->nosave)) {
8896                      $savebutton = true;
8897                  }
8898                  $fullname = $setting->get_full_name();
8899                  if (array_key_exists($fullname, $adminroot->errors)) {
8900                      $data = $adminroot->errors[$fullname]->data;
8901                  } else {
8902                      $data = $setting->get_setting();
8903                  // do not use defaults if settings not available - upgradesettings handles the defaults!
8904                  }
8905                  $sectionsettings[] = $setting->output_html($data, $query);
8906              }
8907          }
8908  
8909          $tpldata->results[] = (object) [
8910              'title' => $heading,
8911              'path' => $path,
8912              'url' => $headingurl->out(false),
8913              'settings' => $sectionsettings
8914          ];
8915      }
8916  
8917      $tpldata->showsave = $savebutton;
8918      $tpldata->hasresults = !empty($tpldata->results);
8919  
8920      return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
8921  }
8922  
8923  /**
8924   * Internal function - returns arrays of html pages with uninitialised settings
8925   *
8926   * @param object $node Instance of admin_category or admin_settingpage
8927   * @return array
8928   */
8929  function admin_output_new_settings_by_page($node) {
8930      global $OUTPUT;
8931      $return = array();
8932  
8933      if ($node instanceof admin_category) {
8934          $entries = array_keys($node->children);
8935          foreach ($entries as $entry) {
8936              $return += admin_output_new_settings_by_page($node->children[$entry]);
8937          }
8938  
8939      } else if ($node instanceof admin_settingpage) {
8940              $newsettings = array();
8941              foreach ($node->settings as $setting) {
8942                  if (is_null($setting->get_setting())) {
8943                      $newsettings[] = $setting;
8944                  }
8945              }
8946              if (count($newsettings) > 0) {
8947                  $adminroot = admin_get_root();
8948                  $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
8949                  $page .= '<fieldset class="adminsettings">'."\n";
8950                  foreach ($newsettings as $setting) {
8951                      $fullname = $setting->get_full_name();
8952                      if (array_key_exists($fullname, $adminroot->errors)) {
8953                          $data = $adminroot->errors[$fullname]->data;
8954                      } else {
8955                          $data = $setting->get_setting();
8956                          if (is_null($data)) {
8957                              $data = $setting->get_defaultsetting();
8958                          }
8959                      }
8960                      $page .= '<div class="clearer"><!-- --></div>'."\n";
8961                      $page .= $setting->output_html($data);
8962                  }
8963                  $page .= '</fieldset>';
8964                  $return[$node->name] = $page;
8965              }
8966          }
8967  
8968      return $return;
8969  }
8970  
8971  /**
8972   * Format admin settings
8973   *
8974   * @param object $setting
8975   * @param string $title label element
8976   * @param string $form form fragment, html code - not highlighted automatically
8977   * @param string $description
8978   * @param mixed $label link label to id, true by default or string being the label to connect it to
8979   * @param string $warning warning text
8980   * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
8981   * @param string $query search query to be highlighted
8982   * @return string XHTML
8983   */
8984  function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
8985      global $CFG, $OUTPUT;
8986  
8987      $context = (object) [
8988          'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
8989          'fullname' => $setting->get_full_name(),
8990      ];
8991  
8992      // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
8993      if ($label === true) {
8994          $context->labelfor = $setting->get_id();
8995      } else if ($label === false) {
8996          $context->labelfor = '';
8997      } else {
8998          $context->labelfor = $label;
8999      }
9000  
9001      $form .= $setting->output_setting_flags();
9002  
9003      $context->warning = $warning;
9004      $context->override = '';
9005      if (empty($setting->plugin)) {
9006          if (array_key_exists($setting->name, $CFG->config_php_settings)) {
9007              $context->override = get_string('configoverride', 'admin');
9008          }
9009      } else {
9010          if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
9011              $context->override = get_string('configoverride', 'admin');
9012          }
9013      }
9014  
9015      $defaults = array();
9016      if (!is_null($defaultinfo)) {
9017          if ($defaultinfo === '') {
9018              $defaultinfo = get_string('emptysettingvalue', 'admin');
9019          }
9020          $defaults[] = $defaultinfo;
9021      }
9022  
9023      $context->default = null;
9024      $setting->get_setting_flag_defaults($defaults);
9025      if (!empty($defaults)) {
9026          $defaultinfo = implode(', ', $defaults);
9027          $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
9028          $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
9029      }
9030  
9031  
9032      $context->error = '';
9033      $adminroot = admin_get_root();
9034      if (array_key_exists($context->fullname, $adminroot->errors)) {
9035          $context->error = $adminroot->errors[$context->fullname]->error;
9036      }
9037  
9038      if ($dependenton = $setting->get_dependent_on()) {
9039          $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
9040      }
9041  
9042      $context->id = 'admin-' . $setting->name;
9043      $context->title = highlightfast($query, $title);
9044      $context->name = highlightfast($query, $context->name);
9045      $context->description = highlight($query, markdown_to_html($description));
9046      $context->element = $form;
9047      $context->forceltr = $setting->get_force_ltr();
9048      $context->customcontrol = $setting->has_custom_form_control();
9049  
9050      return $OUTPUT->render_from_template('core_admin/setting', $context);
9051  }
9052  
9053  /**
9054   * Based on find_new_settings{@link ()}  in upgradesettings.php
9055   * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
9056   *
9057   * @param object $node Instance of admin_category, or admin_settingpage
9058   * @return boolean true if any settings haven't been initialised, false if they all have
9059   */
9060  function any_new_admin_settings($node) {
9061  
9062      if ($node instanceof admin_category) {
9063          $entries = array_keys($node->children);
9064          foreach ($entries as $entry) {
9065              if (any_new_admin_settings($node->children[$entry])) {
9066                  return true;
9067              }
9068          }
9069  
9070      } else if ($node instanceof admin_settingpage) {
9071              foreach ($node->settings as $setting) {
9072                  if ($setting->get_setting() === NULL) {
9073                      return true;
9074                  }
9075              }
9076          }
9077  
9078      return false;
9079  }
9080  
9081  /**
9082   * Given a table and optionally a column name should replaces be done?
9083   *
9084   * @param string $table name
9085   * @param string $column name
9086   * @return bool success or fail
9087   */
9088  function db_should_replace($table, $column = ''): bool {
9089  
9090      // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
9091      $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
9092          'events_queue', 'repository_instance_config', 'block_instances', 'files'];
9093  
9094      // Don't process these.
9095      if (in_array($table, $skiptables)) {
9096          return false;
9097      }
9098  
9099      // To be safe never replace inside a table that looks related to logging.
9100      if (preg_match('/(^|_)logs?($|_)/', $table)) {
9101          return false;
9102      }
9103  
9104      // Do column based exclusions.
9105      if (!empty($column)) {
9106          // Don't touch anything that looks like a hash.
9107          if (preg_match('/hash$/', $column)) {
9108              return false;
9109          }
9110      }
9111  
9112      return true;
9113  }
9114  
9115  /**
9116   * Moved from admin/replace.php so that we can use this in cron
9117   *
9118   * @param string $search string to look for
9119   * @param string $replace string to replace
9120   * @return bool success or fail
9121   */
9122  function db_replace($search, $replace) {
9123      global $DB, $CFG, $OUTPUT;
9124  
9125      // Turn off time limits, sometimes upgrades can be slow.
9126      core_php_time_limit::raise();
9127  
9128      if (!$tables = $DB->get_tables() ) {    // No tables yet at all.
9129          return false;
9130      }
9131      foreach ($tables as $table) {
9132  
9133          if (!db_should_replace($table)) {
9134              continue;
9135          }
9136  
9137          if ($columns = $DB->get_columns($table)) {
9138              $DB->set_debug(true);
9139              foreach ($columns as $column) {
9140                  if (!db_should_replace($table, $column->name)) {
9141                      continue;
9142                  }
9143                  $DB->replace_all_text($table, $column, $search, $replace);
9144              }
9145              $DB->set_debug(false);
9146          }
9147      }
9148  
9149      // delete modinfo caches
9150      rebuild_course_cache(0, true);
9151  
9152      // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9153      $blocks = core_component::get_plugin_list('block');
9154      foreach ($blocks as $blockname=>$fullblock) {
9155          if ($blockname === 'NEWBLOCK') {   // Someone has unzipped the template, ignore it
9156              continue;
9157          }
9158  
9159          if (!is_readable($fullblock.'/lib.php')) {
9160              continue;
9161          }
9162  
9163          $function = 'block_'.$blockname.'_global_db_replace';
9164          include_once($fullblock.'/lib.php');
9165          if (!function_exists($function)) {
9166              continue;
9167          }
9168  
9169          echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9170          $function($search, $replace);
9171          echo $OUTPUT->notification("...finished", 'notifysuccess');
9172      }
9173  
9174      // Trigger an event.
9175      $eventargs = [
9176          'context' => context_system::instance(),
9177          'other' => [
9178              'search' => $search,
9179              'replace' => $replace
9180          ]
9181      ];
9182      $event = \core\event\database_text_field_content_replaced::create($eventargs);
9183      $event->trigger();
9184  
9185      purge_all_caches();
9186  
9187      return true;
9188  }
9189  
9190  /**
9191   * Manage repository settings
9192   *
9193   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9194   */
9195  class admin_setting_managerepository extends admin_setting {
9196  /** @var string */
9197      private $baseurl;
9198  
9199      /**
9200       * calls parent::__construct with specific arguments
9201       */
9202      public function __construct() {
9203          global $CFG;
9204          parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9205          $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9206      }
9207  
9208      /**
9209       * Always returns true, does nothing
9210       *
9211       * @return true
9212       */
9213      public function get_setting() {
9214          return true;
9215      }
9216  
9217      /**
9218       * Always returns true does nothing
9219       *
9220       * @return true
9221       */
9222      public function get_defaultsetting() {
9223          return true;
9224      }
9225  
9226      /**
9227       * Always returns s_managerepository
9228       *
9229       * @return string Always return 's_managerepository'
9230       */
9231      public function get_full_name() {
9232          return 's_managerepository';
9233      }
9234  
9235      /**
9236       * Always returns '' doesn't do anything
9237       */
9238      public function write_setting($data) {
9239          $url = $this->baseurl . '&amp;new=' . $data;
9240          return '';
9241      // TODO
9242      // Should not use redirect and exit here
9243      // Find a better way to do this.
9244      // redirect($url);
9245      // exit;
9246      }
9247  
9248      /**
9249       * Searches repository plugins for one that matches $query
9250       *
9251       * @param string $query The string to search for
9252       * @return bool true if found, false if not
9253       */
9254      public function is_related($query) {
9255          if (parent::is_related($query)) {
9256              return true;
9257          }
9258  
9259          $repositories= core_component::get_plugin_list('repository');
9260          foreach ($repositories as $p => $dir) {
9261              if (strpos($p, $query) !== false) {
9262                  return true;
9263              }
9264          }
9265          foreach (repository::get_types() as $instance) {
9266              $title = $instance->get_typename();
9267              if (strpos(core_text::strtolower($title), $query) !== false) {
9268                  return true;
9269              }
9270          }
9271          return false;
9272      }
9273  
9274      /**
9275       * Helper function that generates a moodle_url object
9276       * relevant to the repository
9277       */
9278  
9279      function repository_action_url($repository) {
9280          return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9281      }
9282  
9283      /**
9284       * Builds XHTML to display the control
9285       *
9286       * @param string $data Unused
9287       * @param string $query
9288       * @return string XHTML
9289       */
9290      public function output_html($data, $query='') {
9291          global $CFG, $USER, $OUTPUT;
9292  
9293          // Get strings that are used
9294          $strshow = get_string('on', 'repository');
9295          $strhide = get_string('off', 'repository');
9296          $strdelete = get_string('disabled', 'repository');
9297  
9298          $actionchoicesforexisting = array(
9299              'show' => $strshow,
9300              'hide' => $strhide,
9301              'delete' => $strdelete
9302          );
9303  
9304          $actionchoicesfornew = array(
9305              'newon' => $strshow,
9306              'newoff' => $strhide,
9307              'delete' => $strdelete
9308          );
9309  
9310          $return = '';
9311          $return .= $OUTPUT->box_start('generalbox');
9312  
9313          // Set strings that are used multiple times
9314          $settingsstr = get_string('settings');
9315          $disablestr = get_string('disable');
9316  
9317          // Table to list plug-ins
9318          $table = new html_table();
9319          $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9320          $table->align = array('left', 'center', 'center', 'center', 'center');
9321          $table->data = array();
9322  
9323          // Get list of used plug-ins
9324          $repositorytypes = repository::get_types();
9325          if (!empty($repositorytypes)) {
9326              // Array to store plugins being used
9327              $alreadyplugins = array();
9328              $totalrepositorytypes = count($repositorytypes);
9329              $updowncount = 1;
9330              foreach ($repositorytypes as $i) {
9331                  $settings = '';
9332                  $typename = $i->get_typename();
9333                  // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9334                  $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9335                  $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9336  
9337                  if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9338                      // Calculate number of instances in order to display them for the Moodle administrator
9339                      if (!empty($instanceoptionnames)) {
9340                          $params = array();
9341                          $params['context'] = array(context_system::instance());
9342                          $params['onlyvisible'] = false;
9343                          $params['type'] = $typename;
9344                          $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9345                          // site instances
9346                          $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9347                          $params['context'] = array();
9348                          $instances = repository::static_function($typename, 'get_instances', $params);
9349                          $courseinstances = array();
9350                          $userinstances = array();
9351  
9352                          foreach ($instances as $instance) {
9353                              $repocontext = context::instance_by_id($instance->instance->contextid);
9354                              if ($repocontext->contextlevel == CONTEXT_COURSE) {
9355                                  $courseinstances[] = $instance;
9356                              } else if ($repocontext->contextlevel == CONTEXT_USER) {
9357                                  $userinstances[] = $instance;
9358                              }
9359                          }
9360                          // course instances
9361                          $instancenumber = count($courseinstances);
9362                          $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9363  
9364                          // user private instances
9365                          $instancenumber =  count($userinstances);
9366                          $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9367                      } else {
9368                          $admininstancenumbertext = "";
9369                          $courseinstancenumbertext = "";
9370                          $userinstancenumbertext = "";
9371                      }
9372  
9373                      $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9374  
9375                      $settings .= $OUTPUT->container_start('mdl-left');
9376                      $settings .= '<br/>';
9377                      $settings .= $admininstancenumbertext;
9378                      $settings .= '<br/>';
9379                      $settings .= $courseinstancenumbertext;
9380                      $settings .= '<br/>';
9381                      $settings .= $userinstancenumbertext;
9382                      $settings .= $OUTPUT->container_end();
9383                  }
9384                  // Get the current visibility
9385                  if ($i->get_visible()) {
9386                      $currentaction = 'show';
9387                  } else {
9388                      $currentaction = 'hide';
9389                  }
9390  
9391                  $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9392  
9393                  // Display up/down link
9394                  $updown = '';
9395                  // Should be done with CSS instead.
9396                  $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9397  
9398                  if ($updowncount > 1) {
9399                      $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9400                      $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9401                  }
9402                  else {
9403                      $updown .= $spacer;
9404                  }
9405                  if ($updowncount < $totalrepositorytypes) {
9406                      $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9407                      $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9408                  }
9409                  else {
9410                      $updown .= $spacer;
9411                  }
9412  
9413                  $updowncount++;
9414  
9415                  $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9416  
9417                  if (!in_array($typename, $alreadyplugins)) {
9418                      $alreadyplugins[] = $typename;
9419                  }
9420              }
9421          }
9422  
9423          // Get all the plugins that exist on disk
9424          $plugins = core_component::get_plugin_list('repository');
9425          if (!empty($plugins)) {
9426              foreach ($plugins as $plugin => $dir) {
9427                  // Check that it has not already been listed
9428                  if (!in_array($plugin, $alreadyplugins)) {
9429                      $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9430                      $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9431                  }
9432              }
9433          }
9434  
9435          $return .= html_writer::table($table);
9436          $return .= $OUTPUT->box_end();
9437          return highlight($query, $return);
9438      }
9439  }
9440  
9441  /**
9442   * Special checkbox for enable mobile web service
9443   * If enable then we store the service id of the mobile service into config table
9444   * If disable then we unstore the service id from the config table
9445   */
9446  class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9447  
9448      /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9449      private $restuse;
9450  
9451      /**
9452       * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9453       *
9454       * @return boolean
9455       */
9456      private function is_protocol_cap_allowed() {
9457          global $DB, $CFG;
9458  
9459          // If the $this->restuse variable is not set, it needs to be set.
9460          if (empty($this->restuse) and $this->restuse!==false) {
9461              $params = array();
9462              $params['permission'] = CAP_ALLOW;
9463              $params['roleid'] = $CFG->defaultuserroleid;
9464              $params['capability'] = 'webservice/rest:use';
9465              $this->restuse = $DB->record_exists('role_capabilities', $params);
9466          }
9467  
9468          return $this->restuse;
9469      }
9470  
9471      /**
9472       * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9473       * @param type $status true to allow, false to not set
9474       */
9475      private function set_protocol_cap($status) {
9476          global $CFG;
9477          if ($status and !$this->is_protocol_cap_allowed()) {
9478              //need to allow the cap
9479              $permission = CAP_ALLOW;
9480              $assign = true;
9481          } else if (!$status and $this->is_protocol_cap_allowed()){
9482              //need to disallow the cap
9483              $permission = CAP_INHERIT;
9484              $assign = true;
9485          }
9486          if (!empty($assign)) {
9487              $systemcontext = context_system::instance();
9488              assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9489          }
9490      }
9491  
9492      /**
9493       * Builds XHTML to display the control.
9494       * The main purpose of this overloading is to display a warning when https
9495       * is not supported by the server
9496       * @param string $data Unused
9497       * @param string $query
9498       * @return string XHTML
9499       */
9500      public function output_html($data, $query='') {
9501          global $OUTPUT;
9502          $html = parent::output_html($data, $query);
9503  
9504          if ((string)$data === $this->yes) {
9505              $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9506              foreach ($notifications as $notification) {
9507                  $message = get_string($notification[0], $notification[1]);
9508                  $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9509              }
9510          }
9511  
9512          return $html;
9513      }
9514  
9515      /**
9516       * Retrieves the current setting using the objects name
9517       *
9518       * @return string
9519       */
9520      public function get_setting() {
9521          global $CFG;
9522  
9523          // First check if is not set.
9524          $result = $this->config_read($this->name);
9525          if (is_null($result)) {
9526              return null;
9527          }
9528  
9529          // For install cli script, $CFG->defaultuserroleid is not set so return 0
9530          // Or if web services aren't enabled this can't be,
9531          if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9532              return 0;
9533          }
9534  
9535          require_once($CFG->dirroot . '/webservice/lib.php');
9536          $webservicemanager = new webservice();
9537          $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9538          if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9539              return $result;
9540          } else {
9541              return 0;
9542          }
9543      }
9544  
9545      /**
9546       * Save the selected setting
9547       *
9548       * @param string $data The selected site
9549       * @return string empty string or error message
9550       */
9551      public function write_setting($data) {
9552          global $DB, $CFG;
9553  
9554          //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9555          if (empty($CFG->defaultuserroleid)) {
9556              return '';
9557          }
9558  
9559          $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9560  
9561          require_once($CFG->dirroot . '/webservice/lib.php');
9562          $webservicemanager = new webservice();
9563  
9564          $updateprotocol = false;
9565          if ((string)$data === $this->yes) {
9566               //code run when enable mobile web service
9567               //enable web service systeme if necessary
9568               set_config('enablewebservices', true);
9569  
9570               //enable mobile service
9571               $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9572               $mobileservice->enabled = 1;
9573               $webservicemanager->update_external_service($mobileservice);
9574  
9575               // Enable REST server.
9576               $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9577  
9578               if (!in_array('rest', $activeprotocols)) {
9579                   $activeprotocols[] = 'rest';
9580                   $updateprotocol = true;
9581               }
9582  
9583               if ($updateprotocol) {
9584                   set_config('webserviceprotocols', implode(',', $activeprotocols));
9585               }
9586  
9587               // Allow rest:use capability for authenticated user.
9588               $this->set_protocol_cap(true);
9589  
9590           } else {
9591               //disable web service system if no other services are enabled
9592               $otherenabledservices = $DB->get_records_select('external_services',
9593                       'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
9594                           'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
9595               if (empty($otherenabledservices)) {
9596                   set_config('enablewebservices', false);
9597  
9598                   // Also disable REST server.
9599                   $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9600  
9601                   $protocolkey = array_search('rest', $activeprotocols);
9602                   if ($protocolkey !== false) {
9603                      unset($activeprotocols[$protocolkey]);
9604                      $updateprotocol = true;
9605                   }
9606  
9607                   if ($updateprotocol) {
9608                      set_config('webserviceprotocols', implode(',', $activeprotocols));
9609                   }
9610  
9611                   // Disallow rest:use capability for authenticated user.
9612                   $this->set_protocol_cap(false);
9613               }
9614  
9615               //disable the mobile service
9616               $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9617               $mobileservice->enabled = 0;
9618               $webservicemanager->update_external_service($mobileservice);
9619           }
9620  
9621          return (parent::write_setting($data));
9622      }
9623  }
9624  
9625  /**
9626   * Special class for management of external services
9627   *
9628   * @author Petr Skoda (skodak)
9629   */
9630  class admin_setting_manageexternalservices extends admin_setting {
9631      /**
9632       * Calls parent::__construct with specific arguments
9633       */
9634      public function __construct() {
9635          $this->nosave = true;
9636          parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9637      }
9638  
9639      /**
9640       * Always returns true, does nothing
9641       *
9642       * @return true
9643       */
9644      public function get_setting() {
9645          return true;
9646      }
9647  
9648      /**
9649       * Always returns true, does nothing
9650       *
9651       * @return true
9652       */
9653      public function get_defaultsetting() {
9654          return true;
9655      }
9656  
9657      /**
9658       * Always returns '', does not write anything
9659       *
9660       * @return string Always returns ''
9661       */
9662      public function write_setting($data) {
9663      // do not write any setting
9664          return '';
9665      }
9666  
9667      /**
9668       * Checks if $query is one of the available external services
9669       *
9670       * @param string $query The string to search for
9671       * @return bool Returns true if found, false if not
9672       */
9673      public function is_related($query) {
9674          global $DB;
9675  
9676          if (parent::is_related($query)) {
9677              return true;
9678          }
9679  
9680          $services = $DB->get_records('external_services', array(), 'id, name');
9681          foreach ($services as $service) {
9682              if (strpos(core_text::strtolower($service->name), $query) !== false) {
9683                  return true;
9684              }
9685          }
9686          return false;
9687      }
9688  
9689      /**
9690       * Builds the XHTML to display the control
9691       *
9692       * @param string $data Unused
9693       * @param string $query
9694       * @return string
9695       */
9696      public function output_html($data, $query='') {
9697          global $CFG, $OUTPUT, $DB;
9698  
9699          // display strings
9700          $stradministration = get_string('administration');
9701          $stredit = get_string('edit');
9702          $strservice = get_string('externalservice', 'webservice');
9703          $strdelete = get_string('delete');
9704          $strplugin = get_string('plugin', 'admin');
9705          $stradd = get_string('add');
9706          $strfunctions = get_string('functions', 'webservice');
9707          $strusers = get_string('users');
9708          $strserviceusers = get_string('serviceusers', 'webservice');
9709  
9710          $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9711          $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9712          $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9713  
9714          // built in services
9715           $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9716           $return = "";
9717           if (!empty($services)) {
9718              $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9719  
9720  
9721  
9722              $table = new html_table();
9723              $table->head  = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9724              $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9725              $table->id = 'builtinservices';
9726              $table->attributes['class'] = 'admintable externalservices generaltable';
9727              $table->data  = array();
9728  
9729              // iterate through auth plugins and add to the display table
9730              foreach ($services as $service) {
9731                  $name = $service->name;
9732  
9733                  // hide/show link
9734                  if ($service->enabled) {
9735                      $displayname = "<span>$name</span>";
9736                  } else {
9737                      $displayname = "<span class=\"dimmed_text\">$name</span>";
9738                  }
9739  
9740                  $plugin = $service->component;
9741  
9742                  $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9743  
9744                  if ($service->restrictedusers) {
9745                      $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9746                  } else {
9747                      $users = get_string('allusers', 'webservice');
9748                  }
9749  
9750                  $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9751  
9752                  // add a row to the table
9753                  $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9754              }
9755              $return .= html_writer::table($table);
9756          }
9757  
9758          // Custom services
9759          $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9760          $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9761  
9762          $table = new html_table();
9763          $table->head  = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9764          $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9765          $table->id = 'customservices';
9766          $table->attributes['class'] = 'admintable externalservices generaltable';
9767          $table->data  = array();
9768  
9769          // iterate through auth plugins and add to the display table
9770          foreach ($services as $service) {
9771              $name = $service->name;
9772  
9773              // hide/show link
9774              if ($service->enabled) {
9775                  $displayname = "<span>$name</span>";
9776              } else {
9777                  $displayname = "<span class=\"dimmed_text\">$name</span>";
9778              }
9779  
9780              // delete link
9781              $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9782  
9783              $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9784  
9785              if ($service->restrictedusers) {
9786                  $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9787              } else {
9788                  $users = get_string('allusers', 'webservice');
9789              }
9790  
9791              $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9792  
9793              // add a row to the table
9794              $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9795          }
9796          // add new custom service option
9797          $return .= html_writer::table($table);
9798  
9799          $return .= '<br />';
9800          // add a token to the table
9801          $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9802  
9803          return highlight($query, $return);
9804      }
9805  }
9806  
9807  /**
9808   * Special class for overview of external services
9809   *
9810   * @author Jerome Mouneyrac
9811   */
9812  class admin_setting_webservicesoverview extends admin_setting {
9813  
9814      /**
9815       * Calls parent::__construct with specific arguments
9816       */
9817      public function __construct() {
9818          $this->nosave = true;
9819          parent::__construct('webservicesoverviewui',
9820                          get_string('webservicesoverview', 'webservice'), '', '');
9821      }
9822  
9823      /**
9824       * Always returns true, does nothing
9825       *
9826       * @return true
9827       */
9828      public function get_setting() {
9829          return true;
9830      }
9831  
9832      /**
9833       * Always returns true, does nothing
9834       *
9835       * @return true
9836       */
9837      public function get_defaultsetting() {
9838          return true;
9839      }
9840  
9841      /**
9842       * Always returns '', does not write anything
9843       *
9844       * @return string Always returns ''
9845       */
9846      public function write_setting($data) {
9847          // do not write any setting
9848          return '';
9849      }
9850  
9851      /**
9852       * Builds the XHTML to display the control
9853       *
9854       * @param string $data Unused
9855       * @param string $query
9856       * @return string
9857       */
9858      public function output_html($data, $query='') {
9859          global $CFG, $OUTPUT;
9860  
9861          $return = "";
9862          $brtag = html_writer::empty_tag('br');
9863  
9864          /// One system controlling Moodle with Token
9865          $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
9866          $table = new html_table();
9867          $table->head = array(get_string('step', 'webservice'), get_string('status'),
9868              get_string('description'));
9869          $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9870          $table->id = 'onesystemcontrol';
9871          $table->attributes['class'] = 'admintable wsoverview generaltable';
9872          $table->data = array();
9873  
9874          $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
9875                  . $brtag . $brtag;
9876  
9877          /// 1. Enable Web Services
9878          $row = array();
9879          $url = new moodle_url("/admin/search.php?query=enablewebservices");
9880          $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
9881                          array('href' => $url));
9882          $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
9883          if ($CFG->enablewebservices) {
9884              $status = get_string('yes');
9885          }
9886          $row[1] = $status;
9887          $row[2] = get_string('enablewsdescription', 'webservice');
9888          $table->data[] = $row;
9889  
9890          /// 2. Enable protocols
9891          $row = array();
9892          $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
9893          $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
9894                          array('href' => $url));
9895          $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
9896          //retrieve activated protocol
9897          $active_protocols = empty($CFG->webserviceprotocols) ?
9898                  array() : explode(',', $CFG->webserviceprotocols);
9899          if (!empty($active_protocols)) {
9900              $status = "";
9901              foreach ($active_protocols as $protocol) {
9902                  $status .= $protocol . $brtag;
9903              }
9904          }
9905          $row[1] = $status;
9906          $row[2] = get_string('enableprotocolsdescription', 'webservice');
9907          $table->data[] = $row;
9908  
9909          /// 3. Create user account
9910          $row = array();
9911          $url = new moodle_url("/user/editadvanced.php?id=-1");
9912          $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
9913                          array('href' => $url));
9914          $row[1] = "";
9915          $row[2] = get_string('createuserdescription', 'webservice');
9916          $table->data[] = $row;
9917  
9918          /// 4. Add capability to users
9919          $row = array();
9920          $url = new moodle_url("/admin/roles/check.php?contextid=1");
9921          $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
9922                          array('href' => $url));
9923          $row[1] = "";
9924          $row[2] = get_string('checkusercapabilitydescription', 'webservice');
9925          $table->data[] = $row;
9926  
9927          /// 5. Select a web service
9928          $row = array();
9929          $url = new moodle_url("/admin/settings.php?section=externalservices");
9930          $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
9931                          array('href' => $url));
9932          $row[1] = "";
9933          $row[2] = get_string('createservicedescription', 'webservice');
9934          $table->data[] = $row;
9935  
9936          /// 6. Add functions
9937          $row = array();
9938          $url = new moodle_url("/admin/settings.php?section=externalservices");
9939          $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
9940                          array('href' => $url));
9941          $row[1] = "";
9942          $row[2] = get_string('addfunctionsdescription', 'webservice');
9943          $table->data[] = $row;
9944  
9945          /// 7. Add the specific user
9946          $row = array();
9947          $url = new moodle_url("/admin/settings.php?section=externalservices");
9948          $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
9949                          array('href' => $url));
9950          $row[1] = "";
9951          $row[2] = get_string('selectspecificuserdescription', 'webservice');
9952          $table->data[] = $row;
9953  
9954          /// 8. Create token for the specific user
9955          $row = array();
9956          $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
9957          $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
9958                          array('href' => $url));
9959          $row[1] = "";
9960          $row[2] = get_string('createtokenforuserdescription', 'webservice');
9961          $table->data[] = $row;
9962  
9963          /// 9. Enable the documentation
9964          $row = array();
9965          $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
9966          $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
9967                          array('href' => $url));
9968          $status = '<span class="warning">' . get_string('no') . '</span>';
9969          if ($CFG->enablewsdocumentation) {
9970              $status = get_string('yes');
9971          }
9972          $row[1] = $status;
9973          $row[2] = get_string('enabledocumentationdescription', 'webservice');
9974          $table->data[] = $row;
9975  
9976          /// 10. Test the service
9977          $row = array();
9978          $url = new moodle_url("/admin/webservice/testclient.php");
9979          $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
9980                          array('href' => $url));
9981          $row[1] = "";
9982          $row[2] = get_string('testwithtestclientdescription', 'webservice');
9983          $table->data[] = $row;
9984  
9985          $return .= html_writer::table($table);
9986  
9987          /// Users as clients with token
9988          $return .= $brtag . $brtag . $brtag;
9989          $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
9990          $table = new html_table();
9991          $table->head = array(get_string('step', 'webservice'), get_string('status'),
9992              get_string('description'));
9993          $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9994          $table->id = 'userasclients';
9995          $table->attributes['class'] = 'admintable wsoverview generaltable';
9996          $table->data = array();
9997  
9998          $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
9999                  $brtag . $brtag;
10000  
10001          /// 1. Enable Web Services
10002          $row = array();
10003          $url = new moodle_url("/admin/search.php?query=enablewebservices");
10004          $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10005                          array('href' => $url));
10006          $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10007          if ($CFG->enablewebservices) {
10008              $status = get_string('yes');
10009          }
10010          $row[1] = $status;
10011          $row[2] = get_string('enablewsdescription', 'webservice');
10012          $table->data[] = $row;
10013  
10014          /// 2. Enable protocols
10015          $row = array();
10016          $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10017          $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10018                          array('href' => $url));
10019          $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
10020          //retrieve activated protocol
10021          $active_protocols = empty($CFG->webserviceprotocols) ?
10022                  array() : explode(',', $CFG->webserviceprotocols);
10023          if (!empty($active_protocols)) {
10024              $status = "";
10025              foreach ($active_protocols as $protocol) {
10026                  $status .= $protocol . $brtag;
10027              }
10028          }
10029          $row[1] = $status;
10030          $row[2] = get_string('enableprotocolsdescription', 'webservice');
10031          $table->data[] = $row;
10032  
10033  
10034          /// 3. Select a web service
10035          $row = array();
10036          $url = new moodle_url("/admin/settings.php?section=externalservices");
10037          $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10038                          array('href' => $url));
10039          $row[1] = "";
10040          $row[2] = get_string('createserviceforusersdescription', 'webservice');
10041          $table->data[] = $row;
10042  
10043          /// 4. Add functions
10044          $row = array();
10045          $url = new moodle_url("/admin/settings.php?section=externalservices");
10046          $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10047                          array('href' => $url));
10048          $row[1] = "";
10049          $row[2] = get_string('addfunctionsdescription', 'webservice');
10050          $table->data[] = $row;
10051  
10052          /// 5. Add capability to users
10053          $row = array();
10054          $url = new moodle_url("/admin/roles/check.php?contextid=1");
10055          $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
10056                          array('href' => $url));
10057          $row[1] = "";
10058          $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
10059          $table->data[] = $row;
10060  
10061          /// 6. Test the service
10062          $row = array();
10063          $url = new moodle_url("/admin/webservice/testclient.php");
10064          $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10065                          array('href' => $url));
10066          $row[1] = "";
10067          $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
10068          $table->data[] = $row;
10069  
10070          $return .= html_writer::table($table);
10071  
10072          return highlight($query, $return);
10073      }
10074  
10075  }
10076  
10077  
10078  /**
10079   * Special class for web service protocol administration.
10080   *
10081   * @author Petr Skoda (skodak)
10082   */
10083  class admin_setting_managewebserviceprotocols extends admin_setting {
10084  
10085      /**
10086       * Calls parent::__construct with specific arguments
10087       */
10088      public function __construct() {
10089          $this->nosave = true;
10090          parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
10091      }
10092  
10093      /**
10094       * Always returns true, does nothing
10095       *
10096       * @return true
10097       */
10098      public function get_setting() {
10099          return true;
10100      }
10101  
10102      /**
10103       * Always returns true, does nothing
10104       *
10105       * @return true
10106       */
10107      public function get_defaultsetting() {
10108          return true;
10109      }
10110  
10111      /**
10112       * Always returns '', does not write anything
10113       *
10114       * @return string Always returns ''
10115       */
10116      public function write_setting($data) {
10117      // do not write any setting
10118          return '';
10119      }
10120  
10121      /**
10122       * Checks if $query is one of the available webservices
10123       *
10124       * @param string $query The string to search for
10125       * @return bool Returns true if found, false if not
10126       */
10127      public function is_related($query) {
10128          if (parent::is_related($query)) {
10129              return true;
10130          }
10131  
10132          $protocols = core_component::get_plugin_list('webservice');
10133          foreach ($protocols as $protocol=>$location) {
10134              if (strpos($protocol, $query) !== false) {
10135                  return true;
10136              }
10137              $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
10138              if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
10139                  return true;
10140              }
10141          }
10142          return false;
10143      }
10144  
10145      /**
10146       * Builds the XHTML to display the control
10147       *
10148       * @param string $data Unused
10149       * @param string $query
10150       * @return string
10151       */
10152      public function output_html($data, $query='') {
10153          global $CFG, $OUTPUT;
10154  
10155          // display strings
10156          $stradministration = get_string('administration');
10157          $strsettings = get_string('settings');
10158          $stredit = get_string('edit');
10159          $strprotocol = get_string('protocol', 'webservice');
10160          $strenable = get_string('enable');
10161          $strdisable = get_string('disable');
10162          $strversion = get_string('version');
10163  
10164          $protocols_available = core_component::get_plugin_list('webservice');
10165          $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10166          ksort($protocols_available);
10167  
10168          foreach ($active_protocols as $key=>$protocol) {
10169              if (empty($protocols_available[$protocol])) {
10170                  unset($active_protocols[$key]);
10171              }
10172          }
10173  
10174          $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10175          $return .= $OUTPUT->box_start('generalbox webservicesui');
10176  
10177          $table = new html_table();
10178          $table->head  = array($strprotocol, $strversion, $strenable, $strsettings);
10179          $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10180          $table->id = 'webserviceprotocols';
10181          $table->attributes['class'] = 'admintable generaltable';
10182          $table->data  = array();
10183  
10184          // iterate through auth plugins and add to the display table
10185          $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10186          foreach ($protocols_available as $protocol => $location) {
10187              $name = get_string('pluginname', 'webservice_'.$protocol);
10188  
10189              $plugin = new stdClass();
10190              if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10191                  include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10192              }
10193              $version = isset($plugin->version) ? $plugin->version : '';
10194  
10195              // hide/show link
10196              if (in_array($protocol, $active_protocols)) {
10197                  $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10198                  $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10199                  $displayname = "<span>$name</span>";
10200              } else {
10201                  $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10202                  $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10203                  $displayname = "<span class=\"dimmed_text\">$name</span>";
10204              }
10205  
10206              // settings link
10207              if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10208                  $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10209              } else {
10210                  $settings = '';
10211              }
10212  
10213              // add a row to the table
10214              $table->data[] = array($displayname, $version, $hideshow, $settings);
10215          }
10216          $return .= html_writer::table($table);
10217          $return .= get_string('configwebserviceplugins', 'webservice');
10218          $return .= $OUTPUT->box_end();
10219  
10220          return highlight($query, $return);
10221      }
10222  }
10223  
10224  
10225  /**
10226   * Special class for web service token administration.
10227   *
10228   * @author Jerome Mouneyrac
10229   */
10230  class admin_setting_managewebservicetokens extends admin_setting {
10231  
10232      /**
10233       * Calls parent::__construct with specific arguments
10234       */
10235      public function __construct() {
10236          $this->nosave = true;
10237          parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
10238      }
10239  
10240      /**
10241       * Always returns true, does nothing
10242       *
10243       * @return true
10244       */
10245      public function get_setting() {
10246          return true;
10247      }
10248  
10249      /**
10250       * Always returns true, does nothing
10251       *
10252       * @return true
10253       */
10254      public function get_defaultsetting() {
10255          return true;
10256      }
10257  
10258      /**
10259       * Always returns '', does not write anything
10260       *
10261       * @return string Always returns ''
10262       */
10263      public function write_setting($data) {
10264      // do not write any setting
10265          return '';
10266      }
10267  
10268      /**
10269       * Builds the XHTML to display the control
10270       *
10271       * @param string $data Unused
10272       * @param string $query
10273       * @return string
10274       */
10275      public function output_html($data, $query='') {
10276          global $CFG, $OUTPUT;
10277  
10278          require_once($CFG->dirroot . '/webservice/classes/token_table.php');
10279          $baseurl = new moodle_url('/' . $CFG->admin . '/settings.php?section=webservicetokens');
10280  
10281          $return = $OUTPUT->box_start('generalbox webservicestokenui');
10282  
10283          if (has_capability('moodle/webservice:managealltokens', context_system::instance())) {
10284              $return .= \html_writer::div(get_string('onlyseecreatedtokens', 'webservice'));
10285          }
10286  
10287          $table = new \webservice\token_table('webservicetokens');
10288          $table->define_baseurl($baseurl);
10289          $table->attributes['class'] = 'admintable generaltable'; // Any need changing?
10290          $table->data  = array();
10291          ob_start();
10292          $table->out(10, false);
10293          $tablehtml = ob_get_contents();
10294          ob_end_clean();
10295          $return .= $tablehtml;
10296  
10297          $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
10298  
10299          $return .= $OUTPUT->box_end();
10300          // add a token to the table
10301          $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
10302          $return .= get_string('add')."</a>";
10303  
10304          return highlight($query, $return);
10305      }
10306  }
10307  
10308  
10309  /**
10310   * Colour picker
10311   *
10312   * @copyright 2010 Sam Hemelryk
10313   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10314   */
10315  class admin_setting_configcolourpicker extends admin_setting {
10316  
10317      /**
10318       * Information for previewing the colour
10319       *
10320       * @var array|null
10321       */
10322      protected $previewconfig = null;
10323  
10324      /**
10325       * Use default when empty.
10326       */
10327      protected $usedefaultwhenempty = true;
10328  
10329      /**
10330       *
10331       * @param string $name
10332       * @param string $visiblename
10333       * @param string $description
10334       * @param string $defaultsetting
10335       * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10336       */
10337      public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
10338              $usedefaultwhenempty = true) {
10339          $this->previewconfig = $previewconfig;
10340          $this->usedefaultwhenempty = $usedefaultwhenempty;
10341          parent::__construct($name, $visiblename, $description, $defaultsetting);
10342          $this->set_force_ltr(true);
10343      }
10344  
10345      /**
10346       * Return the setting
10347       *
10348       * @return mixed returns config if successful else null
10349       */
10350      public function get_setting() {
10351          return $this->config_read($this->name);
10352      }
10353  
10354      /**
10355       * Saves the setting
10356       *
10357       * @param string $data
10358       * @return bool
10359       */
10360      public function write_setting($data) {
10361          $data = $this->validate($data);
10362          if ($data === false) {
10363              return  get_string('validateerror', 'admin');
10364          }
10365          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10366      }
10367  
10368      /**
10369       * Validates the colour that was entered by the user
10370       *
10371       * @param string $data
10372       * @return string|false
10373       */
10374      protected function validate($data) {
10375          /**
10376           * List of valid HTML colour names
10377           *
10378           * @var array
10379           */
10380           $colornames = array(
10381              'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10382              'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10383              'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10384              'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10385              'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10386              'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10387              'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10388              'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10389              'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10390              'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10391              'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10392              'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10393              'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10394              'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10395              'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10396              'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10397              'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10398              'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10399              'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10400              'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10401              'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10402              'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10403              'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10404              'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10405              'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10406              'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10407              'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10408              'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10409              'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10410              'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10411              'whitesmoke', 'yellow', 'yellowgreen'
10412          );
10413  
10414          if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10415              if (strpos($data, '#')!==0) {
10416                  $data = '#'.$data;
10417              }
10418              return $data;
10419          } else if (in_array(strtolower($data), $colornames)) {
10420              return $data;
10421          } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10422              return $data;
10423          } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10424              return $data;
10425          } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10426              return $data;
10427          } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10428              return $data;
10429          } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10430              return $data;
10431          } else if (empty($data)) {
10432              if ($this->usedefaultwhenempty){
10433                  return $this->defaultsetting;
10434              } else {
10435                  return '';
10436              }
10437          } else {
10438              return false;
10439          }
10440      }
10441  
10442      /**
10443       * Generates the HTML for the setting
10444       *
10445       * @global moodle_page $PAGE
10446       * @global core_renderer $OUTPUT
10447       * @param string $data
10448       * @param string $query
10449       */
10450      public function output_html($data, $query = '') {
10451          global $PAGE, $OUTPUT;
10452  
10453          $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10454          $context = (object) [
10455              'id' => $this->get_id(),
10456              'name' => $this->get_full_name(),
10457              'value' => $data,
10458              'icon' => $icon->export_for_template($OUTPUT),
10459              'haspreviewconfig' => !empty($this->previewconfig),
10460              'forceltr' => $this->get_force_ltr(),
10461              'readonly' => $this->is_readonly(),
10462          ];
10463  
10464          $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10465          $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10466  
10467          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10468              $this->get_defaultsetting(), $query);
10469      }
10470  
10471  }
10472  
10473  
10474  /**
10475   * Class used for uploading of one file into file storage,
10476   * the file name is stored in config table.
10477   *
10478   * Please note you need to implement your own '_pluginfile' callback function,
10479   * this setting only stores the file, it does not deal with file serving.
10480   *
10481   * @copyright 2013 Petr Skoda {@link http://skodak.org}
10482   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10483   */
10484  class admin_setting_configstoredfile extends admin_setting {
10485      /** @var array file area options - should be one file only */
10486      protected $options;
10487      /** @var string name of the file area */
10488      protected $filearea;
10489      /** @var int intemid */
10490      protected $itemid;
10491      /** @var string used for detection of changes */
10492      protected $oldhashes;
10493  
10494      /**
10495       * Create new stored file setting.
10496       *
10497       * @param string $name low level setting name
10498       * @param string $visiblename human readable setting name
10499       * @param string $description description of setting
10500       * @param mixed $filearea file area for file storage
10501       * @param int $itemid itemid for file storage
10502       * @param array $options file area options
10503       */
10504      public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
10505          parent::__construct($name, $visiblename, $description, '');
10506          $this->filearea = $filearea;
10507          $this->itemid   = $itemid;
10508          $this->options  = (array)$options;
10509          $this->customcontrol = true;
10510      }
10511  
10512      /**
10513       * Applies defaults and returns all options.
10514       * @return array
10515       */
10516      protected function get_options() {
10517          global $CFG;
10518  
10519          require_once("$CFG->libdir/filelib.php");
10520          require_once("$CFG->dirroot/repository/lib.php");
10521          $defaults = array(
10522              'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10523              'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10524              'context' => context_system::instance());
10525          foreach($this->options as $k => $v) {
10526              $defaults[$k] = $v;
10527          }
10528  
10529          return $defaults;
10530      }
10531  
10532      public function get_setting() {
10533          return $this->config_read($this->name);
10534      }
10535  
10536      public function write_setting($data) {
10537          global $USER;
10538  
10539          // Let's not deal with validation here, this is for admins only.
10540          $current = $this->get_setting();
10541          if (empty($data) && $current === null) {
10542              // This will be the case when applying default settings (installation).
10543              return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10544          } else if (!is_number($data)) {
10545              // Draft item id is expected here!
10546              return get_string('errorsetting', 'admin');
10547          }
10548  
10549          $options = $this->get_options();
10550          $fs = get_file_storage();
10551          $component = is_null($this->plugin) ? 'core' : $this->plugin;
10552  
10553          $this->oldhashes = null;
10554          if ($current) {
10555              $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10556              if ($file = $fs->get_file_by_hash($hash)) {
10557                  $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10558              }
10559              unset($file);
10560          }
10561  
10562          if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10563              // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10564              // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10565              // with an error because the draft area does not exist, as he did not use it.
10566              $usercontext = context_user::instance($USER->id);
10567              if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10568                  return get_string('errorsetting', 'admin');
10569              }
10570          }
10571  
10572          file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10573          $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10574  
10575          $filepath = '';
10576          if ($files) {
10577              /** @var stored_file $file */
10578              $file = reset($files);
10579              $filepath = $file->get_filepath().$file->get_filename();
10580          }
10581  
10582          return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10583      }
10584  
10585      public function post_write_settings($original) {
10586          $options = $this->get_options();
10587          $fs = get_file_storage();
10588          $component = is_null($this->plugin) ? 'core' : $this->plugin;
10589  
10590          $current = $this->get_setting();
10591          $newhashes = null;
10592          if ($current) {
10593              $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10594              if ($file = $fs->get_file_by_hash($hash)) {
10595                  $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10596              }
10597              unset($file);
10598          }
10599  
10600          if ($this->oldhashes === $newhashes) {
10601              $this->oldhashes = null;
10602              return false;
10603          }
10604          $this->oldhashes = null;
10605  
10606          $callbackfunction = $this->updatedcallback;
10607          if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10608              $callbackfunction($this->get_full_name());
10609          }
10610          return true;
10611      }
10612  
10613      public function output_html($data, $query = '') {
10614          global $PAGE, $CFG;
10615  
10616          $options = $this->get_options();
10617          $id = $this->get_id();
10618          $elname = $this->get_full_name();
10619          $draftitemid = file_get_submitted_draft_itemid($elname);
10620          $component = is_null($this->plugin) ? 'core' : $this->plugin;
10621          file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10622  
10623          // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10624          require_once("$CFG->dirroot/lib/form/filemanager.php");
10625  
10626          $fmoptions = new stdClass();
10627          $fmoptions->mainfile       = $options['mainfile'];
10628          $fmoptions->maxbytes       = $options['maxbytes'];
10629          $fmoptions->maxfiles       = $options['maxfiles'];
10630          $fmoptions->client_id      = uniqid();
10631          $fmoptions->itemid         = $draftitemid;
10632          $fmoptions->subdirs        = $options['subdirs'];
10633          $fmoptions->target         = $id;
10634          $fmoptions->accepted_types = $options['accepted_types'];
10635          $fmoptions->return_types   = $options['return_types'];
10636          $fmoptions->context        = $options['context'];
10637          $fmoptions->areamaxbytes   = $options['areamaxbytes'];
10638  
10639          $fm = new form_filemanager($fmoptions);
10640          $output = $PAGE->get_renderer('core', 'files');
10641          $html = $output->render($fm);
10642  
10643          $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
10644          $html .= '<input value="" id="'.$id.'" type="hidden" />';
10645  
10646          return format_admin_setting($this, $this->visiblename,
10647              '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>',
10648              $this->description, true, '', '', $query);
10649      }
10650  }
10651  
10652  
10653  /**
10654   * Administration interface for user specified regular expressions for device detection.
10655   *
10656   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10657   */
10658  class admin_setting_devicedetectregex extends admin_setting {
10659  
10660      /**
10661       * Calls parent::__construct with specific args
10662       *
10663       * @param string $name
10664       * @param string $visiblename
10665       * @param string $description
10666       * @param mixed $defaultsetting
10667       */
10668      public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10669          global $CFG;
10670          parent::__construct($name, $visiblename, $description, $defaultsetting);
10671      }
10672  
10673      /**
10674       * Return the current setting(s)
10675       *
10676       * @return array Current settings array
10677       */
10678      public function get_setting() {
10679          global $CFG;
10680  
10681          $config = $this->config_read($this->name);
10682          if (is_null($config)) {
10683              return null;
10684          }
10685  
10686          return $this->prepare_form_data($config);
10687      }
10688  
10689      /**
10690       * Save selected settings
10691       *
10692       * @param array $data Array of settings to save
10693       * @return bool
10694       */
10695      public function write_setting($data) {
10696          if (empty($data)) {
10697              $data = array();
10698          }
10699  
10700          if ($this->config_write($this->name, $this->process_form_data($data))) {
10701              return ''; // success
10702          } else {
10703              return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10704          }
10705      }
10706  
10707      /**
10708       * Return XHTML field(s) for regexes
10709       *
10710       * @param array $data Array of options to set in HTML
10711       * @return string XHTML string for the fields and wrapping div(s)
10712       */
10713      public function output_html($data, $query='') {
10714          global $OUTPUT;
10715  
10716          $context = (object) [
10717              'expressions' => [],
10718              'name' => $this->get_full_name()
10719          ];
10720  
10721          if (empty($data)) {
10722              $looplimit = 1;
10723          } else {
10724              $looplimit = (count($data)/2)+1;
10725          }
10726  
10727          for ($i=0; $i<$looplimit; $i++) {
10728  
10729              $expressionname = 'expression'.$i;
10730  
10731              if (!empty($data[$expressionname])){
10732                  $expression = $data[$expressionname];
10733              } else {
10734                  $expression = '';
10735              }
10736  
10737              $valuename = 'value'.$i;
10738  
10739              if (!empty($data[$valuename])){
10740                  $value = $data[$valuename];
10741              } else {
10742                  $value= '';
10743              }
10744  
10745              $context->expressions[] = [
10746                  'index' => $i,
10747                  'expression' => $expression,
10748                  'value' => $value
10749              ];
10750          }
10751  
10752          $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10753  
10754          return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10755      }
10756  
10757      /**
10758       * Converts the string of regexes
10759       *
10760       * @see self::process_form_data()
10761       * @param $regexes string of regexes
10762       * @return array of form fields and their values
10763       */
10764      protected function prepare_form_data($regexes) {
10765  
10766          $regexes = json_decode($regexes);
10767  
10768          $form = array();
10769  
10770          $i = 0;
10771  
10772          foreach ($regexes as $value => $regex) {
10773              $expressionname  = 'expression'.$i;
10774              $valuename = 'value'.$i;
10775  
10776              $form[$expressionname] = $regex;
10777              $form[$valuename] = $value;
10778              $i++;
10779          }
10780  
10781          return $form;
10782      }
10783  
10784      /**
10785       * Converts the data from admin settings form into a string of regexes
10786       *
10787       * @see self::prepare_form_data()
10788       * @param array $data array of admin form fields and values
10789       * @return false|string of regexes
10790       */
10791      protected function process_form_data(array $form) {
10792  
10793          $count = count($form); // number of form field values
10794  
10795          if ($count % 2) {
10796              // we must get five fields per expression
10797              return false;
10798          }
10799  
10800          $regexes = array();
10801          for ($i = 0; $i < $count / 2; $i++) {
10802              $expressionname  = "expression".$i;
10803              $valuename       = "value".$i;
10804  
10805              $expression = trim($form['expression'.$i]);
10806              $value      = trim($form['value'.$i]);
10807  
10808              if (empty($expression)){
10809                  continue;
10810              }
10811  
10812              $regexes[$value] = $expression;
10813          }
10814  
10815          $regexes = json_encode($regexes);
10816  
10817          return $regexes;
10818      }
10819  
10820  }
10821  
10822  /**
10823   * Multiselect for current modules
10824   *
10825   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10826   */
10827  class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10828      private $excludesystem;
10829  
10830      /**
10831       * Calls parent::__construct - note array $choices is not required
10832       *
10833       * @param string $name setting name
10834       * @param string $visiblename localised setting name
10835       * @param string $description setting description
10836       * @param array $defaultsetting a plain array of default module ids
10837       * @param bool $excludesystem If true, excludes modules with 'system' archetype
10838       */
10839      public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10840              $excludesystem = true) {
10841          parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10842          $this->excludesystem = $excludesystem;
10843      }
10844  
10845      /**
10846       * Loads an array of current module choices
10847       *
10848       * @return bool always return true
10849       */
10850      public function load_choices() {
10851          if (is_array($this->choices)) {
10852              return true;
10853          }
10854          $this->choices = array();
10855  
10856          global $CFG, $DB;
10857          $records = $DB->get_records('modules', array('visible'=>1), 'name');
10858          foreach ($records as $record) {
10859              // Exclude modules if the code doesn't exist
10860              if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
10861                  // Also exclude system modules (if specified)
10862                  if (!($this->excludesystem &&
10863                          plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
10864                          MOD_ARCHETYPE_SYSTEM)) {
10865                      $this->choices[$record->id] = $record->name;
10866                  }
10867              }
10868          }
10869          return true;
10870      }
10871  }
10872  
10873  /**
10874   * Admin setting to show if a php extension is enabled or not.
10875   *
10876   * @copyright 2013 Damyon Wiese
10877   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10878   */
10879  class admin_setting_php_extension_enabled extends admin_setting {
10880  
10881      /** @var string The name of the extension to check for */
10882      private $extension;
10883  
10884      /**
10885       * Calls parent::__construct with specific arguments
10886       */
10887      public function __construct($name, $visiblename, $description, $extension) {
10888          $this->extension = $extension;
10889          $this->nosave = true;
10890          parent::__construct($name, $visiblename, $description, '');
10891      }
10892  
10893      /**
10894       * Always returns true, does nothing
10895       *
10896       * @return true
10897       */
10898      public function get_setting() {
10899          return true;
10900      }
10901  
10902      /**
10903       * Always returns true, does nothing
10904       *
10905       * @return true
10906       */
10907      public function get_defaultsetting() {
10908          return true;
10909      }
10910  
10911      /**
10912       * Always returns '', does not write anything
10913       *
10914       * @return string Always returns ''
10915       */
10916      public function write_setting($data) {
10917          // Do not write any setting.
10918          return '';
10919      }
10920  
10921      /**
10922       * Outputs the html for this setting.
10923       * @return string Returns an XHTML string
10924       */
10925      public function output_html($data, $query='') {
10926          global $OUTPUT;
10927  
10928          $o = '';
10929          if (!extension_loaded($this->extension)) {
10930              $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
10931  
10932              $o .= format_admin_setting($this, $this->visiblename, $warning);
10933          }
10934          return $o;
10935      }
10936  }
10937  
10938  /**
10939   * Server timezone setting.
10940   *
10941   * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10942   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10943   * @author    Petr Skoda <petr.skoda@totaralms.com>
10944   */
10945  class admin_setting_servertimezone extends admin_setting_configselect {
10946      /**
10947       * Constructor.
10948       */
10949      public function __construct() {
10950          $default = core_date::get_default_php_timezone();
10951          if ($default === 'UTC') {
10952              // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
10953              $default = 'Europe/London';
10954          }
10955  
10956          parent::__construct('timezone',
10957              new lang_string('timezone', 'core_admin'),
10958              new lang_string('configtimezone', 'core_admin'), $default, null);
10959      }
10960  
10961      /**
10962       * Lazy load timezone options.
10963       * @return bool true if loaded, false if error
10964       */
10965      public function load_choices() {
10966          global $CFG;
10967          if (is_array($this->choices)) {
10968              return true;
10969          }
10970  
10971          $current = isset($CFG->timezone) ? $CFG->timezone : null;
10972          $this->choices = core_date::get_list_of_timezones($current, false);
10973          if ($current == 99) {
10974              // Do not show 99 unless it is current value, we want to get rid of it over time.
10975              $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
10976                  core_date::get_default_php_timezone());
10977          }
10978  
10979          return true;
10980      }
10981  }
10982  
10983  /**
10984   * Forced user timezone setting.
10985   *
10986   * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10987   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10988   * @author    Petr Skoda <petr.skoda@totaralms.com>
10989   */
10990  class admin_setting_forcetimezone extends admin_setting_configselect {
10991      /**
10992       * Constructor.
10993       */
10994      public function __construct() {
10995          parent::__construct('forcetimezone',
10996              new lang_string('forcetimezone', 'core_admin'),
10997              new lang_string('helpforcetimezone', 'core_admin'), '99', null);
10998      }
10999  
11000      /**
11001       * Lazy load timezone options.
11002       * @return bool true if loaded, false if error
11003       */
11004      public function load_choices() {
11005          global $CFG;
11006          if (is_array($this->choices)) {
11007              return true;
11008          }
11009  
11010          $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
11011          $this->choices = core_date::get_list_of_timezones($current, true);
11012          $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
11013  
11014          return true;
11015      }
11016  }
11017  
11018  
11019  /**
11020   * Search setup steps info.
11021   *
11022   * @package core
11023   * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
11024   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11025   */
11026  class admin_setting_searchsetupinfo extends admin_setting {
11027  
11028      /**
11029       * Calls parent::__construct with specific arguments
11030       */
11031      public function __construct() {
11032          $this->nosave = true;
11033          parent::__construct('searchsetupinfo', '', '', '');
11034      }
11035  
11036      /**
11037       * Always returns true, does nothing
11038       *
11039       * @return true
11040       */
11041      public function get_setting() {
11042          return true;
11043      }
11044  
11045      /**
11046       * Always returns true, does nothing
11047       *
11048       * @return true
11049       */
11050      public function get_defaultsetting() {
11051          return true;
11052      }
11053  
11054      /**
11055       * Always returns '', does not write anything
11056       *
11057       * @param array $data
11058       * @return string Always returns ''
11059       */
11060      public function write_setting($data) {
11061          // Do not write any setting.
11062          return '';
11063      }
11064  
11065      /**
11066       * Builds the HTML to display the control
11067       *
11068       * @param string $data Unused
11069       * @param string $query
11070       * @return string
11071       */
11072      public function output_html($data, $query='') {
11073          global $CFG, $OUTPUT, $ADMIN;
11074  
11075          $return = '';
11076          $brtag = html_writer::empty_tag('br');
11077  
11078          $searchareas = \core_search\manager::get_search_areas_list();
11079          $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
11080          $anyindexed = false;
11081          foreach ($searchareas as $areaid => $searcharea) {
11082              list($componentname, $varname) = $searcharea->get_config_var_name();
11083              if (get_config($componentname, $varname . '_indexingstart')) {
11084                  $anyindexed = true;
11085                  break;
11086              }
11087          }
11088  
11089          $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
11090  
11091          $table = new html_table();
11092          $table->head = array(get_string('step', 'search'), get_string('status'));
11093          $table->colclasses = array('leftalign step', 'leftalign status');
11094          $table->id = 'searchsetup';
11095          $table->attributes['class'] = 'admintable generaltable';
11096          $table->data = array();
11097  
11098          $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
11099  
11100          // Select a search engine.
11101          $row = array();
11102          $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
11103          $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
11104                          array('href' => $url));
11105  
11106          $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11107          if (!empty($CFG->searchengine)) {
11108              $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
11109                  array('class' => 'badge badge-success'));
11110  
11111          }
11112          $row[1] = $status;
11113          $table->data[] = $row;
11114  
11115          // Available areas.
11116          $row = array();
11117          $url = new moodle_url('/admin/searchareas.php');
11118          $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
11119                          array('href' => $url));
11120  
11121          $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11122          if ($anyenabled) {
11123              $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11124  
11125          }
11126          $row[1] = $status;
11127          $table->data[] = $row;
11128  
11129          // Setup search engine.
11130          $row = array();
11131          if (empty($CFG->searchengine)) {
11132              $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
11133              $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11134          } else {
11135              if ($ADMIN->locate('search' . $CFG->searchengine)) {
11136                  $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
11137                  $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
11138              } else {
11139                  $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
11140              }
11141  
11142              // Check the engine status.
11143              $searchengine = \core_search\manager::search_engine_instance();
11144              try {
11145                  $serverstatus = $searchengine->is_server_ready();
11146              } catch (\moodle_exception $e) {
11147                  $serverstatus = $e->getMessage();
11148              }
11149              if ($serverstatus === true) {
11150                  $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11151              } else {
11152                  $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
11153              }
11154              $row[1] = $status;
11155          }
11156          $table->data[] = $row;
11157  
11158          // Indexed data.
11159          $row = array();
11160          $url = new moodle_url('/admin/searchareas.php');
11161          $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11162          if ($anyindexed) {
11163              $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11164          } else {
11165              $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11166          }
11167          $row[1] = $status;
11168          $table->data[] = $row;
11169  
11170          // Enable global search.
11171          $row = array();
11172          $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11173          $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11174                          array('href' => $url));
11175          $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11176          if (\core_search\manager::is_global_search_enabled()) {
11177              $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11178          }
11179          $row[1] = $status;
11180          $table->data[] = $row;
11181  
11182          $return .= html_writer::table($table);
11183  
11184          return highlight($query, $return);
11185      }
11186  
11187  }
11188  
11189  /**
11190   * Used to validate the contents of SCSS code and ensuring they are parsable.
11191   *
11192   * It does not attempt to detect undefined SCSS variables because it is designed
11193   * to be used without knowledge of other config/scss included.
11194   *
11195   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11196   * @copyright 2016 Dan Poltawski <dan@moodle.com>
11197   */
11198  class admin_setting_scsscode extends admin_setting_configtextarea {
11199  
11200      /**
11201       * Validate the contents of the SCSS to ensure its parsable. Does not
11202       * attempt to detect undefined scss variables.
11203       *
11204       * @param string $data The scss code from text field.
11205       * @return mixed bool true for success or string:error on failure.
11206       */
11207      public function validate($data) {
11208          if (empty($data)) {
11209              return true;
11210          }
11211  
11212          $scss = new core_scss();
11213          try {
11214              $scss->compile($data);
11215          } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
11216              return get_string('scssinvalid', 'admin', $e->getMessage());
11217          } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
11218              // Silently ignore this - it could be a scss variable defined from somewhere
11219              // else which we are not examining here.
11220              return true;
11221          }
11222  
11223          return true;
11224      }
11225  }
11226  
11227  
11228  /**
11229   * Administration setting to define a list of file types.
11230   *
11231   * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
11232   * @copyright 2017 David Mudrák <david@moodle.com>
11233   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11234   */
11235  class admin_setting_filetypes extends admin_setting_configtext {
11236  
11237      /** @var array Allow selection from these file types only. */
11238      protected $onlytypes = [];
11239  
11240      /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
11241      protected $allowall = true;
11242  
11243      /** @var core_form\filetypes_util instance to use as a helper. */
11244      protected $util = null;
11245  
11246      /**
11247       * Constructor.
11248       *
11249       * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
11250       * @param string $visiblename Localised label of the setting
11251       * @param string $description Localised description of the setting
11252       * @param string $defaultsetting Default setting value.
11253       * @param array $options Setting widget options, an array with optional keys:
11254       *   'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
11255       *   'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
11256       */
11257      public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
11258  
11259          parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
11260  
11261          if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
11262              $this->onlytypes = $options['onlytypes'];
11263          }
11264  
11265          if (!$this->onlytypes && array_key_exists('allowall', $options)) {
11266              $this->allowall = (bool)$options['allowall'];
11267          }
11268  
11269          $this->util = new \core_form\filetypes_util();
11270      }
11271  
11272      /**
11273       * Normalize the user's input and write it to the database as comma separated list.
11274       *
11275       * Comma separated list as a text representation of the array was chosen to
11276       * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
11277       *
11278       * @param string $data Value submitted by the admin.
11279       * @return string Epty string if all good, error message otherwise.
11280       */
11281      public function write_setting($data) {
11282          return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
11283      }
11284  
11285      /**
11286       * Validate data before storage
11287       *
11288       * @param string $data The setting values provided by the admin
11289       * @return bool|string True if ok, the string if error found
11290       */
11291      public function validate($data) {
11292  
11293          // No need to call parent's validation here as we are PARAM_RAW.
11294  
11295          if ($this->util->is_whitelisted($data, $this->onlytypes)) {
11296              return true;
11297  
11298          } else {
11299              $troublemakers = $this->util->get_not_whitelisted($data, $this->onlytypes);
11300              return get_string('filetypesnotwhitelisted', 'core_form', implode(' ', $troublemakers));
11301          }
11302      }
11303  
11304      /**
11305       * Return an HTML string for the setting element.
11306       *
11307       * @param string $data The current setting value
11308       * @param string $query Admin search query to be highlighted
11309       * @return string HTML to be displayed
11310       */
11311      public function output_html($data, $query='') {
11312          global $OUTPUT, $PAGE;
11313  
11314          $default = $this->get_defaultsetting();
11315          $context = (object) [
11316              'id' => $this->get_id(),
11317              'name' => $this->get_full_name(),
11318              'value' => $data,
11319              'descriptions' => $this->util->describe_file_types($data),
11320          ];
11321          $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11322  
11323          $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11324              $this->get_id(),
11325              $this->visiblename->out(),
11326              $this->onlytypes,
11327              $this->allowall,
11328          ]);
11329  
11330          return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11331      }
11332  
11333      /**
11334       * Should the values be always displayed in LTR mode?
11335       *
11336       * We always return true here because these values are not RTL compatible.
11337       *
11338       * @return bool True because these values are not RTL compatible.
11339       */
11340      public function get_force_ltr() {
11341          return true;
11342      }
11343  }
11344  
11345  /**
11346   * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11347   *
11348   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11349   * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11350   */
11351  class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11352  
11353      /**
11354       * Constructor.
11355       *
11356       * @param string $name
11357       * @param string $visiblename
11358       * @param string $description
11359       * @param mixed $defaultsetting string or array
11360       * @param mixed $paramtype
11361       * @param string $cols
11362       * @param string $rows
11363       */
11364      public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11365                                  $cols = '60', $rows = '8') {
11366          parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11367          // Pre-set force LTR to false.
11368          $this->set_force_ltr(false);
11369      }
11370  
11371      /**
11372       * Validate the content and format of the age of digital consent map to ensure it is parsable.
11373       *
11374       * @param string $data The age of digital consent map from text field.
11375       * @return mixed bool true for success or string:error on failure.
11376       */
11377      public function validate($data) {
11378          if (empty($data)) {
11379              return true;
11380          }
11381  
11382          try {
11383              \core_auth\digital_consent::parse_age_digital_consent_map($data);
11384          } catch (\moodle_exception $e) {
11385              return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11386          }
11387  
11388          return true;
11389      }
11390  }
11391  
11392  /**
11393   * Selection of plugins that can work as site policy handlers
11394   *
11395   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11396   * @copyright 2018 Marina Glancy
11397   */
11398  class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11399  
11400      /**
11401       * Constructor
11402       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11403       *        for ones in config_plugins.
11404       * @param string $visiblename localised
11405       * @param string $description long localised info
11406       * @param string $defaultsetting
11407       */
11408      public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11409          parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11410      }
11411  
11412      /**
11413       * Lazy-load the available choices for the select box
11414       */
11415      public function load_choices() {
11416          if (during_initial_install()) {
11417              return false;
11418          }
11419          if (is_array($this->choices)) {
11420              return true;
11421          }
11422  
11423          $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11424          $manager = new \core_privacy\local\sitepolicy\manager();
11425          $plugins = $manager->get_all_handlers();
11426          foreach ($plugins as $pname => $unused) {
11427              $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11428                  ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11429          }
11430  
11431          return true;
11432      }
11433  }
11434  
11435  /**
11436   * Used to validate theme presets code and ensuring they compile well.
11437   *
11438   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11439   * @copyright 2019 Bas Brands <bas@moodle.com>
11440   */
11441  class admin_setting_configthemepreset extends admin_setting_configselect {
11442  
11443      /** @var string The name of the theme to check for */
11444      private $themename;
11445  
11446      /**
11447       * Constructor
11448       * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11449       * or 'myplugin/mysetting' for ones in config_plugins.
11450       * @param string $visiblename localised
11451       * @param string $description long localised info
11452       * @param string|int $defaultsetting
11453       * @param array $choices array of $value=>$label for each selection
11454       * @param string $themename name of theme to check presets for.
11455       */
11456      public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11457          $this->themename = $themename;
11458          parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11459      }
11460  
11461      /**
11462       * Write settings if validated
11463       *
11464       * @param string $data
11465       * @return string
11466       */
11467      public function write_setting($data) {
11468          $validated = $this->validate($data);
11469          if ($validated !== true) {
11470              return $validated;
11471          }
11472          return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11473      }
11474  
11475      /**
11476       * Validate the preset file to ensure its parsable.
11477       *
11478       * @param string $data The preset file chosen.
11479       * @return mixed bool true for success or string:error on failure.
11480       */
11481      public function validate($data) {
11482  
11483          if (in_array($data, ['default.scss', 'plain.scss'])) {
11484              return true;
11485          }
11486  
11487          $fs = get_file_storage();
11488          $theme = theme_config::load($this->themename);
11489          $context = context_system::instance();
11490  
11491          // If the preset has not changed there is no need to validate it.
11492          if ($theme->settings->preset == $data) {
11493              return true;
11494          }
11495  
11496          if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11497              // This operation uses a lot of resources.
11498              raise_memory_limit(MEMORY_EXTRA);
11499              core_php_time_limit::raise(300);
11500  
11501              // TODO: MDL-62757 When changing anything in this method please do not forget to check
11502              // if the get_css_content_from_scss() method in class theme_config needs updating too.
11503  
11504              $compiler = new core_scss();
11505              $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11506              $compiler->append_raw_scss($presetfile->get_content());
11507              if ($scssproperties = $theme->get_scss_property()) {
11508                  $compiler->setImportPaths($scssproperties[0]);
11509              }
11510              $compiler->append_raw_scss($theme->get_extra_scss_code());
11511  
11512              try {
11513                  $compiler->to_css();
11514              } catch (Exception $e) {
11515                  return get_string('invalidthemepreset', 'admin', $e->getMessage());
11516              }
11517  
11518              // Try to save memory.
11519              $compiler = null;
11520              unset($compiler);
11521          }
11522  
11523          return true;
11524      }
11525  }
11526  
11527  /**
11528   * Selection of plugins that can work as H5P libraries handlers
11529   *
11530   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11531   * @copyright 2020 Sara Arjona <sara@moodle.com>
11532   */
11533  class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11534  
11535      /**
11536       * Constructor
11537       * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11538       *        for ones in config_plugins.
11539       * @param string $visiblename localised
11540       * @param string $description long localised info
11541       * @param string $defaultsetting
11542       */
11543      public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11544          parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11545      }
11546  
11547      /**
11548       * Lazy-load the available choices for the select box
11549       */
11550      public function load_choices() {
11551          if (during_initial_install()) {
11552              return false;
11553          }
11554          if (is_array($this->choices)) {
11555              return true;
11556          }
11557  
11558          $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11559          foreach ($this->choices as $name => $class) {
11560              $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11561                  ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11562          }
11563  
11564          return true;
11565      }
11566  }