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