pagelib.php 75.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
defacer's avatar
 
defacer committed
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
25
 * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
27
 */

28
defined('MOODLE_INTERNAL') || die();
tjhunt's avatar
tjhunt committed
29

30
31
/**
 * $PAGE is a central store of information about the current page we are
tjhunt's avatar
tjhunt committed
32
33
34
 * generating in response to the user's request.
 *
 * It does not do very much itself
35
36
37
 * except keep track of information, however, it serves as the access point to
 * some more significant components like $PAGE->theme, $PAGE->requires,
 * $PAGE->blocks, etc.
38
 *
tjhunt's avatar
tjhunt committed
39
 * @copyright 2009 Tim Hunt
40
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
tjhunt's avatar
tjhunt committed
41
 * @since Moodle 2.0
42
 * @package core
43
 * @category page
44
45
46
 *
 * The following properties are alphabetical. Please keep it that way so that its
 * easy to maintain.
tjhunt's avatar
tjhunt committed
47
 *
48
49
 * @property-read string $activityname The type of activity we are in, for example 'forum' or 'quiz'.
 *      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
52
53
 *      the forum or quiz table) that this page belongs to. Will be null
 *      if this page is not within a module.
 * @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
57
 * @property-read string $bodyclasses A string to use within the class attribute on the body tag.
 * @property-read string $bodyid A string to use as the id of the body tag.
Petr Skoda's avatar
Petr Skoda committed
58
 * @property-read string $button The HTML to go where the Turn editing on button normally goes.
59
60
61
62
63
 * @property-read bool $cacheable Defaults to true. Set to false to stop the page being cached at all.
 * @property-read array $categories An array of all the categories the page course belongs to,
 *      starting with the immediately containing category, and working out to
 *      the top-level category. This may be the empty array if we are in the
 *      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
67
68
 *      if this page is not within a module. This is a full cm object, as loaded
 *      by get_coursemodule_from_id or get_coursemodule_from_instance,
 *      so the extra modname and name fields are present.
69
70
 * @property-read context $context The main context to which this page belongs.
 * @property-read stdClass $course The current course that we are inside - a row from the
71
72
 *      course table. (Also available as $COURSE global.) If we are not inside
 *      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.
Petr Skoda's avatar
Petr Skoda committed
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
78
 * @property-read string $heading The main heading that should be displayed at the top of the <body>.
 * @property-read string $headingmenu The menu (or actions) to display in the heading
79
80
81
82
 * @property-read array $layout_options An arrays with options for the layout file.
 * @property-read array $legacythemeinuse True if the legacy browser theme is in use.
 * @property-read navbar $navbar The navbar object used to display the navbar
 * @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
85
 *      mainly for internal use by the rendering code.
 * @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
89
 * @property-read int $periodicrefreshdelay The periodic refresh delay to use with meta refresh
 * @property-read page_requirements_manager $requires Tracks the JavaScript, CSS files, etc. required by this page.
90
91
 * @property-read string $requestip The IP address of the current request, null if unknown.
 * @property-read string $requestorigin The type of request 'web', 'ws', 'cli', 'restore', etc.
92
 * @property-read settings_navigation $settingsnav The settings navigation
93
94
 * @property-read int $state One of the STATE_... constants
 * @property-read string $subpage The subpage identifier, if any.
95
 * @property-read theme_config $theme The theme for this page.
96
97
 * @property-read string $title The title that should go in the <head> section of the HTML of this page.
 * @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
109
    const STATE_IN_BODY = 2;

110
111
112
113
114
    /**
     * The state the page is when the footer has been printed and its function is
     * complete.
     */
    const STATE_DONE = 3;
115

116
    /**
117
118
     * @var int The current state of the page. The state a page is within
     * determines what actions are possible for it.
119
     */
120
121
    protected $_state = self::STATE_BEFORE_HEADER;

122
    /**
123
124
     * @var stdClass The course currently associated with this page.
     * If not has been provided the front page course is used.
125
     */
126
127
    protected $_course = null;

128
    /**
129
130
     * @var cm_info If this page belongs to a module, this is the cm_info module
     * description object.
131
132
133
134
     */
    protected $_cm = null;

    /**
135
136
137
     * @var stdClass If $_cm is not null, then this will hold the corresponding
     * row from the modname table. For example, if $_cm->modname is 'quiz', this
     * will be a row from the quiz table.
138
139
140
141
     */
    protected $_module = null;

    /**
142
     * @var context The context that this page belongs to.
143
     */
