[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/ -> adminlib.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1