Merge branch 'wip-MDL-30979-m23' of git://github.com/samhemelryk/moodle
[moodle.git] / lib / pagelib.php
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/>.
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  */
28 defined('MOODLE_INTERNAL') || die();
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 blocks_manager $blocks The blocks manager object for this page.
55  * @property-read string $bodyclasses A string to use within the class attribute on the body tag.
56  * @property-read string $bodyid A string to use as the id of the body tag.
57  * @property-read string $button The HTML to go where the Turn editing on button normally goes.
58  * @property-read bool $cacheable Defaults to true. Set to false to stop the page being cached at all.
59  * @property-read array $categories An array of all the categories the page course belongs to,
60  *      starting with the immediately containing category, and working out to
61  *      the top-level category. This may be the empty array if we are in the
62  *      front page course.
63  * @property-read mixed $category The category that the page course belongs to.
64  * @property-read cm_info $cm The course_module that this page belongs to. Will be null
65  *      if this page is not within a module. This is a full cm object, as loaded
66  *      by get_coursemodule_from_id or get_coursemodule_from_instance,
67  *      so the extra modname and name fields are present.
68  * @property-read context $context The main context to which this page belongs.
69  * @property-read stdClass $course The current course that we are inside - a row from the
70  *      course table. (Also available as $COURSE global.) If we are not inside
71  *      an actual course, this will be the site course.
72  * @property-read string $devicetypeinuse The name of the device type in use
73  * @property-read string $docspath The path to the Moodle docs for this page.
74  * @property-read string $focuscontrol The id of the HTML element to be focused when the page has loaded.
75  * @property-read bool $headerprinted True if the page header has already been printed.
76  * @property-read string $heading The main heading that should be displayed at the top of the <body>.
77  * @property-read string $headingmenu The menu (or actions) to display in the heading
78  * @property-read array $layout_options An arrays with options for the layout file.
79  * @property-read array $legacythemeinuse True if the legacy browser theme is in use.
80  * @property-read navbar $navbar The navbar object used to display the navbar
81  * @property-read global_navigation $navigation The navigation structure for this page.
82  * @property-read xml_container_stack $opencontainers Tracks XHTML tags on this page that have been opened but not closed.
83  *      mainly for internal use by the rendering code.
84  * @property-read string $pagelayout The general type of page this is. For example 'normal', 'popup', 'home'.
85  *      Allows the theme to display things differently, if it wishes to.
86  * @property-read string $pagetype The page type string, should be used as the id for the body tag in the theme.
87  * @property-read int $periodicrefreshdelay The periodic refresh delay to use with meta refresh
88  * @property-read page_requirements_manager $requires Tracks the JavaScript, CSS files, etc. required by this page.
89  * @property-read settings_navigation $settingsnav The settings navigation
90  * @property-read int $state One of the STATE_... constants
91  * @property-read string $subpage The subpage identifier, if any.
92  * @property-read theme_config $theme The theme for this page.
93  * @property-read string $title The title that should go in the <head> section of the HTML of this page.
94  * @property-read moodle_url $url The moodle url object for this page.
95  */
96 class moodle_page {
98     /** The state of the page before it has printed the header **/
99     const STATE_BEFORE_HEADER = 0;
101     /** The state the page is in temporarily while the header is being printed **/
102     const STATE_PRINTING_HEADER = 1;
104     /** The state the page is in while content is presumably being printed **/
105     const STATE_IN_BODY = 2;
107     /**
108      * The state the page is when the footer has been printed and its function is
109      * complete.
110      */
111     const STATE_DONE = 3;
113     /**
114      * @var int The current state of the page. The state a page is within
115      * determines what actions are possible for it.
116      */
117     protected $_state = self::STATE_BEFORE_HEADER;
119     /**
120      * @var stdClass The course currently associated with this page.
121      * If not has been provided the front page course is used.
122      */
123     protected $_course = null;
125     /**
126      * @var cm_info If this page belongs to a module, this is the cm_info module
127      * description object.
128      */
129     protected $_cm = null;
131     /**
132      * @var stdClass If $_cm is not null, then this will hold the corresponding
133      * row from the modname table. For example, if $_cm->modname is 'quiz', this
134      * will be a row from the quiz table.
135      */
136     protected $_module = null;
138     /**
139      * @var context The context that this page belongs to.
140      */
141     protected $_context = null;
143     /**
144      * @var array This holds any categories that $_course belongs to, starting with the
145      * particular category it belongs to, and working out through any parent
146      * categories to the top level. These are loaded progressively, if needed.
147      * There are three states. $_categories = null initially when nothing is
148      * loaded; $_categories = array($id => $cat, $parentid => null) when we have
149      * loaded $_course->category, but not any parents; and a complete array once
150      * everything is loaded.
151      */
152     protected $_categories = null;
154     /**
155      * @var array An array of CSS classes that should be added to the body tag in HTML.
156      */
157     protected $_bodyclasses = array();
159     /**
160      * @var string The title for the page. Used within the title tag in the HTML head.
161      */
162     protected $_title = '';
164     /**
165      * @var string The string to use as the heading of the page. Shown near the top of the
166      * page within most themes.
167      */
168     protected $_heading = '';
170     /**
171      * @var string The pagetype is used to describe the page and defaults to a representation
172      * of the physical path to the page e.g. my-index, mod-quiz-attempt
173      */
174     protected $_pagetype = null;
176     /**
177      * @var string The pagelayout to use when displaying this page. The
178      * pagelayout needs to have been defined by the theme in use, or one of its
179      * parents. By default base is used however standard is the more common layout.
180      * Note that this gets automatically set by core during operations like
181      * require_login.
182      */
183     protected $_pagelayout = 'base';
185     /**
186      * @var array List of theme layout options, these are ignored by core.
187      * To be used in individual theme layout files only.
188      */
189     protected $_layout_options = array();
191     /**
192      * @var string An optional arbitrary parameter that can be set on pages where the context
193      * and pagetype is not enough to identify the page.
194      */
195     protected $_subpage = '';
197     /**
198      * @var string Set a different path to use for the 'Moodle docs for this page' link.
199      * By default, it uses the path of the file for instance mod/quiz/attempt.
200      */
201     protected $_docspath = null;
203     /**
204      * @var string A legacy class that will be added to the body tag
205      */
206     protected $_legacyclass = null;
208     /**
209      * @var moodle_url The URL for this page. This is mandatory and must be set
210      * before output is started.
211      */
212     protected $_url = null;
214     /**
215      * @var array An array of links to alternative versions of this page.
216      * Primarily used for RSS versions of the current page.
217      */
218     protected $_alternateversions = array();
220     /**
221      * @var block_manager The blocks manager for this page. It is reponsible for
222      * the blocks and there content on this page.
223      */
224     protected $_blocks = null;
226     /**
227      * @var page_requirements_manager Page requirements manager. It is reponsible
228      * for all JavaScript and CSS resources required by this page.
229      */
230     protected $_requires = null;
232     /**
233      * @var string The capability required by the user in order to edit blocks
234      * and block settings on this page.
235      */
236     protected $_blockseditingcap = 'moodle/site:manageblocks';
238     /**
239      * @var bool An internal flag to record when block actions have been processed.
240      * Remember block actions occur on the current URL and it is important that
241      * even they are never executed more than once.
242      */
243     protected $_block_actions_done = false;
245     /**
246      * @var array An array of any other capabilities the current user must have
247      * in order to editing the page and/or its content (not just blocks).
248      */
249     protected $_othereditingcaps = array();
251     /**
252      * @var bool Sets whether this page should be cached by the browser or not.
253      * If it is set to true (default) the page is served with caching headers.
254      */
255     protected $_cacheable = true;
257     /**
258      * @var string Can be set to the ID of an element on the page, if done that
259      * element receives focus when the page loads.
260      */
261     protected $_focuscontrol = '';
263     /**
264      * @var string HTML to go where the turn on editing button is located. This
265      * is nearly a legacy item and not used very often any more.
266      */
267     protected $_button = '';
269     /**
270      * @var theme_config The theme to use with this page. This has to be properly
271      * initialised via {@link moodle_page::initialise_theme_and_output()} which
272      * happens magically before any operation that requires it.
273      */
274     protected $_theme = null;
276     /**
277      * @var global_navigation Contains the global navigation structure.
278      */
279     protected $_navigation = null;
281     /**
282      * @var settings_navigation Contains the settings navigation structure.
283      */
284     protected $_settingsnav = null;
286     /**
287      * @var navbar Contains the navbar structure.
288      */
289     protected $_navbar = null;
291     /**
292      * @var string The menu (or actions) to display in the heading.
293      */
294     protected $_headingmenu = null;
296     /**
297      * @var array stack trace. Then the theme is initialised, we save the stack
298      * trace, for use in error messages.
299      */
300     protected $_wherethemewasinitialised = null;
302     /**
303      * @var xhtml_container_stack Tracks XHTML tags on this page that have been
304      * opened but not closed.
305      */
306     protected $_opencontainers;
308     /**
309      * @var int Sets the page to refresh after a given delay (in seconds) using
310      * meta refresh in {@link standard_head_html()} in outputlib.php
311      * If set to null(default) the page is not refreshed
312      */
313     protected $_periodicrefreshdelay = null;
315     /**
316      * @var stdClass This is simply to improve backwards compatibility. If old
317      * code relies on a page class that implements print_header, or complex logic
318      * in user_allowed_editing then we stash an instance of that other class here,
319      * and delegate to it in certain situations.
320      */
321     protected $_legacypageobject = null;
323     /**
324      * @var array Associative array of browser shortnames (as used by check_browser_version)
325      * and their minimum required versions
326      */
327     protected $_legacybrowsers = array('MSIE' => 6.0);
329     /**
330      * @var string Is set to the name of the device type in use.
331      * This will we worked out when it is first used.
332      */
333     protected $_devicetypeinuse = null;
335     /**
336      * @var bool Used to determine if HTTPS should be required for login.
337      */
338     protected $_https_login_required = false;
340     /**
341      * @var bool Determines if popup notifications allowed on this page.
342      * Code such as the quiz module disables popup notifications in situations
343      * such as upgrading or completing a quiz.
344      */
345     protected $_popup_notification_allowed = true;
347     // Magic getter methods =============================================================
348     // Due to the __get magic below, you normally do not call these as $PAGE->magic_get_x
349     // methods, but instead use the $PAGE->x syntax.
351     /**
352      * Please do not call this method directly, use the ->state syntax. {@link moodle_page::__get()}.
353      * @return integer one of the STATE_XXX constants. You should not normally need
354      * to use this in your code. It is intended for internal use by this class
355      * and its friends like print_header, to check that everything is working as
356      * expected. Also accessible as $PAGE->state.
357      */
358     protected function magic_get_state() {
359         return $this->_state;
360     }
362     /**
363      * Please do not call this method directly, use the ->headerprinted syntax. {@link moodle_page::__get()}.
364      * @return bool has the header already been printed?
365      */
366     protected function magic_get_headerprinted() {
367         return $this->_state >= self::STATE_IN_BODY;
368     }
370     /**
371      * Please do not call this method directly, use the ->course syntax. {@link moodle_page::__get()}.
372      * @return stdClass the current course that we are inside - a row from the
373      * course table. (Also available as $COURSE global.) If we are not inside
374      * an actual course, this will be the site course.
375      */
376     protected function magic_get_course() {
377         global $SITE;
378         if (is_null($this->_course)) {
379             return $SITE;
380         }
381         return $this->_course;
382     }
384     /**
385      * Please do not call this method directly, use the ->cm syntax. {@link moodle_page::__get()}.
386      * @return cm_info the course_module that this page belongs to. Will be null
387      * if this page is not within a module. This is a full cm object, as loaded
388      * by get_coursemodule_from_id or get_coursemodule_from_instance,
389      * so the extra modname and name fields are present.
390      */
391     protected function magic_get_cm() {
392         return $this->_cm;
393     }
395     /**
396      * Please do not call this method directly, use the ->activityrecord syntax. {@link moodle_page::__get()}.
397      * @return stdClass the row from the activities own database table (for example
398      * the forum or quiz table) that this page belongs to. Will be null
399      * if this page is not within a module.
400      */
401     protected function magic_get_activityrecord() {
402         if (is_null($this->_module) && !is_null($this->_cm)) {
403             $this->load_activity_record();
404         }
405         return $this->_module;
406     }
408     /**
409      * Please do not call this method directly, use the ->activityname syntax. {@link moodle_page::__get()}.
410      * @return string the The type of activity we are in, for example 'forum' or 'quiz'.
411      * Will be null if this page is not within a module.
412      */
413     protected function magic_get_activityname() {
414         if (is_null($this->_cm)) {
415             return null;
416         }
417         return $this->_cm->modname;
418     }
420     /**
421      * Please do not call this method directly, use the ->category syntax. {@link moodle_page::__get()}.
422      * @return stdClass the category that the page course belongs to. If there isn't one
423      * (that is, if this is the front page course) returns null.
424      */
425     protected function magic_get_category() {
426         $this->ensure_category_loaded();
427         if (!empty($this->_categories)) {
428             return reset($this->_categories);
429         } else {
430             return null;
431         }
432     }
434     /**
435      * Please do not call this method directly, use the ->categories syntax. {@link moodle_page::__get()}.
436      * @return array an array of all the categories the page course belongs to,
437      * starting with the immediately containing category, and working out to
438      * the top-level category. This may be the empty array if we are in the
439      * front page course.
440      */
441     protected function magic_get_categories() {
442         $this->ensure_categories_loaded();
443         return $this->_categories;
444     }
446     /**
447      * Please do not call this method directly, use the ->context syntax. {@link moodle_page::__get()}.
448      * @return context the main context to which this page belongs.
449      */
450     protected function magic_get_context() {
451         if (is_null($this->_context)) {
452             if (CLI_SCRIPT or NO_MOODLE_COOKIES) {
453                 // cli scripts work in system context, do not annoy devs with debug info
454                 // very few scripts do not use cookies, we can safely use system as default context there
455             } else {
456                 debugging('Coding problem: $PAGE->context was not set. You may have forgotten '
457                     .'to call require_login() or $PAGE->set_context(). The page may not display '
458                     .'correctly as a result');
459             }
460             $this->_context = get_context_instance(CONTEXT_SYSTEM);
461         }
462         return $this->_context;
463     }
465     /**
466      * Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
467      * @return string e.g. 'my-index' or 'mod-quiz-attempt'.
468      */
469     protected function magic_get_pagetype() {
470         global $CFG;
471         if (is_null($this->_pagetype) || isset($CFG->pagepath)) {
472             $this->initialise_default_pagetype();
473         }
474         return $this->_pagetype;
475     }
477     /**
478      * Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
479      * @return string The id to use on the body tag, uses {@link magic_get_pagetype()}.
480      */
481     protected function magic_get_bodyid() {
482         return 'page-'.$this->pagetype;
483     }
485     /**
486      * Please do not call this method directly, use the ->pagelayout syntax. {@link moodle_page::__get()}.
487      * @return string the general type of page this is. For example 'standard', 'popup', 'home'.
488      *      Allows the theme to display things differently, if it wishes to.
489      */
490     protected function magic_get_pagelayout() {
491         return $this->_pagelayout;
492     }
494     /**
495      * Please do not call this method directly, use the ->layout_tions syntax. {@link moodle_page::__get()}.
496      * @return array returns arrys with options for layout file
497      */
498     protected function magic_get_layout_options() {
499         return $this->_layout_options;
500     }
502     /**
503      * Please do not call this method directly, use the ->subpage syntax. {@link moodle_page::__get()}.
504      * @return string The subpage identifier, if any.
505      */
506     protected function magic_get_subpage() {
507         return $this->_subpage;
508     }
510     /**
511      * Please do not call this method directly, use the ->bodyclasses syntax. {@link moodle_page::__get()}.
512      * @return string the class names to put on the body element in the HTML.
513      */
514     protected function magic_get_bodyclasses() {
515         return implode(' ', array_keys($this->_bodyclasses));
516     }
518     /**
519      * Please do not call this method directly, use the ->title syntax. {@link moodle_page::__get()}.
520      * @return string the title that should go in the <head> section of the HTML of this page.
521      */
522     protected function magic_get_title() {
523         return $this->_title;
524     }
526     /**
527      * Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
528      * @return string the main heading that should be displayed at the top of the <body>.
529      */
530     protected function magic_get_heading() {
531         return $this->_heading;
532     }
534     /**
535      * Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
536      * @return string The menu (or actions) to display in the heading
537      */
538     protected function magic_get_headingmenu() {
539         return $this->_headingmenu;
540     }
542     /**
543      * Please do not call this method directly, use the ->docspath syntax. {@link moodle_page::__get()}.
544      * @return string the path to the Moodle docs for this page.
545      */
546     protected function magic_get_docspath() {
547         if (is_string($this->_docspath)) {
548             return $this->_docspath;
549         } else {
550             return str_replace('-', '/', $this->pagetype);
551         }
552     }
554     /**
555      * Please do not call this method directly, use the ->url syntax. {@link moodle_page::__get()}.
556      * @return moodle_url the clean URL required to load the current page. (You
557      * should normally use this in preference to $ME or $FULLME.)
558      */
559     protected function magic_get_url() {
560         global $FULLME;
561         if (is_null($this->_url)) {
562             debugging('This page did not call $PAGE->set_url(...). Using '.s($FULLME), DEBUG_DEVELOPER);
563             $this->_url = new moodle_url($FULLME);
564             // Make sure the guessed URL cannot lead to dangerous redirects.
565             $this->_url->remove_params('sesskey');
566         }
567         return new moodle_url($this->_url); // Return a clone for safety.
568     }
570     /**
571      * The list of alternate versions of this page.
572      * @return array mime type => object with ->url and ->title.
573      */
574     protected function magic_get_alternateversions() {
575         return $this->_alternateversions;
576     }
578     /**
579      * Please do not call this method directly, use the ->blocks syntax. {@link moodle_page::__get()}.
580      * @return blocks_manager the blocks manager object for this page.
581      */
582     protected function magic_get_blocks() {
583         global $CFG;
584         if (is_null($this->_blocks)) {
585             if (!empty($CFG->blockmanagerclass)) {
586                 $classname = $CFG->blockmanagerclass;
587             } else {
588                 $classname = 'block_manager';
589             }
590             $this->_blocks = new $classname($this);
591         }
592         return $this->_blocks;
593     }
595     /**
596      * Please do not call this method directly, use the ->requires syntax. {@link moodle_page::__get()}.
597      * @return page_requirements_manager tracks the JavaScript, CSS files, etc. required by this page.
598      */
599     protected function magic_get_requires() {
600         global $CFG;
601         if (is_null($this->_requires)) {
602             $this->_requires = new page_requirements_manager();
603         }
604         return $this->_requires;
605     }
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     }
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     }
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     }
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     }
642     /**
643      * Please do not call this method directly, use the ->devicetypeinuse syntax. {@link moodle_page::__get()}.
644      * @return string The device type being used.
645      */
646     protected function magic_get_devicetypeinuse() {
647         if (empty($this->_devicetypeinuse)) {
648             $this->_devicetypeinuse = get_user_device_type();
649         }
650         return $this->_devicetypeinuse;
651     }
653     /**
654      * Please do not call this method directly, use the ->legacythemeinuse syntax. {@link moodle_page::__get()}.
655      * @deprecated since 2.1
656      * @return bool
657      */
658     protected function magic_get_legacythemeinuse() {
659         debugging('$PAGE->legacythemeinuse is a deprecated property - please use $PAGE->devicetypeinuse and check if it is equal to legacy.', DEBUG_DEVELOPER);
660         return ($this->devicetypeinuse == 'legacy');
661     }
663     /**
664      * Please do not call this method directly use the ->periodicrefreshdelay syntax
665      * {@link moodle_page::__get()}
666      * @return int The periodic refresh delay to use with meta refresh
667      */
668     protected function magic_get_periodicrefreshdelay() {
669         return $this->_periodicrefreshdelay;
670     }
672     /**
673      * Please do not call this method directly use the ->opencontainers syntax. {@link moodle_page::__get()}
674      * @return xhtml_container_stack tracks XHTML tags on this page that have been opened but not closed.
675      *      mainly for internal use by the rendering code.
676      */
677     protected function magic_get_opencontainers() {
678         if (is_null($this->_opencontainers)) {
679             $this->_opencontainers = new xhtml_container_stack();
680         }
681         return $this->_opencontainers;
682     }
684     /**
685      * Return the navigation object
686      * @return global_navigation
687      */
688     protected function magic_get_navigation() {
689         if ($this->_navigation === null) {
690             $this->_navigation = new global_navigation($this);
691         }
692         return $this->_navigation;
693     }
695     /**
696      * Return a navbar object
697      * @return navbar
698      */
699     protected function magic_get_navbar() {
700         if ($this->_navbar === null) {
701             $this->_navbar = new navbar($this);
702         }
703         return $this->_navbar;
704     }
706     /**
707      * Returns the settings navigation object
708      * @return settings_navigation
709      */
710     protected function magic_get_settingsnav() {
711         if ($this->_settingsnav === null) {
712             $this->_settingsnav = new settings_navigation($this);
713             $this->_settingsnav->initialise();
714         }
715         return $this->_settingsnav;
716     }
718     /**
719      * PHP overloading magic to make the $PAGE->course syntax work by redirecting
720      * it to the corresponding $PAGE->magic_get_course() method if there is one, and
721      * throwing an exception if not.
722      *
723      * @param string $name property name
724      * @return mixed
725      */
726     public function __get($name) {
727         $getmethod = 'magic_get_' . $name;
728         if (method_exists($this, $getmethod)) {
729             return $this->$getmethod();
730         } else {
731             throw new coding_exception('Unknown property ' . $name . ' of $PAGE.');
732         }
733     }
735     /**
736      * PHP overloading magic to catch obvious coding errors.
737      *
738      * This method has been created to catch obvious coding errors where the
739      * developer has tried to set a page property using $PAGE->key = $value.
740      * In the moodle_page class all properties must be set using the appropriate
741      * $PAGE->set_something($value) method.
742      *
743      * @param string $name property name
744      * @param mixed $value Value
745      * @return void Throws exception if field not defined in page class
746      */
747     public function __set($name, $value) {
748         if (method_exists($this, 'set_' . $name)) {
749             throw new coding_exception('Invalid attempt to modify page object', "Use \$PAGE->set_$name() instead.");
750         } else {
751             throw new coding_exception('Invalid attempt to modify page object', "Unknown property $name");
752         }
753     }
755     // Other information getting methods ==========================================
757     /**
758      * Returns instance of page renderer
759      *
760      * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
761      * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
762      * @param string $target one of rendering target constants
763      * @return renderer_base
764      */
765     public function get_renderer($component, $subtype = null, $target = null) {
766         return $this->magic_get_theme()->get_renderer($this, $component, $subtype, $target);
767     }
769     /**
770      * Checks to see if there are any items on the navbar object
771      *
772      * @return bool true if there are, false if not
773      */
774     public function has_navbar() {
775         if ($this->_navbar === null) {
776             $this->_navbar = new navbar($this);
777         }
778         return $this->_navbar->has_items();
779     }
781     /**
782      * Should the current user see this page in editing mode.
783      * That is, are they allowed to edit this page, and are they currently in
784      * editing mode.
785      * @return bool
786      */
787     public function user_is_editing() {
788         global $USER;
789         return !empty($USER->editing) && $this->user_allowed_editing();
790     }
792     /**
793      * Does the user have permission to edit blocks on this page.
794      * @return bool
795      */
796     public function user_can_edit_blocks() {
797         return has_capability($this->_blockseditingcap, $this->_context);
798     }
800     /**
801      * Does the user have permission to see this page in editing mode.
802      * @return bool
803      */
804     public function user_allowed_editing() {
805         if ($this->_legacypageobject) {
806             return $this->_legacypageobject->user_allowed_editing();
807         }
808         return has_any_capability($this->all_editing_caps(), $this->_context);
809     }
811     /**
812      * Get a description of this page. Normally displayed in the footer in
813      * developer debug mode.
814      * @return string
815      */
816     public function debug_summary() {
817         $summary = '';
818         $summary .= 'General type: ' . $this->pagelayout . '. ';
819         if (!during_initial_install()) {
820             $summary .= 'Context ' . print_context_name($this->_context) . ' (context id ' . $this->_context->id . '). ';
821         }
822         $summary .= 'Page type ' . $this->pagetype .  '. ';
823         if ($this->subpage) {
824             'Sub-page ' . $this->subpage .  '. ';
825         }
826         return $summary;
827     }
829     // Setter methods =============================================================
831     /**
832      * Set the state. The state must be one of that STATE_... constants, and
833      * the state is only allowed to advance one step at a time.
834      *
835      * @param integer $state The new state.
836      */
837     public function set_state($state) {
838         if ($state != $this->_state + 1 || $state > self::STATE_DONE) {
839             throw new coding_exception('Invalid state passed to moodle_page::set_state. We are in state ' .
840                     $this->_state . ' and state ' . $state . ' was requested.');
841         }
843         if ($state == self::STATE_PRINTING_HEADER) {
844             $this->starting_output();
845         }
847         $this->_state = $state;
848     }
850     /**
851      * Set the current course. This sets both $PAGE->course and $COURSE. It also
852      * sets the right theme and locale.
853      *
854      * Normally you don't need to call this function yourself, require_login will
855      * call it for you if you pass a $course to it. You can use this function
856      * on pages that do need to call require_login().
857      *
858      * Sets $PAGE->context to the course context, if it is not already set.
859      *
860      * @param stdClass $course the course to set as the global course.
861      */
862     public function set_course($course) {
863         global $COURSE, $PAGE;
865         if (empty($course->id)) {
866             throw new coding_exception('$course passed to moodle_page::set_course does not look like a proper course object.');
867         }
869         $this->ensure_theme_not_set();
871         if (!empty($this->_course->id) && $this->_course->id != $course->id) {
872             $this->_categories = null;
873         }
875         $this->_course = clone($course);
877         if ($this === $PAGE) {
878             $COURSE = $this->_course;
879             moodle_setlocale();
880         }
882         if (!$this->_context) {
883             $this->set_context(get_context_instance(CONTEXT_COURSE, $this->_course->id));
884         }
885     }
887     /**
888      * Set the main context to which this page belongs.
889      *
890      * @param context $context a context object, normally obtained with get_context_instance.
891      */
892     public function set_context($context) {
893         if ($context === null) {
894             // extremely ugly hack which sets context to some value in order to prevent warnings,
895             // use only for core error handling!!!!
896             if (!$this->_context) {
897                 $this->_context = get_context_instance(CONTEXT_SYSTEM);
898             }
899             return;
900         }
902         // ideally we should set context only once
903         if (isset($this->_context)) {
904             if ($context->id == $this->_context->id) {
905                 // fine - no change needed
906             } else if ($this->_context->contextlevel == CONTEXT_SYSTEM or $this->_context->contextlevel == CONTEXT_COURSE) {
907                 // hmm - not ideal, but it might produce too many warnings due to the design of require_login
908             } else if ($this->_context->contextlevel == CONTEXT_MODULE and $this->_context->id == get_parent_contextid($context)) {
909                 // hmm - most probably somebody did require_login() and after that set the block context
910             } else {
911                 // we do not want devs to do weird switching of context levels on the fly,
912                 // because we might have used the context already such as in text filter in page title
913                 debugging('Coding problem: unsupported modification of PAGE->context from '.$this->_context->contextlevel.' to '.$context->contextlevel);
914             }
915         }
917         $this->_context = $context;
918     }
920     /**
921      * The course module that this page belongs to (if it does belong to one).
922      *
923      * @param stdClass|cm_info $cm a record from course_modules table or cm_info from get_fast_modinfo().
924      * @param stdClass $course
925      * @param stdClass $module
926      * @return void
927      */
928     public function set_cm($cm, $course = null, $module = null) {
929         global $DB;
931         if (!isset($cm->id) || !isset($cm->course)) {
932             throw new coding_exception('Invalid $cm parameter for $PAGE object, it has to be instance of cm_info or record from the course_modules table.');
933         }
935         if (!$this->_course || $this->_course->id != $cm->course) {
936             if (!$course) {
937                 $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
938             }
939             if ($course->id != $cm->course) {
940                 throw new coding_exception('The course you passed to $PAGE->set_cm does not correspond to the $cm.');
941             }
942             $this->set_course($course);
943         }
945         // make sure we have a $cm from get_fast_modinfo as this contains activity access details
946         if (!($cm instanceof cm_info)) {
947             $modinfo = get_fast_modinfo($this->_course);
948             $cm = $modinfo->get_cm($cm->id);
949         }
950         $this->_cm = $cm;
952         // unfortunately the context setting is a mess, let's try to work around some common block problems and show some debug messages
953         if (empty($this->_context) or $this->_context->contextlevel != CONTEXT_BLOCK) {
954             $context = get_context_instance(CONTEXT_MODULE, $cm->id);
955             $this->set_context($context);
956         }
958         if ($module) {
959             $this->set_activity_record($module);
960         }
961     }
963     /**
964      * Sets the activity record. This could be a row from the main table for a
965      * module. For instance if the current module (cm) is a forum this should be a row
966      * from the forum table.
967      *
968      * @param stdClass $module A row from the main database table for the module that this
969      * page belongs to.
970      * @return void
971      */
972     public function set_activity_record($module) {
973         if (is_null($this->_cm)) {
974             throw new coding_exception('You cannot call $PAGE->set_activity_record until after $PAGE->cm has been set.');
975         }
976         if ($module->id != $this->_cm->instance || $module->course != $this->_course->id) {
977             throw new coding_exception('The activity record your are trying to set does not seem to correspond to the cm that has been set.');
978         }
979         $this->_module = $module;
980     }
982     /**
983      * Sets the pagetype to use for this page.
984      *
985      * Normally you do not need to set this manually, it is automatically created
986      * from the script name. However, on some pages this is overridden.
987      * For example the page type for course/view.php includes the course format,
988      * for example 'course-view-weeks'. This gets used as the id attribute on
989      * <body> and also for determining which blocks are displayed.
990      *
991      * @param string $pagetype e.g. 'my-index' or 'mod-quiz-attempt'.
992      */
993     public function set_pagetype($pagetype) {
994         $this->_pagetype = $pagetype;
995     }
997     /**
998      * Sets the layout to use for this page.
999      *
1000      * The page layout determines how the page will be displayed, things such as
1001      * block regions, content areas, etc are controlled by the layout.
1002      * The theme in use for the page will determine that the layout contains.
1003      *
1004      * This properly defaults to 'base', so you only need to call this function if
1005      * you want something different. The exact range of supported layouts is specified
1006      * in the standard theme.
1007      *
1008      * For an idea of the common page layouts see
1009      * {@link http://docs.moodle.org/dev/Themes_2.0#The_different_layouts_as_of_August_17th.2C_2010}
1010      * But please keep in mind that it may be (and normally is) out of date.
1011      * The only place to find an accurate up-to-date list of the page layouts
1012      * available for your version of Moodle is {@link theme/base/config.php}
1013      *
1014      * @param string $pagelayout the page layout this is. For example 'popup', 'home'.
1015      */
1016     public function set_pagelayout($pagelayout) {
1017         /**
1018          * Uncomment this to debug theme pagelayout issues like missing blocks.
1019          *
1020          * if (!empty($this->_wherethemewasinitialised) && $pagelayout != $this->_pagelayout) {
1021          *     debugging('Page layout has already been set and cannot be changed.', DEBUG_DEVELOPER);
1022          * }
1023          */
1024         $this->_pagelayout = $pagelayout;
1025     }
1027     /**
1028      * If context->id and pagetype are not enough to uniquely identify this page,
1029      * then you can set a subpage id as well. For example, the tags page sets
1030      *
1031      * @param string $subpage an arbitrary identifier that, along with context->id
1032      *      and pagetype, uniquely identifies this page.
1033      */
1034     public function set_subpage($subpage) {
1035         if (empty($subpage)) {
1036             $this->_subpage = '';
1037         } else {
1038             $this->_subpage = $subpage;
1039         }
1040     }
1042     /**
1043      * Adds a CSS class to the body tag of the page.
1044      *
1045      * @param string $class add this class name ot the class attribute on the body tag.
1046      */
1047     public function add_body_class($class) {
1048         if ($this->_state > self::STATE_BEFORE_HEADER) {
1049             throw new coding_exception('Cannot call moodle_page::add_body_class after output has been started.');
1050         }
1051         $this->_bodyclasses[$class] = 1;
1052     }
1054     /**
1055      * Adds an array of body classes to the body tag of this page.
1056      *
1057      * @param array $classes this utility method calls add_body_class for each array element.
1058      */
1059     public function add_body_classes($classes) {
1060         foreach ($classes as $class) {
1061             $this->add_body_class($class);
1062         }
1063     }
1065     /**
1066      * Sets the title for the page.
1067      * This is normally used within the title tag in the head of the page.
1068      *
1069      * @param string $title the title that should go in the <head> section of the HTML of this page.
1070      */
1071     public function set_title($title) {
1072         $title = format_string($title);
1073         $title = str_replace('"', '&quot;', $title);
1074         $this->_title = $title;
1075     }
1077     /**
1078      * Sets the heading to use for the page.
1079      * This is normally used as the main heading at the top of the content.
1080      *
1081      * @param string $heading the main heading that should be displayed at the top of the <body>.
1082      */
1083     public function set_heading($heading) {
1084         $this->_heading = format_string($heading);
1085     }
1087     /**
1088      * Sets some HTML to use next to the heading {@link moodle_page::set_heading()}
1089      *
1090      * @param string $menu The menu/content to show in the heading
1091      */
1092     public function set_headingmenu($menu) {
1093         $this->_headingmenu = $menu;
1094     }
1096     /**
1097      * Set the course category this page belongs to manually.
1098      *
1099      * This automatically sets $PAGE->course to be the site course. You cannot
1100      * use this method if you have already set $PAGE->course - in that case,
1101      * the category must be the one that the course belongs to. This also
1102      * automatically sets the page context to the category context.
1103      *
1104      * @param integer $categoryid The id of the category to set.
1105      */
1106     public function set_category_by_id($categoryid) {
1107         global $SITE, $DB;
1108         if (!is_null($this->_course)) {
1109             throw new coding_exception('Attempt to manually set the course category when the course has been set. This is not allowed.');
1110         }
1111         if (is_array($this->_categories)) {
1112             throw new coding_exception('Course category has already been set. You are not allowed to change it.');
1113         }
1114         $this->ensure_theme_not_set();
1115         $this->set_course($SITE);
1116         $this->load_category($categoryid);
1117         $this->set_context(get_context_instance(CONTEXT_COURSECAT, $categoryid));
1118     }
1120     /**
1121      * Set a different path to use for the 'Moodle docs for this page' link.
1122      *
1123      * By default, it uses the pagetype, which is normally the same as the
1124      * script name. So, for example, for mod/quiz/attempt.php, pagetype is
1125      * mod-quiz-attempt, and so docspath is mod/quiz/attempt.
1126      *
1127      * @param string $path the path to use at the end of the moodle docs URL.
1128      */
1129     public function set_docs_path($path) {
1130         $this->_docspath = $path;
1131     }
1133     /**
1134      * You should call this method from every page to set the cleaned-up URL
1135      * that should be used to return to this page.
1136      *
1137      * Used, for example, by the blocks editing UI to know where to return the
1138      * user after an action.
1139      * For example, course/view.php does:
1140      *      $id = optional_param('id', 0, PARAM_INT);
1141      *      $PAGE->set_url('/course/view.php', array('id' => $id));
1142      *
1143      * @param moodle_url|string $url URL relative to $CFG->wwwroot or {@link moodle_url} instance
1144      * @param array $params parameters to add to the URL
1145      */
1146     public function set_url($url, array $params = null) {
1147         global $CFG;
1149         if (is_string($url)) {
1150             if (strpos($url, 'http') === 0) {
1151                 // ok
1152             } else if (strpos($url, '/') === 0) {
1153                 // we have to use httpswwwroot here, because of loginhttps pages
1154                 $url = $CFG->httpswwwroot . $url;
1155             } else {
1156                 throw new coding_exception('Invalid parameter $url, has to be full url or in shortened form starting with /.');
1157             }
1158         }
1160         $this->_url = new moodle_url($url, $params);
1162         $fullurl = $this->_url->out_omit_querystring();
1163         if (strpos($fullurl, "$CFG->httpswwwroot/") !== 0) {
1164             debugging('Most probably incorrect set_page() url argument, it does not match the httpswwwroot!');
1165         }
1166         $shorturl = str_replace("$CFG->httpswwwroot/", '', $fullurl);
1168         if (is_null($this->_pagetype)) {
1169             $this->initialise_default_pagetype($shorturl);
1170         }
1171         if (!is_null($this->_legacypageobject)) {
1172             $this->_legacypageobject->set_url($url, $params);
1173         }
1174     }
1176     /**
1177      * Make sure page URL does not contain the given URL parameter.
1178      *
1179      * This should not be necessary if the script has called set_url properly.
1180      * However, in some situations like the block editing actions; when the URL
1181      * has been guessed, it will contain dangerous block-related actions.
1182      * Therefore, the blocks code calls this function to clean up such parameters
1183      * before doing any redirect.
1184      *
1185      * @param string $param the name of the parameter to make sure is not in the
1186      * page URL.
1187      */
1188     public function ensure_param_not_in_url($param) {
1189         $discard = $this->url; // Make sure $this->url is lazy-loaded;
1190         $this->_url->remove_params($param);
1191     }
1193     /**
1194      * There can be alternate versions of some pages (for example an RSS feed version).
1195      * If such other version exist, call this method, and a link to the alternate
1196      * version will be included in the <head> of the page.
1197      *
1198      * @param string $title The title to give the alternate version.
1199      * @param string|moodle_url $url The URL of the alternate version.
1200      * @param string $mimetype The mime-type of the alternate version.
1201      */
1202     public function add_alternate_version($title, $url, $mimetype) {
1203         if ($this->_state > self::STATE_BEFORE_HEADER) {
1204             throw new coding_exception('Cannot call moodle_page::add_alternate_version after output has been started.');
1205         }
1206         $alt = new stdClass;
1207         $alt->title = $title;
1208         $alt->url = $url;
1209         $this->_alternateversions[$mimetype] = $alt;
1210     }
1212     /**
1213      * Specify a form control should be focused when the page has loaded.
1214      *
1215      * @param string $controlid the id of the HTML element to be focused.
1216      */
1217     public function set_focuscontrol($controlid) {
1218         $this->_focuscontrol = $controlid;
1219     }
1221     /**
1222      * Specify a fragment of HTML that goes where the 'Turn editing on' button normally goes.
1223      *
1224      * @param string $html the HTML to display there.
1225      */
1226     public function set_button($html) {
1227         $this->_button = $html;
1228     }
1230     /**
1231      * Set the capability that allows users to edit blocks on this page.
1232      *
1233      * Normally the default of 'moodle/site:manageblocks' is used, but a few
1234      * pages like the My Moodle page need to use a different capability
1235      * like 'moodle/my:manageblocks'.
1236      *
1237      * @param string $capability a capability.
1238      */
1239     public function set_blocks_editing_capability($capability) {
1240         $this->_blockseditingcap = $capability;
1241     }
1243     /**
1244      * Some pages let you turn editing on for reasons other than editing blocks.
1245      * If that is the case, you can pass other capabilities that let the user
1246      * edit this page here.
1247      *
1248      * @param string|array $capability either a capability, or an array of capabilities.
1249      */
1250     public function set_other_editing_capability($capability) {
1251         if (is_array($capability)) {
1252             $this->_othereditingcaps = array_unique($this->_othereditingcaps + $capability);
1253         } else {
1254             $this->_othereditingcaps[] = $capability;
1255         }
1256     }
1258     /**
1259      * Sets whether the browser should cache this page or not.
1260      *
1261      * @return bool $cacheable can this page be cached by the user's browser.
1262      */
1263     public function set_cacheable($cacheable) {
1264         $this->_cacheable = $cacheable;
1265     }
1267     /**
1268      * Sets the page to periodically refresh
1269      *
1270      * This function must be called before $OUTPUT->header has been called or
1271      * a coding exception will be thrown.
1272      *
1273      * @param int $delay Sets the delay before refreshing the page, if set to null
1274      *     refresh is cancelled
1275      */
1276     public function set_periodic_refresh_delay($delay=null) {
1277         if ($this->_state > self::STATE_BEFORE_HEADER) {
1278             throw new coding_exception('You cannot set a periodic refresh delay after the header has been printed');
1279         }
1280         if ($delay===null) {
1281             $this->_periodicrefreshdelay = null;
1282         } else if (is_int($delay)) {
1283             $this->_periodicrefreshdelay = $delay;
1284         }
1285     }
1287     /**
1288      * Force this page to use a particular theme.
1289      *
1290      * Please use this cautiously.
1291      * It is only intended to be used by the themes selector admin page.
1292      *
1293      * @param string $themename the name of the theme to use.
1294      */
1295     public function force_theme($themename) {
1296         $this->ensure_theme_not_set();
1297         $this->_theme = theme_config::load($themename);
1298     }
1300     /**
1301      * This function indicates that current page requires the https
1302      * when $CFG->loginhttps enabled.
1303      *
1304      * By using this function properly, we can ensure 100% https-ized pages
1305      * at our entire discretion (login, forgot_password, change_password)
1306      * @return void
1307      */
1308     public function https_required() {
1309         global $CFG;
1311         if (!is_null($this->_url)) {
1312             throw new coding_exception('https_required() must be used before setting page url!');
1313         }
1315         $this->ensure_theme_not_set();
1317         $this->_https_login_required = true;
1319         if (!empty($CFG->loginhttps)) {
1320             $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
1321         } else {
1322             $CFG->httpswwwroot = $CFG->wwwroot;
1323         }
1324     }
1326     /**
1327      * Makes sure that page previously marked with https_required()
1328      * is really using https://, if not it redirects to https://
1329      *
1330      * @return void (may redirect to https://self)
1331      */
1332     public function verify_https_required() {
1333         global $CFG, $FULLME;
1335         if (is_null($this->_url)) {
1336             throw new coding_exception('verify_https_required() must be called after setting page url!');
1337         }
1339         if (!$this->_https_login_required) {
1340             throw new coding_exception('verify_https_required() must be called only after https_required()!');
1341         }
1343         if (empty($CFG->loginhttps)) {
1344             // https not required, so stop checking
1345             return;
1346         }
1348         if (strpos($this->_url, 'https://')) {
1349             // detect if incorrect PAGE->set_url() used, it is recommended to use root-relative paths there
1350             throw new coding_exception('Invalid page url specified, it must start with https:// for pages that set https_required()!');
1351         }
1353         if (!empty($CFG->sslproxy)) {
1354             // it does not make much sense to use sslproxy and loginhttps at the same time
1355             return;
1356         }
1358         // now the real test and redirect!
1359         // NOTE: do NOT use this test for detection of https on current page because this code is not compatible with SSL proxies,
1360         //       instead use strpos($CFG->httpswwwroot, 'https:') === 0
1361         if (strpos($FULLME, 'https:') !== 0) {
1362             // this may lead to infinite redirect on misconfigured sites, in that case use $CFG->loginhttps=0; in /config.php
1363             redirect($this->_url);
1364         }
1365     }
1367     // Initialisation methods =====================================================
1368     // These set various things up in a default way.
1370     /**
1371      * This method is called when the page first moves out of the STATE_BEFORE_HEADER
1372      * state. This is our last change to initialise things.
1373      */
1374     protected function starting_output() {
1375         global $CFG;
1377         if (!during_initial_install()) {
1378             $this->blocks->load_blocks();
1379             if (empty($this->_block_actions_done)) {
1380                 $this->_block_actions_done = true;
1381                 if ($this->blocks->process_url_actions($this)) {
1382                     redirect($this->url->out(false));
1383                 }
1384             }
1385             $this->blocks->create_all_block_instances();
1386         }
1388         // If maintenance mode is on, change the page header.
1389         if (!empty($CFG->maintenance_enabled)) {
1390             $this->set_button('<a href="' . $CFG->wwwroot . '/' . $CFG->admin .
1391                     '/settings.php?section=maintenancemode">' . get_string('maintenancemode', 'admin') .
1392                     '</a> ' . $this->button);
1394             $title = $this->title;
1395             if ($title) {
1396                 $title .= ' - ';
1397             }
1398             $this->set_title($title . get_string('maintenancemode', 'admin'));
1399         } else {
1400             // Show the messaging popup if there are messages
1401             message_popup_window();
1402         }
1404         $this->initialise_standard_body_classes();
1405     }
1407     /**
1408      * Method for use by Moodle core to set up the theme. Do not
1409      * use this in your own code.
1410      *
1411      * Make sure the right theme for this page is loaded. Tell our
1412      * blocks_manager about the theme block regions, and then, if
1413      * we are $PAGE, set up the global $OUTPUT.
1414      *
1415      * @return void
1416      */
1417     public function initialise_theme_and_output() {
1418         global $OUTPUT, $PAGE, $SITE;
1420         if (!empty($this->_wherethemewasinitialised)) {
1421             return;
1422         }
1424         if (!during_initial_install()) {
1425             // detect PAGE->context mess
1426             $this->magic_get_context();
1427         }
1429         if (!$this->_course && !during_initial_install()) {
1430             $this->set_course($SITE);
1431         }
1433         if (is_null($this->_theme)) {
1434             $themename = $this->resolve_theme();
1435             $this->_theme = theme_config::load($themename);
1436             $this->_layout_options = $this->_theme->pagelayout_options($this->pagelayout);
1437         }
1439         $this->_theme->setup_blocks($this->pagelayout, $this->blocks);
1441         if ($this === $PAGE) {
1442             $OUTPUT = $this->get_renderer('core');
1443         }
1445         $this->_wherethemewasinitialised = debug_backtrace();
1446     }
1448     /**
1449      * Work out the theme this page should use.
1450      *
1451      * This depends on numerous $CFG settings, and the properties of this page.
1452      *
1453      * @return string the name of the theme that should be used on this page.
1454      */
1455     protected function resolve_theme() {
1456         global $CFG, $USER, $SESSION;
1458         if (empty($CFG->themeorder)) {
1459             $themeorder = array('course', 'category', 'session', 'user', 'site');
1460         } else {
1461             $themeorder = $CFG->themeorder;
1462             // Just in case, make sure we always use the site theme if nothing else matched.
1463             $themeorder[] = 'site';
1464         }
1466         $mnetpeertheme = '';
1467         if (isloggedin() and isset($CFG->mnet_localhost_id) and $USER->mnethostid != $CFG->mnet_localhost_id) {
1468             require_once($CFG->dirroot.'/mnet/peer.php');
1469             $mnetpeer = new mnet_peer();
1470             $mnetpeer->set_id($USER->mnethostid);
1471             if ($mnetpeer->force_theme == 1 && $mnetpeer->theme != '') {
1472                 $mnetpeertheme = $mnetpeer->theme;
1473             }
1474         }
1476         foreach ($themeorder as $themetype) {
1477             switch ($themetype) {
1478                 case 'course':
1479                     if (!empty($CFG->allowcoursethemes) && !empty($this->_course->theme) && $this->devicetypeinuse == 'default') {
1480                         return $this->_course->theme;
1481                     }
1483                 case 'category':
1484                     if (!empty($CFG->allowcategorythemes) && $this->devicetypeinuse == 'default') {
1485                         $categories = $this->categories;
1486                         foreach ($categories as $category) {
1487                             if (!empty($category->theme)) {
1488                                 return $category->theme;
1489                             }
1490                         }
1491                     }
1493                 case 'session':
1494                     if (!empty($SESSION->theme)) {
1495                         return $SESSION->theme;
1496                     }
1498                 case 'user':
1499                     if (!empty($CFG->allowuserthemes) && !empty($USER->theme) && $this->devicetypeinuse == 'default') {
1500                         if ($mnetpeertheme) {
1501                             return $mnetpeertheme;
1502                         } else {
1503                             return $USER->theme;
1504                         }
1505                     }
1507                 case 'site':
1508                     if ($mnetpeertheme) {
1509                         return $mnetpeertheme;
1510                     }
1511                     // First try for the device the user is using.
1512                     $devicetheme = get_selected_theme_for_device_type($this->devicetypeinuse);
1513                     if (!empty($devicetheme)) {
1514                         return $devicetheme;
1515                     }
1516                     // Next try for the default device (as a fallback)
1517                     $devicetheme = get_selected_theme_for_device_type('default');
1518                     if (!empty($devicetheme)) {
1519                         return $devicetheme;
1520                     }
1521                     // The default device theme isn't set up - use the overall default theme.
1522                     return theme_config::DEFAULT_THEME;
1523             }
1524         }
1525     }
1528     /**
1529      * Sets ->pagetype from the script name. For example, if the script that was
1530      * run is mod/quiz/view.php, ->pagetype will be set to 'mod-quiz-view'.
1531      *
1532      * @param string $script the path to the script that should be used to
1533      * initialise ->pagetype. If not passed the $SCRIPT global will be used.
1534      * If legacy code has set $CFG->pagepath that will be used instead, and a
1535      * developer warning issued.
1536      */
1537     protected function initialise_default_pagetype($script = null) {
1538         global $CFG, $SCRIPT;
1540         if (isset($CFG->pagepath)) {
1541             debugging('Some code appears to have set $CFG->pagepath. That was a horrible deprecated thing. ' .
1542                     'Don\'t do it! Try calling $PAGE->set_pagetype() instead.');
1543             $script = $CFG->pagepath;
1544             unset($CFG->pagepath);
1545         }
1547         if (is_null($script)) {
1548             $script = ltrim($SCRIPT, '/');
1549             $len = strlen($CFG->admin);
1550             if (substr($script, 0, $len) == $CFG->admin) {
1551                 $script = 'admin' . substr($script, $len);
1552             }
1553         }
1555         $path = str_replace('.php', '', $script);
1556         if (substr($path, -1) == '/') {
1557             $path .= 'index';
1558         }
1560         if (empty($path) || $path == 'index') {
1561             $this->_pagetype = 'site-index';
1562         } else {
1563             $this->_pagetype = str_replace('/', '-', $path);
1564         }
1565     }
1567     /**
1568      * Initialises the CSS classes that will be added to body tag of the page.
1569      *
1570      * The function is responsible for adding all of the critical CSS classes
1571      * that describe the current page, and its state.
1572      * This includes classes that describe the following for example:
1573      *    - Current language
1574      *    - Language direction
1575      *    - YUI CSS initialisation
1576      *    - Pagelayout
1577      * These are commonly used in CSS to target specific types of pages.
1578      */
1579     protected function initialise_standard_body_classes() {
1580         global $CFG, $USER;
1582         $pagetype = $this->pagetype;
1583         if ($pagetype == 'site-index') {
1584             $this->_legacyclass = 'course';
1585         } else if (substr($pagetype, 0, 6) == 'admin-') {
1586             $this->_legacyclass = 'admin';
1587         }
1588         $this->add_body_class($this->_legacyclass);
1590         $pathbits = explode('-', trim($pagetype));
1591         for ($i=1;$i<count($pathbits);$i++) {
1592             $this->add_body_class('path-'.join('-',array_slice($pathbits, 0, $i)));
1593         }
1595         $this->add_body_classes(get_browser_version_classes());
1596         $this->add_body_class('dir-' . get_string('thisdirection', 'langconfig'));
1597         $this->add_body_class('lang-' . current_language());
1598         $this->add_body_class('yui-skin-sam'); // Make YUI happy, if it is used.
1599         $this->add_body_class('yui3-skin-sam'); // Make YUI3 happy, if it is used.
1600         $this->add_body_class($this->url_to_class_name($CFG->wwwroot));
1602         $this->add_body_class('pagelayout-' . $this->_pagelayout); // extra class describing current page layout
1604         if (!during_initial_install()) {
1605             $this->add_body_class('course-' . $this->_course->id);
1606             $this->add_body_class('context-' . $this->_context->id);
1607         }
1609         if (!empty($this->_cm)) {
1610             $this->add_body_class('cmid-' . $this->_cm->id);
1611         }
1613         if (!empty($CFG->allowcategorythemes)) {
1614             $this->ensure_category_loaded();
1615             foreach ($this->_categories as $catid => $notused) {
1616                 $this->add_body_class('category-' . $catid);
1617             }
1618         } else {
1619             $catid = 0;
1620             if (is_array($this->_categories)) {
1621                 $catids = array_keys($this->_categories);
1622                 $catid = reset($catids);
1623             } else if (!empty($this->_course->category)) {
1624                 $catid = $this->_course->category;
1625             }
1626             if ($catid) {
1627                 $this->add_body_class('category-' . $catid);
1628             }
1629         }
1631         if (!isloggedin()) {
1632             $this->add_body_class('notloggedin');
1633         }
1635         if (!empty($USER->editing)) {
1636             $this->add_body_class('editing');
1637             if (optional_param('bui_moveid', false, PARAM_INT)) {
1638                $this->add_body_class('blocks-moving');
1639         }
1640         }
1642         if (!empty($CFG->blocksdrag)) {
1643             $this->add_body_class('drag');
1644         }
1646         if ($this->_devicetypeinuse != 'default') {
1647             $this->add_body_class($this->_devicetypeinuse . 'theme');
1648         }
1649     }
1651     /**
1652      * Loads the activity record for the current CM object associated with this
1653      * page.
1654      *
1655      * This will load {@link moodle_page::$_module} with a row from the related
1656      * module table in the database.
1657      * For instance if {@link moodle_page::$_cm} is a forum then a row from the
1658      * forum table will be loaded.
1659      */
1660     protected function load_activity_record() {
1661         global $DB;
1662         if (is_null($this->_cm)) {
1663             return;
1664         }
1665         $this->_module = $DB->get_record($this->_cm->modname, array('id' => $this->_cm->instance));
1666     }
1668     /**
1669      * This function ensures that the category of the current course has been
1670      * loaded, and if not, the function loads it now.
1671      *
1672      * @return void
1673      * @throws coding_exception
1674      */
1675     protected function ensure_category_loaded() {
1676         if (is_array($this->_categories)) {
1677             return; // Already done.
1678         }
1679         if (is_null($this->_course)) {
1680             throw new coding_exception('Attempt to get the course category for this page before the course was set.');
1681         }
1682         if ($this->_course->category == 0) {
1683             $this->_categories = array();
1684         } else {
1685             $this->load_category($this->_course->category);
1686         }
1687     }
1689     /**
1690      * Loads the requested category into the pages categories array.
1691      *
1692      * @param ing $categoryid
1693      * @throws moodle_exception
1694      */
1695     protected function load_category($categoryid) {
1696         global $DB;
1697         $category = $DB->get_record('course_categories', array('id' => $categoryid));
1698         if (!$category) {
1699             throw new moodle_exception('unknowncategory');
1700         }
1701         $this->_categories[$category->id] = $category;
1702         $parentcategoryids = explode('/', trim($category->path, '/'));
1703         array_pop($parentcategoryids);
1704         foreach (array_reverse($parentcategoryids) as $catid) {
1705             $this->_categories[$catid] = null;
1706         }
1707     }
1709     /**
1710      * Ensures that the category the current course is within, as well as all of
1711      * its parent categories, have been loaded.
1712      *
1713      * @return void
1714      */
1715     protected function ensure_categories_loaded() {
1716         global $DB;
1717         $this->ensure_category_loaded();
1718         if (!is_null(end($this->_categories))) {
1719             return; // Already done.
1720         }
1721         $idstoload = array_keys($this->_categories);
1722         array_shift($idstoload);
1723         $categories = $DB->get_records_list('course_categories', 'id', $idstoload);
1724         foreach ($idstoload as $catid) {
1725             $this->_categories[$catid] = $categories[$catid];
1726         }
1727     }
1729     /**
1730      * Ensure the theme has not been loaded yet. If it has an exception is thrown.
1731      * @source
1732      *
1733      * @throws coding_exception
1734      */
1735     protected function ensure_theme_not_set() {
1736         if (!is_null($this->_theme)) {
1737             throw new coding_exception('The theme has already been set up for this page ready for output. ' .
1738                     'Therefore, you can no longer change the theme, or anything that might affect what ' .
1739                     'the current theme is, for example, the course.',
1740                     'Stack trace when the theme was set up: ' . format_backtrace($this->_wherethemewasinitialised));
1741         }
1742     }
1744     /**
1745      * Converts the provided URL into a CSS class that be used within the page.
1746      * This is primarily used to add the wwwroot to the body tag as a CSS class.
1747      *
1748      * @param string $url
1749      * @return string
1750      */
1751     protected function url_to_class_name($url) {
1752         $bits = parse_url($url);
1753         $class = str_replace('.', '-', $bits['host']);
1754         if (!empty($bits['port'])) {
1755             $class .= '--' . $bits['port'];
1756         }
1757         if (!empty($bits['path'])) {
1758             $path = trim($bits['path'], '/');
1759             if ($path) {
1760                 $class .= '--' . str_replace('/', '-', $path);
1761             }
1762         }
1763         return $class;
1764     }
1766     /**
1767      * Combines all of the required editing caps for the page and returns them
1768      * as an array.
1769      *
1770      * @return array
1771      */
1772     protected function all_editing_caps() {
1773         $caps = $this->_othereditingcaps;
1774         $caps[] = $this->_blockseditingcap;
1775         return $caps;
1776     }
1778     // Deprecated fields and methods for backwards compatibility ==================
1780     /**
1781      * Returns the page type.
1782      *
1783      * @deprecated since Moodle 2.0 - use $PAGE->pagetype instead.
1784      * @return string page type.
1785      */
1786     public function get_type() {
1787         debugging('Call to deprecated method moodle_page::get_type. Please use $PAGE->pagetype instead.');
1788         return $this->get_pagetype();
1789     }
1791     /**
1792      * Returns the page type.
1793      *
1794      * @deprecated since Moodle 2.0 - use $PAGE->pagetype instead.
1795      * @return string this is what page_id_and_class used to return via the $getclass parameter.
1796      */
1797     public function get_format_name() {
1798         return $this->get_pagetype();
1799     }
1801     /**
1802      * Returns the course associated with this page.
1803      *
1804      * @deprecated since Moodle 2.0 - use $PAGE->course instead.
1805      * @return stdClass course.
1806      */
1807     public function get_courserecord() {
1808         debugging('Call to deprecated method moodle_page::get_courserecord. Please use $PAGE->course instead.');
1809         return $this->get_course();
1810     }
1812     /**
1813      * Returns the legacy page class.
1814      *
1815      * @deprecated since Moodle 2.0
1816      * @return string this is what page_id_and_class used to return via the $getclass parameter.
1817      */
1818     public function get_legacyclass() {
1819         if (is_null($this->_legacyclass)) {
1820             $this->initialise_standard_body_classes();
1821         }
1822         debugging('Call to deprecated method moodle_page::get_legacyclass.');
1823         return $this->_legacyclass;
1824     }
1826     /**
1827      * Returns an array of block regions on this page.
1828      *
1829      * @deprecated since Moodle 2.0 - use $PAGE->blocks->get_regions() instead
1830      * @return array the places on this page where blocks can go.
1831      */
1832     function blocks_get_positions() {
1833         debugging('Call to deprecated method moodle_page::blocks_get_positions. Use $PAGE->blocks->get_regions() instead.');
1834         return $this->blocks->get_regions();
1835     }
1837     /**
1838      * Returns the default block region.
1839      *
1840      * @deprecated since Moodle 2.0 - use $PAGE->blocks->get_default_region() instead
1841      * @return string the default place for blocks on this page.
1842      */
1843     function blocks_default_position() {
1844         debugging('Call to deprecated method moodle_page::blocks_default_position. Use $PAGE->blocks->get_default_region() instead.');
1845         return $this->blocks->get_default_region();
1846     }
1848     /**
1849      * Returns the default block to use of the page.
1850      * This function no longer does anything. DO NOT USE.
1851      *
1852      * @deprecated since Moodle 2.0 - no longer used.
1853      */
1854     function blocks_get_default() {
1855         debugging('Call to deprecated method moodle_page::blocks_get_default. This method has no function any more.');
1856     }
1858     /**
1859      * Moves a block.
1860      * This function no longer does anything. DO NOT USE.
1861      *
1862      * @deprecated since Moodle 2.0 - no longer used.
1863      */
1864     function blocks_move_position(&$instance, $move) {
1865         debugging('Call to deprecated method moodle_page::blocks_move_position. This method has no function any more.');
1866     }
1868     /**
1869      * Returns the URL parameters for the current page.
1870      *
1871      * @deprecated since Moodle 2.0 - use $this->url->params() instead.
1872      * @return array URL parameters for this page.
1873      */
1874     function url_get_parameters() {
1875         debugging('Call to deprecated method moodle_page::url_get_parameters. Use $this->url->params() instead.');
1876         return $this->url->params();
1877     }
1879     /**
1880      * Returns the URL path of the current page.
1881      *
1882      * @deprecated since Moodle 2.0 - use $this->url->params() instead.
1883      * @return string URL for this page without parameters.
1884      */
1885     function url_get_path() {
1886         debugging('Call to deprecated method moodle_page::url_get_path. Use $this->url->out() instead.');
1887         return $this->url->out();
1888     }
1890     /**
1891      * Returns the full URL for this page.
1892      *
1893      * @deprecated since Moodle 2.0 - use $this->url->out() instead.
1894      * @return string full URL for this page.
1895      */
1896     function url_get_full($extraparams = array()) {
1897         debugging('Call to deprecated method moodle_page::url_get_full. Use $this->url->out() instead.');
1898         return $this->url->out(true, $extraparams);
1899     }
1901     /**
1902      * Returns the legacy page object.
1903      *
1904      * @deprecated since Moodle 2.0 - just a backwards compatibility hook.
1905      * @return moodle_page
1906      */
1907     function set_legacy_page_object($pageobject) {
1908         return $this->_legacypageobject = $pageobject;
1909     }
1911     /**
1912      * Prints a header... DO NOT USE!
1913      *
1914      * @deprecated since Moodle 2.0 - page objects should no longer be doing print_header.
1915      * @param mixed $_ ...
1916      */
1917     function print_header($_) {
1918         if (is_null($this->_legacypageobject)) {
1919             throw new coding_exception('You have called print_header on $PAGE when there is not a legacy page class present.');
1920         }
1921         debugging('You should not longer be doing print_header via a page class.', DEBUG_DEVELOPER);
1922         $args = func_get_args();
1923         call_user_func_array(array($this->_legacypageobject, 'print_header'), $args);
1924     }
1926     /**
1927      * Returns the ID for this page. DO NOT USE!
1928      *
1929      * @deprecated since Moodle 2.0
1930      * @return the 'page id'. This concept no longer exists.
1931      */
1932     function get_id() {
1933         debugging('Call to deprecated method moodle_page::get_id(). It should not be necessary any more.', DEBUG_DEVELOPER);
1934         if (!is_null($this->_legacypageobject)) {
1935             return $this->_legacypageobject->get_id();
1936         }
1937         return 0;
1938     }
1940     /**
1941      * Returns the ID for this page. DO NOT USE!
1942      *
1943      * @deprecated since Moodle 2.0
1944      * @return the 'page id'. This concept no longer exists.
1945      */
1946     function get_pageid() {
1947         debugging('Call to deprecated method moodle_page::get_pageid(). It should not be necessary any more.', DEBUG_DEVELOPER);
1948         if (!is_null($this->_legacypageobject)) {
1949             return $this->_legacypageobject->get_id();
1950         }
1951         return 0;
1952     }
1954     /**
1955      * Returns the module record for this page.
1956      *
1957      * @deprecated since Moodle 2.0 - user $PAGE->cm instead.
1958      * @return $this->cm;
1959      */
1960     function get_modulerecord() {
1961         return $this->cm;
1962     }
1964     /**
1965      * Returns true if the page URL has beem set.
1966      *
1967      * @return bool
1968      */
1969     public function has_set_url() {
1970         return ($this->_url!==null);
1971     }
1973     /**
1974      * Gets set when the block actions for the page have been processed.
1975      *
1976      * @param bool $setting
1977      */
1978     public function set_block_actions_done($setting = true) {
1979         $this->_block_actions_done = $setting;
1980     }
1982     /**
1983      * Are popup notifications allowed on this page?
1984      * Popup notifications may be disallowed in situations such as while upgrading or completing a quiz
1985      *
1986      * @return bool true if popup notifications may be displayed
1987      */
1988     public function get_popup_notification_allowed() {
1989         return $this->_popup_notification_allowed;
1990     }
1992     /**
1993      * Allow or disallow popup notifications on this page. Popups are allowed by default.
1994      *
1995      * @param bool $allowed true if notifications are allowed. False if not allowed. They are allowed by default.
1996      */
1997     public function set_popup_notification_allowed($allowed) {
1998         $this->_popup_notification_allowed = $allowed;
1999     }
2002 /**
2003  * Not needed any more. DO NOT USE!
2004  *
2005  * @deprecated since Moodle 2.0
2006  * @param string $path the folder path
2007  * @return array an array of page types.
2008  */
2009 function page_import_types($path) {
2010     global $CFG;
2011     debugging('Call to deprecated function page_import_types.', DEBUG_DEVELOPER);
2014 /**
2015  * Do not use this any more. The global $PAGE is automatically created for you.
2016  * If you need custom behaviour, you should just set properties of that object.
2017  *
2018  * @deprecated since Moodle 2.0
2019  * @param integer $instance legacy page instance id.
2020  * @return moodle_page The global $PAGE object.
2021  */
2022 function page_create_instance($instance) {
2023     global $PAGE;
2024     return page_create_object($PAGE->pagetype, $instance);
2027 /**
2028  * Do not use this any more. The global $PAGE is automatically created for you.
2029  * If you need custom behaviour, you should just set properties of that object.
2030  *
2031  * @deprecated since Moodle 2.0
2032  * @return moodle_page The global $PAGE object.
2033  */
2034 function page_create_object($type, $id = NULL) {
2035     global $CFG, $PAGE, $SITE, $ME;
2036     debugging('Call to deprecated function page_create_object.', DEBUG_DEVELOPER);
2038     $data = new stdClass;
2039     $data->pagetype = $type;
2040     $data->pageid = $id;
2042     $classname = page_map_class($type);
2043     if (!$classname) {
2044         return $PAGE;
2045     }
2046     $legacypage = new $classname;
2047     $legacypage->init_quick($data);
2049     $course = $PAGE->course;
2050     if ($course->id != $SITE->id) {
2051         $legacypage->set_course($course);
2052     } else {
2053         try {
2054             $category = $PAGE->category;
2055         } catch (coding_exception $e) {
2056             // Was not set before, so no need to try to set it again.
2057             $category = false;
2058         }
2059         if ($category) {
2060             $legacypage->set_category_by_id($category->id);
2061         } else {
2062             $legacypage->set_course($SITE);
2063         }
2064     }
2066     $legacypage->set_pagetype($type);
2068     $legacypage->set_url($ME);
2069     $PAGE->set_url(str_replace($CFG->wwwroot . '/', '', $legacypage->url_get_full()));
2071     $PAGE->set_pagetype($type);
2072     $PAGE->set_legacy_page_object($legacypage);
2073     return $PAGE;
2076 /**
2077  * You should not be writing page subclasses any more. Just set properties on the
2078  * global $PAGE object to control its behaviour.
2079  *
2080  * @deprecated since Moodle 2.0
2081  * @return mixed Null if there is not a valid page mapping, or the mapping if
2082  *     it has been set.
2083  */
2084 function page_map_class($type, $classname = NULL) {
2085     global $CFG;
2087     static $mappings = array(
2088         PAGE_COURSE_VIEW => 'page_course',
2089     );
2091     if (!empty($type) && !empty($classname)) {
2092         $mappings[$type] = $classname;
2093     }
2095     if (!isset($mappings[$type])) {
2096         debugging('Page class mapping requested for unknown type: '.$type);
2097         return null;
2098     } else if (empty($classname) && !class_exists($mappings[$type])) {
2099         debugging('Page class mapping for id "'.$type.'" exists but class "'.$mappings[$type].'" is not defined');
2100         return null;
2101     }
2103     return $mappings[$type];
2106 /**
2107  * Parent class from which all Moodle page classes derive
2108  *
2109  * @deprecated since Moodle 2.0
2110  * @package core
2111  * @category page
2112  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
2113  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2114  */
2115 class page_base extends moodle_page {
2116     /**
2117      * @var int The numeric identifier of the page being described.
2118      */
2119     public $id = null;
2121     /**
2122      * Returns the page id
2123      * @deprecated since Moodle 2.0
2124      * @return int Returns the id of the page.
2125      */
2126     public function get_id() {
2127         return $this->id;
2128     }
2130     /**
2131      * Initialize the data members of the parent class
2132      * @param scalar $data
2133      */
2134     public function init_quick($data) {
2135         $this->id   = $data->pageid;
2136     }
2138     /**
2139      * DOES NOTHING... DO NOT USE.
2140      * @deprecated since Moodle 2.0
2141      */
2142     public function init_full() {}
2145 /**
2146  * Class that models the behavior of a moodle course.
2147  * Although this does nothing, this class declaration should be left for now
2148  * since there may be legacy class doing class page_... extends page_course
2149  *
2150  * @deprecated since Moodle 2.0
2151  * @package core
2152  * @category page
2153  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
2154  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2155  */
2156 class page_course extends page_base {}
2158 /**
2159  * Class that models the common parts of all activity modules
2160  *
2161  * @deprecated since Moodle 2.0
2162  * @package core
2163  * @category page
2164  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
2165  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2166  */
2167 class page_generic_activity extends page_base {
2169     /**
2170      * Although this function is deprecated, it should be left here because
2171      * people upgrading legacy code need to copy it. See
2172      * http://docs.moodle.org/dev/Migrating_your_code_to_the_2.0_rendering_API
2173      *
2174      * @param string $title
2175      * @param array $morenavlinks
2176      * @param string $bodytags
2177      * @param string $meta
2178      */
2179     function print_header($title, $morenavlinks = NULL, $bodytags = '', $meta = '') {
2180         global $USER, $CFG, $PAGE, $OUTPUT;
2182         $this->init_full();
2183         $replacements = array(
2184             '%fullname%' => format_string($this->activityrecord->name)
2185         );
2186         foreach ($replacements as $search => $replace) {
2187             $title = str_replace($search, $replace, $title);
2188         }
2190         $buttons = '<table><tr><td>'.$OUTPUT->update_module_button($this->modulerecord->id, $this->activityname).'</td>';
2191         if ($this->user_allowed_editing()) {
2192             $buttons .= '<td><form method="get" action="view.php"><div>'.
2193                 '<input type="hidden" name="id" value="'.$this->modulerecord->id.'" />'.
2194                 '<input type="hidden" name="edit" value="'.($this->user_is_editing()?'off':'on').'" />'.
2195                 '<input type="submit" value="'.get_string($this->user_is_editing()?'blockseditoff':'blocksediton').'" /></div></form></td>';
2196         }
2197         $buttons .= '</tr></table>';
2199         if (!empty($morenavlinks) && is_array($morenavlinks)) {
2200             foreach ($morenavlinks as $navitem) {
2201                 if (is_array($navitem) && array_key_exists('name', $navitem)) {
2202                     $link = null;
2203                     if (array_key_exists('link', $navitem)) {
2204                         $link = $navitem['link'];
2205                     }
2206                     $PAGE->navbar->add($navitem['name'], $link);
2207                 }
2208             }
2209         }
2211         $PAGE->set_title($title);
2212         $PAGE->set_heading($this->course->fullname);
2213         $PAGE->set_button($buttons);
2214         echo $OUTPUT->header();
2215     }