144
145
    protected $_context = null;

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
Petr Skoda's avatar
Petr Skoda committed
149
     * categories to the top level. These are loaded progressively, if needed.
150
151
152
153
154
155
156
     * There are three states. $_categories = null initially when nothing is
     * loaded; $_categories = array($id => $cat, $parentid => null) when we have
     * loaded $_course->category, but not any parents; and a complete array once
     * everything is loaded.
     */
    protected $_categories = null;

157
    /**
158
     * @var array An array of CSS classes that should be added to the body tag in HTML.
159
     */
160
161
    protected $_bodyclasses = array();

162
    /**
163
     * @var string The title for the page. Used within the title tag in the HTML head.
164
     */
165
166
    protected $_title = '';

167
    /**
168
     * @var string The string to use as the heading of the page. Shown near the top of the
169
170
     * page within most themes.
     */
171
172
    protected $_heading = '';

173
    /**
174
     * @var string The pagetype is used to describe the page and defaults to a representation
175
176
     * of the physical path to the page e.g. my-index, mod-quiz-attempt
     */
177
178
    protected $_pagetype = null;

179
    /**
180
181
182
     * @var string The pagelayout to use when displaying this page. The
     * pagelayout needs to have been defined by the theme in use, or one of its
     * parents. By default base is used however standard is the more common layout.
183
184
185
     * Note that this gets automatically set by core during operations like
     * require_login.
     */
186
    protected $_pagelayout = 'base';
187
188

    /**
189
     * @var array List of theme layout options, these are ignored by core.
190
191
     * To be used in individual theme layout files only.
     */
192
    protected $_layout_options = null;
193

194
    /**
195
     * @var string An optional arbitrary parameter that can be set on pages where the context
196
197
     * and pagetype is not enough to identify the page.
     */
198
    protected $_subpage = '';
199

200
    /**
201
     * @var string Set a different path to use for the 'Moodle docs for this page' link.
202
203
     * By default, it uses the path of the file for instance mod/quiz/attempt.
     */
tjhunt's avatar
tjhunt committed
204
205
    protected $_docspath = null;

206
    /**
207
     * @var string A legacy class that will be added to the body tag
208
     */
209
210
    protected $_legacyclass = null;

211
    /**
212
213
     * @var moodle_url The URL for this page. This is mandatory and must be set
     * before output is started.
214
     */
215
216
    protected $_url = null;

217
    /**
218
219
     * @var array An array of links to alternative versions of this page.
     * Primarily used for RSS versions of the current page.
220
     */
221
222
    protected $_alternateversions = array();

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
228
    protected $_blocks = null;

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
234
    protected $_requires = null;

235
    /**
236
237
     * @var string The capability required by the user in order to edit blocks
     * and block settings on this page.
238
     */
239
240
    protected $_blockseditingcap = 'moodle/site:manageblocks';

241
    /**
242
     * @var bool An internal flag to record when block actions have been processed.
243
244
245
     * Remember block actions occur on the current URL and it is important that
     * even they are never executed more than once.
     */
246
247
    protected $_block_actions_done = false;

248
    /**
249
250
     * @var array An array of any other capabilities the current user must have
     * in order to editing the page and/or its content (not just blocks).
251
     */
252
253
    protected $_othereditingcaps = array();

254
    /**
255
256
     * @var bool Sets whether this page should be cached by the browser or not.
     * If it is set to true (default) the page is served with caching headers.
257
     */
258
259
    protected $_cacheable = true;

260
    /**
261
262
     * @var string Can be set to the ID of an element on the page, if done that
     * element receives focus when the page loads.
263
     */
264
265
    protected $_focuscontrol = '';

266
    /**
267
268
     * @var string HTML to go where the turn on editing button is located. This
     * 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
301
     * @var array stack trace. Then the theme is initialised, we save the stack
     * trace, for use in error messages.
302
303
304
     */
    protected $_wherethemewasinitialised = null;

305
    /**
306
307
     * @var xhtml_container_stack Tracks XHTML tags on this page that have been
     * opened but not closed.
308
     */
309
310
    protected $_opencontainers;

311
    /**
312
313
     * @var int Sets the page to refresh after a given delay (in seconds) using
     * meta refresh in {@link standard_head_html()} in outputlib.php
314
315
316
317
     * If set to null(default) the page is not refreshed
     */
    protected $_periodicrefreshdelay = null;

318
    /**
319
     * @var array Associative array of browser shortnames (as used by check_browser_version)
320
321
322
323
324
     * and their minimum required versions
     */
    protected $_legacybrowsers = array('MSIE' => 6.0);

    /**
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
334
    protected $_https_login_required = false;

335
    /**
336
     * @var bool Determines if popup notifications allowed on this page.
337
338
339
     * Code such as the quiz module disables popup notifications in situations
     * such as upgrading or completing a quiz.
     */
340
341
    protected $_popup_notification_allowed = true;

342
343
344
    // Magic getter methods =============================================================
    // Due to the __get magic below, you normally do not call these as $PAGE->magic_get_x
    // 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
Petr Skoda's avatar
Petr Skoda committed
349
     * to use this in your code. It is intended for internal use by this class
350
351
352
     * and its friends like print_header, to check that everything is working as
     * expected. Also accessible as $PAGE->state.
     */
353
    protected function magic_get_state() {
354
355
356
357
        return $this->_state;
    }

    /**
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
363
364
365
        return $this->_state >= self::STATE_IN_BODY;
    }

    /**
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
373
374
375
376
377
378
        global $SITE;
        if (is_null($this->_course)) {
            return $SITE;
        }
        return $this->_course;
    }

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
383
384
385
     * if this page is not within a module. This is a full cm object, as loaded
     * by get_coursemodule_from_id or get_coursemodule_from_instance,
     * so the extra modname and name fields are present.
     */
386
    protected function magic_get_cm() {
387
388
389
390
        return $this->_cm;
    }

    /**
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
394
     * the forum or quiz table) that this page belongs to. Will be null
     * if this page is not within a module.
395
     */
396
    protected function magic_get_activityrecord() {
397
398
399
400
401
402
403
        if (is_null($this->_module) && !is_null($this->_cm)) {
            $this->load_activity_record();
        }
        return $this->_module;
    }

    /**
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
410
411
412
413
414
        if (is_null($this->_cm)) {
            return null;
        }
        return $this->_cm->modname;
    }

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
419
     * (that is, if this is the front page course) returns null.
     */
420
    protected function magic_get_category() {
421
422
423
424
425
426
427
428
429
        $this->ensure_category_loaded();
        if (!empty($this->_categories)) {
            return reset($this->_categories);
        } else {
            return null;
        }
    }

    /**
430
     * Please do not call this method directly, use the ->categories syntax. {@link moodle_page::__get()}.
431
432
433
434
435
     * @return array an array of all the categories the page course belongs to,
     * starting with the immediately containing category, and working out to
     * the top-level category. This may be the empty array if we are in the
     * front page course.
     */
436
    protected function magic_get_categories() {
437
438
439
440
        $this->ensure_categories_loaded();
        return $this->_categories;
    }

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
        global $CFG;
447
        if (is_null($this->_context)) {
448
            if (CLI_SCRIPT or NO_MOODLE_COOKIES) {
449
450
                // Cli scripts work in system context, do not annoy devs with debug info.
                // Very few scripts do not use cookies, we can safely use system as default context there.
451
452
453
454
            } else if (AJAX_SCRIPT && $CFG->debugdeveloper) {
                // Throw exception inside AJAX script in developer mode, otherwise the debugging message may be missed.
                throw new coding_exception('$PAGE->context was not set. You may have forgotten '
                    .'to call require_login() or $PAGE->set_context()');
455
            } else {
456
457
458
                debugging('Coding problem: $PAGE->context was not set. You may have forgotten '
                    .'to call require_login() or $PAGE->set_context(). The page may not display '
                    .'correctly as a result');
459
            }
460
            $this->_context = context_system::instance();
461
462
463
464
        }
        return $this->_context;
    }

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
472
        if (is_null($this->_pagetype) || isset($CFG->pagepath)) {
            $this->initialise_default_pagetype();
473
474
475
476
        }
        return $this->_pagetype;
    }

477
    /**
478
     * Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
479
480
481
482
483
484
     * @return string The id to use on the body tag, uses {@link magic_get_pagetype()}.
     */
    protected function magic_get_bodyid() {
        return 'page-'.$this->pagetype;
    }

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
489
     *      Allows the theme to display things differently, if it wishes to.
     */
490
    protected function magic_get_pagelayout() {
491
492
493
494
        return $this->_pagelayout;
    }

    /**
495
496
     * Please do not call this method directly, use the ->layout_options syntax. {@link moodle_page::__get()}.
     * @return array returns arrays with options for layout file
497
     */
498
    protected function magic_get_layout_options() {
499
500
501
        if (!is_array($this->_layout_options)) {
            $this->_layout_options = $this->_theme->pagelayout_options($this->pagelayout);
        }
502
        return $this->_layout_options;
503
504
505
    }

    /**
506
     * Please do not call this method directly, use the ->subpage syntax. {@link moodle_page::__get()}.
507
     * @return string The subpage identifier, if any.
508
     */
509
    protected function magic_get_subpage() {
510
511
512
        return $this->_subpage;
    }

513
    /**
514
     * Please do not call this method directly, use the ->bodyclasses syntax. {@link moodle_page::__get()}.
515
516
     * @return string the class names to put on the body element in the HTML.
     */
517
    protected function magic_get_bodyclasses() {
518
519
520
        return implode(' ', array_keys($this->_bodyclasses));
    }

tjhunt's avatar
tjhunt committed
521
    /**
522
     * Please do not call this method directly, use the ->title syntax. {@link moodle_page::__get()}.
523
524
     * @return string the title that should go in the <head> section of the HTML of this page.
     */
525
    protected function magic_get_title() {
526
527
528
529
        return $this->_title;
    }

    /**
530
     * Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
531
532
     * @return string the main heading that should be displayed at the top of the <body>.
     */
533
    protected function magic_get_heading() {
534
535
536
        return $this->_heading;
    }

537
    /**
538
     * Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
539
540
     * @return string The menu (or actions) to display in the heading
     */
541
    protected function magic_get_headingmenu() {
542
543
544
        return $this->_headingmenu;
    }

545
    /**
546
     * Please do not call this method directly, use the ->docspath syntax. {@link moodle_page::__get()}.
547
     * @return string the path to the Moodle docs for this page.
tjhunt's avatar
tjhunt committed
548
     */
549
    protected function magic_get_docspath() {
tjhunt's avatar
tjhunt committed
550
551
552
553
554
555
556
        if (is_string($this->_docspath)) {
            return $this->_docspath;
        } else {
            return str_replace('-', '/', $this->pagetype);
        }
    }

557
    /**
558
     * Please do not call this method directly, use the ->url syntax. {@link moodle_page::__get()}.
559
560
561
     * @return moodle_url the clean URL required to load the current page. (You
     * should normally use this in preference to $ME or $FULLME.)
     */
562
    protected function magic_get_url() {
563
        global $FULLME;
564
        if (is_null($this->_url)) {
565
            debugging('This page did not call $PAGE->set_url(...). Using '.s($FULLME), DEBUG_DEVELOPER);
566
567
568
            $this->_url = new moodle_url($FULLME);
            // Make sure the guessed URL cannot lead to dangerous redirects.
            $this->_url->remove_params('sesskey');
569
        }
570
571
572
573
        return new moodle_url($this->_url); // Return a clone for safety.
    }

    /**
574
575
576
     * The list of alternate versions of this page.
     * @return array mime type => object with ->url and ->title.
     */
577
    protected function magic_get_alternateversions() {
578
579
580
581
        return $this->_alternateversions;
    }

    /**
582
     * Please do not call this method directly, use the ->blocks syntax. {@link moodle_page::__get()}.
583
     * @return block_manager the blocks manager object for this page.
584
     */
585
    protected function magic_get_blocks() {
586
        global $CFG;
587
        if (is_null($this->_blocks)) {
588
            if (!empty($CFG->blockmanagerclass)) {
589
590
591
                if (!empty($CFG->blockmanagerclassfile)) {
                    require_once($CFG->blockmanagerclassfile);
                }
592
593
594
595
596
                $classname = $CFG->blockmanagerclass;
            } else {
                $classname = 'block_manager';
            }
            $this->_blocks = new $classname($this);
597
598
        }
        return $this->_blocks;
599
600
    }

601
    /**
602
     * Please do not call this method directly, use the ->requires syntax. {@link moodle_page::__get()}.
603
     * @return page_requirements_manager tracks the JavaScript, CSS files, etc. required by this page.
604
     */
605
    protected function magic_get_requires() {
606
607
608
609
610
611
        if (is_null($this->_requires)) {
            $this->_requires = new page_requirements_manager();
        }
        return $this->_requires;
    }

612
    /**
613
     * Please do not call this method directly, use the ->cacheable syntax. {@link moodle_page::__get()}.
614
     * @return bool can this page be cached by the user's browser.
615
     */
616
    protected function magic_get_cacheable() {
617
618
619
620
        return $this->_cacheable;
    }

    /**
621
     * Please do not call this method directly, use the ->focuscontrol syntax. {@link moodle_page::__get()}.
Petr Skoda's avatar
Petr Skoda committed
622
     * @return string the id of the HTML element to be focused when the page has loaded.
623
     */
624
    protected function magic_get_focuscontrol() {
625
626
627
628
        return $this->_focuscontrol;
    }

    /**
629
     * Please do not call this method directly, use the ->button syntax. {@link moodle_page::__get()}.
Petr Skoda's avatar
Petr Skoda committed
630
     * @return string the HTML to go where the Turn editing on button normally goes.
631
     */
632
    protected function magic_get_button() {
633
        return $this->_button;
634
635
    }

636
    /**
637
     * Please do not call this method directly, use the ->theme syntax. {@link moodle_page::__get()}.
638
     * @return theme_config the initialised theme for this page.
639
     */
640
    protected function magic_get_theme() {
641
642
643
644
645
646
        if (is_null($this->_theme)) {
            $this->initialise_theme_and_output();
        }
        return $this->_theme;
    }

647
648
649
    /**
     * Returns an array of minipulations or false if there are none to make.
     *
Tim Hunt's avatar
Tim Hunt committed
650
     * @since Moodle 2.5.1 2.6
651
652
653
654
655
656
657
658
659
660
661
662
     * @return bool|array
     */
    protected function magic_get_blockmanipulations() {
        if (!right_to_left()) {
            return false;
        }
        if (is_null($this->_theme)) {
            $this->initialise_theme_and_output();
        }
        return $this->_theme->blockrtlmanipulations;
    }

663
    /**
664
     * Please do not call this method directly, use the ->devicetypeinuse syntax. {@link moodle_page::__get()}.
665
     * @return string The device type being used.
666
     */
667
    protected function magic_get_devicetypeinuse() {
668
        if (empty($this->_devicetypeinuse)) {
669
            $this->_devicetypeinuse = core_useragent::get_user_device_type();
670
671
672
673
        }
        return $this->_devicetypeinuse;
    }

674
    /**
675
     * Please do not call this method directly use the ->periodicrefreshdelay syntax
676
     * {@link moodle_page::__get()}
677
     * @return int The periodic refresh delay to use with meta refresh
678
     */
679
    protected function magic_get_periodicrefreshdelay() {
680
681
682
        return $this->_periodicrefreshdelay;
    }

683
    /**
684
     * Please do not call this method directly use the ->opencontainers syntax. {@link moodle_page::__get()}
685
686
687
     * @return xhtml_container_stack tracks XHTML tags on this page that have been opened but not closed.
     *      mainly for internal use by the rendering code.
     */
688
    protected function magic_get_opencontainers() {
689
690
691
692
693
694
        if (is_null($this->_opencontainers)) {
            $this->_opencontainers = new xhtml_container_stack();
        }
        return $this->_opencontainers;
    }

695
696
697
698
    /**
     * Return the navigation object
     * @return global_navigation
     */
699
    protected function magic_get_navigation() {
700
        if ($this->_navigation === null) {
701
            $this->_navigation = new global_navigation($this);
702
703
704
705
706
707
708
709
        }
        return $this->_navigation;
    }

    /**
     * Return a navbar object
     * @return navbar
     */
710
    protected function magic_get_navbar() {
711
712
713
714
715
716
717
718
719
720
        if ($this->_navbar === null) {
            $this->_navbar = new navbar($this);
        }
        return $this->_navbar;
    }

    /**
     * Returns the settings navigation object
     * @return settings_navigation
     */
721
    protected function magic_get_settingsnav() {
722
723
724
725
726
727
        if ($this->_settingsnav === null) {
            $this->_settingsnav = new settings_navigation($this);
            $this->_settingsnav->initialise();
        }
        return $this->_settingsnav;
    }
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759

    /**
     * Returns request IP address.
     *
     * @return string IP address or null if unknown
     */
    protected function magic_get_requestip() {
        return getremoteaddr(null);
    }

    /**
     * Returns the origin of current request.
     *
     * Note: constants are not required because we need to use these values in logging and reports.
     *
     * @return string 'web', 'ws', 'cli', 'restore', etc.
     */
    protected function magic_get_requestorigin() {
        if (class_exists('restore_controller', false) && restore_controller::is_executing()) {
            return 'restore';
        }

        if (WS_SERVER) {
            return 'ws';
        }

        if (CLI_SCRIPT) {
            return 'cli';
        }

        return 'web';
    }
760

