Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • /lib/ -> pagelib.php (source)

    Differences Between: [Versions 28 and 29] [Versions 28 and 30] [Versions 28 and 31] [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

       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   * This file contains the moodle_page class. There is normally a single instance
      19   * of this class in the $PAGE global variable. This class is a central repository
      20   * of information about the page we are building up to send back to the user.
      21   *
      22   * @package core
      23   * @category page
      24   * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
      25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      26   */
      27  
      28  defined('MOODLE_INTERNAL') || die();
      29  
      30  /**
      31   * $PAGE is a central store of information about the current page we are
      32   * generating in response to the user's request.
      33   *
      34   * It does not do very much itself
      35   * except keep track of information, however, it serves as the access point to
      36   * some more significant components like $PAGE->theme, $PAGE->requires,
      37   * $PAGE->blocks, etc.
      38   *
      39   * @copyright 2009 Tim Hunt
      40   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      41   * @since Moodle 2.0
      42   * @package core
      43   * @category page
      44   *
      45   * The following properties are alphabetical. Please keep it that way so that its
      46   * easy to maintain.
      47   *
      48   * @property-read string $activityname The type of activity we are in, for example 'forum' or 'quiz'.
      49   *      Will be null if this page is not within a module.
      50   * @property-read stdClass $activityrecord The row from the activities own database table (for example
      51   *      the forum or quiz table) that this page belongs to. Will be null
      52   *      if this page is not within a module.
      53   * @property-read array $alternativeversions Mime type => object with ->url and ->title.
      54   * @property-read block_manager $blocks The blocks manager object for this page.
      55   * @property-read array $blockmanipulations
      56   * @property-read string $bodyclasses A string to use within the class attribute on the body tag.
      57   * @property-read string $bodyid A string to use as the id of the body tag.
      58   * @property-read string $button The HTML to go where the Turn editing on button normally goes.
      59   * @property-read bool $cacheable Defaults to true. Set to false to stop the page being cached at all.
      60   * @property-read array $categories An array of all the categories the page course belongs to,
      61   *      starting with the immediately containing category, and working out to
      62   *      the top-level category. This may be the empty array if we are in the
      63   *      front page course.
      64   * @property-read mixed $category The category that the page course belongs to.
      65   * @property-read cm_info $cm The course_module that this page belongs to. Will be null
      66   *      if this page is not within a module. This is a full cm object, as loaded
      67   *      by get_coursemodule_from_id or get_coursemodule_from_instance,
      68   *      so the extra modname and name fields are present.
      69   * @property-read context $context The main context to which this page belongs.
      70   * @property-read stdClass $course The current course that we are inside - a row from the
      71   *      course table. (Also available as $COURSE global.) If we are not inside
      72   *      an actual course, this will be the site course.
      73   * @property-read string $devicetypeinuse The name of the device type in use
      74   * @property-read string $docspath The path to the Moodle docs for this page.
      75   * @property-read string $focuscontrol The id of the HTML element to be focused when the page has loaded.
      76   * @property-read bool $headerprinted True if the page header has already been printed.
      77   * @property-read string $heading The main heading that should be displayed at the top of the <body>.
      78   * @property-read string $headingmenu The menu (or actions) to display in the heading
      79   * @property-read array $layout_options An arrays with options for the layout file.
      80   * @property-read array $legacythemeinuse True if the legacy browser theme is in use.
      81   * @property-read navbar $navbar The navbar object used to display the navbar
      82   * @property-read global_navigation $navigation The navigation structure for this page.
      83   * @property-read xhtml_container_stack $opencontainers Tracks XHTML tags on this page that have been opened but not closed.
      84   *      mainly for internal use by the rendering code.
      85   * @property-read string $pagelayout The general type of page this is. For example 'normal', 'popup', 'home'.
      86   *      Allows the theme to display things differently, if it wishes to.
      87   * @property-read string $pagetype The page type string, should be used as the id for the body tag in the theme.
      88   * @property-read int $periodicrefreshdelay The periodic refresh delay to use with meta refresh
      89   * @property-read page_requirements_manager $requires Tracks the JavaScript, CSS files, etc. required by this page.
      90   * @property-read string $requestip The IP address of the current request, null if unknown.
      91   * @property-read string $requestorigin The type of request 'web', 'ws', 'cli', 'restore', etc.
      92   * @property-read settings_navigation $settingsnav The settings navigation
      93   * @property-read int $state One of the STATE_... constants
      94   * @property-read string $subpage The subpage identifier, if any.
      95   * @property-read theme_config $theme The theme for this page.
      96   * @property-read string $title The title that should go in the <head> section of the HTML of this page.
      97   * @property-read moodle_url $url The moodle url object for this page.
      98   */
      99  class moodle_page {
     100  
     101      /** The state of the page before it has printed the header **/
     102      const STATE_BEFORE_HEADER = 0;
     103  
     104      /** The state the page is in temporarily while the header is being printed **/
     105      const STATE_PRINTING_HEADER = 1;
     106  
     107      /** The state the page is in while content is presumably being printed **/
     108      const STATE_IN_BODY = 2;
     109  
     110      /**
     111       * The state the page is when the footer has been printed and its function is
     112       * complete.
     113       */
     114      const STATE_DONE = 3;
     115  
     116      /**
     117       * @var int The current state of the page. The state a page is within
     118       * determines what actions are possible for it.
     119       */
     120      protected $_state = self::STATE_BEFORE_HEADER;
     121  
     122      /**
     123       * @var stdClass The course currently associated with this page.
     124       * If not has been provided the front page course is used.
     125       */
     126      protected $_course = null;
     127  
     128      /**
     129       * @var cm_info If this page belongs to a module, this is the cm_info module
     130       * description object.
     131       */
     132      protected $_cm = null;
     133  
     134      /**
     135       * @var stdClass If $_cm is not null, then this will hold the corresponding
     136       * row from the modname table. For example, if $_cm->modname is 'quiz', this
     137       * will be a row from the quiz table.
     138       */
     139      protected $_module = null;
     140  
     141      /**
     142       * @var context The context that this page belongs to.
     143       */
     144      protected $_context = null;
     145  
     146      /**
     147       * @var array This holds any categories that $_course belongs to, starting with the
     148       * particular category it belongs to, and working out through any parent
     149       * categories to the top level. These are loaded progressively, if needed.
     150       * There are three states. $_categories = null initially when nothing is
     151       * loaded; $_categories = array($id => $cat, $parentid => null) when we have
     152       * loaded $_course->category, but not any parents; and a complete array once
     153       * everything is loaded.
     154       */
     155      protected $_categories = null;
     156  
     157      /**
     158       * @var array An array of CSS classes that should be added to the body tag in HTML.
     159       */
     160      protected $_bodyclasses = array();
     161  
     162      /**
     163       * @var string The title for the page. Used within the title tag in the HTML head.
     164       */
     165      protected $_title = '';
     166  
     167      /**
     168       * @var string The string to use as the heading of the page. Shown near the top of the
     169       * page within most themes.
     170       */
     171      protected $_heading = '';
     172  
     173      /**
     174       * @var string The pagetype is used to describe the page and defaults to a representation
     175       * of the physical path to the page e.g. my-index, mod-quiz-attempt
     176       */
     177      protected $_pagetype = null;
     178  
     179      /**
     180       * @var string The pagelayout to use when displaying this page. The
     181       * pagelayout needs to have been defined by the theme in use, or one of its
     182       * parents. By default base is used however standard is the more common layout.
     183       * Note that this gets automatically set by core during operations like
     184       * require_login.
     185       */
     186      protected $_pagelayout = 'base';
     187  
     188      /**
     189       * @var array List of theme layout options, these are ignored by core.
     190       * To be used in individual theme layout files only.
     191       */
     192      protected $_layout_options = null;
     193  
     194      /**
     195       * @var string An optional arbitrary parameter that can be set on pages where the context
     196       * and pagetype is not enough to identify the page.
     197       */
     198      protected $_subpage = '';
     199  
     200      /**
     201       * @var string Set a different path to use for the 'Moodle docs for this page' link.
     202       * By default, it uses the path of the file for instance mod/quiz/attempt.
     203       */
     204      protected $_docspath = null;
     205  
     206      /**
     207       * @var string A legacy class that will be added to the body tag
     208       */
     209      protected $_legacyclass = null;
     210  
     211      /**
     212       * @var moodle_url The URL for this page. This is mandatory and must be set
     213       * before output is started.
     214       */
     215      protected $_url = null;
     216  
     217      /**
     218       * @var array An array of links to alternative versions of this page.
     219       * Primarily used for RSS versions of the current page.
     220       */
     221      protected $_alternateversions = array();
     222  
     223      /**
     224       * @var block_manager The blocks manager for this page. It is responsible for
     225       * the blocks and there content on this page.
     226       */
     227      protected $_blocks = null;
     228  
     229      /**
     230       * @var page_requirements_manager Page requirements manager. It is responsible
     231       * for all JavaScript and CSS resources required by this page.
     232       */
     233      protected $_requires = null;
     234  
     235      /**
     236       * @var string The capability required by the user in order to edit blocks
     237       * and block settings on this page.
     238       */
     239      protected $_blockseditingcap = 'moodle/site:manageblocks';
     240  
     241      /**
     242       * @var bool An internal flag to record when block actions have been processed.
     243       * Remember block actions occur on the current URL and it is important that
     244       * even they are never executed more than once.
     245       */
     246      protected $_block_actions_done = false;
     247  
     248      /**
     249       * @var array An array of any other capabilities the current user must have
     250       * in order to editing the page and/or its content (not just blocks).
     251       */
     252      protected $_othereditingcaps = array();
     253  
     254      /**
     255       * @var bool Sets whether this page should be cached by the browser or not.
     256       * If it is set to true (default) the page is served with caching headers.
     257       */
     258      protected $_cacheable = true;
     259  
     260      /**
     261       * @var string Can be set to the ID of an element on the page, if done that
     262       * element receives focus when the page loads.
     263       */
     264      protected $_focuscontrol = '';
     265  
     266      /**
     267       * @var string HTML to go where the turn on editing button is located. This
     268       * is nearly a legacy item and not used very often any more.
     269       */
     270      protected $_button = '';
     271  
     272      /**
     273       * @var theme_config The theme to use with this page. This has to be properly
     274       * initialised via {@link moodle_page::initialise_theme_and_output()} which
     275       * happens magically before any operation that requires it.
     276       */
     277      protected $_theme = null;
     278  
     279      /**
     280       * @var global_navigation Contains the global navigation structure.
     281       */
     282      protected $_navigation = null;
     283  
     284      /**
     285       * @var settings_navigation Contains the settings navigation structure.
     286       */
     287      protected $_settingsnav = null;
     288  
     289      /**
     290       * @var navbar Contains the navbar structure.
     291       */
     292      protected $_navbar = null;
     293  
     294      /**
     295       * @var string The menu (or actions) to display in the heading.
     296       */
     297      protected $_headingmenu = null;
     298  
     299      /**
     300       * @var array stack trace. Then the theme is initialised, we save the stack
     301       * trace, for use in error messages.
     302       */
     303      protected $_wherethemewasinitialised = null;
     304  
     305      /**
     306       * @var xhtml_container_stack Tracks XHTML tags on this page that have been
     307       * opened but not closed.
     308       */
     309      protected $_opencontainers;
     310  
     311      /**
     312       * @var int Sets the page to refresh after a given delay (in seconds) using
     313       * meta refresh in {@link standard_head_html()} in outputlib.php
     314       * If set to null(default) the page is not refreshed
     315       */
     316      protected $_periodicrefreshdelay = null;
     317  
     318      /**
     319       * @var array Associative array of browser shortnames (as used by check_browser_version)
     320       * and their minimum required versions
     321       */
     322      protected $_legacybrowsers = array('MSIE' => 6.0);
     323  
     324      /**
     325       * @var string Is set to the name of the device type in use.
     326       * This will we worked out when it is first used.
     327       */
     328      protected $_devicetypeinuse = null;
     329  
     330      /**
     331       * @var bool Used to determine if HTTPS should be required for login.
     332       */
     333      protected $_https_login_required = false;
     334  
     335      /**
     336       * @var bool Determines if popup notifications allowed on this page.
     337       * Code such as the quiz module disables popup notifications in situations
     338       * such as upgrading or completing a quiz.
     339       */
     340      protected $_popup_notification_allowed = true;
     341  
     342      // Magic getter methods =============================================================
     343      // Due to the __get magic below, you normally do not call these as $PAGE->magic_get_x
     344      // methods, but instead use the $PAGE->x syntax.
     345  
     346      /**
     347       * Please do not call this method directly, use the ->state syntax. {@link moodle_page::__get()}.
     348       * @return integer one of the STATE_XXX constants. You should not normally need
     349       * to use this in your code. It is intended for internal use by this class
     350       * and its friends like print_header, to check that everything is working as
     351       * expected. Also accessible as $PAGE->state.
     352       */
     353      protected function magic_get_state() {
     354          return $this->_state;
     355      }
     356  
     357      /**
     358       * Please do not call this method directly, use the ->headerprinted syntax. {@link moodle_page::__get()}.
     359       * @return bool has the header already been printed?
     360       */
     361      protected function magic_get_headerprinted() {
     362          return $this->_state >= self::STATE_IN_BODY;
     363      }
     364  
     365      /**
     366       * Please do not call this method directly, use the ->course syntax. {@link moodle_page::__get()}.
     367       * @return stdClass the current course that we are inside - a row from the
     368       * course table. (Also available as $COURSE global.) If we are not inside
     369       * an actual course, this will be the site course.
     370       */
     371      protected function magic_get_course() {
     372          global $SITE;
     373          if (is_null($this->_course)) {
     374              return $SITE;
     375          }
     376          return $this->_course;
     377      }
     378  
     379      /**
     380       * Please do not call this method directly, use the ->cm syntax. {@link moodle_page::__get()}.
     381       * @return cm_info the course_module that this page belongs to. Will be null
     382       * if this page is not within a module. This is a full cm object, as loaded
     383       * by get_coursemodule_from_id or get_coursemodule_from_instance,
     384       * so the extra modname and name fields are present.
     385       */
     386      protected function magic_get_cm() {
     387          return $this->_cm;
     388      }
     389  
     390      /**
     391       * Please do not call this method directly, use the ->activityrecord syntax. {@link moodle_page::__get()}.
     392       * @return stdClass the row from the activities own database table (for example
     393       * the forum or quiz table) that this page belongs to. Will be null
     394       * if this page is not within a module.
     395       */
     396      protected function magic_get_activityrecord() {
     397          if (is_null($this->_module) && !is_null($this->_cm)) {
     398              $this->load_activity_record();
     399          }
     400          return $this->_module;
     401      }
     402  
     403      /**
     404       * Please do not call this method directly, use the ->activityname syntax. {@link moodle_page::__get()}.
     405       * @return string the The type of activity we are in, for example 'forum' or 'quiz'.
     406       * Will be null if this page is not within a module.
     407       */
     408      protected function magic_get_activityname() {
     409          if (is_null($this->_cm)) {
     410              return null;
     411          }
     412          return $this->_cm->modname;
     413      }
     414  
     415      /**
     416       * Please do not call this method directly, use the ->category syntax. {@link moodle_page::__get()}.
     417       * @return stdClass the category that the page course belongs to. If there isn't one
     418       * (that is, if this is the front page course) returns null.
     419       */
     420      protected function magic_get_category() {
     421          $this->ensure_category_loaded();
     422          if (!empty($this->_categories)) {
     423              return reset($this->_categories);
     424          } else {
     425              return null;
     426          }
     427      }
     428  
     429      /**
     430       * Please do not call this method directly, use the ->categories syntax. {@link moodle_page::__get()}.
     431       * @return array an array of all the categories the page course belongs to,
     432       * starting with the immediately containing category, and working out to
     433       * the top-level category. This may be the empty array if we are in the
     434       * front page course.
     435       */
     436      protected function magic_get_categories() {
     437          $this->ensure_categories_loaded();
     438          return $this->_categories;
     439      }
     440  
     441      /**
     442       * Please do not call this method directly, use the ->context syntax. {@link moodle_page::__get()}.
     443       * @return context the main context to which this page belongs.
     444       */
     445      protected function magic_get_context() {
     446          if (is_null($this->_context)) {
     447              if (CLI_SCRIPT or NO_MOODLE_COOKIES) {
     448                  // Cli scripts work in system context, do not annoy devs with debug info.
     449                  // Very few scripts do not use cookies, we can safely use system as default context there.
     450              } else {
     451                  debugging('Coding problem: $PAGE->context was not set. You may have forgotten '
     452                      .'to call require_login() or $PAGE->set_context(). The page may not display '
     453                      .'correctly as a result');
     454              }
     455              $this->_context = context_system::instance();
     456          }
     457          return $this->_context;
     458      }
     459  
     460      /**
     461       * Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
     462       * @return string e.g. 'my-index' or 'mod-quiz-attempt'.
     463       */
     464      protected function magic_get_pagetype() {
     465          global $CFG;
     466          if (is_null($this->_pagetype) || isset($CFG->pagepath)) {
     467              $this->initialise_default_pagetype();
     468          }
     469          return $this->_pagetype;
     470      }
     471  
     472      /**
     473       * Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
     474       * @return string The id to use on the body tag, uses {@link magic_get_pagetype()}.
     475       */
     476      protected function magic_get_bodyid() {
     477          return 'page-'.$this->pagetype;
     478      }
     479  
     480      /**
     481       * Please do not call this method directly, use the ->pagelayout syntax. {@link moodle_page::__get()}.
     482       * @return string the general type of page this is. For example 'standard', 'popup', 'home'.
     483       *      Allows the theme to display things differently, if it wishes to.
     484       */
     485      protected function magic_get_pagelayout() {
     486          return $this->_pagelayout;
     487      }
     488  
     489      /**
     490       * Please do not call this method directly, use the ->layout_options syntax. {@link moodle_page::__get()}.
     491       * @return array returns arrays with options for layout file
     492       */
     493      protected function magic_get_layout_options() {
     494          if (!is_array($this->_layout_options)) {
     495              $this->_layout_options = $this->_theme->pagelayout_options($this->pagelayout);
     496          }
     497          return $this->_layout_options;
     498      }
     499  
     500      /**
     501       * Please do not call this method directly, use the ->subpage syntax. {@link moodle_page::__get()}.
     502       * @return string The subpage identifier, if any.
     503       */
     504      protected function magic_get_subpage() {
     505          return $this->_subpage;
     506      }
     507  
     508      /**
     509       * Please do not call this method directly, use the ->bodyclasses syntax. {@link moodle_page::__get()}.
     510       * @return string the class names to put on the body element in the HTML.
     511       */
     512      protected function magic_get_bodyclasses() {
     513          return implode(' ', array_keys($this->_bodyclasses));
     514      }
     515  
     516      /**
     517       * Please do not call this method directly, use the ->title syntax. {@link moodle_page::__get()}.
     518       * @return string the title that should go in the <head> section of the HTML of this page.
     519       */
     520      protected function magic_get_title() {
     521          return $this->_title;
     522      }
     523  
     524      /**
     525       * Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
     526       * @return string the main heading that should be displayed at the top of the <body>.
     527       */
     528      protected function magic_get_heading() {
     529          return $this->_heading;
     530      }
     531  
     532      /**
     533       * Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
     534       * @return string The menu (or actions) to display in the heading
     535       */
     536      protected function magic_get_headingmenu() {
     537          return $this->_headingmenu;
     538      }
     539  
     540      /**
     541       * Please do not call this method directly, use the ->docspath syntax. {@link moodle_page::__get()}.
     542       * @return string the path to the Moodle docs for this page.
     543       */
     544      protected function magic_get_docspath() {
     545          if (is_string($this->_docspath)) {
     546              return $this->_docspath;
     547          } else {
     548              return str_replace('-', '/', $this->pagetype);
     549          }
     550      }
     551  
     552      /**
     553       * Please do not call this method directly, use the ->url syntax. {@link moodle_page::__get()}.
     554       * @return moodle_url the clean URL required to load the current page. (You
     555       * should normally use this in preference to $ME or $FULLME.)
     556       */
     557      protected function magic_get_url() {
     558          global $FULLME;
     559          if (is_null($this->_url)) {
     560              debugging('This page did not call $PAGE->set_url(...). Using '.s($FULLME), DEBUG_DEVELOPER);
     561              $this->_url = new moodle_url($FULLME);
     562              // Make sure the guessed URL cannot lead to dangerous redirects.
     563              $this->_url->remove_params('sesskey');
     564          }
     565          return new moodle_url($this->_url); // Return a clone for safety.
     566      }
     567  
     568      /**
     569       * The list of alternate versions of this page.
     570       * @return array mime type => object with ->url and ->title.
     571       */
     572      protected function magic_get_alternateversions() {
     573          return $this->_alternateversions;
     574      }
     575  
     576      /**
     577       * Please do not call this method directly, use the ->blocks syntax. {@link moodle_page::__get()}.
     578       * @return block_manager the blocks manager object for this page.
     579       */
     580      protected function magic_get_blocks() {
     581          global $CFG;
     582          if (is_null($this->_blocks)) {
     583              if (!empty($CFG->blockmanagerclass)) {
     584                  if (!empty($CFG->blockmanagerclassfile)) {
     585                      require_once($CFG->blockmanagerclassfile);
     586                  }
     587                  $classname = $CFG->blockmanagerclass;
     588              } else {
     589                  $classname = 'block_manager';
     590              }
     591              $this->_blocks = new $classname($this);
     592          }
     593          return $this->_blocks;
     594      }
     595  
     596      /**
     597       * Please do not call this method directly, use the ->requires syntax. {@link moodle_page::__get()}.
     598       * @return page_requirements_manager tracks the JavaScript, CSS files, etc. required by this page.
     599       */
     600      protected function magic_get_requires() {
     601          if (is_null($this->_requires)) {
     602              $this->_requires = new page_requirements_manager();
     603          }
     604          return $this->_requires;
     605      }
     606  
     607      /**
     608       * Please do not call this method directly, use the ->cacheable syntax. {@link moodle_page::__get()}.
     609       * @return bool can this page be cached by the user's browser.
     610       */
     611      protected function magic_get_cacheable() {
     612          return $this->_cacheable;
     613      }
     614  
     615      /**
     616       * Please do not call this method directly, use the ->focuscontrol syntax. {@link moodle_page::__get()}.
     617       * @return string the id of the HTML element to be focused when the page has loaded.
     618       */
     619      protected function magic_get_focuscontrol() {
     620          return $this->_focuscontrol;
     621      }
     622  
     623      /**
     624       * Please do not call this method directly, use the ->button syntax. {@link moodle_page::__get()}.
     625       * @return string the HTML to go where the Turn editing on button normally goes.
     626       */
     627      protected function magic_get_button() {
     628          return $this->_button;
     629      }
     630  
     631      /**
     632       * Please do not call this method directly, use the ->theme syntax. {@link moodle_page::__get()}.
     633       * @return theme_config the initialised theme for this page.
     634       */
     635      protected function magic_get_theme() {
     636          if (is_null($this->_theme)) {
     637              $this->initialise_theme_and_output();
     638          }
     639          return $this->_theme;
     640      }
     641  
     642      /**
     643       * Returns an array of minipulations or false if there are none to make.
     644       *
     645       * @since Moodle 2.5.1 2.6
     646       * @return bool|array
     647       */
     648      protected function magic_get_blockmanipulations() {
     649          if (!right_to_left()) {
     650              return false;
     651          }
     652          if (is_null($this->_theme)) {
     653              $this->initialise_theme_and_output();
     654          }
     655          return $this->_theme->blockrtlmanipulations;
     656      }
     657  
     658      /**
     659       * Please do not call this method directly, use the ->devicetypeinuse syntax. {@link moodle_page::__get()}.
     660       * @return string The device type being used.
     661       */
     662      protected function magic_get_devicetypeinuse() {
     663          if (empty($this->_devicetypeinuse)) {
     664              $this->_devicetypeinuse = core_useragent::get_user_device_type();
     665          }
     666          return $this->_devicetypeinuse;
     667      }
     668  
     669      /**
     670       * Please do not call this method directly use the ->periodicrefreshdelay syntax
     671       * {@link moodle_page::__get()}
     672       * @return int The periodic refresh delay to use with meta refresh
     673       */
     674      protected function magic_get_periodicrefreshdelay() {
     675          return $this->_periodicrefreshdelay;
     676      }
     677  
     678      /**
     679       * Please do not call this method directly use the ->opencontainers syntax. {@link moodle_page::__get()}
     680       * @return xhtml_container_stack tracks XHTML tags on this page that have been opened but not closed.
     681       *      mainly for internal use by the rendering code.
     682       */
     683      protected function magic_get_opencontainers() {
     684          if (is_null($this->_opencontainers)) {
     685              $this->_opencontainers = new xhtml_container_stack();
     686          }
     687          return $this->_opencontainers;
     688      }
     689  
     690      /**
     691       * Return the navigation object
     692       * @return global_navigation
     693       */
     694      protected function magic_get_navigation() {
     695          if ($this->_navigation === null) {
     696              $this->_navigation = new global_navigation($this);
     697          }
     698          return $this->_navigation;
     699      }
     700  
     701      /**
     702       * Return a navbar object
     703       * @return navbar
     704       */
     705      protected function magic_get_navbar() {
     706          if ($this->_navbar === null) {
     707              $this->_navbar = new navbar($this);
     708          }
     709          return $this->_navbar;
     710      }
     711  
     712      /**
     713       * Returns the settings navigation object
     714       * @return settings_navigation
     715       */
     716      protected function magic_get_settingsnav() {
     717          if ($this->_settingsnav === null) {
     718              $this->_settingsnav = new settings_navigation($this);
     719              $this->_settingsnav->initialise();
     720          }
     721          return $this->_settingsnav;
     722      }
     723  
     724      /**
     725       * Returns request IP address.
     726       *
     727       * @return string IP address or null if unknown
     728       */
     729      protected function magic_get_requestip() {
     730          return getremoteaddr(null);
     731      }
     732  
     733      /**
     734       * Returns the origin of current request.
     735       *
     736       * Note: constants are not required because we need to use these values in logging and reports.
     737       *
     738       * @return string 'web', 'ws', 'cli', 'restore', etc.
     739       */
     740      protected function magic_get_requestorigin() {
     741          if (class_exists('restore_controller', false) && restore_controller::is_executing()) {
     742              return 'restore';
     743          }
     744  
     745          if (WS_SERVER) {
     746              return 'ws';
     747          }
     748  
     749          if (CLI_SCRIPT) {
     750              return 'cli';
     751          }
     752  
     753          return 'web';
     754      }
     755  
     756      /**
     757       * PHP overloading magic to make the $PAGE->course syntax work by redirecting
     758       * it to the corresponding $PAGE->magic_get_course() method if there is one, and
     759       * throwing an exception if not.
     760       *
     761       * @param string $name property name
     762       * @return mixed
     763       * @throws coding_exception
     764       */
     765      public function __get($name) {
     766          $getmethod = 'magic_get_' . $name;
     767          if (method_exists($this, $getmethod)) {
     768              return $this->$getmethod();
     769          } else {
     770              throw new coding_exception('Unknown property ' . $name . ' of $PAGE.');
     771          }
     772      }
     773  
     774      /**
     775       * PHP overloading magic to catch obvious coding errors.
     776       *
     777       * This method has been created to catch obvious coding errors where the
     778       * developer has tried to set a page property using $PAGE->key = $value.
     779       * In the moodle_page class all properties must be set using the appropriate
     780       * $PAGE->set_something($value) method.
     781       *
     782       * @param string $name property name
     783       * @param mixed $value Value
     784       * @return void Throws exception if field not defined in page class
     785       * @throws coding_exception
     786       */
     787      public function __set($name, $value) {
     788          if (method_exists($this, 'set_' . $name)) {
     789              throw new coding_exception('Invalid attempt to modify page object', "Use \$PAGE->set_$name() instead.");
     790          } else {
     791              throw new coding_exception('Invalid attempt to modify page object', "Unknown property $name");
     792          }
     793      }
     794  
     795      // Other information getting methods ==========================================.
     796  
     797      /**
     798       * Returns instance of page renderer
     799       *
     800       * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
     801       * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
     802       * @param string $target one of rendering target constants
     803       * @return renderer_base
     804       */
     805      public function get_renderer($component, $subtype = null, $target = null) {
     806          if ($this->pagelayout === 'maintenance') {
     807              // If the page is using the maintenance layout then we're going to force target to maintenance.
     808              // This leads to a special core renderer that is designed to block access to API's that are likely unavailable for this
     809              // page layout.
     810              $target = RENDERER_TARGET_MAINTENANCE;
     811          }
     812          return $this->magic_get_theme()->get_renderer($this, $component, $subtype, $target);
     813      }
     814  
     815      /**
     816       * Checks to see if there are any items on the navbar object
     817       *
     818       * @return bool true if there are, false if not
     819       */
     820      public function has_navbar() {
     821          if ($this->_navbar === null) {
     822              $this->_navbar = new navbar($this);
     823          }
     824          return $this->_navbar->has_items();
     825      }
     826  
     827      /**
     828       * Should the current user see this page in editing mode.
     829       * That is, are they allowed to edit this page, and are they currently in
     830       * editing mode.
     831       * @return bool
     832       */
     833      public function user_is_editing() {
     834          global $USER;
     835          return !empty($USER->editing) && $this->user_allowed_editing();
     836      }
     837  
     838      /**
     839       * Does the user have permission to edit blocks on this page.
     840       * @return bool
     841       */
     842      public function user_can_edit_blocks() {
     843          return has_capability($this->_blockseditingcap, $this->_context);
     844      }
     845  
     846      /**
     847       * Does the user have permission to see this page in editing mode.
     848       * @return bool
     849       */
     850      public function user_allowed_editing() {
     851          return has_any_capability($this->all_editing_caps(), $this->_context);
     852      }
     853  
     854      /**
     855       * Get a description of this page. Normally displayed in the footer in developer debug mode.
     856       * @return string
     857       */
     858      public function debug_summary() {
     859          $summary = '';
     860          $summary .= 'General type: ' . $this->pagelayout . '. ';
     861          if (!during_initial_install()) {
     862              $summary .= 'Context ' . $this->context->get_context_name() . ' (context id ' . $this->_context->id . '). ';
     863          }
     864          $summary .= 'Page type ' . $this->pagetype .  '. ';
     865          if ($this->subpage) {
     866              $summary .= 'Sub-page ' . $this->subpage .  '. ';
     867          }
     868          return $summary;
     869      }
     870  
     871      // Setter methods =============================================================.
     872  
     873      /**
     874       * Set the state.
     875       *
     876       * The state must be one of that STATE_... constants, and the state is only allowed to advance one step at a time.
     877       *
     878       * @param int $state The new state.
     879       * @throws coding_exception
     880       */
     881      public function set_state($state) {
     882          if ($state != $this->_state + 1 || $state > self::STATE_DONE) {
     883              throw new coding_exception('Invalid state passed to moodle_page::set_state. We are in state ' .
     884                      $this->_state . ' and state ' . $state . ' was requested.');
     885          }
     886  
     887          if ($state == self::STATE_PRINTING_HEADER) {
     888              $this->starting_output();
     889          }
     890  
     891          $this->_state = $state;
     892      }
     893  
     894      /**
     895       * Set the current course. This sets both $PAGE->course and $COURSE. It also
     896       * sets the right theme and locale.
     897       *
     898       * Normally you don't need to call this function yourself, require_login will
     899       * call it for you if you pass a $course to it. You can use this function
     900       * on pages that do need to call require_login().
     901       *
     902       * Sets $PAGE->context to the course context, if it is not already set.
     903       *
     904       * @param stdClass $course the course to set as the global course.
     905       * @throws coding_exception
     906       */
     907      public function set_course($course) {
     908          global $COURSE, $PAGE, $CFG, $SITE;
     909  
     910          if (empty($course->id)) {
     911              throw new coding_exception('$course passed to moodle_page::set_course does not look like a proper course object.');
     912          }
     913  
     914          $this->ensure_theme_not_set();
     915  
     916          if (!empty($this->_course->id) && $this->_course->id != $course->id) {
     917              $this->_categories = null;
     918          }
     919  
     920          $this->_course = clone($course);
     921  
     922          if ($this === $PAGE) {
     923              $COURSE = $this->_course;
     924              moodle_setlocale();
     925          }
     926  
     927          if (!$this->_context) {
     928              $this->set_context(context_course::instance($this->_course->id));
     929          }
     930  
     931          // Notify course format that this page is set for the course.
     932          if ($this->_course->id != $SITE->id) {
     933              require_once($CFG->dirroot.'/course/lib.php');
     934              $courseformat = course_get_format($this->_course);
     935              $this->add_body_class('format-'. $courseformat->get_format());
     936              $courseformat->page_set_course($this);
     937          } else {
     938              $this->add_body_class('format-site');
     939          }
     940      }
     941  
     942      /**
     943       * Set the main context to which this page belongs.
     944       *
     945       * @param context $context a context object. You normally get this with context_xxxx::instance().
     946       */
     947      public function set_context($context) {
     948          if ($context === null) {
     949              // Extremely ugly hack which sets context to some value in order to prevent warnings,
     950              // use only for core error handling!!!!
     951              if (!$this->_context) {
     952                  $this->_context = context_system::instance();
     953              }
     954              return;
     955          }
     956  
     957          // Ideally we should set context only once.
     958          if (isset($this->_context) && $context->id !== $this->_context->id) {
     959              $current = $this->_context->contextlevel;
     960              if ($current == CONTEXT_SYSTEM or $current == CONTEXT_COURSE) {
     961                  // Hmm - not ideal, but it might produce too many warnings due to the design of require_login.
     962              } else if ($current == CONTEXT_MODULE and ($parentcontext = $context->get_parent_context()) and
     963                  $this->_context->id == $parentcontext->id) {
     964                  // Hmm - most probably somebody did require_login() and after that set the block context.
     965              } else {
     966                  // We do not want devs to do weird switching of context levels on the fly because we might have used
     967                  // the context already such as in text filter in page title.
     968                  // This is explicitly allowed for webservices though which may
     969                  // call "external_api::validate_context on many contexts in a single request.
     970                  if (!WS_SERVER) {
     971                      debugging("Coding problem: unsupported modification of PAGE->context from {$current} to {$context->contextlevel}");
     972                  }
     973              }
     974          }
     975  
     976          $this->_context = $context;
     977      }
     978  
     979      /**
     980       * The course module that this page belongs to (if it does belong to one).
     981       *
     982       * @param stdClass|cm_info $cm a record from course_modules table or cm_info from get_fast_modinfo().
     983       * @param stdClass $course
     984       * @param stdClass $module
     985       * @return void
     986       * @throws coding_exception
     987       */
     988      public function set_cm($cm, $course = null, $module = null) {
     989          global $DB, $CFG, $SITE;
     990  
     991          if (!isset($cm->id) || !isset($cm->course)) {
     992              throw new coding_exception('Invalid $cm. It has to be instance of cm_info or record from the course_modules table.');
     993          }
     994  
     995          if (!$this->_course || $this->_course->id != $cm->course) {
     996              if (!$course) {
     997                  $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
     998              }
     999              if ($course->id != $cm->course) {
    1000                  throw new coding_exception('The course you passed to $PAGE->set_cm does not correspond to the $cm.');
    1001              }
    1002              $this->set_course($course);
    1003          }
    1004  
    1005          // Make sure we have a $cm from get_fast_modinfo as this contains activity access details.
    1006          if (!($cm instanceof cm_info)) {
    1007              $modinfo = get_fast_modinfo($this->_course);
    1008              $cm = $modinfo->get_cm($cm->id);
    1009          }
    1010          $this->_cm = $cm;
    1011  
    1012          // Unfortunately the context setting is a mess.
    1013          // Let's try to work around some common block problems and show some debug messages.
    1014          if (empty($this->_context) or $this->_context->contextlevel != CONTEXT_BLOCK) {
    1015              $context = context_module::instance($cm->id);
    1016              $this->set_context($context);
    1017          }
    1018  
    1019          if ($module) {
    1020              $this->set_activity_record($module);
    1021          }
    1022  
    1023          // Notify course format that this page is set for the course module.
    1024          if ($this->_course->id != $SITE->id) {
    1025              require_once($CFG->dirroot.'/course/lib.php');
    1026              course_get_format($this->_course)->page_set_cm($this);
    1027          }
    1028      }
    1029  
    1030      /**
    1031       * Sets the activity record. This could be a row from the main table for a
    1032       * module. For instance if the current module (cm) is a forum this should be a row
    1033       * from the forum table.
    1034       *
    1035       * @param stdClass $module A row from the main database table for the module that this page belongs to.
    1036       * @throws coding_exception
    1037       */
    1038      public function set_activity_record($module) {
    1039          if (is_null($this->_cm)) {
    1040              throw new coding_exception('You cannot call $PAGE->set_activity_record until after $PAGE->cm has been set.');
    1041          }
    1042          if ($module->id != $this->_cm->instance || $module->course != $this->_course->id) {
    1043              throw new coding_exception('The activity record does not seem to correspond to the cm that has been set.');
    1044          }
    1045          $this->_module = $module;
    1046      }
    1047  
    1048      /**
    1049       * Sets the pagetype to use for this page.
    1050       *
    1051       * Normally you do not need to set this manually, it is automatically created
    1052       * from the script name. However, on some pages this is overridden.
    1053       * For example the page type for course/view.php includes the course format,
    1054       * for example 'course-view-weeks'. This gets used as the id attribute on
    1055       * <body> and also for determining which blocks are displayed.
    1056       *
    1057       * @param string $pagetype e.g. 'my-index' or 'mod-quiz-attempt'.
    1058       */
    1059      public function set_pagetype($pagetype) {
    1060          $this->_pagetype = $pagetype;
    1061      }
    1062  
    1063      /**
    1064       * Sets the layout to use for this page.
    1065       *
    1066       * The page layout determines how the page will be displayed, things such as
    1067       * block regions, content areas, etc are controlled by the layout.
    1068       * The theme in use for the page will determine that the layout contains.
    1069       *
    1070       * This properly defaults to 'base', so you only need to call this function if
    1071       * you want something different. The exact range of supported layouts is specified
    1072       * in the standard theme.
    1073       *
    1074       * For an idea of the common page layouts see
    1075       * {@link http://docs.moodle.org/dev/Themes_2.0#The_different_layouts_as_of_August_17th.2C_2010}
    1076       * But please keep in mind that it may be (and normally is) out of date.
    1077       * The only place to find an accurate up-to-date list of the page layouts
    1078       * available for your version of Moodle is {@link theme/base/config.php}
    1079       *
    1080       * @param string $pagelayout the page layout this is. For example 'popup', 'home'.
    1081       */
    1082      public function set_pagelayout($pagelayout) {
    1083          // Uncomment this to debug theme pagelayout issues like missing blocks.
    1084          // if (!empty($this->_wherethemewasinitialised) && $pagelayout != $this->_pagelayout)
    1085          //     debugging('Page layout has already been set and cannot be changed.', DEBUG_DEVELOPER);
    1086          $this->_pagelayout = $pagelayout;
    1087      }
    1088  
    1089      /**
    1090       * If context->id and pagetype are not enough to uniquely identify this page,
    1091       * then you can set a subpage id as well. For example, the tags page sets
    1092       *
    1093       * @param string $subpage an arbitrary identifier that, along with context->id
    1094       *      and pagetype, uniquely identifies this page.
    1095       */
    1096      public function set_subpage($subpage) {
    1097          if (empty($subpage)) {
    1098              $this->_subpage = '';
    1099          } else {
    1100              $this->_subpage = $subpage;
    1101          }
    1102      }
    1103  
    1104      /**
    1105       * Adds a CSS class to the body tag of the page.
    1106       *
    1107       * @param string $class add this class name ot the class attribute on the body tag.
    1108       * @throws coding_exception
    1109       */
    1110      public function add_body_class($class) {
    1111          if ($this->_state > self::STATE_BEFORE_HEADER) {
    1112              throw new coding_exception('Cannot call moodle_page::add_body_class after output has been started.');
    1113          }
    1114          $this->_bodyclasses[$class] = 1;
    1115      }
    1116  
    1117      /**
    1118       * Adds an array of body classes to the body tag of this page.
    1119       *
    1120       * @param array $classes this utility method calls add_body_class for each array element.
    1121       */
    1122      public function add_body_classes($classes) {
    1123          foreach ($classes as $class) {
    1124              $this->add_body_class($class);
    1125          }
    1126      }
    1127  
    1128      /**
    1129       * Sets the title for the page.
    1130       * This is normally used within the title tag in the head of the page.
    1131       *
    1132       * @param string $title the title that should go in the <head> section of the HTML of this page.
    1133       */
    1134      public function set_title($title) {
    1135          $title = format_string($title);
    1136          $title = strip_tags($title);
    1137          $title = str_replace('"', '&quot;', $title);
    1138          $this->_title = $title;
    1139      }
    1140  
    1141      /**
    1142       * Sets the heading to use for the page.
    1143       * This is normally used as the main heading at the top of the content.
    1144       *
    1145       * @param string $heading the main heading that should be displayed at the top of the <body>.
    1146       */
    1147      public function set_heading($heading) {
    1148          $this->_heading = format_string($heading);
    1149      }
    1150  
    1151      /**
    1152       * Sets some HTML to use next to the heading {@link moodle_page::set_heading()}
    1153       *
    1154       * @param string $menu The menu/content to show in the heading
    1155       */
    1156      public function set_headingmenu($menu) {
    1157          $this->_headingmenu = $menu;
    1158      }
    1159  
    1160      /**
    1161       * Set the course category this page belongs to manually.
    1162       *
    1163       * This automatically sets $PAGE->course to be the site course. You cannot
    1164       * use this method if you have already set $PAGE->course - in that case,
    1165       * the category must be the one that the course belongs to. This also
    1166       * automatically sets the page context to the category context.
    1167       *
    1168       * @param int $categoryid The id of the category to set.
    1169       * @throws coding_exception
    1170       */
    1171      public function set_category_by_id($categoryid) {
    1172          global $SITE;
    1173          if (!is_null($this->_course)) {
    1174              throw new coding_exception('Course has already been set. You cannot change the category now.');
    1175          }
    1176          if (is_array($this->_categories)) {
    1177              throw new coding_exception('Course category has already been set. You cannot to change it now.');
    1178          }
    1179          $this->ensure_theme_not_set();
    1180          $this->set_course($SITE);
    1181          $this->load_category($categoryid);
    1182          $this->set_context(context_coursecat::instance($categoryid));
    1183      }
    1184  
    1185      /**
    1186       * Set a different path to use for the 'Moodle docs for this page' link.
    1187       *
    1188       * By default, it uses the pagetype, which is normally the same as the
    1189       * script name. So, for example, for mod/quiz/attempt.php, pagetype is
    1190       * mod-quiz-attempt, and so docspath is mod/quiz/attempt.
    1191       *
    1192       * @param string $path the path to use at the end of the moodle docs URL.
    1193       */
    1194      public function set_docs_path($path) {
    1195          $this->_docspath = $path;
    1196      }
    1197  
    1198      /**
    1199       * You should call this method from every page to set the URL that should be used to return to this page.
    1200       *
    1201       * Used, for example, by the blocks editing UI to know where to return the
    1202       * user after an action.
    1203       * For example, course/view.php does:
    1204       *      $id = optional_param('id', 0, PARAM_INT);
    1205       *      $PAGE->set_url('/course/view.php', array('id' => $id));
    1206       *
    1207       * @param moodle_url|string $url URL relative to $CFG->wwwroot or {@link moodle_url} instance
    1208       * @param array $params parameters to add to the URL
    1209       * @throws coding_exception
    1210       */
    1211      public function set_url($url, array $params = null) {
    1212          global $CFG;
    1213  
    1214          if (is_string($url) && strpos($url, 'http') !== 0) {
    1215              if (strpos($url, '/') === 0) {
    1216                  // We have to use httpswwwroot here, because of loginhttps pages.
    1217                  $url = $CFG->httpswwwroot . $url;
    1218              } else {
    1219                  throw new coding_exception('Invalid parameter $url, has to be full url or in shortened form starting with /.');
    1220              }
    1221          }
    1222  
    1223          $this->_url = new moodle_url($url, $params);
    1224  
    1225          $fullurl = $this->_url->out_omit_querystring();
    1226          if (strpos($fullurl, "$CFG->httpswwwroot/") !== 0) {
    1227              debugging('Most probably incorrect set_page() url argument, it does not match the httpswwwroot!');
    1228          }
    1229          $shorturl = str_replace("$CFG->httpswwwroot/", '', $fullurl);
    1230  
    1231          if (is_null($this->_pagetype)) {
    1232              $this->initialise_default_pagetype($shorturl);
    1233          }
    1234      }
    1235  
    1236      /**
    1237       * Make sure page URL does not contain the given URL parameter.
    1238       *
    1239       * This should not be necessary if the script has called set_url properly.
    1240       * However, in some situations like the block editing actions; when the URL
    1241       * has been guessed, it will contain dangerous block-related actions.
    1242       * Therefore, the blocks code calls this function to clean up such parameters
    1243       * before doing any redirect.
    1244       *
    1245       * @param string $param the name of the parameter to make sure is not in the
    1246       * page URL.
    1247       */
    1248      public function ensure_param_not_in_url($param) {
    1249          $this->_url->remove_params($param);
    1250      }
    1251  
    1252      /**
    1253       * Sets an alternative version of this page.
    1254       *
    1255       * There can be alternate versions of some pages (for example an RSS feed version).
    1256       * Call this method for each alternative version available.
    1257       * For each alternative version a link will be included in the <head> tag.
    1258       *
    1259       * @param string $title The title to give the alternate version.
    1260       * @param string|moodle_url $url The URL of the alternate version.
    1261       * @param string $mimetype The mime-type of the alternate version.
    1262       * @throws coding_exception
    1263       */
    1264      public function add_alternate_version($title, $url, $mimetype) {
    1265          if ($this->_state > self::STATE_BEFORE_HEADER) {
    1266              throw new coding_exception('Cannot call moodle_page::add_alternate_version after output has been started.');
    1267          }
    1268          $alt = new stdClass;
    1269          $alt->title = $title;
    1270          $alt->url = $url;
    1271          $this->_alternateversions[$mimetype] = $alt;
    1272      }
    1273  
    1274      /**
    1275       * Specify a form control should be focused when the page has loaded.
    1276       *
    1277       * @param string $controlid the id of the HTML element to be focused.
    1278       */
    1279      public function set_focuscontrol($controlid) {
    1280          $this->_focuscontrol = $controlid;
    1281      }
    1282  
    1283      /**
    1284       * Specify a fragment of HTML that goes where the 'Turn editing on' button normally goes.
    1285       *
    1286       * @param string $html the HTML to display there.
    1287       */
    1288      public function set_button($html) {
    1289          $this->_button = $html;
    1290      }
    1291  
    1292      /**
    1293       * Set the capability that allows users to edit blocks on this page.
    1294       *
    1295       * Normally the default of 'moodle/site:manageblocks' is used, but a few
    1296       * pages like the My Moodle page need to use a different capability
    1297       * like 'moodle/my:manageblocks'.
    1298       *
    1299       * @param string $capability a capability.
    1300       */
    1301      public function set_blocks_editing_capability($capability) {
    1302          $this->_blockseditingcap = $capability;
    1303      }
    1304  
    1305      /**
    1306       * Some pages let you turn editing on for reasons other than editing blocks.
    1307       * If that is the case, you can pass other capabilities that let the user
    1308       * edit this page here.
    1309       *
    1310       * @param string|array $capability either a capability, or an array of capabilities.
    1311       */
    1312      public function set_other_editing_capability($capability) {
    1313          if (is_array($capability)) {
    1314              $this->_othereditingcaps = array_unique($this->_othereditingcaps + $capability);
    1315          } else {
    1316              $this->_othereditingcaps[] = $capability;
    1317          }
    1318      }
    1319  
    1320      /**
    1321       * Sets whether the browser should cache this page or not.
    1322       *
    1323       * @param bool $cacheable can this page be cached by the user's browser.
    1324       */
    1325      public function set_cacheable($cacheable) {
    1326          $this->_cacheable = $cacheable;
    1327      }
    1328  
    1329      /**
    1330       * Sets the page to periodically refresh
    1331       *
    1332       * This function must be called before $OUTPUT->header has been called or
    1333       * a coding exception will be thrown.
    1334       *
    1335       * @param int $delay Sets the delay before refreshing the page, if set to null refresh is cancelled.
    1336       * @throws coding_exception
    1337       */
    1338      public function set_periodic_refresh_delay($delay = null) {
    1339          if ($this->_state > self::STATE_BEFORE_HEADER) {
    1340              throw new coding_exception('You cannot set a periodic refresh delay after the header has been printed');
    1341          }
    1342          if ($delay === null) {
    1343              $this->_periodicrefreshdelay = null;
    1344          } else if (is_int($delay)) {
    1345              $this->_periodicrefreshdelay = $delay;
    1346          }
    1347      }
    1348  
    1349      /**
    1350       * Force this page to use a particular theme.
    1351       *
    1352       * Please use this cautiously.
    1353       * It is only intended to be used by the themes selector admin page.
    1354       *
    1355       * @param string $themename the name of the theme to use.
    1356       */
    1357      public function force_theme($themename) {
    1358          $this->ensure_theme_not_set();
    1359          $this->_theme = theme_config::load($themename);
    1360      }
    1361  
    1362      /**
    1363       * Reload theme settings.
    1364       *
    1365       * This is used when we need to reset settings
    1366       * because they are now double cached in theme.
    1367       */
    1368      public function reload_theme() {
    1369          if (!is_null($this->_theme)) {
    1370              $this->_theme = theme_config::load($this->_theme->name);
    1371          }
    1372      }
    1373  
    1374      /**
    1375       * This function indicates that current page requires the https when $CFG->loginhttps enabled.
    1376       *
    1377       * By using this function properly, we can ensure 100% https-ized pages
    1378       * at our entire discretion (login, forgot_password, change_password)
    1379       *
    1380       * @return void
    1381       * @throws coding_exception
    1382       */
    1383      public function https_required() {
    1384          global $CFG;
    1385  
    1386          if (!is_null($this->_url)) {
    1387              throw new coding_exception('https_required() must be used before setting page url!');
    1388          }
    1389  
    1390          $this->ensure_theme_not_set();
    1391  
    1392          $this->_https_login_required = true;
    1393  
    1394          if (!empty($CFG->loginhttps)) {
    1395              $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
    1396          } else {
    1397              $CFG->httpswwwroot = $CFG->wwwroot;
    1398          }
    1399      }
    1400  
    1401      /**
    1402       * Makes sure that page previously marked with https_required() is really using https://, if not it redirects to https://
    1403       *
    1404       * @return void (may redirect to https://self)
    1405       * @throws coding_exception
    1406       */
    1407      public function verify_https_required() {
    1408          global $CFG, $FULLME;
    1409  
    1410          if (is_null($this->_url)) {
    1411              throw new coding_exception('verify_https_required() must be called after setting page url!');
    1412          }
    1413  
    1414          if (!$this->_https_login_required) {
    1415              throw new coding_exception('verify_https_required() must be called only after https_required()!');
    1416          }
    1417  
    1418          if (empty($CFG->loginhttps)) {
    1419              // Https not required, so stop checking.
    1420              return;
    1421          }
    1422  
    1423          if (strpos($this->_url, 'https://')) {
    1424              // Detect if incorrect PAGE->set_url() used, it is recommended to use root-relative paths there.
    1425              throw new coding_exception('Invalid page url. It must start with https:// for pages that set https_required()!');
    1426          }
    1427  
    1428          if (!empty($CFG->sslproxy)) {
    1429              // It does not make much sense to use sslproxy and loginhttps at the same time.
    1430              return;
    1431          }
    1432  
    1433          // Now the real test and redirect!
    1434          // NOTE: do NOT use this test for detection of https on current page because this code is not compatible with SSL proxies,
    1435          //       instead use is_https().
    1436          if (strpos($FULLME, 'https:') !== 0) {
    1437              // This may lead to infinite redirect on an incorrectly configured site.
    1438              // In that case set $CFG->loginhttps=0; within /config.php.
    1439              redirect($this->_url);
    1440          }
    1441      }
    1442  
    1443      // Initialisation methods =====================================================
    1444      // These set various things up in a default way.
    1445  
    1446      /**
    1447       * This method is called when the page first moves out of the STATE_BEFORE_HEADER
    1448       * state. This is our last change to initialise things.
    1449       */
    1450      protected function starting_output() {
    1451          global $CFG;
    1452  
    1453          if (!during_initial_install()) {
    1454              $this->blocks->load_blocks();
    1455              if (empty($this->_block_actions_done)) {
    1456                  $this->_block_actions_done = true;
    1457                  if ($this->blocks->process_url_actions($this)) {
    1458                      redirect($this->url->out(false));
    1459                  }
    1460              }
    1461              $this->blocks->create_all_block_instances();
    1462          }
    1463  
    1464          // If maintenance mode is on, change the page header.
    1465          if (!empty($CFG->maintenance_enabled)) {
    1466              $this->set_button('<a href="' . $CFG->wwwroot . '/' . $CFG->admin .
    1467                      '/settings.php?section=maintenancemode">' . get_string('maintenancemode', 'admin') .
    1468                      '</a> ' . $this->button);
    1469  
    1470              $title = $this->title;
    1471              if ($title) {
    1472                  $title .= ' - ';
    1473              }
    1474              $this->set_title($title . get_string('maintenancemode', 'admin'));
    1475          } else {
    1476              // Show the messaging popup if there are messages.
    1477              message_popup_window();
    1478          }
    1479  
    1480          $this->initialise_standard_body_classes();
    1481      }
    1482  
    1483      /**
    1484       * Method for use by Moodle core to set up the theme. Do not
    1485       * use this in your own code.
    1486       *
    1487       * Make sure the right theme for this page is loaded. Tell our
    1488       * blocks_manager about the theme block regions, and then, if
    1489       * we are $PAGE, set up the global $OUTPUT.
    1490       *
    1491       * @return void
    1492       */
    1493      public function initialise_theme_and_output() {
    1494          global $OUTPUT, $PAGE, $SITE, $CFG;
    1495  
    1496          if (!empty($this->_wherethemewasinitialised)) {
    1497              return;
    1498          }
    1499  
    1500          if (!during_initial_install()) {
    1501              // Detect PAGE->context mess.
    1502              $this->magic_get_context();
    1503          }
    1504  
    1505          if (!$this->_course && !during_initial_install()) {
    1506              $this->set_course($SITE);
    1507          }
    1508  
    1509          if (is_null($this->_theme)) {
    1510              $themename = $this->resolve_theme();
    1511              $this->_theme = theme_config::load($themename);
    1512          }
    1513  
    1514          $this->_theme->setup_blocks($this->pagelayout, $this->blocks);
    1515          if ($this->_theme->enable_dock && !empty($CFG->allowblockstodock)) {
    1516              $this->requires->strings_for_js(array('addtodock', 'undockitem', 'dockblock', 'undockblock', 'undockall', 'hidedockpanel', 'hidepanel'), 'block');
    1517              $this->requires->string_for_js('thisdirectionvertical', 'langconfig');
    1518              $this->requires->yui_module('moodle-core-dock-loader', 'M.core.dock.loader.initLoader');
    1519          }
    1520  
    1521          if ($this === $PAGE) {
    1522              $target = null;
    1523              if ($this->pagelayout === 'maintenance') {
    1524                  // If the page is using the maintenance layout then we're going to force target to maintenance.
    1525                  // This leads to a special core renderer that is designed to block access to API's that are likely unavailable for this
    1526                  // page layout.
    1527                  $target = RENDERER_TARGET_MAINTENANCE;
    1528              }
    1529              $OUTPUT = $this->get_renderer('core', null, $target);
    1530          }
    1531  
    1532          $this->_wherethemewasinitialised = debug_backtrace();
    1533      }
    1534  
    1535      /**
    1536       * Work out the theme this page should use.
    1537       *
    1538       * This depends on numerous $CFG settings, and the properties of this page.
    1539       *
    1540       * @return string the name of the theme that should be used on this page.
    1541       */
    1542      protected function resolve_theme() {
    1543          global $CFG, $USER, $SESSION;
    1544  
    1545          if (empty($CFG->themeorder)) {
    1546              $themeorder = array('course', 'category', 'session', 'user', 'site');
    1547          } else {
    1548              $themeorder = $CFG->themeorder;
    1549              // Just in case, make sure we always use the site theme if nothing else matched.
    1550              $themeorder[] = 'site';
    1551          }
    1552  
    1553          $mnetpeertheme = '';
    1554          if (isloggedin() and isset($CFG->mnet_localhost_id) and $USER->mnethostid != $CFG->mnet_localhost_id) {
    1555              require_once($CFG->dirroot.'/mnet/peer.php');
    1556              $mnetpeer = new mnet_peer();
    1557              $mnetpeer->set_id($USER->mnethostid);
    1558              if ($mnetpeer->force_theme == 1 && $mnetpeer->theme != '') {
    1559                  $mnetpeertheme = $mnetpeer->theme;
    1560              }
    1561          }
    1562  
    1563          $devicetheme = core_useragent::get_device_type_theme($this->devicetypeinuse);
    1564  
    1565          // The user is using another device than default, and we have a theme for that, we should use it.
    1566          $hascustomdevicetheme = core_useragent::DEVICETYPE_DEFAULT != $this->devicetypeinuse && !empty($devicetheme);
    1567  
    1568          foreach ($themeorder as $themetype) {
    1569  
    1570              switch ($themetype) {
    1571                  case 'course':
    1572                      if (!empty($CFG->allowcoursethemes) && !empty($this->_course->theme) && !$hascustomdevicetheme) {
    1573                          return $this->_course->theme;
    1574                      }
    1575                  break;
    1576  
    1577                  case 'category':
    1578                      if (!empty($CFG->allowcategorythemes) && !$hascustomdevicetheme) {
    1579                          $categories = $this->categories;
    1580                          foreach ($categories as $category) {
    1581                              if (!empty($category->theme)) {
    1582                                  return $category->theme;
    1583                              }
    1584                          }
    1585                      }
    1586                  break;
    1587  
    1588                  case 'session':
    1589                      if (!empty($SESSION->theme)) {
    1590                          return $SESSION->theme;
    1591                      }
    1592                  break;
    1593  
    1594                  case 'user':
    1595                      if (!empty($CFG->allowuserthemes) && !empty($USER->theme) && !$hascustomdevicetheme) {
    1596                          if ($mnetpeertheme) {
    1597                              return $mnetpeertheme;
    1598                          } else {
    1599                              return $USER->theme;
    1600                          }
    1601                      }
    1602                  break;
    1603  
    1604                  case 'site':
    1605                      if ($mnetpeertheme) {
    1606                          return $mnetpeertheme;
    1607                      }
    1608                      // First try for the device the user is using.
    1609                      if (!empty($devicetheme)) {
    1610                          return $devicetheme;
    1611                      }
    1612                      // Next try for the default device (as a fallback).
    1613                      $devicetheme = core_useragent::get_device_type_theme(core_useragent::DEVICETYPE_DEFAULT);
    1614                      if (!empty($devicetheme)) {
    1615                          return $devicetheme;
    1616                      }
    1617                      // The default device theme isn't set up - use the overall default theme.
    1618                      return theme_config::DEFAULT_THEME;
    1619              }
    1620          }
    1621  
    1622          // We should most certainly have resolved a theme by now. Something has gone wrong.
    1623          debugging('Error resolving the theme to use for this page.', DEBUG_DEVELOPER);
    1624          return theme_config::DEFAULT_THEME;
    1625      }
    1626  
    1627  
    1628      /**
    1629       * Sets ->pagetype from the script name. For example, if the script that was
    1630       * run is mod/quiz/view.php, ->pagetype will be set to 'mod-quiz-view'.
    1631       *
    1632       * @param string $script the path to the script that should be used to
    1633       * initialise ->pagetype. If not passed the $SCRIPT global will be used.
    1634       * If legacy code has set $CFG->pagepath that will be used instead, and a
    1635       * developer warning issued.
    1636       */
    1637      protected function initialise_default_pagetype($script = null) {
    1638          global $CFG, $SCRIPT;
    1639  
    1640          if (isset($CFG->pagepath)) {
    1641              debugging('Some code appears to have set $CFG->pagepath. That was a horrible deprecated thing. ' .
    1642                      'Don\'t do it! Try calling $PAGE->set_pagetype() instead.');
    1643              $script = $CFG->pagepath;
    1644              unset($CFG->pagepath);
    1645          }
    1646  
    1647          if (is_null($script)) {
    1648              $script = ltrim($SCRIPT, '/');
    1649              $len = strlen($CFG->admin);
    1650              if (substr($script, 0, $len) == $CFG->admin) {
    1651                  $script = 'admin' . substr($script, $len);
    1652              }
    1653          }
    1654  
    1655          $path = str_replace('.php', '', $script);
    1656          if (substr($path, -1) == '/') {
    1657              $path .= 'index';
    1658          }
    1659  
    1660          if (empty($path) || $path == 'index') {
    1661              $this->_pagetype = 'site-index';
    1662          } else {
    1663              $this->_pagetype = str_replace('/', '-', $path);
    1664          }
    1665      }
    1666  
    1667      /**
    1668       * Initialises the CSS classes that will be added to body tag of the page.
    1669       *
    1670       * The function is responsible for adding all of the critical CSS classes
    1671       * that describe the current page, and its state.
    1672       * This includes classes that describe the following for example:
    1673       *    - Current language
    1674       *    - Language direction
    1675       *    - YUI CSS initialisation
    1676       *    - Pagelayout
    1677       * These are commonly used in CSS to target specific types of pages.
    1678       */
    1679      protected function initialise_standard_body_classes() {
    1680          global $CFG, $USER;
    1681  
    1682          $pagetype = $this->pagetype;
    1683          if ($pagetype == 'site-index') {
    1684              $this->_legacyclass = 'course';
    1685          } else if (substr($pagetype, 0, 6) == 'admin-') {
    1686              $this->_legacyclass = 'admin';
    1687          }
    1688          $this->add_body_class($this->_legacyclass);
    1689  
    1690          $pathbits = explode('-', trim($pagetype));
    1691          for ($i = 1; $i < count($pathbits); $i++) {
    1692              $this->add_body_class('path-' . join('-', array_slice($pathbits, 0, $i)));
    1693          }
    1694  
    1695          $this->add_body_classes(core_useragent::get_browser_version_classes());
    1696          $this->add_body_class('dir-' . get_string('thisdirection', 'langconfig'));
    1697          $this->add_body_class('lang-' . current_language());
    1698          $this->add_body_class('yui-skin-sam'); // Make YUI happy, if it is used.
    1699          $this->add_body_class('yui3-skin-sam'); // Make YUI3 happy, if it is used.
    1700          $this->add_body_class($this->url_to_class_name($CFG->wwwroot));
    1701  
    1702          // Extra class describing current page layout.
    1703          $this->add_body_class('pagelayout-' . $this->_pagelayout);
    1704  
    1705          if (!during_initial_install()) {
    1706              $this->add_body_class('course-' . $this->_course->id);
    1707              $this->add_body_class('context-' . $this->_context->id);
    1708          }
    1709  
    1710          if (!empty($this->_cm)) {
    1711              $this->add_body_class('cmid-' . $this->_cm->id);
    1712          }
    1713  
    1714          if (!empty($CFG->allowcategorythemes)) {
    1715              $this->ensure_category_loaded();
    1716              foreach ($this->_categories as $catid => $notused) {
    1717                  $this->add_body_class('category-' . $catid);
    1718              }
    1719          } else {
    1720              $catid = 0;
    1721              if (is_array($this->_categories)) {
    1722                  $catids = array_keys($this->_categories);
    1723                  $catid = reset($catids);
    1724              } else if (!empty($this->_course->category)) {
    1725                  $catid = $this->_course->category;
    1726              }
    1727              if ($catid) {
    1728                  $this->add_body_class('category-' . $catid);
    1729              }
    1730          }
    1731  
    1732          if (!isloggedin()) {
    1733              $this->add_body_class('notloggedin');
    1734          }
    1735  
    1736          if (!empty($USER->editing)) {
    1737              $this->add_body_class('editing');
    1738              if (optional_param('bui_moveid', false, PARAM_INT)) {
    1739                  $this->add_body_class('blocks-moving');
    1740              }
    1741          }
    1742  
    1743          if (!empty($CFG->blocksdrag)) {
    1744              $this->add_body_class('drag');
    1745          }
    1746  
    1747          if ($this->_devicetypeinuse != 'default') {
    1748              $this->add_body_class($this->_devicetypeinuse . 'theme');
    1749          }
    1750      }
    1751  
    1752      /**
    1753       * Loads the activity record for the current CM object associated with this
    1754       * page.
    1755       *
    1756       * This will load {@link moodle_page::$_module} with a row from the related
    1757       * module table in the database.
    1758       * For instance if {@link moodle_page::$_cm} is a forum then a row from the
    1759       * forum table will be loaded.
    1760       */
    1761      protected function load_activity_record() {
    1762          global $DB;
    1763          if (is_null($this->_cm)) {
    1764              return;
    1765          }
    1766          $this->_module = $DB->get_record($this->_cm->modname, array('id' => $this->_cm->instance));
    1767      }
    1768  
    1769      /**
    1770       * This function ensures that the category of the current course has been
    1771       * loaded, and if not, the function loads it now.
    1772       *
    1773       * @return void
    1774       * @throws coding_exception
    1775       */
    1776      protected function ensure_category_loaded() {
    1777          if (is_array($this->_categories)) {
    1778              return; // Already done.
    1779          }
    1780          if (is_null($this->_course)) {
    1781              throw new coding_exception('Attempt to get the course category for this page before the course was set.');
    1782          }
    1783          if ($this->_course->category == 0) {
    1784              $this->_categories = array();
    1785          } else {
    1786              $this->load_category($this->_course->category);
    1787          }
    1788      }
    1789  
    1790      /**
    1791       * Loads the requested category into the pages categories array.
    1792       *
    1793       * @param int $categoryid
    1794       * @throws moodle_exception
    1795       */
    1796      protected function load_category($categoryid) {
    1797          global $DB;
    1798          $category = $DB->get_record('course_categories', array('id' => $categoryid));
    1799          if (!$category) {
    1800              throw new moodle_exception('unknowncategory');
    1801          }
    1802          $this->_categories[$category->id] = $category;
    1803          $parentcategoryids = explode('/', trim($category->path, '/'));
    1804          array_pop($parentcategoryids);
    1805          foreach (array_reverse($parentcategoryids) as $catid) {
    1806              $this->_categories[$catid] = null;
    1807          }
    1808      }
    1809  
    1810      /**
    1811       * Ensures that the category the current course is within, as well as all of
    1812       * its parent categories, have been loaded.
    1813       *
    1814       * @return void
    1815       */
    1816      protected function ensure_categories_loaded() {
    1817          global $DB;
    1818          $this->ensure_category_loaded();
    1819          if (!is_null(end($this->_categories))) {
    1820              return; // Already done.
    1821          }
    1822          $idstoload = array_keys($this->_categories);
    1823          array_shift($idstoload);
    1824          $categories = $DB->get_records_list('course_categories', 'id', $idstoload);
    1825          foreach ($idstoload as $catid) {
    1826              $this->_categories[$catid] = $categories[$catid];
    1827          }
    1828      }
    1829  
    1830      /**
    1831       * Ensure the theme has not been loaded yet. If it has an exception is thrown.
    1832       *
    1833       * @throws coding_exception
    1834       */
    1835      protected function ensure_theme_not_set() {
    1836          // This is explicitly allowed for webservices though which may process many course contexts in a single request.
    1837          if (WS_SERVER) {
    1838              return;
    1839          }
    1840  
    1841          if (!is_null($this->_theme)) {
    1842              throw new coding_exception('The theme has already been set up for this page ready for output. ' .
    1843                      'Therefore, you can no longer change the theme, or anything that might affect what ' .
    1844                      'the current theme is, for example, the course.',
    1845                      'Stack trace when the theme was set up: ' . format_backtrace($this->_wherethemewasinitialised));
    1846          }
    1847      }
    1848  
    1849      /**
    1850       * Converts the provided URL into a CSS class that be used within the page.
    1851       * This is primarily used to add the wwwroot to the body tag as a CSS class.
    1852       *
    1853       * @param string $url
    1854       * @return string
    1855       */
    1856      protected function url_to_class_name($url) {
    1857          $bits = parse_url($url);
    1858          $class = str_replace('.', '-', $bits['host']);
    1859          if (!empty($bits['port'])) {
    1860              $class .= '--' . $bits['port'];
    1861          }
    1862          if (!empty($bits['path'])) {
    1863              $path = trim($bits['path'], '/');
    1864              if ($path) {
    1865                  $class .= '--' . str_replace('/', '-', $path);
    1866              }
    1867          }
    1868          return $class;
    1869      }
    1870  
    1871      /**
    1872       * Combines all of the required editing caps for the page and returns them
    1873       * as an array.
    1874       *
    1875       * @return array
    1876       */
    1877      protected function all_editing_caps() {
    1878          $caps = $this->_othereditingcaps;
    1879          $caps[] = $this->_blockseditingcap;
    1880          return $caps;
    1881      }
    1882  
    1883      /**
    1884       * Returns true if the page URL has beem set.
    1885       *
    1886       * @return bool
    1887       */
    1888      public function has_set_url() {
    1889          return ($this->_url!==null);
    1890      }
    1891  
    1892      /**
    1893       * Gets set when the block actions for the page have been processed.
    1894       *
    1895       * @param bool $setting
    1896       */
    1897      public function set_block_actions_done($setting = true) {
    1898          $this->_block_actions_done = $setting;
    1899      }
    1900  
    1901      /**
    1902       * Are popup notifications allowed on this page?
    1903       * Popup notifications may be disallowed in situations such as while upgrading or completing a quiz
    1904       *
    1905       * @return bool true if popup notifications may be displayed
    1906       */
    1907      public function get_popup_notification_allowed() {
    1908          return $this->_popup_notification_allowed;
    1909      }
    1910  
    1911      /**
    1912       * Allow or disallow popup notifications on this page. Popups are allowed by default.
    1913       *
    1914       * @param bool $allowed true if notifications are allowed. False if not allowed. They are allowed by default.
    1915       */
    1916      public function set_popup_notification_allowed($allowed) {
    1917          $this->_popup_notification_allowed = $allowed;
    1918      }
    1919  
    1920      /**
    1921       * Returns the block region having made any required theme manipulations.
    1922       *
    1923       * @since Moodle 2.5.1 2.6
    1924       * @param string $region
    1925       * @return string
    1926       */
    1927      public function apply_theme_region_manipulations($region) {
    1928          if ($this->blockmanipulations && isset($this->blockmanipulations[$region])) {
    1929              $regionwas = $region;
    1930              $regionnow = $this->blockmanipulations[$region];
    1931              if ($this->blocks->is_known_region($regionwas) && $this->blocks->is_known_region($regionnow)) {
    1932                  // Both the before and after regions are known so we can swap them over.
    1933                  return $regionnow;
    1934              }
    1935              // We didn't know about both, we won't swap them over.
    1936              return $regionwas;
    1937          }
    1938          return $region;
    1939      }
    1940  }
    

    Search This Site: