Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  // This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
  16  
  17  namespace core\context;
  18  
  19  use core\context;
  20  use stdClass;
  21  use coding_exception, moodle_url;
  22  
  23  /**
  24   * System context class
  25   *
  26   * @package   core_access
  27   * @category  access
  28   * @copyright Petr Skoda
  29   * @license   https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   * @since     Moodle 4.2
  31   */
  32  class system extends context {
  33      /** @var int numeric context level value matching legacy CONTEXT_SYSTEM */
  34      public const LEVEL = 10;
  35  
  36      /**
  37       * Please use \core\context\system::instance() if you need the instance of context.
  38       *
  39       * @param stdClass $record
  40       */
  41      protected function __construct(stdClass $record) {
  42          parent::__construct($record);
  43          if ($record->contextlevel != self::LEVEL) {
  44              throw new coding_exception('Invalid $record->contextlevel in core\context\system constructor.');
  45          }
  46      }
  47  
  48      /**
  49       * Returns short context name.
  50       *
  51       * @since Moodle 4.2
  52       *
  53       * @return string
  54       */
  55      public static function get_short_name(): string {
  56          return 'system';
  57      }
  58  
  59      /**
  60       * Returns human readable context level name.
  61       *
  62       * @return string the human readable context level name.
  63       */
  64      public static function get_level_name() {
  65          return get_string('coresystem');
  66      }
  67  
  68      /**
  69       * Returns human readable context identifier.
  70       *
  71       * @param boolean $withprefix does not apply to system context
  72       * @param boolean $short does not apply to system context
  73       * @param boolean $escape does not apply to system context
  74       * @return string the human readable context name.
  75       */
  76      public function get_context_name($withprefix = true, $short = false, $escape = true) {
  77          return self::get_level_name();
  78      }
  79  
  80      /**
  81       * Returns the most relevant URL for this context.
  82       *
  83       * @return moodle_url
  84       */
  85      public function get_url() {
  86          return new moodle_url('/');
  87      }
  88  
  89      /**
  90       * Returns list of all role archetypes that are compatible
  91       * with role assignments in context level.
  92       * @since Moodle 4.2
  93       *
  94       * @return int[]
  95       */
  96      protected static function get_compatible_role_archetypes(): array {
  97          return ['manager', 'coursecreator'];
  98      }
  99  
 100      /**
 101       * Returns list of all possible parent context levels.
 102       * @since Moodle 4.2
 103       *
 104       * @return int[]
 105       */
 106      public static function get_possible_parent_levels(): array {
 107          return [];
 108      }
 109  
 110      /**
 111       * Returns array of relevant context capability records.
 112       *
 113       * @param string $sort
 114       * @return array
 115       */
 116      public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
 117          global $DB;
 118  
 119          return $DB->get_records('capabilities', [], $sort);
 120      }
 121  
 122      /**
 123       * Create missing context instances at system context
 124       */
 125      protected static function create_level_instances() {
 126          // Nothing to do here, the system context is created automatically in installer.
 127          self::instance(0);
 128      }
 129  
 130      /**
 131       * Returns system context instance.
 132       *
 133       * @param int $instanceid should be 0
 134       * @param int $strictness
 135       * @param bool $cache
 136       * @return system context instance
 137       */
 138      public static function instance($instanceid = 0, $strictness = MUST_EXIST, $cache = true) {
 139          global $DB;
 140  
 141          if ($instanceid != 0) {
 142              debugging('context_system::instance(): invalid $id parameter detected, should be 0');
 143          }
 144  
 145          // SYSCONTEXTID is cached in local cache to eliminate 1 query per page.
 146          if (defined('SYSCONTEXTID') && $cache) {
 147              if (!isset(context::$systemcontext)) {
 148                  $record = new stdClass();
 149                  $record->id = SYSCONTEXTID;
 150                  $record->contextlevel = self::LEVEL;
 151                  $record->instanceid = 0;
 152                  $record->path = '/'.SYSCONTEXTID;
 153                  $record->depth = 1;
 154                  $record->locked = 0;
 155                  context::$systemcontext = new system($record);
 156              }
 157              return context::$systemcontext;
 158          }
 159  
 160          try {
 161              // We ignore the strictness completely because system context must exist except during install.
 162              $record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
 163          } catch (\dml_exception $e) {
 164              // Table or record does not exist.
 165              if (!during_initial_install()) {
 166                  // Do not mess with system context after install, it simply must exist.
 167                  throw $e;
 168              }
 169              $record = null;
 170          }
 171  
 172          if (!$record) {
 173              $record = new stdClass();
 174              $record->contextlevel = self::LEVEL;
 175              $record->instanceid = 0;
 176              $record->depth = 1;
 177              $record->path = null; // Not known before insert.
 178              $record->locked = 0;
 179  
 180              try {
 181                  if ($DB->count_records('context')) {
 182                      // Contexts already exist, this is very weird, system must be first!!!
 183                      return null;
 184                  }
 185                  if (defined('SYSCONTEXTID')) {
 186                      // This would happen only in unittest on sites that went through weird 1.7 upgrade.
 187                      $record->id = SYSCONTEXTID;
 188                      $DB->import_record('context', $record);
 189                      $DB->get_manager()->reset_sequence('context');
 190                  } else {
 191                      $record->id = $DB->insert_record('context', $record);
 192                  }
 193              } catch (\dml_exception $e) {
 194                  // Can not create context - table does not exist yet, sorry.
 195                  return null;
 196              }
 197          }
 198  
 199          if ($record->instanceid != 0) {
 200              // This is very weird, somebody must be messing with context table.
 201              debugging('Invalid system context detected');
 202          }
 203  
 204          if ($record->depth != 1 || $record->path != '/'.$record->id) {
 205              // Fix path if necessary, initial install or path reset.
 206              $record->depth = 1;
 207              $record->path = '/'.$record->id;
 208              $DB->update_record('context', $record);
 209          }
 210  
 211          if (empty($record->locked)) {
 212              $record->locked = 0;
 213          }
 214  
 215          if (!defined('SYSCONTEXTID')) {
 216              define('SYSCONTEXTID', $record->id);
 217          }
 218  
 219          context::$systemcontext = new system($record);
 220          return context::$systemcontext;
 221      }
 222  
 223      /**
 224       * Returns all site contexts except the system context, DO NOT call on production servers!!
 225       *
 226       * Contexts are not cached.
 227       *
 228       * @return array
 229       */
 230      public function get_child_contexts() {
 231          global $DB;
 232  
 233          debugging('Fetching of system context child courses is strongly discouraged'
 234              . ' on production servers (it may eat all available memory)!');
 235  
 236          // Just get all the contexts except for system level
 237          // and hope we don't OOM in the process - don't cache.
 238          $sql = "SELECT c.*
 239                    FROM {context} c
 240                   WHERE contextlevel > " . self::LEVEL;
 241          $records = $DB->get_records_sql($sql);
 242  
 243          $result = array();
 244          foreach ($records as $record) {
 245              $result[$record->id] = context::create_instance_from_record($record);
 246          }
 247  
 248          return $result;
 249      }
 250  
 251      /**
 252       * Returns sql necessary for purging of stale context instances.
 253       *
 254       * @return string cleanup SQL
 255       */
 256      protected static function get_cleanup_sql() {
 257          $sql = "
 258                    SELECT c.*
 259                      FROM {context} c
 260                     WHERE 1=2
 261                 ";
 262  
 263          return $sql;
 264      }
 265  
 266      /**
 267       * Rebuild context paths and depths at system context level.
 268       *
 269       * @param bool $force
 270       */
 271      protected static function build_paths($force) {
 272          global $DB;
 273  
 274          /* note: ignore $force here, we always do full test of system context */
 275  
 276          // Exactly one record must exist.
 277          $record = $DB->get_record('context', array('contextlevel' => self::LEVEL), '*', MUST_EXIST);
 278  
 279          if ($record->instanceid != 0) {
 280              debugging('Invalid system context detected');
 281          }
 282  
 283          if (defined('SYSCONTEXTID') && $record->id != SYSCONTEXTID) {
 284              debugging('Invalid SYSCONTEXTID detected');
 285          }
 286  
 287          if ($record->depth != 1 || $record->path != '/'.$record->id) {
 288              // Fix path if necessary, initial install or path reset.
 289              $record->depth = 1;
 290              $record->path = '/'.$record->id;
 291              $DB->update_record('context', $record);
 292          }
 293      }
 294  
 295      /**
 296       * Set whether this context has been locked or not.
 297       *
 298       * @param   bool    $locked
 299       * @return  $this
 300       */
 301      public function set_locked(bool $locked) {
 302          if ($locked) {
 303              throw new \coding_exception('It is not possible to lock the system context');
 304          }
 305          return parent::set_locked($locked);
 306      }
 307  }