761
    /**
762
     * PHP overloading magic to make the $PAGE->course syntax work by redirecting
763
     * it to the corresponding $PAGE->magic_get_course() method if there is one, and
764
     * throwing an exception if not.
765
     *
766
     * @param string $name property name
767
     * @return mixed
768
     * @throws coding_exception
769
     */
770
771
    public function __get($name) {
        $getmethod = 'magic_get_' . $name;
772
773
774
        if (method_exists($this, $getmethod)) {
            return $this->$getmethod();
        } else {
775
776
777
778
779
            throw new coding_exception('Unknown property ' . $name . ' of $PAGE.');
        }
    }

    /**
780
781
782
783
784
785
     * PHP overloading magic to catch obvious coding errors.
     *
     * This method has been created to catch obvious coding errors where the
     * developer has tried to set a page property using $PAGE->key = $value.
     * In the moodle_page class all properties must be set using the appropriate
     * $PAGE->set_something($value) method.
786
     *
787
     * @param string $name property name
788
     * @param mixed $value Value
789
     * @return void Throws exception if field not defined in page class
790
     * @throws coding_exception
791
792
793
794
795
796
     */
    public function __set($name, $value) {
        if (method_exists($this, 'set_' . $name)) {
            throw new coding_exception('Invalid attempt to modify page object', "Use \$PAGE->set_$name() instead.");
        } else {
            throw new coding_exception('Invalid attempt to modify page object', "Unknown property $name");
797
798
799
        }
    }

800
    // Other information getting methods ==========================================.
801

802
803
    /**
     * Returns instance of page renderer
804
     *
805
806
     * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
     * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
807
     * @param string $target one of rendering target constants
808
809
     * @return renderer_base
     */
810
    public function get_renderer($component, $subtype = null, $target = null) {
811
812
813
814
815
816
        if ($this->pagelayout === 'maintenance') {
            // If the page is using the maintenance layout then we're going to force target to maintenance.
            // This leads to a special core renderer that is designed to block access to API's that are likely unavailable for this
            // page layout.
            $target = RENDERER_TARGET_MAINTENANCE;
        }
817
        return $this->magic_get_theme()->get_renderer($this, $component, $subtype, $target);
818
819
    }

820
821
    /**
     * Checks to see if there are any items on the navbar object
822
     *
823
824
825
826
827
828
829
830
831
     * @return bool true if there are, false if not
     */
    public function has_navbar() {
        if ($this->_navbar === null) {
            $this->_navbar = new navbar($this);
        }
        return $this->_navbar->has_items();
    }

832
    /**
833
834
835
     * Switches from the regular requirements manager to the fragment requirements manager to
     * capture all necessary JavaScript to display a chunk of HTML such as an mform. This is for use
     * by the get_fragment() web service and not for use elsewhere.
836
     */
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
    public function start_collecting_javascript_requirements() {
        global $CFG;
        require_once($CFG->libdir.'/outputfragmentrequirementslib.php');

        // Check that the requirements manager has not already been switched.
        if (get_class($this->_requires) == 'fragment_requirements_manager') {
            throw new coding_exception('JavaScript collection has already been started.');
        }
        // The header needs to have been called to flush out the generic JavaScript for the page. This allows only
        // JavaScript for the fragment to be collected. _wherethemewasinitialised is set when header() is called.
        if (!empty($this->_wherethemewasinitialised)) {
            // Change the current requirements manager over to the fragment manager to capture JS.
            $this->_requires = new fragment_requirements_manager();
        } else {
            throw new coding_exception('$OUTPUT->header() needs to be called before collecting JavaScript requirements.');
        }
853
854
    }

855
    /**
856
     * Should the current user see this page in editing mode.
857
858
     * That is, are they allowed to edit this page, and are they currently in
     * editing mode.
859
     * @return bool
860
861
862
863
864
865
     */
    public function user_is_editing() {
        global $USER;
        return !empty($USER->editing) && $this->user_allowed_editing();
    }

866
    /**
867
     * Does the user have permission to edit blocks on this page.
868
     * @return bool
869
870
871
872
873
     */
    public function user_can_edit_blocks() {
        return has_capability($this->_blockseditingcap, $this->_context);
    }

874
    /**
875
     * Does the user have permission to see this page in editing mode.
876
     * @return bool
877
878
     */
    public function user_allowed_editing() {
879
        return has_any_capability($this->all_editing_caps(), $this->_context);
880
881
    }

882
    /**
883
     * Get a description of this page. Normally displayed in the footer in developer debug mode.
884
     * @return string