1 |
efrain |
1 |
<?php
|
|
|
2 |
// This file is part of Moodle - http://moodle.org/
|
|
|
3 |
//
|
|
|
4 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
5 |
// it under the terms of the GNU General Public License as published by
|
|
|
6 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
7 |
// (at your option) any later version.
|
|
|
8 |
//
|
|
|
9 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
12 |
// GNU General Public License for more details.
|
|
|
13 |
//
|
|
|
14 |
// You should have received a copy of the GNU General Public License
|
|
|
15 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16 |
|
|
|
17 |
/**
|
|
|
18 |
* This file contains the moodle_page class. There is normally a single instance
|
|
|
19 |
* of this class in the $PAGE global variable. This class is a central repository
|
|
|
20 |
* of information about the page we are building up to send back to the user.
|
|
|
21 |
*
|
|
|
22 |
* @package core
|
|
|
23 |
* @category page
|
|
|
24 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
25 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
26 |
*/
|
|
|
27 |
|
|
|
28 |
defined('MOODLE_INTERNAL') || die();
|
|
|
29 |
|
|
|
30 |
use core\navigation\views\primary;
|
|
|
31 |
use core\navigation\views\secondary;
|
|
|
32 |
use core\navigation\output\primary as primaryoutput;
|
|
|
33 |
use core\output\activity_header;
|
|
|
34 |
|
|
|
35 |
/**
|
|
|
36 |
* $PAGE is a central store of information about the current page we are
|
|
|
37 |
* generating in response to the user's request.
|
|
|
38 |
*
|
|
|
39 |
* It does not do very much itself
|
|
|
40 |
* except keep track of information, however, it serves as the access point to
|
|
|
41 |
* some more significant components like $PAGE->theme, $PAGE->requires,
|
|
|
42 |
* $PAGE->blocks, etc.
|
|
|
43 |
*
|
|
|
44 |
* @copyright 2009 Tim Hunt
|
|
|
45 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
46 |
* @since Moodle 2.0
|
|
|
47 |
* @package core
|
|
|
48 |
* @category page
|
|
|
49 |
*
|
|
|
50 |
* The following properties are alphabetical. Please keep it that way so that its
|
|
|
51 |
* easy to maintain.
|
|
|
52 |
*
|
|
|
53 |
* @property-read string $activityname The type of activity we are in, for example 'forum' or 'quiz'.
|
|
|
54 |
* Will be null if this page is not within a module.
|
|
|
55 |
* @property-read stdClass $activityrecord The row from the activities own database table (for example
|
|
|
56 |
* the forum or quiz table) that this page belongs to. Will be null
|
|
|
57 |
* if this page is not within a module.
|
|
|
58 |
* @property-read activity_header $activityheader The activity header for the page, representing standard components
|
|
|
59 |
* displayed within the header
|
|
|
60 |
* @property-read array $alternativeversions Mime type => object with ->url and ->title.
|
|
|
61 |
* @property-read block_manager $blocks The blocks manager object for this page.
|
|
|
62 |
* @property-read array $blockmanipulations
|
|
|
63 |
* @property-read string $bodyclasses A string to use within the class attribute on the body tag.
|
|
|
64 |
* @property-read string $bodyid A string to use as the id of the body tag.
|
|
|
65 |
* @property-read string $button The HTML to go where the Turn editing on button normally goes.
|
|
|
66 |
* @property-read bool $cacheable Defaults to true. Set to false to stop the page being cached at all.
|
|
|
67 |
* @property-read array $categories An array of all the categories the page course belongs to,
|
|
|
68 |
* starting with the immediately containing category, and working out to
|
|
|
69 |
* the top-level category. This may be the empty array if we are in the
|
|
|
70 |
* front page course.
|
|
|
71 |
* @property-read mixed $category The category that the page course belongs to.
|
|
|
72 |
* @property-read cm_info $cm The course_module that this page belongs to. Will be null
|
|
|
73 |
* if this page is not within a module. This is a full cm object, as loaded
|
|
|
74 |
* by get_coursemodule_from_id or get_coursemodule_from_instance,
|
|
|
75 |
* so the extra modname and name fields are present.
|
|
|
76 |
* @property-read context $context The main context to which this page belongs.
|
|
|
77 |
* @property-read stdClass $course The current course that we are inside - a row from the
|
|
|
78 |
* course table. (Also available as $COURSE global.) If we are not inside
|
|
|
79 |
* an actual course, this will be the site course.
|
|
|
80 |
* @property-read string $devicetypeinuse The name of the device type in use
|
|
|
81 |
* @property-read string $docspath The path to the Documentation for this page.
|
|
|
82 |
* @property-read string $focuscontrol The id of the HTML element to be focused when the page has loaded.
|
|
|
83 |
* @property-read bool $headerprinted True if the page header has already been printed.
|
|
|
84 |
* @property-read string $heading The main heading that should be displayed at the top of the <body>.
|
|
|
85 |
* @property-read string $headingmenu The menu (or actions) to display in the heading
|
|
|
86 |
* @property-read array $layout_options An arrays with options for the layout file.
|
|
|
87 |
* @property-read array $legacythemeinuse True if the legacy browser theme is in use.
|
|
|
88 |
* @property-read navbar $navbar The navbar object used to display the navbar
|
|
|
89 |
* @property-read secondary $secondarynav The secondary navigation object
|
|
|
90 |
* used to display the secondarynav in boost
|
|
|
91 |
* @property-read primary $primarynav The primary navigation object used to display the primary nav in boost
|
|
|
92 |
* @property-read primaryoutput $primarynavcombined The primary navigation object used to display the primary nav in boost
|
|
|
93 |
* @property-read global_navigation $navigation The navigation structure for this page.
|
|
|
94 |
* @property-read xhtml_container_stack $opencontainers Tracks XHTML tags on this page that have been opened but not closed.
|
|
|
95 |
* mainly for internal use by the rendering code.
|
|
|
96 |
* @property-read string $pagelayout The general type of page this is. For example 'normal', 'popup', 'home'.
|
|
|
97 |
* Allows the theme to display things differently, if it wishes to.
|
|
|
98 |
* @property-read string $pagetype The page type string, should be used as the id for the body tag in the theme.
|
|
|
99 |
* @property-read int $periodicrefreshdelay The periodic refresh delay to use with meta refresh
|
|
|
100 |
* @property-read page_requirements_manager $requires Tracks the JavaScript, CSS files, etc. required by this page.
|
|
|
101 |
* @property-read string $requestip The IP address of the current request, null if unknown.
|
|
|
102 |
* @property-read string $requestorigin The type of request 'web', 'ws', 'cli', 'restore', etc.
|
|
|
103 |
* @property-read settings_navigation $settingsnav The settings navigation
|
|
|
104 |
* @property-read int $state One of the STATE_... constants
|
|
|
105 |
* @property-read string $subpage The subpage identifier, if any.
|
|
|
106 |
* @property-read theme_config $theme The theme for this page.
|
|
|
107 |
* @property-read string $title The title that should go in the <head> section of the HTML of this page.
|
|
|
108 |
* @property-read moodle_url $url The moodle url object for this page.
|
|
|
109 |
*/
|
|
|
110 |
class moodle_page {
|
|
|
111 |
|
|
|
112 |
/** The state of the page before it has printed the header **/
|
|
|
113 |
const STATE_BEFORE_HEADER = 0;
|
|
|
114 |
|
|
|
115 |
/** The state the page is in temporarily while the header is being printed **/
|
|
|
116 |
const STATE_PRINTING_HEADER = 1;
|
|
|
117 |
|
|
|
118 |
/** The state the page is in while content is presumably being printed **/
|
|
|
119 |
const STATE_IN_BODY = 2;
|
|
|
120 |
|
|
|
121 |
/**
|
|
|
122 |
* The state the page is when the footer has been printed and its function is
|
|
|
123 |
* complete.
|
|
|
124 |
*/
|
|
|
125 |
const STATE_DONE = 3;
|
|
|
126 |
|
|
|
127 |
/**
|
|
|
128 |
* The separator used for separating page title elements.
|
|
|
129 |
*/
|
|
|
130 |
const TITLE_SEPARATOR = ' | ';
|
|
|
131 |
|
|
|
132 |
/**
|
|
|
133 |
* @var int The current state of the page. The state a page is within
|
|
|
134 |
* determines what actions are possible for it.
|
|
|
135 |
*/
|
|
|
136 |
protected $_state = self::STATE_BEFORE_HEADER;
|
|
|
137 |
|
|
|
138 |
/**
|
|
|
139 |
* @var stdClass The course currently associated with this page.
|
|
|
140 |
* If not has been provided the front page course is used.
|
|
|
141 |
*/
|
|
|
142 |
protected $_course = null;
|
|
|
143 |
|
|
|
144 |
/**
|
|
|
145 |
* @var cm_info If this page belongs to a module, this is the cm_info module
|
|
|
146 |
* description object.
|
|
|
147 |
*/
|
|
|
148 |
protected $_cm = null;
|
|
|
149 |
|
|
|
150 |
/**
|
|
|
151 |
* @var stdClass If $_cm is not null, then this will hold the corresponding
|
|
|
152 |
* row from the modname table. For example, if $_cm->modname is 'quiz', this
|
|
|
153 |
* will be a row from the quiz table.
|
|
|
154 |
*/
|
|
|
155 |
protected $_module = null;
|
|
|
156 |
|
|
|
157 |
/**
|
|
|
158 |
* @var context The context that this page belongs to.
|
|
|
159 |
*/
|
|
|
160 |
protected $_context = null;
|
|
|
161 |
|
|
|
162 |
/**
|
|
|
163 |
* @var array This holds any categories that $_course belongs to, starting with the
|
|
|
164 |
* particular category it belongs to, and working out through any parent
|
|
|
165 |
* categories to the top level. These are loaded progressively, if needed.
|
|
|
166 |
* There are three states. $_categories = null initially when nothing is
|
|
|
167 |
* loaded; $_categories = array($id => $cat, $parentid => null) when we have
|
|
|
168 |
* loaded $_course->category, but not any parents; and a complete array once
|
|
|
169 |
* everything is loaded.
|
|
|
170 |
*/
|
|
|
171 |
protected $_categories = null;
|
|
|
172 |
|
|
|
173 |
/**
|
|
|
174 |
* @var array An array of CSS classes that should be added to the body tag in HTML.
|
|
|
175 |
*/
|
|
|
176 |
protected $_bodyclasses = array();
|
|
|
177 |
|
|
|
178 |
/**
|
|
|
179 |
* @var string The title for the page. Used within the title tag in the HTML head.
|
|
|
180 |
*/
|
|
|
181 |
protected $_title = '';
|
|
|
182 |
|
|
|
183 |
/**
|
|
|
184 |
* @var string The string to use as the heading of the page. Shown near the top of the
|
|
|
185 |
* page within most themes.
|
|
|
186 |
*/
|
|
|
187 |
protected $_heading = '';
|
|
|
188 |
|
|
|
189 |
/**
|
|
|
190 |
* @var string The pagetype is used to describe the page and defaults to a representation
|
|
|
191 |
* of the physical path to the page e.g. my-index, mod-quiz-attempt
|
|
|
192 |
*/
|
|
|
193 |
protected $_pagetype = null;
|
|
|
194 |
|
|
|
195 |
/**
|
|
|
196 |
* @var string The pagelayout to use when displaying this page. The
|
|
|
197 |
* pagelayout needs to have been defined by the theme in use, or one of its
|
|
|
198 |
* parents. By default base is used however standard is the more common layout.
|
|
|
199 |
* Note that this gets automatically set by core during operations like
|
|
|
200 |
* require_login.
|
|
|
201 |
*/
|
|
|
202 |
protected $_pagelayout = 'base';
|
|
|
203 |
|
|
|
204 |
/**
|
|
|
205 |
* @var array List of theme layout options, these are ignored by core.
|
|
|
206 |
* To be used in individual theme layout files only.
|
|
|
207 |
*/
|
|
|
208 |
protected $_layout_options = null;
|
|
|
209 |
|
|
|
210 |
/**
|
|
|
211 |
* @var string An optional arbitrary parameter that can be set on pages where the context
|
|
|
212 |
* and pagetype is not enough to identify the page.
|
|
|
213 |
*/
|
|
|
214 |
protected $_subpage = '';
|
|
|
215 |
|
|
|
216 |
/**
|
|
|
217 |
* @var string Set a different path to use for the 'Documentation for this page' link.
|
|
|
218 |
* By default, it uses the path of the file for instance mod/quiz/attempt.
|
|
|
219 |
*/
|
|
|
220 |
protected $_docspath = null;
|
|
|
221 |
|
|
|
222 |
/**
|
|
|
223 |
* @var string A legacy class that will be added to the body tag
|
|
|
224 |
*/
|
|
|
225 |
protected $_legacyclass = null;
|
|
|
226 |
|
|
|
227 |
/**
|
|
|
228 |
* @var moodle_url The URL for this page. This is mandatory and must be set
|
|
|
229 |
* before output is started.
|
|
|
230 |
*/
|
|
|
231 |
protected $_url = null;
|
|
|
232 |
|
|
|
233 |
/**
|
|
|
234 |
* @var array An array of links to alternative versions of this page.
|
|
|
235 |
* Primarily used for RSS versions of the current page.
|
|
|
236 |
*/
|
|
|
237 |
protected $_alternateversions = array();
|
|
|
238 |
|
|
|
239 |
/**
|
|
|
240 |
* @var block_manager The blocks manager for this page. It is responsible for
|
|
|
241 |
* the blocks and there content on this page.
|
|
|
242 |
*/
|
|
|
243 |
protected $_blocks = null;
|
|
|
244 |
|
|
|
245 |
/**
|
|
|
246 |
* @var page_requirements_manager Page requirements manager. It is responsible
|
|
|
247 |
* for all JavaScript and CSS resources required by this page.
|
|
|
248 |
*/
|
|
|
249 |
protected $_requires = null;
|
|
|
250 |
|
|
|
251 |
/** @var page_requirements_manager Saves the requirement manager object used before switching to to fragments one. */
|
|
|
252 |
protected $savedrequires = null;
|
|
|
253 |
|
|
|
254 |
/**
|
|
|
255 |
* @var string The capability required by the user in order to edit blocks
|
|
|
256 |
* and block settings on this page.
|
|
|
257 |
*/
|
|
|
258 |
protected $_blockseditingcap = 'moodle/site:manageblocks';
|
|
|
259 |
|
|
|
260 |
/**
|
|
|
261 |
* @var bool An internal flag to record when block actions have been processed.
|
|
|
262 |
* Remember block actions occur on the current URL and it is important that
|
|
|
263 |
* even they are never executed more than once.
|
|
|
264 |
*/
|
|
|
265 |
protected $_block_actions_done = false;
|
|
|
266 |
|
|
|
267 |
/**
|
|
|
268 |
* @var array An array of any other capabilities the current user must have
|
|
|
269 |
* in order to editing the page and/or its content (not just blocks).
|
|
|
270 |
*/
|
|
|
271 |
protected $_othereditingcaps = array();
|
|
|
272 |
|
|
|
273 |
/**
|
|
|
274 |
* @var bool Sets whether this page should be cached by the browser or not.
|
|
|
275 |
* If it is set to true (default) the page is served with caching headers.
|
|
|
276 |
*/
|
|
|
277 |
protected $_cacheable = true;
|
|
|
278 |
|
|
|
279 |
/**
|
|
|
280 |
* @var string Can be set to the ID of an element on the page, if done that
|
|
|
281 |
* element receives focus when the page loads.
|
|
|
282 |
*/
|
|
|
283 |
protected $_focuscontrol = '';
|
|
|
284 |
|
|
|
285 |
/**
|
|
|
286 |
* @var string HTML to go where the turn on editing button is located. This
|
|
|
287 |
* is nearly a legacy item and not used very often any more.
|
|
|
288 |
*/
|
|
|
289 |
protected $_button = '';
|
|
|
290 |
|
|
|
291 |
/**
|
|
|
292 |
* @var theme_config The theme to use with this page. This has to be properly
|
|
|
293 |
* initialised via {@link moodle_page::initialise_theme_and_output()} which
|
|
|
294 |
* happens magically before any operation that requires it.
|
|
|
295 |
*/
|
|
|
296 |
protected $_theme = null;
|
|
|
297 |
|
|
|
298 |
/**
|
|
|
299 |
* @var global_navigation Contains the global navigation structure.
|
|
|
300 |
*/
|
|
|
301 |
protected $_navigation = null;
|
|
|
302 |
|
|
|
303 |
/**
|
|
|
304 |
* @var settings_navigation Contains the settings navigation structure.
|
|
|
305 |
*/
|
|
|
306 |
protected $_settingsnav = null;
|
|
|
307 |
|
|
|
308 |
/**
|
|
|
309 |
* @var flat_navigation Contains a list of nav nodes, most closely related to the current page.
|
|
|
310 |
*/
|
|
|
311 |
protected $_flatnav = null;
|
|
|
312 |
|
|
|
313 |
/**
|
|
|
314 |
* @var secondary Contains the nav nodes that will appear
|
|
|
315 |
* in the secondary navigation.
|
|
|
316 |
*/
|
|
|
317 |
protected $_secondarynav = null;
|
|
|
318 |
|
|
|
319 |
/**
|
|
|
320 |
* @var primary Contains the nav nodes that will appear
|
|
|
321 |
* in the primary navigation.
|
|
|
322 |
*/
|
|
|
323 |
protected $_primarynav = null;
|
|
|
324 |
|
|
|
325 |
/**
|
|
|
326 |
* @var primaryoutput Contains the combined nav nodes that will appear
|
|
|
327 |
* in the primary navigation. Includes - primarynav, langmenu, usermenu
|
|
|
328 |
*/
|
|
|
329 |
protected $_primarynavcombined = null;
|
|
|
330 |
|
|
|
331 |
/**
|
|
|
332 |
* @var navbar Contains the navbar structure.
|
|
|
333 |
*/
|
|
|
334 |
protected $_navbar = null;
|
|
|
335 |
|
|
|
336 |
/**
|
|
|
337 |
* @var string The menu (or actions) to display in the heading.
|
|
|
338 |
*/
|
|
|
339 |
protected $_headingmenu = null;
|
|
|
340 |
|
|
|
341 |
/**
|
|
|
342 |
* @var array stack trace. Then the theme is initialised, we save the stack
|
|
|
343 |
* trace, for use in error messages.
|
|
|
344 |
*/
|
|
|
345 |
protected $_wherethemewasinitialised = null;
|
|
|
346 |
|
|
|
347 |
/**
|
|
|
348 |
* @var xhtml_container_stack Tracks XHTML tags on this page that have been
|
|
|
349 |
* opened but not closed.
|
|
|
350 |
*/
|
|
|
351 |
protected $_opencontainers;
|
|
|
352 |
|
|
|
353 |
/**
|
|
|
354 |
* @var int Sets the page to refresh after a given delay (in seconds) using
|
|
|
355 |
* meta refresh in {@link standard_head_html()} in outputlib.php
|
|
|
356 |
* If set to null(default) the page is not refreshed
|
|
|
357 |
*/
|
|
|
358 |
protected $_periodicrefreshdelay = null;
|
|
|
359 |
|
|
|
360 |
/**
|
|
|
361 |
* @var array Associative array of browser shortnames (as used by check_browser_version)
|
|
|
362 |
* and their minimum required versions
|
|
|
363 |
*/
|
|
|
364 |
protected $_legacybrowsers = array('MSIE' => 6.0);
|
|
|
365 |
|
|
|
366 |
/**
|
|
|
367 |
* @var string Is set to the name of the device type in use.
|
|
|
368 |
* This will we worked out when it is first used.
|
|
|
369 |
*/
|
|
|
370 |
protected $_devicetypeinuse = null;
|
|
|
371 |
|
|
|
372 |
/**
|
|
|
373 |
* @var bool Used to determine if HTTPS should be required for login.
|
|
|
374 |
*/
|
|
|
375 |
protected $_https_login_required = false;
|
|
|
376 |
|
|
|
377 |
/**
|
|
|
378 |
* @var bool Determines if popup notifications allowed on this page.
|
|
|
379 |
* Code such as the quiz module disables popup notifications in situations
|
|
|
380 |
* such as upgrading or completing a quiz.
|
|
|
381 |
*/
|
|
|
382 |
protected $_popup_notification_allowed = true;
|
|
|
383 |
|
|
|
384 |
/**
|
|
|
385 |
* @var bool Is the settings menu being forced to display on this page (activities / resources only).
|
|
|
386 |
* This is only used by themes that use the settings menu.
|
|
|
387 |
*/
|
|
|
388 |
protected $_forcesettingsmenu = false;
|
|
|
389 |
|
|
|
390 |
/**
|
|
|
391 |
* @var array Array of header actions HTML to add to the page header actions menu.
|
|
|
392 |
*/
|
|
|
393 |
protected $_headeractions = [];
|
|
|
394 |
|
|
|
395 |
/**
|
|
|
396 |
* @var bool Should the region main settings menu be rendered in the header.
|
|
|
397 |
*/
|
|
|
398 |
protected $_regionmainsettingsinheader = false;
|
|
|
399 |
|
|
|
400 |
/**
|
|
|
401 |
* @var bool Should the secondary menu be rendered.
|
|
|
402 |
*/
|
|
|
403 |
protected $_hassecondarynavigation = true;
|
|
|
404 |
|
|
|
405 |
/**
|
|
|
406 |
* @var bool Should the secondary menu be rendered as a tablist as opposed to a menubar.
|
|
|
407 |
*/
|
|
|
408 |
protected $_hastablistsecondarynavigation = false;
|
|
|
409 |
|
|
|
410 |
/**
|
|
|
411 |
* @var string the key of the secondary node to be activated.
|
|
|
412 |
*/
|
|
|
413 |
protected $_activekeysecondary = null;
|
|
|
414 |
|
|
|
415 |
/**
|
|
|
416 |
* @var string the key of the primary node to be activated.
|
|
|
417 |
*/
|
|
|
418 |
protected $_activenodeprimary = null;
|
|
|
419 |
|
|
|
420 |
/**
|
|
|
421 |
* @var activity_header The activity header for the page.
|
|
|
422 |
*/
|
|
|
423 |
protected $_activityheader;
|
|
|
424 |
|
|
|
425 |
/**
|
|
|
426 |
* @var bool The value of displaying the navigation overflow.
|
|
|
427 |
*/
|
|
|
428 |
protected $_navigationoverflow = true;
|
|
|
429 |
|
|
|
430 |
/**
|
|
|
431 |
* @var bool Whether to override/remove all editing capabilities for blocks on the page.
|
|
|
432 |
*/
|
|
|
433 |
protected $_forcelockallblocks = false;
|
|
|
434 |
|
|
|
435 |
/**
|
|
|
436 |
* @var bool Indicates whether the course index drawer should be shown.
|
|
|
437 |
*/
|
|
|
438 |
protected bool $_showcourseindex = true;
|
|
|
439 |
|
|
|
440 |
/**
|
|
|
441 |
* Force the settings menu to be displayed on this page. This will only force the
|
|
|
442 |
* settings menu on an activity / resource page that is being displayed on a theme that
|
|
|
443 |
* uses a settings menu.
|
|
|
444 |
*
|
|
|
445 |
* @param bool $forced default of true, can be sent false to turn off the force.
|
|
|
446 |
*/
|
|
|
447 |
public function force_settings_menu($forced = true) {
|
|
|
448 |
$this->_forcesettingsmenu = $forced;
|
|
|
449 |
}
|
|
|
450 |
|
|
|
451 |
/**
|
|
|
452 |
* Check to see if the settings menu is forced to display on this activity / resource page.
|
|
|
453 |
* This only applies to themes that use the settings menu.
|
|
|
454 |
*
|
|
|
455 |
* @return bool True if the settings menu is forced to display.
|
|
|
456 |
*/
|
|
|
457 |
public function is_settings_menu_forced() {
|
|
|
458 |
return $this->_forcesettingsmenu;
|
|
|
459 |
}
|
|
|
460 |
|
|
|
461 |
// Magic getter methods =============================================================
|
|
|
462 |
// Due to the __get magic below, you normally do not call these as $PAGE->magic_get_x
|
|
|
463 |
// methods, but instead use the $PAGE->x syntax.
|
|
|
464 |
|
|
|
465 |
/**
|
|
|
466 |
* Please do not call this method directly, use the ->state syntax. {@link moodle_page::__get()}.
|
|
|
467 |
* @return integer one of the STATE_XXX constants. You should not normally need
|
|
|
468 |
* to use this in your code. It is intended for internal use by this class
|
|
|
469 |
* and its friends like print_header, to check that everything is working as
|
|
|
470 |
* expected. Also accessible as $PAGE->state.
|
|
|
471 |
*/
|
|
|
472 |
protected function magic_get_state() {
|
|
|
473 |
return $this->_state;
|
|
|
474 |
}
|
|
|
475 |
|
|
|
476 |
/**
|
|
|
477 |
* Please do not call this method directly, use the ->headerprinted syntax. {@link moodle_page::__get()}.
|
|
|
478 |
* @return bool has the header already been printed?
|
|
|
479 |
*/
|
|
|
480 |
protected function magic_get_headerprinted() {
|
|
|
481 |
return $this->_state >= self::STATE_IN_BODY;
|
|
|
482 |
}
|
|
|
483 |
|
|
|
484 |
/**
|
|
|
485 |
* Please do not call this method directly, use the ->course syntax. {@link moodle_page::__get()}.
|
|
|
486 |
* @return stdClass the current course that we are inside - a row from the
|
|
|
487 |
* course table. (Also available as $COURSE global.) If we are not inside
|
|
|
488 |
* an actual course, this will be the site course.
|
|
|
489 |
*/
|
|
|
490 |
protected function magic_get_course() {
|
|
|
491 |
global $SITE;
|
|
|
492 |
if (is_null($this->_course)) {
|
|
|
493 |
return $SITE;
|
|
|
494 |
}
|
|
|
495 |
return $this->_course;
|
|
|
496 |
}
|
|
|
497 |
|
|
|
498 |
/**
|
|
|
499 |
* Please do not call this method directly, use the ->cm syntax. {@link moodle_page::__get()}.
|
|
|
500 |
* @return cm_info the course_module that this page belongs to. Will be null
|
|
|
501 |
* if this page is not within a module. This is a full cm object, as loaded
|
|
|
502 |
* by get_coursemodule_from_id or get_coursemodule_from_instance,
|
|
|
503 |
* so the extra modname and name fields are present.
|
|
|
504 |
*/
|
|
|
505 |
protected function magic_get_cm() {
|
|
|
506 |
return $this->_cm;
|
|
|
507 |
}
|
|
|
508 |
|
|
|
509 |
/**
|
|
|
510 |
* Please do not call this method directly, use the ->activityrecord syntax. {@link moodle_page::__get()}.
|
|
|
511 |
* @return stdClass the row from the activities own database table (for example
|
|
|
512 |
* the forum or quiz table) that this page belongs to. Will be null
|
|
|
513 |
* if this page is not within a module.
|
|
|
514 |
*/
|
|
|
515 |
protected function magic_get_activityrecord() {
|
|
|
516 |
if (is_null($this->_module) && !is_null($this->_cm)) {
|
|
|
517 |
$this->load_activity_record();
|
|
|
518 |
}
|
|
|
519 |
return $this->_module;
|
|
|
520 |
}
|
|
|
521 |
|
|
|
522 |
/**
|
|
|
523 |
* Please do not call this method directly, use the ->activityname syntax. {@link moodle_page::__get()}.
|
|
|
524 |
* @return string the The type of activity we are in, for example 'forum' or 'quiz'.
|
|
|
525 |
* Will be null if this page is not within a module.
|
|
|
526 |
*/
|
|
|
527 |
protected function magic_get_activityname() {
|
|
|
528 |
if (is_null($this->_cm)) {
|
|
|
529 |
return null;
|
|
|
530 |
}
|
|
|
531 |
return $this->_cm->modname;
|
|
|
532 |
}
|
|
|
533 |
|
|
|
534 |
/**
|
|
|
535 |
* Please do not call this method directly, use the ->category syntax. {@link moodle_page::__get()}.
|
|
|
536 |
* @return stdClass|null the category that the page course belongs to. If there isn't one
|
|
|
537 |
* (that is, if this is the front page course) returns null.
|
|
|
538 |
*/
|
|
|
539 |
protected function magic_get_category() {
|
|
|
540 |
$this->ensure_category_loaded();
|
|
|
541 |
if (!empty($this->_categories)) {
|
|
|
542 |
return reset($this->_categories);
|
|
|
543 |
} else {
|
|
|
544 |
return null;
|
|
|
545 |
}
|
|
|
546 |
}
|
|
|
547 |
|
|
|
548 |
/**
|
|
|
549 |
* Please do not call this method directly, use the ->categories syntax. {@link moodle_page::__get()}.
|
|
|
550 |
* @return array an array of all the categories the page course belongs to,
|
|
|
551 |
* starting with the immediately containing category, and working out to
|
|
|
552 |
* the top-level category. This may be the empty array if we are in the
|
|
|
553 |
* front page course.
|
|
|
554 |
*/
|
|
|
555 |
protected function magic_get_categories() {
|
|
|
556 |
$this->ensure_categories_loaded();
|
|
|
557 |
return $this->_categories;
|
|
|
558 |
}
|
|
|
559 |
|
|
|
560 |
/**
|
|
|
561 |
* Please do not call this method directly, use the ->context syntax. {@link moodle_page::__get()}.
|
|
|
562 |
* @return context the main context to which this page belongs.
|
|
|
563 |
*/
|
|
|
564 |
protected function magic_get_context() {
|
|
|
565 |
global $CFG;
|
|
|
566 |
if (is_null($this->_context)) {
|
|
|
567 |
if (CLI_SCRIPT or NO_MOODLE_COOKIES) {
|
|
|
568 |
// Cli scripts work in system context, do not annoy devs with debug info.
|
|
|
569 |
// Very few scripts do not use cookies, we can safely use system as default context there.
|
|
|
570 |
} else if (AJAX_SCRIPT && $CFG->debugdeveloper) {
|
|
|
571 |
// Throw exception inside AJAX script in developer mode, otherwise the debugging message may be missed.
|
|
|
572 |
throw new coding_exception('$PAGE->context was not set. You may have forgotten '
|
|
|
573 |
.'to call require_login() or $PAGE->set_context()');
|
|
|
574 |
} else {
|
|
|
575 |
debugging('Coding problem: $PAGE->context was not set. You may have forgotten '
|
|
|
576 |
.'to call require_login() or $PAGE->set_context(). The page may not display '
|
|
|
577 |
.'correctly as a result');
|
|
|
578 |
}
|
|
|
579 |
$this->_context = context_system::instance();
|
|
|
580 |
}
|
|
|
581 |
return $this->_context;
|
|
|
582 |
}
|
|
|
583 |
|
|
|
584 |
/**
|
|
|
585 |
* Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
|
|
|
586 |
* @return string e.g. 'my-index' or 'mod-quiz-attempt'.
|
|
|
587 |
*/
|
|
|
588 |
protected function magic_get_pagetype() {
|
|
|
589 |
global $CFG;
|
|
|
590 |
if (is_null($this->_pagetype) || isset($CFG->pagepath)) {
|
|
|
591 |
$this->initialise_default_pagetype();
|
|
|
592 |
}
|
|
|
593 |
return $this->_pagetype;
|
|
|
594 |
}
|
|
|
595 |
|
|
|
596 |
/**
|
|
|
597 |
* Please do not call this method directly, use the ->pagetype syntax. {@link moodle_page::__get()}.
|
|
|
598 |
* @return string The id to use on the body tag, uses {@link magic_get_pagetype()}.
|
|
|
599 |
*/
|
|
|
600 |
protected function magic_get_bodyid() {
|
|
|
601 |
return 'page-'.$this->pagetype;
|
|
|
602 |
}
|
|
|
603 |
|
|
|
604 |
/**
|
|
|
605 |
* Please do not call this method directly, use the ->pagelayout syntax. {@link moodle_page::__get()}.
|
|
|
606 |
* @return string the general type of page this is. For example 'standard', 'popup', 'home'.
|
|
|
607 |
* Allows the theme to display things differently, if it wishes to.
|
|
|
608 |
*/
|
|
|
609 |
protected function magic_get_pagelayout() {
|
|
|
610 |
return $this->_pagelayout;
|
|
|
611 |
}
|
|
|
612 |
|
|
|
613 |
/**
|
|
|
614 |
* Please do not call this method directly, use the ->layout_options syntax. {@link moodle_page::__get()}.
|
|
|
615 |
* @return array returns arrays with options for layout file
|
|
|
616 |
*/
|
|
|
617 |
protected function magic_get_layout_options() {
|
|
|
618 |
if (!is_array($this->_layout_options)) {
|
|
|
619 |
$this->_layout_options = $this->_theme->pagelayout_options($this->pagelayout);
|
|
|
620 |
}
|
|
|
621 |
return $this->_layout_options;
|
|
|
622 |
}
|
|
|
623 |
|
|
|
624 |
/**
|
|
|
625 |
* Please do not call this method directly, use the ->subpage syntax. {@link moodle_page::__get()}.
|
|
|
626 |
* @return string The subpage identifier, if any.
|
|
|
627 |
*/
|
|
|
628 |
protected function magic_get_subpage() {
|
|
|
629 |
return $this->_subpage;
|
|
|
630 |
}
|
|
|
631 |
|
|
|
632 |
/**
|
|
|
633 |
* Please do not call this method directly, use the ->bodyclasses syntax. {@link moodle_page::__get()}.
|
|
|
634 |
* @return string the class names to put on the body element in the HTML.
|
|
|
635 |
*/
|
|
|
636 |
protected function magic_get_bodyclasses() {
|
|
|
637 |
return implode(' ', array_keys($this->_bodyclasses));
|
|
|
638 |
}
|
|
|
639 |
|
|
|
640 |
/**
|
|
|
641 |
* Please do not call this method directly, use the ->title syntax. {@link moodle_page::__get()}.
|
|
|
642 |
* @return string the title that should go in the <head> section of the HTML of this page.
|
|
|
643 |
*/
|
|
|
644 |
protected function magic_get_title() {
|
|
|
645 |
return $this->_title;
|
|
|
646 |
}
|
|
|
647 |
|
|
|
648 |
/**
|
|
|
649 |
* Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
|
|
|
650 |
* @return string the main heading that should be displayed at the top of the <body>.
|
|
|
651 |
*/
|
|
|
652 |
protected function magic_get_heading() {
|
|
|
653 |
return $this->_heading;
|
|
|
654 |
}
|
|
|
655 |
|
|
|
656 |
/**
|
|
|
657 |
* Please do not call this method directly, use the ->heading syntax. {@link moodle_page::__get()}.
|
|
|
658 |
* @return string The menu (or actions) to display in the heading
|
|
|
659 |
*/
|
|
|
660 |
protected function magic_get_headingmenu() {
|
|
|
661 |
return $this->_headingmenu;
|
|
|
662 |
}
|
|
|
663 |
|
|
|
664 |
/**
|
|
|
665 |
* Please do not call this method directly, use the ->docspath syntax. {@link moodle_page::__get()}.
|
|
|
666 |
* @return string the path to the Documentation for this page.
|
|
|
667 |
*/
|
|
|
668 |
protected function magic_get_docspath() {
|
|
|
669 |
if (is_string($this->_docspath)) {
|
|
|
670 |
return $this->_docspath;
|
|
|
671 |
} else {
|
|
|
672 |
return str_replace('-', '/', $this->pagetype);
|
|
|
673 |
}
|
|
|
674 |
}
|
|
|
675 |
|
|
|
676 |
/**
|
|
|
677 |
* Please do not call this method directly, use the ->url syntax. {@link moodle_page::__get()}.
|
|
|
678 |
* @return moodle_url the clean URL required to load the current page. (You
|
|
|
679 |
* should normally use this in preference to $ME or $FULLME.)
|
|
|
680 |
*/
|
|
|
681 |
protected function magic_get_url() {
|
|
|
682 |
global $FULLME;
|
|
|
683 |
if (is_null($this->_url)) {
|
|
|
684 |
debugging('This page did not call $PAGE->set_url(...). Using '.s($FULLME), DEBUG_DEVELOPER);
|
|
|
685 |
$this->_url = new moodle_url($FULLME);
|
|
|
686 |
// Make sure the guessed URL cannot lead to dangerous redirects.
|
|
|
687 |
$this->_url->remove_params('sesskey');
|
|
|
688 |
}
|
|
|
689 |
return new moodle_url($this->_url); // Return a clone for safety.
|
|
|
690 |
}
|
|
|
691 |
|
|
|
692 |
/**
|
|
|
693 |
* The list of alternate versions of this page.
|
|
|
694 |
* @return array mime type => object with ->url and ->title.
|
|
|
695 |
*/
|
|
|
696 |
protected function magic_get_alternateversions() {
|
|
|
697 |
return $this->_alternateversions;
|
|
|
698 |
}
|
|
|
699 |
|
|
|
700 |
/**
|
|
|
701 |
* Please do not call this method directly, use the ->blocks syntax. {@link moodle_page::__get()}.
|
|
|
702 |
* @return block_manager the blocks manager object for this page.
|
|
|
703 |
*/
|
|
|
704 |
protected function magic_get_blocks() {
|
|
|
705 |
global $CFG;
|
|
|
706 |
if (is_null($this->_blocks)) {
|
|
|
707 |
if (!empty($CFG->blockmanagerclass)) {
|
|
|
708 |
if (!empty($CFG->blockmanagerclassfile)) {
|
|
|
709 |
require_once($CFG->blockmanagerclassfile);
|
|
|
710 |
}
|
|
|
711 |
$classname = $CFG->blockmanagerclass;
|
|
|
712 |
} else {
|
|
|
713 |
$classname = 'block_manager';
|
|
|
714 |
}
|
|
|
715 |
$this->_blocks = new $classname($this);
|
|
|
716 |
}
|
|
|
717 |
return $this->_blocks;
|
|
|
718 |
}
|
|
|
719 |
|
|
|
720 |
/**
|
|
|
721 |
* Please do not call this method directly, use the ->requires syntax. {@link moodle_page::__get()}.
|
|
|
722 |
* @return page_requirements_manager tracks the JavaScript, CSS files, etc. required by this page.
|
|
|
723 |
*/
|
|
|
724 |
protected function magic_get_requires() {
|
|
|
725 |
if (is_null($this->_requires)) {
|
|
|
726 |
$this->_requires = new page_requirements_manager();
|
|
|
727 |
}
|
|
|
728 |
return $this->_requires;
|
|
|
729 |
}
|
|
|
730 |
|
|
|
731 |
/**
|
|
|
732 |
* Please do not call this method directly, use the ->cacheable syntax. {@link moodle_page::__get()}.
|
|
|
733 |
* @return bool can this page be cached by the user's browser.
|
|
|
734 |
*/
|
|
|
735 |
protected function magic_get_cacheable() {
|
|
|
736 |
return $this->_cacheable;
|
|
|
737 |
}
|
|
|
738 |
|
|
|
739 |
/**
|
|
|
740 |
* Please do not call this method directly, use the ->focuscontrol syntax. {@link moodle_page::__get()}.
|
|
|
741 |
* @return string the id of the HTML element to be focused when the page has loaded.
|
|
|
742 |
*/
|
|
|
743 |
protected function magic_get_focuscontrol() {
|
|
|
744 |
return $this->_focuscontrol;
|
|
|
745 |
}
|
|
|
746 |
|
|
|
747 |
/**
|
|
|
748 |
* Please do not call this method directly, use the ->button syntax. {@link moodle_page::__get()}.
|
|
|
749 |
* @return string the HTML to go where the Turn editing on button normally goes.
|
|
|
750 |
*/
|
|
|
751 |
protected function magic_get_button() {
|
|
|
752 |
return $this->_button;
|
|
|
753 |
}
|
|
|
754 |
|
|
|
755 |
/**
|
|
|
756 |
* Please do not call this method directly, use the ->theme syntax. {@link moodle_page::__get()}.
|
|
|
757 |
* @return theme_config the initialised theme for this page.
|
|
|
758 |
*/
|
|
|
759 |
protected function magic_get_theme() {
|
|
|
760 |
if (is_null($this->_theme)) {
|
|
|
761 |
$this->initialise_theme_and_output();
|
|
|
762 |
}
|
|
|
763 |
return $this->_theme;
|
|
|
764 |
}
|
|
|
765 |
|
|
|
766 |
/**
|
|
|
767 |
* Returns an array of minipulations or false if there are none to make.
|
|
|
768 |
*
|
|
|
769 |
* @since Moodle 2.5.1 2.6
|
|
|
770 |
* @return bool|array
|
|
|
771 |
*/
|
|
|
772 |
protected function magic_get_blockmanipulations() {
|
|
|
773 |
if (!right_to_left()) {
|
|
|
774 |
return false;
|
|
|
775 |
}
|
|
|
776 |
if (is_null($this->_theme)) {
|
|
|
777 |
$this->initialise_theme_and_output();
|
|
|
778 |
}
|
|
|
779 |
return $this->_theme->blockrtlmanipulations;
|
|
|
780 |
}
|
|
|
781 |
|
|
|
782 |
/**
|
|
|
783 |
* Please do not call this method directly, use the ->devicetypeinuse syntax. {@link moodle_page::__get()}.
|
|
|
784 |
* @return string The device type being used.
|
|
|
785 |
*/
|
|
|
786 |
protected function magic_get_devicetypeinuse() {
|
|
|
787 |
if (empty($this->_devicetypeinuse)) {
|
|
|
788 |
$this->_devicetypeinuse = core_useragent::get_user_device_type();
|
|
|
789 |
}
|
|
|
790 |
return $this->_devicetypeinuse;
|
|
|
791 |
}
|
|
|
792 |
|
|
|
793 |
/**
|
|
|
794 |
* Please do not call this method directly use the ->periodicrefreshdelay syntax
|
|
|
795 |
* {@link moodle_page::__get()}
|
|
|
796 |
* @return int The periodic refresh delay to use with meta refresh
|
|
|
797 |
*/
|
|
|
798 |
protected function magic_get_periodicrefreshdelay() {
|
|
|
799 |
return $this->_periodicrefreshdelay;
|
|
|
800 |
}
|
|
|
801 |
|
|
|
802 |
/**
|
|
|
803 |
* Please do not call this method directly use the ->opencontainers syntax. {@link moodle_page::__get()}
|
|
|
804 |
* @return xhtml_container_stack tracks XHTML tags on this page that have been opened but not closed.
|
|
|
805 |
* mainly for internal use by the rendering code.
|
|
|
806 |
*/
|
|
|
807 |
protected function magic_get_opencontainers() {
|
|
|
808 |
if (is_null($this->_opencontainers)) {
|
|
|
809 |
$this->_opencontainers = new xhtml_container_stack();
|
|
|
810 |
}
|
|
|
811 |
return $this->_opencontainers;
|
|
|
812 |
}
|
|
|
813 |
|
|
|
814 |
/**
|
|
|
815 |
* Return the navigation object
|
|
|
816 |
* @return global_navigation
|
|
|
817 |
*/
|
|
|
818 |
protected function magic_get_navigation() {
|
|
|
819 |
if ($this->_navigation === null) {
|
|
|
820 |
$this->_navigation = new global_navigation($this);
|
|
|
821 |
}
|
|
|
822 |
return $this->_navigation;
|
|
|
823 |
}
|
|
|
824 |
|
|
|
825 |
/**
|
|
|
826 |
* Return a navbar object
|
|
|
827 |
* @return navbar
|
|
|
828 |
*/
|
|
|
829 |
protected function magic_get_navbar() {
|
|
|
830 |
if ($this->_navbar === null) {
|
|
|
831 |
$this->_navbar = new navbar($this);
|
|
|
832 |
}
|
|
|
833 |
return $this->_navbar;
|
|
|
834 |
}
|
|
|
835 |
|
|
|
836 |
/**
|
|
|
837 |
* Returns the settings navigation object
|
|
|
838 |
* @return settings_navigation
|
|
|
839 |
*/
|
|
|
840 |
protected function magic_get_settingsnav() {
|
|
|
841 |
if ($this->_settingsnav === null) {
|
|
|
842 |
$this->_settingsnav = new settings_navigation($this);
|
|
|
843 |
$this->_settingsnav->initialise();
|
|
|
844 |
}
|
|
|
845 |
return $this->_settingsnav;
|
|
|
846 |
}
|
|
|
847 |
|
|
|
848 |
/**
|
|
|
849 |
* Returns the flat navigation object
|
|
|
850 |
* @return flat_navigation
|
|
|
851 |
*/
|
|
|
852 |
protected function magic_get_flatnav() {
|
|
|
853 |
if ($this->_flatnav === null) {
|
|
|
854 |
$this->_flatnav = new flat_navigation($this);
|
|
|
855 |
$this->_flatnav->initialise();
|
|
|
856 |
}
|
|
|
857 |
return $this->_flatnav;
|
|
|
858 |
}
|
|
|
859 |
|
|
|
860 |
/**
|
|
|
861 |
* Returns the activity header object
|
|
|
862 |
* @return activity_header
|
|
|
863 |
*/
|
|
|
864 |
protected function magic_get_activityheader(): activity_header {
|
|
|
865 |
global $USER;
|
|
|
866 |
if ($this->_activityheader === null) {
|
|
|
867 |
$class = activity_header::class;
|
|
|
868 |
// Try and load a custom class first.
|
|
|
869 |
if (class_exists("mod_{$this->activityname}\\output\\activity_header")) {
|
|
|
870 |
$class = "mod_{$this->activityname}\\output\\activity_header";
|
|
|
871 |
}
|
|
|
872 |
|
|
|
873 |
$this->_activityheader = new $class($this, $USER);
|
|
|
874 |
}
|
|
|
875 |
return $this->_activityheader;
|
|
|
876 |
}
|
|
|
877 |
|
|
|
878 |
/**
|
|
|
879 |
* Returns the secondary navigation object
|
|
|
880 |
*
|
|
|
881 |
* @todo MDL-74939 Remove support for old 'local\views\secondary' class location
|
|
|
882 |
* @return secondary
|
|
|
883 |
*/
|
|
|
884 |
protected function magic_get_secondarynav() {
|
|
|
885 |
if ($this->_secondarynav === null) {
|
|
|
886 |
$class = 'core\navigation\views\secondary';
|
|
|
887 |
// Try and load a custom class first.
|
|
|
888 |
if (class_exists("mod_{$this->activityname}\\navigation\\views\\secondary")) {
|
|
|
889 |
$class = "mod_{$this->activityname}\\navigation\\views\\secondary";
|
|
|
890 |
} else if (class_exists("mod_{$this->activityname}\\local\\views\\secondary")) {
|
|
|
891 |
// For backwards compatibility, support the old location for this class (it was in a
|
|
|
892 |
// 'local' namespace which shouldn't be used for core APIs).
|
|
|
893 |
debugging("The class mod_{$this->activityname}}\\local\\views\\secondary uses a deprecated " .
|
|
|
894 |
"namespace. Please move it to mod_{$this->activityname}\\navigation\\views\\secondary.",
|
|
|
895 |
DEBUG_DEVELOPER);
|
|
|
896 |
$class = "mod_{$this->activityname}\\local\\views\\secondary";
|
|
|
897 |
}
|
|
|
898 |
|
|
|
899 |
$this->_secondarynav = new $class($this);
|
|
|
900 |
$this->_secondarynav->initialise();
|
|
|
901 |
}
|
|
|
902 |
return $this->_secondarynav;
|
|
|
903 |
}
|
|
|
904 |
|
|
|
905 |
/**
|
|
|
906 |
* Returns the primary navigation object
|
|
|
907 |
* @return primary
|
|
|
908 |
*/
|
|
|
909 |
protected function magic_get_primarynav() {
|
|
|
910 |
if ($this->_primarynav === null) {
|
|
|
911 |
$this->_primarynav = new primary($this);
|
|
|
912 |
$this->_primarynav->initialise();
|
|
|
913 |
}
|
|
|
914 |
return $this->_primarynav;
|
|
|
915 |
}
|
|
|
916 |
|
|
|
917 |
/**
|
|
|
918 |
* Returns the primary navigation object
|
|
|
919 |
* @return primaryoutput
|
|
|
920 |
*/
|
|
|
921 |
protected function magic_get_primarynavcombined() {
|
|
|
922 |
if ($this->_primarynavcombined === null) {
|
|
|
923 |
$this->_primarynavcombined = new primaryoutput($this);
|
|
|
924 |
}
|
|
|
925 |
return $this->_primarynavcombined;
|
|
|
926 |
}
|
|
|
927 |
|
|
|
928 |
/**
|
|
|
929 |
* Returns request IP address.
|
|
|
930 |
*
|
|
|
931 |
* @return string IP address or null if unknown
|
|
|
932 |
*/
|
|
|
933 |
protected function magic_get_requestip() {
|
|
|
934 |
return getremoteaddr(null);
|
|
|
935 |
}
|
|
|
936 |
|
|
|
937 |
/**
|
|
|
938 |
* Returns the origin of current request.
|
|
|
939 |
*
|
|
|
940 |
* Note: constants are not required because we need to use these values in logging and reports.
|
|
|
941 |
*
|
|
|
942 |
* @return string 'web', 'ws', 'cli', 'restore', etc.
|
|
|
943 |
*/
|
|
|
944 |
protected function magic_get_requestorigin() {
|
|
|
945 |
if (class_exists('restore_controller', false) && restore_controller::is_executing()) {
|
|
|
946 |
return 'restore';
|
|
|
947 |
}
|
|
|
948 |
|
|
|
949 |
if (WS_SERVER) {
|
|
|
950 |
return 'ws';
|
|
|
951 |
}
|
|
|
952 |
|
|
|
953 |
if (CLI_SCRIPT) {
|
|
|
954 |
return 'cli';
|
|
|
955 |
}
|
|
|
956 |
|
|
|
957 |
return 'web';
|
|
|
958 |
}
|
|
|
959 |
|
|
|
960 |
/**
|
|
|
961 |
* PHP overloading magic to make the $PAGE->course syntax work by redirecting
|
|
|
962 |
* it to the corresponding $PAGE->magic_get_course() method if there is one, and
|
|
|
963 |
* throwing an exception if not.
|
|
|
964 |
*
|
|
|
965 |
* @param string $name property name
|
|
|
966 |
* @return mixed
|
|
|
967 |
* @throws coding_exception
|
|
|
968 |
*/
|
|
|
969 |
public function __get($name) {
|
|
|
970 |
$getmethod = 'magic_get_' . $name;
|
|
|
971 |
if (method_exists($this, $getmethod)) {
|
|
|
972 |
return $this->$getmethod();
|
|
|
973 |
} else {
|
|
|
974 |
throw new coding_exception('Unknown property ' . $name . ' of $PAGE.');
|
|
|
975 |
}
|
|
|
976 |
}
|
|
|
977 |
|
|
|
978 |
/**
|
|
|
979 |
* PHP overloading magic to catch obvious coding errors.
|
|
|
980 |
*
|
|
|
981 |
* This method has been created to catch obvious coding errors where the
|
|
|
982 |
* developer has tried to set a page property using $PAGE->key = $value.
|
|
|
983 |
* In the moodle_page class all properties must be set using the appropriate
|
|
|
984 |
* $PAGE->set_something($value) method.
|
|
|
985 |
*
|
|
|
986 |
* @param string $name property name
|
|
|
987 |
* @param mixed $value Value
|
|
|
988 |
* @return void Throws exception if field not defined in page class
|
|
|
989 |
* @throws coding_exception
|
|
|
990 |
*/
|
|
|
991 |
public function __set($name, $value) {
|
|
|
992 |
if (method_exists($this, 'set_' . $name)) {
|
|
|
993 |
throw new coding_exception('Invalid attempt to modify page object', "Use \$PAGE->set_$name() instead.");
|
|
|
994 |
} else {
|
|
|
995 |
throw new coding_exception('Invalid attempt to modify page object', "Unknown property $name");
|
|
|
996 |
}
|
|
|
997 |
}
|
|
|
998 |
|
|
|
999 |
// Other information getting methods ==========================================.
|
|
|
1000 |
|
|
|
1001 |
/**
|
|
|
1002 |
* Returns instance of page renderer
|
|
|
1003 |
*
|
|
|
1004 |
* @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
|
|
|
1005 |
* @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
|
|
|
1006 |
* @param string $target one of rendering target constants
|
|
|
1007 |
* @return renderer_base
|
|
|
1008 |
*/
|
|
|
1009 |
public function get_renderer($component, $subtype = null, $target = null) {
|
|
|
1010 |
if ($this->pagelayout === 'maintenance') {
|
|
|
1011 |
// If the page is using the maintenance layout then we're going to force target to maintenance.
|
|
|
1012 |
// This leads to a special core renderer that is designed to block access to API's that are likely unavailable for this
|
|
|
1013 |
// page layout.
|
|
|
1014 |
$target = RENDERER_TARGET_MAINTENANCE;
|
|
|
1015 |
}
|
|
|
1016 |
return $this->magic_get_theme()->get_renderer($this, $component, $subtype, $target);
|
|
|
1017 |
}
|
|
|
1018 |
|
|
|
1019 |
/**
|
|
|
1020 |
* Checks to see if there are any items on the navbar object
|
|
|
1021 |
*
|
|
|
1022 |
* @return bool true if there are, false if not
|
|
|
1023 |
*/
|
|
|
1024 |
public function has_navbar() {
|
|
|
1025 |
if ($this->_navbar === null) {
|
|
|
1026 |
$this->_navbar = new navbar($this);
|
|
|
1027 |
}
|
|
|
1028 |
return $this->_navbar->has_items();
|
|
|
1029 |
}
|
|
|
1030 |
|
|
|
1031 |
/**
|
|
|
1032 |
* Switches from the regular requirements manager to the fragment requirements manager to
|
|
|
1033 |
* capture all necessary JavaScript to display a chunk of HTML such as an mform. This is for use
|
|
|
1034 |
* by the get_fragment() web service and not for use elsewhere.
|
|
|
1035 |
*/
|
|
|
1036 |
public function start_collecting_javascript_requirements() {
|
|
|
1037 |
global $CFG;
|
|
|
1038 |
require_once($CFG->libdir.'/outputfragmentrequirementslib.php');
|
|
|
1039 |
|
|
|
1040 |
// Check that the requirements manager has not already been switched.
|
|
|
1041 |
if (get_class($this->_requires) == 'fragment_requirements_manager') {
|
|
|
1042 |
throw new coding_exception('JavaScript collection has already been started.');
|
|
|
1043 |
}
|
|
|
1044 |
// The header needs to have been called to flush out the generic JavaScript for the page. This allows only
|
|
|
1045 |
// JavaScript for the fragment to be collected. _wherethemewasinitialised is set when header() is called.
|
|
|
1046 |
if (!empty($this->_wherethemewasinitialised)) {
|
|
|
1047 |
// Change the current requirements manager over to the fragment manager to capture JS.
|
|
|
1048 |
$this->savedrequires = $this->_requires;
|
|
|
1049 |
$this->_requires = new fragment_requirements_manager();
|
|
|
1050 |
} else {
|
|
|
1051 |
throw new coding_exception('$OUTPUT->header() needs to be called before collecting JavaScript requirements.');
|
|
|
1052 |
}
|
|
|
1053 |
}
|
|
|
1054 |
|
|
|
1055 |
/**
|
|
|
1056 |
* Switches back from collecting fragment JS requirement to the original requirement manager
|
|
|
1057 |
*/
|
|
|
1058 |
public function end_collecting_javascript_requirements() {
|
|
|
1059 |
if ($this->savedrequires === null) {
|
|
|
1060 |
throw new coding_exception('JavaScript collection has not been started.');
|
|
|
1061 |
}
|
|
|
1062 |
$this->_requires = $this->savedrequires;
|
|
|
1063 |
$this->savedrequires = null;
|
|
|
1064 |
}
|
|
|
1065 |
|
|
|
1066 |
/**
|
|
|
1067 |
* Should the current user see this page in editing mode.
|
|
|
1068 |
* That is, are they allowed to edit this page, and are they currently in
|
|
|
1069 |
* editing mode.
|
|
|
1070 |
* @return bool
|
|
|
1071 |
*/
|
|
|
1072 |
public function user_is_editing() {
|
|
|
1073 |
global $USER;
|
|
|
1074 |
return !empty($USER->editing) && $this->user_allowed_editing();
|
|
|
1075 |
}
|
|
|
1076 |
|
|
|
1077 |
/**
|
|
|
1078 |
* Does the user have permission to edit blocks on this page.
|
|
|
1079 |
* Can be forced to false by calling the force_lock_all_blocks() method.
|
|
|
1080 |
*
|
|
|
1081 |
* @return bool
|
|
|
1082 |
*/
|
|
|
1083 |
public function user_can_edit_blocks() {
|
|
|
1084 |
return $this->_forcelockallblocks ? false : has_capability($this->_blockseditingcap, $this->_context);
|
|
|
1085 |
}
|
|
|
1086 |
|
|
|
1087 |
/**
|
|
|
1088 |
* Does the user have permission to see this page in editing mode.
|
|
|
1089 |
* @return bool
|
|
|
1090 |
*/
|
|
|
1091 |
public function user_allowed_editing() {
|
|
|
1092 |
return has_any_capability($this->all_editing_caps(), $this->_context);
|
|
|
1093 |
}
|
|
|
1094 |
|
|
|
1095 |
/**
|
|
|
1096 |
* Get a description of this page. Normally displayed in the footer in developer debug mode.
|
|
|
1097 |
* @return string
|
|
|
1098 |
*/
|
|
|
1099 |
public function debug_summary() {
|
|
|
1100 |
$summary = '';
|
|
|
1101 |
$summary .= 'General type: ' . $this->pagelayout . '. ';
|
|
|
1102 |
if (!during_initial_install()) {
|
|
|
1103 |
$summary .= 'Context ' . $this->context->get_context_name() . ' (context id ' . $this->_context->id . '). ';
|
|
|
1104 |
}
|
|
|
1105 |
$summary .= 'Page type ' . $this->pagetype . '. ';
|
|
|
1106 |
if ($this->subpage) {
|
|
|
1107 |
$summary .= 'Sub-page ' . $this->subpage . '. ';
|
|
|
1108 |
}
|
|
|
1109 |
return $summary;
|
|
|
1110 |
}
|
|
|
1111 |
|
|
|
1112 |
// Setter methods =============================================================.
|
|
|
1113 |
|
|
|
1114 |
/**
|
|
|
1115 |
* Set the state.
|
|
|
1116 |
*
|
|
|
1117 |
* The state must be one of that STATE_... constants, and the state is only allowed to advance one step at a time.
|
|
|
1118 |
*
|
|
|
1119 |
* @param int $state The new state.
|
|
|
1120 |
* @throws coding_exception
|
|
|
1121 |
*/
|
|
|
1122 |
public function set_state($state) {
|
|
|
1123 |
if ($state != $this->_state + 1 || $state > self::STATE_DONE) {
|
|
|
1124 |
throw new coding_exception('Invalid state passed to moodle_page::set_state. We are in state ' .
|
|
|
1125 |
$this->_state . ' and state ' . $state . ' was requested.');
|
|
|
1126 |
}
|
|
|
1127 |
|
|
|
1128 |
if ($state == self::STATE_PRINTING_HEADER) {
|
|
|
1129 |
$this->starting_output();
|
|
|
1130 |
}
|
|
|
1131 |
|
|
|
1132 |
$this->_state = $state;
|
|
|
1133 |
}
|
|
|
1134 |
|
|
|
1135 |
/**
|
|
|
1136 |
* Set the current course. This sets both $PAGE->course and $COURSE. It also
|
|
|
1137 |
* sets the right theme and locale.
|
|
|
1138 |
*
|
|
|
1139 |
* Normally you don't need to call this function yourself, require_login will
|
|
|
1140 |
* call it for you if you pass a $course to it. You can use this function
|
|
|
1141 |
* on pages that do need to call require_login().
|
|
|
1142 |
*
|
|
|
1143 |
* Sets $PAGE->context to the course context, if it is not already set.
|
|
|
1144 |
*
|
|
|
1145 |
* @param stdClass $course the course to set as the global course.
|
|
|
1146 |
* @throws coding_exception
|
|
|
1147 |
*/
|
|
|
1148 |
public function set_course($course) {
|
|
|
1149 |
global $COURSE, $PAGE, $CFG, $SITE;
|
|
|
1150 |
|
|
|
1151 |
if (empty($course->id)) {
|
|
|
1152 |
throw new coding_exception('$course passed to moodle_page::set_course does not look like a proper course object.');
|
|
|
1153 |
}
|
|
|
1154 |
|
|
|
1155 |
$this->ensure_theme_not_set();
|
|
|
1156 |
|
|
|
1157 |
if (!empty($this->_course->id) && $this->_course->id != $course->id) {
|
|
|
1158 |
$this->_categories = null;
|
|
|
1159 |
}
|
|
|
1160 |
|
|
|
1161 |
$this->_course = clone($course);
|
|
|
1162 |
|
|
|
1163 |
if ($this === $PAGE) {
|
|
|
1164 |
$COURSE = $this->_course;
|
|
|
1165 |
moodle_setlocale();
|
|
|
1166 |
}
|
|
|
1167 |
|
|
|
1168 |
if (!$this->_context) {
|
|
|
1169 |
$this->set_context(context_course::instance($this->_course->id));
|
|
|
1170 |
}
|
|
|
1171 |
|
|
|
1172 |
// Notify course format that this page is set for the course.
|
|
|
1173 |
if ($this->_course->id != $SITE->id) {
|
|
|
1174 |
require_once($CFG->dirroot.'/course/lib.php');
|
|
|
1175 |
$courseformat = course_get_format($this->_course);
|
|
|
1176 |
$this->add_body_class('format-'. $courseformat->get_format());
|
|
|
1177 |
$courseformat->page_set_course($this);
|
|
|
1178 |
} else {
|
|
|
1179 |
$this->add_body_class('format-site');
|
|
|
1180 |
}
|
|
|
1181 |
}
|
|
|
1182 |
|
|
|
1183 |
/**
|
|
|
1184 |
* Set the main context to which this page belongs.
|
|
|
1185 |
*
|
|
|
1186 |
* @param ?context $context a context object. You normally get this with context_xxxx::instance().
|
|
|
1187 |
*/
|
|
|
1188 |
public function set_context($context) {
|
|
|
1189 |
if ($context === null) {
|
|
|
1190 |
// Extremely ugly hack which sets context to some value in order to prevent warnings,
|
|
|
1191 |
// use only for core error handling!!!!
|
|
|
1192 |
if (!$this->_context) {
|
|
|
1193 |
$this->_context = context_system::instance();
|
|
|
1194 |
}
|
|
|
1195 |
return;
|
|
|
1196 |
}
|
|
|
1197 |
// Ideally we should set context only once.
|
|
|
1198 |
if (isset($this->_context) && $context->id !== $this->_context->id) {
|
|
|
1199 |
$current = $this->_context->contextlevel;
|
|
|
1200 |
if ($current == CONTEXT_SYSTEM or $current == CONTEXT_COURSE) {
|
|
|
1201 |
// Hmm - not ideal, but it might produce too many warnings due to the design of require_login.
|
|
|
1202 |
} else if ($current == CONTEXT_MODULE and ($parentcontext = $context->get_parent_context()) and
|
|
|
1203 |
$this->_context->id == $parentcontext->id) {
|
|
|
1204 |
// Hmm - most probably somebody did require_login() and after that set the block context.
|
|
|
1205 |
} else {
|
|
|
1206 |
// We do not want devs to do weird switching of context levels on the fly because we might have used
|
|
|
1207 |
// the context already such as in text filter in page title.
|
|
|
1208 |
debugging("Coding problem: unsupported modification of PAGE->context from {$current} to {$context->contextlevel}");
|
|
|
1209 |
}
|
|
|
1210 |
}
|
|
|
1211 |
|
|
|
1212 |
$this->_context = $context;
|
|
|
1213 |
}
|
|
|
1214 |
|
|
|
1215 |
/**
|
|
|
1216 |
* The course module that this page belongs to (if it does belong to one).
|
|
|
1217 |
*
|
|
|
1218 |
* @param stdClass|cm_info $cm a record from course_modules table or cm_info from get_fast_modinfo().
|
|
|
1219 |
* @param stdClass $course
|
|
|
1220 |
* @param stdClass $module
|
|
|
1221 |
* @return void
|
|
|
1222 |
* @throws coding_exception
|
|
|
1223 |
*/
|
|
|
1224 |
public function set_cm($cm, $course = null, $module = null) {
|
|
|
1225 |
global $DB, $CFG, $SITE;
|
|
|
1226 |
|
|
|
1227 |
if (!isset($cm->id) || !isset($cm->course)) {
|
|
|
1228 |
throw new coding_exception('Invalid $cm. It has to be instance of cm_info or record from the course_modules table.');
|
|
|
1229 |
}
|
|
|
1230 |
|
|
|
1231 |
if (!$this->_course || $this->_course->id != $cm->course) {
|
|
|
1232 |
if (!$course) {
|
|
|
1233 |
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
|
|
|
1234 |
}
|
|
|
1235 |
if ($course->id != $cm->course) {
|
|
|
1236 |
throw new coding_exception('The course you passed to $PAGE->set_cm does not correspond to the $cm.');
|
|
|
1237 |
}
|
|
|
1238 |
$this->set_course($course);
|
|
|
1239 |
}
|
|
|
1240 |
|
|
|
1241 |
// Make sure we have a $cm from get_fast_modinfo as this contains activity access details.
|
|
|
1242 |
if (!($cm instanceof cm_info)) {
|
|
|
1243 |
$modinfo = get_fast_modinfo($this->_course);
|
|
|
1244 |
$cm = $modinfo->get_cm($cm->id);
|
|
|
1245 |
}
|
|
|
1246 |
$this->_cm = $cm;
|
|
|
1247 |
|
|
|
1248 |
// Unfortunately the context setting is a mess.
|
|
|
1249 |
// Let's try to work around some common block problems and show some debug messages.
|
|
|
1250 |
if (empty($this->_context) or $this->_context->contextlevel != CONTEXT_BLOCK) {
|
|
|
1251 |
$context = context_module::instance($cm->id);
|
|
|
1252 |
$this->set_context($context);
|
|
|
1253 |
}
|
|
|
1254 |
|
|
|
1255 |
if ($module) {
|
|
|
1256 |
$this->set_activity_record($module);
|
|
|
1257 |
}
|
|
|
1258 |
|
|
|
1259 |
// Notify course format that this page is set for the course module.
|
|
|
1260 |
if ($this->_course->id != $SITE->id) {
|
|
|
1261 |
require_once($CFG->dirroot.'/course/lib.php');
|
|
|
1262 |
course_get_format($this->_course)->page_set_cm($this);
|
|
|
1263 |
}
|
|
|
1264 |
}
|
|
|
1265 |
|
|
|
1266 |
/**
|
|
|
1267 |
* Sets the activity record. This could be a row from the main table for a
|
|
|
1268 |
* module. For instance if the current module (cm) is a forum this should be a row
|
|
|
1269 |
* from the forum table.
|
|
|
1270 |
*
|
|
|
1271 |
* @param stdClass $module A row from the main database table for the module that this page belongs to.
|
|
|
1272 |
* @throws coding_exception
|
|
|
1273 |
*/
|
|
|
1274 |
public function set_activity_record($module) {
|
|
|
1275 |
if (is_null($this->_cm)) {
|
|
|
1276 |
throw new coding_exception('You cannot call $PAGE->set_activity_record until after $PAGE->cm has been set.');
|
|
|
1277 |
}
|
|
|
1278 |
if ($module->id != $this->_cm->instance || $module->course != $this->_course->id) {
|
|
|
1279 |
throw new coding_exception('The activity record does not seem to correspond to the cm that has been set.');
|
|
|
1280 |
}
|
|
|
1281 |
$this->_module = $module;
|
|
|
1282 |
}
|
|
|
1283 |
|
|
|
1284 |
/**
|
|
|
1285 |
* Sets the pagetype to use for this page.
|
|
|
1286 |
*
|
|
|
1287 |
* Normally you do not need to set this manually, it is automatically created
|
|
|
1288 |
* from the script name. However, on some pages this is overridden.
|
|
|
1289 |
* For example the page type for course/view.php includes the course format,
|
|
|
1290 |
* for example 'course-view-weeks'. This gets used as the id attribute on
|
|
|
1291 |
* <body> and also for determining which blocks are displayed.
|
|
|
1292 |
*
|
|
|
1293 |
* @param string $pagetype e.g. 'my-index' or 'mod-quiz-attempt'.
|
|
|
1294 |
*/
|
|
|
1295 |
public function set_pagetype($pagetype) {
|
|
|
1296 |
$this->_pagetype = $pagetype;
|
|
|
1297 |
}
|
|
|
1298 |
|
|
|
1299 |
/**
|
|
|
1300 |
* Sets the layout to use for this page.
|
|
|
1301 |
*
|
|
|
1302 |
* The page layout determines how the page will be displayed, things such as
|
|
|
1303 |
* block regions, content areas, etc are controlled by the layout.
|
|
|
1304 |
* The theme in use for the page will determine that the layout contains.
|
|
|
1305 |
*
|
|
|
1306 |
* This properly defaults to 'base', so you only need to call this function if
|
|
|
1307 |
* you want something different. The exact range of supported layouts is specified
|
|
|
1308 |
* in the standard theme.
|
|
|
1309 |
*
|
|
|
1310 |
* For an idea of the common page layouts see
|
|
|
1311 |
* {@link https://docs.moodle.org/dev/Themes_overview#Layouts}
|
|
|
1312 |
* But please keep in mind that it may be (and normally is) out of date.
|
|
|
1313 |
* The only place to find an accurate up-to-date list of the page layouts
|
|
|
1314 |
* available for your version of Moodle is {@link theme/base/config.php}
|
|
|
1315 |
*
|
|
|
1316 |
* @param string $pagelayout the page layout this is. For example 'popup', 'home'.
|
|
|
1317 |
*/
|
|
|
1318 |
public function set_pagelayout($pagelayout) {
|
|
|
1319 |
global $SESSION;
|
|
|
1320 |
|
|
|
1321 |
if (!empty($SESSION->forcepagelayout)) {
|
|
|
1322 |
$this->_pagelayout = $SESSION->forcepagelayout;
|
|
|
1323 |
} else {
|
|
|
1324 |
// Uncomment this to debug theme pagelayout issues like missing blocks.
|
|
|
1325 |
// if (!empty($this->_wherethemewasinitialised) && $pagelayout != $this->_pagelayout)
|
|
|
1326 |
// debugging('Page layout has already been set and cannot be changed.', DEBUG_DEVELOPER);
|
|
|
1327 |
$this->_pagelayout = $pagelayout;
|
|
|
1328 |
}
|
|
|
1329 |
}
|
|
|
1330 |
|
|
|
1331 |
/**
|
|
|
1332 |
* If context->id and pagetype are not enough to uniquely identify this page,
|
|
|
1333 |
* then you can set a subpage id as well. For example, the tags page sets
|
|
|
1334 |
*
|
|
|
1335 |
* @param string $subpage an arbitrary identifier that, along with context->id
|
|
|
1336 |
* and pagetype, uniquely identifies this page.
|
|
|
1337 |
*/
|
|
|
1338 |
public function set_subpage($subpage) {
|
|
|
1339 |
if (empty($subpage)) {
|
|
|
1340 |
$this->_subpage = '';
|
|
|
1341 |
} else {
|
|
|
1342 |
$this->_subpage = $subpage;
|
|
|
1343 |
}
|
|
|
1344 |
}
|
|
|
1345 |
|
|
|
1346 |
/**
|
|
|
1347 |
* Force set secondary_nav. Useful in cases where we dealing with non course modules. e.g. blocks, tools.
|
|
|
1348 |
* @param secondary $nav
|
|
|
1349 |
*/
|
|
|
1350 |
public function set_secondarynav(secondary $nav) {
|
|
|
1351 |
$this->_secondarynav = $nav;
|
|
|
1352 |
}
|
|
|
1353 |
|
|
|
1354 |
/**
|
|
|
1355 |
* Adds a CSS class to the body tag of the page.
|
|
|
1356 |
*
|
|
|
1357 |
* @param string $class add this class name ot the class attribute on the body tag.
|
|
|
1358 |
* @throws coding_exception
|
|
|
1359 |
*/
|
|
|
1360 |
public function add_body_class($class) {
|
|
|
1361 |
if ($this->_state > self::STATE_BEFORE_HEADER) {
|
|
|
1362 |
throw new coding_exception('Cannot call moodle_page::add_body_class after output has been started.');
|
|
|
1363 |
}
|
|
|
1364 |
$this->_bodyclasses[$class] = 1;
|
|
|
1365 |
}
|
|
|
1366 |
|
|
|
1367 |
/**
|
|
|
1368 |
* Adds an array of body classes to the body tag of this page.
|
|
|
1369 |
*
|
|
|
1370 |
* @param array $classes this utility method calls add_body_class for each array element.
|
|
|
1371 |
*/
|
|
|
1372 |
public function add_body_classes($classes) {
|
|
|
1373 |
foreach ($classes as $class) {
|
|
|
1374 |
$this->add_body_class($class);
|
|
|
1375 |
}
|
|
|
1376 |
}
|
|
|
1377 |
|
|
|
1378 |
/**
|
|
|
1379 |
* Sets the title for the page.
|
|
|
1380 |
*
|
|
|
1381 |
* This is normally used within the title tag in the head of the page.
|
|
|
1382 |
*
|
|
|
1383 |
* Some tips for providing a meaningful page title:
|
|
|
1384 |
* - The page title must be accurate and informative.
|
|
|
1385 |
* - If the page causes a change of context (e.g. a search functionality), it should describe the result or change of context
|
|
|
1386 |
* to the user.
|
|
|
1387 |
* - It should be concise.
|
|
|
1388 |
* - If possible, it should uniquely identify the page.
|
|
|
1389 |
* - The most identifying information should come first. (e.g. Submit assignment | Assignment | Moodle)
|
|
|
1390 |
*
|
|
|
1391 |
* For more information, see
|
|
|
1392 |
* {@link https://www.w3.org/WAI/WCAG21/Understanding/page-titled Understanding Success Criterion 2.4.2: Page Titled}
|
|
|
1393 |
*
|
|
|
1394 |
* @param string $title the title that should go in the <head> section of the HTML of this page.
|
|
|
1395 |
* @param bool $appendsitename Appends site name at the end of the given title. It is encouraged to append the site name as this
|
|
|
1396 |
* especially helps with accessibility. If it's necessary to override this, please keep in mind
|
|
|
1397 |
* to ensure that the title provides a concise summary of the page being displayed.
|
|
|
1398 |
*/
|
|
|
1399 |
public function set_title($title, bool $appendsitename = true) {
|
|
|
1400 |
global $CFG;
|
|
|
1401 |
|
|
|
1402 |
$title = format_string($title);
|
|
|
1403 |
$title = strip_tags($title);
|
|
|
1404 |
$title = str_replace('"', '"', $title);
|
|
|
1405 |
|
|
|
1406 |
if ($appendsitename) {
|
|
|
1407 |
// Append the site name at the end of the page title.
|
|
|
1408 |
$sitenamedisplay = 'shortname';
|
|
|
1409 |
if (!empty($CFG->sitenameintitle)) {
|
|
|
1410 |
$sitenamedisplay = $CFG->sitenameintitle;
|
|
|
1411 |
}
|
|
|
1412 |
$site = get_site();
|
|
|
1413 |
if (empty(trim($site->{$sitenamedisplay} ?? ''))) {
|
|
|
1414 |
// If for some reason the site name is not yet set, fall back to 'Moodle'.
|
|
|
1415 |
$title .= self::TITLE_SEPARATOR . 'Moodle';
|
|
|
1416 |
} else {
|
|
|
1417 |
$title .= self::TITLE_SEPARATOR . format_string($site->{$sitenamedisplay});
|
|
|
1418 |
}
|
|
|
1419 |
}
|
|
|
1420 |
|
|
|
1421 |
$this->_title = $title;
|
|
|
1422 |
}
|
|
|
1423 |
|
|
|
1424 |
/**
|
|
|
1425 |
* Sets the heading to use for the page.
|
|
|
1426 |
* This is normally used as the main heading at the top of the content.
|
|
|
1427 |
*
|
|
|
1428 |
* @param string $heading the main heading that should be displayed at the top of the <body>.
|
|
|
1429 |
* @param bool $applyformatting apply format_string() - by default true.
|
|
|
1430 |
* @param bool $clean whether the heading should be cleaned or not when no formatting is applied - by default true.
|
|
|
1431 |
*/
|
|
|
1432 |
public function set_heading($heading, bool $applyformatting = true, bool $clean = true) {
|
|
|
1433 |
$this->_heading = $applyformatting ? format_string($heading) : ($clean ? clean_text($heading) : $heading);
|
|
|
1434 |
}
|
|
|
1435 |
|
|
|
1436 |
/**
|
|
|
1437 |
* Sets some HTML to use next to the heading {@link moodle_page::set_heading()}
|
|
|
1438 |
*
|
|
|
1439 |
* @param string $menu The menu/content to show in the heading
|
|
|
1440 |
*/
|
|
|
1441 |
public function set_headingmenu($menu) {
|
|
|
1442 |
$this->_headingmenu = $menu;
|
|
|
1443 |
}
|
|
|
1444 |
|
|
|
1445 |
/**
|
|
|
1446 |
* Set the course category this page belongs to manually.
|
|
|
1447 |
*
|
|
|
1448 |
* This automatically sets $PAGE->course to be the site course. You cannot
|
|
|
1449 |
* use this method if you have already set $PAGE->course - in that case,
|
|
|
1450 |
* the category must be the one that the course belongs to. This also
|
|
|
1451 |
* automatically sets the page context to the category context.
|
|
|
1452 |
*
|
|
|
1453 |
* @param int $categoryid The id of the category to set.
|
|
|
1454 |
* @throws coding_exception
|
|
|
1455 |
*/
|
|
|
1456 |
public function set_category_by_id($categoryid) {
|
|
|
1457 |
global $SITE;
|
|
|
1458 |
if (!is_null($this->_course)) {
|
|
|
1459 |
throw new coding_exception('Course has already been set. You cannot change the category now.');
|
|
|
1460 |
}
|
|
|
1461 |
if (is_array($this->_categories)) {
|
|
|
1462 |
throw new coding_exception('Course category has already been set. You cannot to change it now.');
|
|
|
1463 |
}
|
|
|
1464 |
$this->ensure_theme_not_set();
|
|
|
1465 |
$this->set_course($SITE);
|
|
|
1466 |
$this->load_category($categoryid);
|
|
|
1467 |
$this->set_context(context_coursecat::instance($categoryid));
|
|
|
1468 |
}
|
|
|
1469 |
|
|
|
1470 |
/**
|
|
|
1471 |
* Set a different path to use for the 'Documentation for this page' link.
|
|
|
1472 |
*
|
|
|
1473 |
* By default, it uses the pagetype, which is normally the same as the
|
|
|
1474 |
* script name. So, for example, for mod/quiz/attempt.php, pagetype is
|
|
|
1475 |
* mod-quiz-attempt, and so docspath is mod/quiz/attempt.
|
|
|
1476 |
*
|
|
|
1477 |
* @param string $path the path to use at the end of the moodle docs URL.
|
|
|
1478 |
*/
|
|
|
1479 |
public function set_docs_path($path) {
|
|
|
1480 |
$this->_docspath = $path;
|
|
|
1481 |
}
|
|
|
1482 |
|
|
|
1483 |
/**
|
|
|
1484 |
* You should call this method from every page to set the URL that should be used to return to this page.
|
|
|
1485 |
*
|
|
|
1486 |
* Used, for example, by the blocks editing UI to know where to return the
|
|
|
1487 |
* user after an action.
|
|
|
1488 |
* For example, course/view.php does:
|
|
|
1489 |
* $id = optional_param('id', 0, PARAM_INT);
|
|
|
1490 |
* $PAGE->set_url('/course/view.php', array('id' => $id));
|
|
|
1491 |
*
|
|
|
1492 |
* @param moodle_url|string $url URL relative to $CFG->wwwroot or {@link moodle_url} instance
|
|
|
1493 |
* @param array $params parameters to add to the URL
|
|
|
1494 |
* @throws coding_exception
|
|
|
1495 |
*/
|
|
|
1496 |
public function set_url($url, array $params = null) {
|
|
|
1497 |
global $CFG;
|
|
|
1498 |
|
|
|
1499 |
if (is_string($url) && strpos($url, 'http') !== 0) {
|
|
|
1500 |
if (strpos($url, '/') === 0) {
|
|
|
1501 |
// Add the wwwroot to the relative url.
|
|
|
1502 |
$url = $CFG->wwwroot . $url;
|
|
|
1503 |
} else {
|
|
|
1504 |
throw new coding_exception('Invalid parameter $url, has to be full url or in shortened form starting with /.');
|
|
|
1505 |
}
|
|
|
1506 |
}
|
|
|
1507 |
|
|
|
1508 |
$this->_url = new moodle_url($url, $params);
|
|
|
1509 |
|
|
|
1510 |
$fullurl = $this->_url->out_omit_querystring();
|
|
|
1511 |
if (strpos($fullurl, "$CFG->wwwroot/") !== 0) {
|
|
|
1512 |
debugging('Most probably incorrect set_page() url argument, it does not match the wwwroot!');
|
|
|
1513 |
}
|
|
|
1514 |
$shorturl = str_replace("$CFG->wwwroot/", '', $fullurl);
|
|
|
1515 |
|
|
|
1516 |
if (is_null($this->_pagetype)) {
|
|
|
1517 |
$this->initialise_default_pagetype($shorturl);
|
|
|
1518 |
}
|
|
|
1519 |
}
|
|
|
1520 |
|
|
|
1521 |
/**
|
|
|
1522 |
* Make sure page URL does not contain the given URL parameter.
|
|
|
1523 |
*
|
|
|
1524 |
* This should not be necessary if the script has called set_url properly.
|
|
|
1525 |
* However, in some situations like the block editing actions; when the URL
|
|
|
1526 |
* has been guessed, it will contain dangerous block-related actions.
|
|
|
1527 |
* Therefore, the blocks code calls this function to clean up such parameters
|
|
|
1528 |
* before doing any redirect.
|
|
|
1529 |
*
|
|
|
1530 |
* @param string $param the name of the parameter to make sure is not in the
|
|
|
1531 |
* page URL.
|
|
|
1532 |
*/
|
|
|
1533 |
public function ensure_param_not_in_url($param) {
|
|
|
1534 |
$this->_url->remove_params($param);
|
|
|
1535 |
}
|
|
|
1536 |
|
|
|
1537 |
/**
|
|
|
1538 |
* Sets an alternative version of this page.
|
|
|
1539 |
*
|
|
|
1540 |
* There can be alternate versions of some pages (for example an RSS feed version).
|
|
|
1541 |
* Call this method for each alternative version available.
|
|
|
1542 |
* For each alternative version a link will be included in the <head> tag.
|
|
|
1543 |
*
|
|
|
1544 |
* @param string $title The title to give the alternate version.
|
|
|
1545 |
* @param string|moodle_url $url The URL of the alternate version.
|
|
|
1546 |
* @param string $mimetype The mime-type of the alternate version.
|
|
|
1547 |
* @throws coding_exception
|
|
|
1548 |
*/
|
|
|
1549 |
public function add_alternate_version($title, $url, $mimetype) {
|
|
|
1550 |
if ($this->_state > self::STATE_BEFORE_HEADER) {
|
|
|
1551 |
throw new coding_exception('Cannot call moodle_page::add_alternate_version after output has been started.');
|
|
|
1552 |
}
|
|
|
1553 |
$alt = new stdClass;
|
|
|
1554 |
$alt->title = $title;
|
|
|
1555 |
$alt->url = $url;
|
|
|
1556 |
$this->_alternateversions[$mimetype] = $alt;
|
|
|
1557 |
}
|
|
|
1558 |
|
|
|
1559 |
/**
|
|
|
1560 |
* Specify a form control should be focused when the page has loaded.
|
|
|
1561 |
*
|
|
|
1562 |
* @param string $controlid the id of the HTML element to be focused.
|
|
|
1563 |
*/
|
|
|
1564 |
public function set_focuscontrol($controlid) {
|
|
|
1565 |
$this->_focuscontrol = $controlid;
|
|
|
1566 |
}
|
|
|
1567 |
|
|
|
1568 |
/**
|
|
|
1569 |
* Specify a fragment of HTML that goes where the 'Turn editing on' button normally goes.
|
|
|
1570 |
*
|
|
|
1571 |
* @param string $html the HTML to display there.
|
|
|
1572 |
*/
|
|
|
1573 |
public function set_button($html) {
|
|
|
1574 |
$this->_button = $html;
|
|
|
1575 |
}
|
|
|
1576 |
|
|
|
1577 |
/**
|
|
|
1578 |
* Set the capability that allows users to edit blocks on this page.
|
|
|
1579 |
*
|
|
|
1580 |
* Normally the default of 'moodle/site:manageblocks' is used, but a few
|
|
|
1581 |
* pages like the My Moodle page need to use a different capability
|
|
|
1582 |
* like 'moodle/my:manageblocks'.
|
|
|
1583 |
*
|
|
|
1584 |
* @param string $capability a capability.
|
|
|
1585 |
*/
|
|
|
1586 |
public function set_blocks_editing_capability($capability) {
|
|
|
1587 |
$this->_blockseditingcap = $capability;
|
|
|
1588 |
}
|
|
|
1589 |
|
|
|
1590 |
/**
|
|
|
1591 |
* Some pages let you turn editing on for reasons other than editing blocks.
|
|
|
1592 |
* If that is the case, you can pass other capabilities that let the user
|
|
|
1593 |
* edit this page here.
|
|
|
1594 |
*
|
|
|
1595 |
* @param string|array $capability either a capability, or an array of capabilities.
|
|
|
1596 |
*/
|
|
|
1597 |
public function set_other_editing_capability($capability) {
|
|
|
1598 |
if (is_array($capability)) {
|
|
|
1599 |
$this->_othereditingcaps = array_unique($this->_othereditingcaps + $capability);
|
|
|
1600 |
} else {
|
|
|
1601 |
$this->_othereditingcaps[] = $capability;
|
|
|
1602 |
}
|
|
|
1603 |
}
|
|
|
1604 |
|
|
|
1605 |
/**
|
|
|
1606 |
* Sets whether the browser should cache this page or not.
|
|
|
1607 |
*
|
|
|
1608 |
* @param bool $cacheable can this page be cached by the user's browser.
|
|
|
1609 |
*/
|
|
|
1610 |
public function set_cacheable($cacheable) {
|
|
|
1611 |
$this->_cacheable = $cacheable;
|
|
|
1612 |
}
|
|
|
1613 |
|
|
|
1614 |
/**
|
|
|
1615 |
* Sets the page to periodically refresh
|
|
|
1616 |
*
|
|
|
1617 |
* This function must be called before $OUTPUT->header has been called or
|
|
|
1618 |
* a coding exception will be thrown.
|
|
|
1619 |
*
|
|
|
1620 |
* @param int $delay Sets the delay before refreshing the page, if set to null refresh is cancelled.
|
|
|
1621 |
* @throws coding_exception
|
|
|
1622 |
*/
|
|
|
1623 |
public function set_periodic_refresh_delay($delay = null) {
|
|
|
1624 |
if ($this->_state > self::STATE_BEFORE_HEADER) {
|
|
|
1625 |
throw new coding_exception('You cannot set a periodic refresh delay after the header has been printed');
|
|
|
1626 |
}
|
|
|
1627 |
if ($delay === null) {
|
|
|
1628 |
$this->_periodicrefreshdelay = null;
|
|
|
1629 |
} else if (is_int($delay)) {
|
|
|
1630 |
$this->_periodicrefreshdelay = $delay;
|
|
|
1631 |
}
|
|
|
1632 |
}
|
|
|
1633 |
|
|
|
1634 |
/**
|
|
|
1635 |
* Force this page to use a particular theme.
|
|
|
1636 |
*
|
|
|
1637 |
* Please use this cautiously.
|
|
|
1638 |
* It is only intended to be used by the themes selector admin page.
|
|
|
1639 |
*
|
|
|
1640 |
* @param string $themename the name of the theme to use.
|
|
|
1641 |
*/
|
|
|
1642 |
public function force_theme($themename) {
|
|
|
1643 |
$this->ensure_theme_not_set();
|
|
|
1644 |
$this->_theme = theme_config::load($themename);
|
|
|
1645 |
}
|
|
|
1646 |
|
|
|
1647 |
/**
|
|
|
1648 |
* Reload theme settings.
|
|
|
1649 |
*
|
|
|
1650 |
* This is used when we need to reset settings
|
|
|
1651 |
* because they are now double cached in theme.
|
|
|
1652 |
*/
|
|
|
1653 |
public function reload_theme() {
|
|
|
1654 |
if (!is_null($this->_theme)) {
|
|
|
1655 |
$this->_theme = theme_config::load($this->_theme->name);
|
|
|
1656 |
}
|
|
|
1657 |
}
|
|
|
1658 |
|
|
|
1659 |
/**
|
|
|
1660 |
* Remove access to editing/moving on all blocks on a page.
|
|
|
1661 |
* This overrides any capabilities and is intended only for pages where no user (including admins) should be able to
|
|
|
1662 |
* modify blocks on the page (eg My Courses).
|
|
|
1663 |
*
|
|
|
1664 |
* @return void
|
|
|
1665 |
*/
|
|
|
1666 |
public function force_lock_all_blocks(): void {
|
|
|
1667 |
$this->_forcelockallblocks = true;
|
|
|
1668 |
}
|
|
|
1669 |
|
|
|
1670 |
/**
|
|
|
1671 |
* @deprecated since Moodle 3.4
|
|
|
1672 |
*/
|
|
|
1673 |
public function https_required() {
|
|
|
1674 |
throw new coding_exception('https_required() cannot be used anymore.');
|
|
|
1675 |
}
|
|
|
1676 |
|
|
|
1677 |
/**
|
|
|
1678 |
* @deprecated since Moodle 3.4
|
|
|
1679 |
*/
|
|
|
1680 |
public function verify_https_required() {
|
|
|
1681 |
throw new coding_exception('verify_https_required() cannot be used anymore.');
|
|
|
1682 |
}
|
|
|
1683 |
|
|
|
1684 |
/**
|
|
|
1685 |
* Allows to 'serialize' the edited page information and store it in the session cache
|
|
|
1686 |
*
|
|
|
1687 |
* Due to Moodle architectural decision and non-SPA approach, a lot of page setup is
|
|
|
1688 |
* happening in the actual page php file, for example, setting course/cm/context,
|
|
|
1689 |
* setting layout and pagetype, requiring capabilities, setting specific block editing
|
|
|
1690 |
* capabilities.
|
|
|
1691 |
*
|
|
|
1692 |
* When storing this information in the session cache we can pass the pagehash (cache key)
|
|
|
1693 |
* as an argument to web services in AJAX requests and retrieve all data associated with
|
|
|
1694 |
* the page without actually executing PHP code on that page.
|
|
|
1695 |
*
|
|
|
1696 |
* @return string|null
|
|
|
1697 |
*/
|
|
|
1698 |
public function get_edited_page_hash(): ?string {
|
|
|
1699 |
global $SESSION;
|
|
|
1700 |
if (!$this->user_is_editing()) {
|
|
|
1701 |
return null;
|
|
|
1702 |
}
|
|
|
1703 |
$url = new moodle_url($this->url);
|
|
|
1704 |
$url->set_anchor(null);
|
|
|
1705 |
$data = [
|
|
|
1706 |
'contextid' => $this->context->id,
|
|
|
1707 |
'url' => $url->out_as_local_url(false),
|
|
|
1708 |
];
|
|
|
1709 |
if (($cm = $this->cm) && $cm->id) {
|
|
|
1710 |
$data['cmid'] = $cm->id;
|
|
|
1711 |
} else if (($course = $this->course) && $course->id) {
|
|
|
1712 |
$data['courseid'] = $course->id;
|
|
|
1713 |
}
|
|
|
1714 |
$keys = ['pagelayout', 'pagetype', 'subpage'];
|
|
|
1715 |
foreach ($keys as $key) {
|
|
|
1716 |
if ("{$this->$key}" !== "") {
|
|
|
1717 |
$data[$key] = $this->$key;
|
|
|
1718 |
}
|
|
|
1719 |
}
|
|
|
1720 |
if ($this->_blockseditingcap !== 'moodle/site:manageblocks') {
|
|
|
1721 |
$data['bcap'] = $this->_blockseditingcap;
|
|
|
1722 |
}
|
|
|
1723 |
if (!empty($this->_othereditingcaps)) {
|
|
|
1724 |
$data['caps'] = $this->_othereditingcaps;
|
|
|
1725 |
}
|
|
|
1726 |
if ($this->_forcelockallblocks) {
|
|
|
1727 |
$data['forcelock'] = true;
|
|
|
1728 |
}
|
|
|
1729 |
$hash = md5(json_encode($data + ['sesskey' => sesskey()]));
|
|
|
1730 |
$SESSION->editedpages = ($SESSION->editedpages ?? []);
|
|
|
1731 |
$SESSION->editedpages[$hash] = $data;
|
|
|
1732 |
return $hash;
|
|
|
1733 |
}
|
|
|
1734 |
|
|
|
1735 |
/**
|
|
|
1736 |
* Retrieves a page that is being edited from the session cache
|
|
|
1737 |
*
|
|
|
1738 |
* {@see self::get_edited_page_hash()}
|
|
|
1739 |
*
|
|
|
1740 |
* @param string $hash
|
|
|
1741 |
* @param int $strictness
|
|
|
1742 |
* @return self|null
|
|
|
1743 |
*/
|
|
|
1744 |
public static function retrieve_edited_page(string $hash, $strictness = IGNORE_MISSING): ?self {
|
|
|
1745 |
global $CFG, $SESSION;
|
|
|
1746 |
$data = $SESSION->editedpages[$hash] ?? null;
|
|
|
1747 |
if (!$data || !is_array($data)
|
|
|
1748 |
|| $hash !== md5(json_encode($data + ['sesskey' => sesskey()]))) {
|
|
|
1749 |
// This can happen if the session cache becomes corrupt or the user logged out and back
|
|
|
1750 |
// in in another window and changed their session. Refreshing the page will generate
|
|
|
1751 |
// and store the correct page hash.
|
|
|
1752 |
if ($strictness === MUST_EXIST) {
|
|
|
1753 |
throw new moodle_exception('editedpagenotfound');
|
|
|
1754 |
}
|
|
|
1755 |
return null;
|
|
|
1756 |
}
|
|
|
1757 |
|
|
|
1758 |
if (!empty($CFG->moodlepageclass)) {
|
|
|
1759 |
if (!empty($CFG->moodlepageclassfile)) {
|
|
|
1760 |
require_once($CFG->moodlepageclassfile);
|
|
|
1761 |
}
|
|
|
1762 |
$classname = $CFG->moodlepageclass;
|
|
|
1763 |
} else {
|
|
|
1764 |
$classname = self::class;
|
|
|
1765 |
}
|
|
|
1766 |
/** @var moodle_page $page */
|
|
|
1767 |
$page = new $classname();
|
|
|
1768 |
$page->set_context(context::instance_by_id($data['contextid']));
|
|
|
1769 |
if (array_key_exists('cmid', $data)) {
|
|
|
1770 |
[$course, $cm] = get_course_and_cm_from_cmid($data['cmid']);
|
|
|
1771 |
$page->set_cm($cm, $course);
|
|
|
1772 |
} else if (array_key_exists('courseid', $data)) {
|
|
|
1773 |
$page->set_course(get_course($data['courseid']));
|
|
|
1774 |
}
|
|
|
1775 |
$page->set_url(new moodle_url($data['url']));
|
|
|
1776 |
$keys = ['pagelayout', 'pagetype', 'subpage'];
|
|
|
1777 |
foreach ($keys as $key) {
|
|
|
1778 |
if (array_key_exists($key, $data)) {
|
|
|
1779 |
$func = "set_{$key}";
|
|
|
1780 |
$page->$func($data[$key]);
|
|
|
1781 |
}
|
|
|
1782 |
}
|
|
|
1783 |
if (array_key_exists('bcap', $data)) {
|
|
|
1784 |
$page->set_blocks_editing_capability($data['bcap']);
|
|
|
1785 |
}
|
|
|
1786 |
if (array_key_exists('caps', $data)) {
|
|
|
1787 |
foreach ($data['caps'] as $cap) {
|
|
|
1788 |
$page->set_other_editing_capability($cap);
|
|
|
1789 |
}
|
|
|
1790 |
}
|
|
|
1791 |
if (array_key_exists('forcelock', $data)) {
|
|
|
1792 |
$page->force_lock_all_blocks();
|
|
|
1793 |
}
|
|
|
1794 |
$page->blocks->add_custom_regions_for_pagetype($page->pagetype);
|
|
|
1795 |
return $page;
|
|
|
1796 |
}
|
|
|
1797 |
|
|
|
1798 |
// Initialisation methods =====================================================
|
|
|
1799 |
// These set various things up in a default way.
|
|
|
1800 |
|
|
|
1801 |
/**
|
|
|
1802 |
* This method is called when the page first moves out of the STATE_BEFORE_HEADER
|
|
|
1803 |
* state. This is our last change to initialise things.
|
|
|
1804 |
*/
|
|
|
1805 |
protected function starting_output() {
|
|
|
1806 |
global $CFG;
|
|
|
1807 |
|
|
|
1808 |
if (!during_initial_install()) {
|
|
|
1809 |
$this->blocks->load_blocks();
|
|
|
1810 |
if (empty($this->_block_actions_done)) {
|
|
|
1811 |
$this->_block_actions_done = true;
|
|
|
1812 |
if ($this->blocks->process_url_actions($this)) {
|
|
|
1813 |
redirect($this->url->out(false));
|
|
|
1814 |
}
|
|
|
1815 |
}
|
|
|
1816 |
$this->blocks->create_all_block_instances();
|
|
|
1817 |
}
|
|
|
1818 |
|
|
|
1819 |
// If maintenance mode is on, change the page header.
|
|
|
1820 |
if (!empty($CFG->maintenance_enabled)) {
|
|
|
1821 |
$this->set_button('<a href="' . $CFG->wwwroot . '/' . $CFG->admin .
|
|
|
1822 |
'/settings.php?section=maintenancemode">' . get_string('maintenancemode', 'admin') .
|
|
|
1823 |
'</a> ' . $this->button);
|
|
|
1824 |
|
|
|
1825 |
$this->set_title(get_string('maintenancemode', 'admin'));
|
|
|
1826 |
}
|
|
|
1827 |
|
|
|
1828 |
$this->initialise_standard_body_classes();
|
|
|
1829 |
}
|
|
|
1830 |
|
|
|
1831 |
/**
|
|
|
1832 |
* Method for use by Moodle core to set up the theme. Do not
|
|
|
1833 |
* use this in your own code.
|
|
|
1834 |
*
|
|
|
1835 |
* Make sure the right theme for this page is loaded. Tell our
|
|
|
1836 |
* blocks_manager about the theme block regions, and then, if
|
|
|
1837 |
* we are $PAGE, set up the global $OUTPUT.
|
|
|
1838 |
*
|
|
|
1839 |
* @return void
|
|
|
1840 |
*/
|
|
|
1841 |
public function initialise_theme_and_output() {
|
|
|
1842 |
global $OUTPUT, $PAGE, $SITE, $CFG;
|
|
|
1843 |
|
|
|
1844 |
if (!empty($this->_wherethemewasinitialised)) {
|
|
|
1845 |
return;
|
|
|
1846 |
}
|
|
|
1847 |
|
|
|
1848 |
if (!during_initial_install()) {
|
|
|
1849 |
// Detect PAGE->context mess.
|
|
|
1850 |
$this->magic_get_context();
|
|
|
1851 |
}
|
|
|
1852 |
|
|
|
1853 |
if (!$this->_course && !during_initial_install()) {
|
|
|
1854 |
$this->set_course($SITE);
|
|
|
1855 |
}
|
|
|
1856 |
|
|
|
1857 |
if (is_null($this->_theme)) {
|
|
|
1858 |
$themename = $this->resolve_theme();
|
|
|
1859 |
$this->_theme = theme_config::load($themename);
|
|
|
1860 |
}
|
|
|
1861 |
|
|
|
1862 |
$this->_theme->setup_blocks($this->pagelayout, $this->blocks);
|
|
|
1863 |
|
|
|
1864 |
if ($this === $PAGE) {
|
|
|
1865 |
$target = null;
|
|
|
1866 |
if ($this->pagelayout === 'maintenance') {
|
|
|
1867 |
// If the page is using the maintenance layout then we're going to force target to maintenance.
|
|
|
1868 |
// This leads to a special core renderer that is designed to block access to API's that are likely unavailable for this
|
|
|
1869 |
// page layout.
|
|
|
1870 |
$target = RENDERER_TARGET_MAINTENANCE;
|
|
|
1871 |
}
|
|
|
1872 |
$OUTPUT = $this->get_renderer('core', null, $target);
|
|
|
1873 |
}
|
|
|
1874 |
|
|
|
1875 |
if (!during_initial_install()) {
|
|
|
1876 |
$filtermanager = filter_manager::instance();
|
|
|
1877 |
$filtermanager->setup_page_for_globally_available_filters($this);
|
|
|
1878 |
}
|
|
|
1879 |
|
|
|
1880 |
$this->_wherethemewasinitialised = debug_backtrace();
|
|
|
1881 |
}
|
|
|
1882 |
|
|
|
1883 |
/**
|
|
|
1884 |
* For diagnostic/debugging purposes, find where the theme setup was triggered.
|
|
|
1885 |
*
|
|
|
1886 |
* @return null|array null if theme not yet setup. Stacktrace if it was.
|
|
|
1887 |
*/
|
|
|
1888 |
public function get_where_theme_was_initialised() {
|
|
|
1889 |
return $this->_wherethemewasinitialised;
|
|
|
1890 |
}
|
|
|
1891 |
|
|
|
1892 |
/**
|
|
|
1893 |
* Reset the theme and output for a new context. This only makes sense from
|
|
|
1894 |
* external::validate_context(). Do not cheat.
|
|
|
1895 |
*/
|
|
|
1896 |
public function reset_theme_and_output() {
|
|
|
1897 |
global $COURSE, $SITE;
|
|
|
1898 |
|
|
|
1899 |
$COURSE = clone($SITE);
|
|
|
1900 |
$this->_theme = null;
|
|
|
1901 |
$this->_wherethemewasinitialised = null;
|
|
|
1902 |
$this->_course = null;
|
|
|
1903 |
$this->_cm = null;
|
|
|
1904 |
$this->_module = null;
|
|
|
1905 |
$this->_context = null;
|
|
|
1906 |
}
|
|
|
1907 |
|
|
|
1908 |
/**
|
|
|
1909 |
* Work out the theme this page should use.
|
|
|
1910 |
*
|
|
|
1911 |
* This depends on numerous $CFG settings, and the properties of this page.
|
|
|
1912 |
*
|
|
|
1913 |
* @return string the name of the theme that should be used on this page.
|
|
|
1914 |
*/
|
|
|
1915 |
protected function resolve_theme() {
|
|
|
1916 |
global $CFG, $USER, $SESSION;
|
|
|
1917 |
|
|
|
1918 |
if (empty($CFG->themeorder)) {
|
|
|
1919 |
$themeorder = array('course', 'category', 'session', 'user', 'cohort', 'site');
|
|
|
1920 |
} else {
|
|
|
1921 |
$themeorder = $CFG->themeorder;
|
|
|
1922 |
// Just in case, make sure we always use the site theme if nothing else matched.
|
|
|
1923 |
$themeorder[] = 'site';
|
|
|
1924 |
}
|
|
|
1925 |
|
|
|
1926 |
$mnetpeertheme = '';
|
|
|
1927 |
$mnetvarsok = isset($CFG->mnet_localhost_id) && isset($USER->mnethostid);
|
|
|
1928 |
if (isloggedin() and $mnetvarsok and $USER->mnethostid != $CFG->mnet_localhost_id) {
|
|
|
1929 |
require_once($CFG->dirroot.'/mnet/peer.php');
|
|
|
1930 |
$mnetpeer = new mnet_peer();
|
|
|
1931 |
$mnetpeer->set_id($USER->mnethostid);
|
|
|
1932 |
if ($mnetpeer->force_theme == 1 && $mnetpeer->theme != '') {
|
|
|
1933 |
$mnetpeertheme = $mnetpeer->theme;
|
|
|
1934 |
}
|
|
|
1935 |
}
|
|
|
1936 |
|
|
|
1937 |
foreach ($themeorder as $themetype) {
|
|
|
1938 |
|
|
|
1939 |
switch ($themetype) {
|
|
|
1940 |
case 'course':
|
|
|
1941 |
if (!empty($CFG->allowcoursethemes) && !empty($this->_course->theme)) {
|
|
|
1942 |
return $this->_course->theme;
|
|
|
1943 |
}
|
|
|
1944 |
break;
|
|
|
1945 |
|
|
|
1946 |
case 'category':
|
|
|
1947 |
if (!empty($CFG->allowcategorythemes) && !empty($this->_course)) {
|
|
|
1948 |
$categories = $this->categories;
|
|
|
1949 |
foreach ($categories as $category) {
|
|
|
1950 |
if (!empty($category->theme)) {
|
|
|
1951 |
return $category->theme;
|
|
|
1952 |
}
|
|
|
1953 |
}
|
|
|
1954 |
}
|
|
|
1955 |
break;
|
|
|
1956 |
|
|
|
1957 |
case 'session':
|
|
|
1958 |
if (!empty($SESSION->theme)) {
|
|
|
1959 |
return $SESSION->theme;
|
|
|
1960 |
}
|
|
|
1961 |
break;
|
|
|
1962 |
|
|
|
1963 |
case 'user':
|
|
|
1964 |
if (!empty($CFG->allowuserthemes) && !empty($USER->theme)) {
|
|
|
1965 |
if ($mnetpeertheme) {
|
|
|
1966 |
return $mnetpeertheme;
|
|
|
1967 |
} else {
|
|
|
1968 |
return $USER->theme;
|
|
|
1969 |
}
|
|
|
1970 |
}
|
|
|
1971 |
break;
|
|
|
1972 |
|
|
|
1973 |
case 'cohort':
|
|
|
1974 |
if (!empty($CFG->allowcohortthemes) && !empty($USER->cohorttheme)) {
|
|
|
1975 |
return $USER->cohorttheme;
|
|
|
1976 |
}
|
|
|
1977 |
break;
|
|
|
1978 |
|
|
|
1979 |
case 'site':
|
|
|
1980 |
if ($mnetpeertheme) {
|
|
|
1981 |
return $mnetpeertheme;
|
|
|
1982 |
}
|
|
|
1983 |
|
|
|
1984 |
// Use theme if it is set in config.
|
|
|
1985 |
if (!empty($CFG->theme)) {
|
|
|
1986 |
return $CFG->theme;
|
|
|
1987 |
}
|
|
|
1988 |
// Use the overall default theme.
|
|
|
1989 |
return theme_config::DEFAULT_THEME;
|
|
|
1990 |
}
|
|
|
1991 |
}
|
|
|
1992 |
|
|
|
1993 |
// We should most certainly have resolved a theme by now. Something has gone wrong.
|
|
|
1994 |
debugging('Error resolving the theme to use for this page.', DEBUG_DEVELOPER);
|
|
|
1995 |
return theme_config::DEFAULT_THEME;
|
|
|
1996 |
}
|
|
|
1997 |
|
|
|
1998 |
|
|
|
1999 |
/**
|
|
|
2000 |
* Sets ->pagetype from the script name. For example, if the script that was
|
|
|
2001 |
* run is mod/quiz/view.php, ->pagetype will be set to 'mod-quiz-view'.
|
|
|
2002 |
*
|
|
|
2003 |
* @param string $script the path to the script that should be used to
|
|
|
2004 |
* initialise ->pagetype. If not passed the $SCRIPT global will be used.
|
|
|
2005 |
* If legacy code has set $CFG->pagepath that will be used instead, and a
|
|
|
2006 |
* developer warning issued.
|
|
|
2007 |
*/
|
|
|
2008 |
protected function initialise_default_pagetype($script = null) {
|
|
|
2009 |
global $CFG, $SCRIPT;
|
|
|
2010 |
|
|
|
2011 |
if (isset($CFG->pagepath)) {
|
|
|
2012 |
debugging('Some code appears to have set $CFG->pagepath. That was a horrible deprecated thing. ' .
|
|
|
2013 |
'Don\'t do it! Try calling $PAGE->set_pagetype() instead.');
|
|
|
2014 |
$script = $CFG->pagepath;
|
|
|
2015 |
unset($CFG->pagepath);
|
|
|
2016 |
}
|
|
|
2017 |
|
|
|
2018 |
if (is_null($script)) {
|
|
|
2019 |
$script = ltrim($SCRIPT ?? '', '/');
|
|
|
2020 |
$len = strlen($CFG->admin);
|
|
|
2021 |
if (substr($script, 0, $len) == $CFG->admin) {
|
|
|
2022 |
$script = 'admin' . substr($script, $len);
|
|
|
2023 |
}
|
|
|
2024 |
}
|
|
|
2025 |
|
|
|
2026 |
$path = str_replace('.php', '', $script);
|
|
|
2027 |
if (substr($path, -1) == '/') {
|
|
|
2028 |
$path .= 'index';
|
|
|
2029 |
}
|
|
|
2030 |
|
|
|
2031 |
if (empty($path) || $path == 'index') {
|
|
|
2032 |
$this->_pagetype = 'site-index';
|
|
|
2033 |
} else {
|
|
|
2034 |
$this->_pagetype = str_replace('/', '-', $path);
|
|
|
2035 |
}
|
|
|
2036 |
}
|
|
|
2037 |
|
|
|
2038 |
/**
|
|
|
2039 |
* Initialises the CSS classes that will be added to body tag of the page.
|
|
|
2040 |
*
|
|
|
2041 |
* The function is responsible for adding all of the critical CSS classes
|
|
|
2042 |
* that describe the current page, and its state.
|
|
|
2043 |
* This includes classes that describe the following for example:
|
|
|
2044 |
* - Current language
|
|
|
2045 |
* - Language direction
|
|
|
2046 |
* - YUI CSS initialisation
|
|
|
2047 |
* - Pagelayout
|
|
|
2048 |
* These are commonly used in CSS to target specific types of pages.
|
|
|
2049 |
*/
|
|
|
2050 |
protected function initialise_standard_body_classes() {
|
|
|
2051 |
global $CFG, $USER;
|
|
|
2052 |
|
|
|
2053 |
$pagetype = $this->pagetype;
|
|
|
2054 |
if ($pagetype == 'site-index') {
|
|
|
2055 |
$this->_legacyclass = 'course';
|
|
|
2056 |
} else if (substr($pagetype, 0, 6) == 'admin-') {
|
|
|
2057 |
$this->_legacyclass = 'admin';
|
|
|
2058 |
}
|
|
|
2059 |
$this->add_body_class($this->_legacyclass);
|
|
|
2060 |
|
|
|
2061 |
$pathbits = explode('-', trim($pagetype));
|
|
|
2062 |
for ($i = 1; $i < count($pathbits); $i++) {
|
|
|
2063 |
$this->add_body_class('path-' . join('-', array_slice($pathbits, 0, $i)));
|
|
|
2064 |
}
|
|
|
2065 |
|
|
|
2066 |
$this->add_body_classes(core_useragent::get_browser_version_classes());
|
|
|
2067 |
$this->add_body_class('dir-' . get_string('thisdirection', 'langconfig'));
|
|
|
2068 |
$this->add_body_class('lang-' . current_language());
|
|
|
2069 |
$this->add_body_class('yui-skin-sam'); // Make YUI happy, if it is used.
|
|
|
2070 |
$this->add_body_class('yui3-skin-sam'); // Make YUI3 happy, if it is used.
|
|
|
2071 |
$this->add_body_class($this->url_to_class_name($CFG->wwwroot));
|
|
|
2072 |
|
|
|
2073 |
// Extra class describing current page layout.
|
|
|
2074 |
$this->add_body_class('pagelayout-' . $this->_pagelayout);
|
|
|
2075 |
|
|
|
2076 |
if (!during_initial_install()) {
|
|
|
2077 |
$this->add_body_class('course-' . $this->_course->id);
|
|
|
2078 |
$this->add_body_class('context-' . $this->_context->id);
|
|
|
2079 |
}
|
|
|
2080 |
|
|
|
2081 |
if (!empty($this->_cm)) {
|
|
|
2082 |
$this->add_body_class('cmid-' . $this->_cm->id);
|
|
|
2083 |
$this->add_body_class('cm-type-' . $this->_cm->modname);
|
|
|
2084 |
}
|
|
|
2085 |
|
|
|
2086 |
if (!empty($CFG->allowcategorythemes) && !empty($this->_course)) {
|
|
|
2087 |
$this->ensure_category_loaded();
|
|
|
2088 |
foreach ($this->_categories as $catid => $notused) {
|
|
|
2089 |
$this->add_body_class('category-' . $catid);
|
|
|
2090 |
}
|
|
|
2091 |
} else {
|
|
|
2092 |
$catid = 0;
|
|
|
2093 |
if (is_array($this->_categories)) {
|
|
|
2094 |
$catids = array_keys($this->_categories);
|
|
|
2095 |
$catid = reset($catids);
|
|
|
2096 |
} else if (!empty($this->_course->category)) {
|
|
|
2097 |
$catid = $this->_course->category;
|
|
|
2098 |
}
|
|
|
2099 |
if ($catid) {
|
|
|
2100 |
$this->add_body_class('category-' . $catid);
|
|
|
2101 |
}
|
|
|
2102 |
}
|
|
|
2103 |
|
|
|
2104 |
if (!isloggedin()) {
|
|
|
2105 |
$this->add_body_class('notloggedin');
|
|
|
2106 |
}
|
|
|
2107 |
|
|
|
2108 |
if ($this->user_is_editing()) {
|
|
|
2109 |
$this->add_body_class('editing');
|
|
|
2110 |
if (optional_param('bui_moveid', false, PARAM_INT)) {
|
|
|
2111 |
$this->add_body_class('blocks-moving');
|
|
|
2112 |
}
|
|
|
2113 |
}
|
|
|
2114 |
|
|
|
2115 |
if (!empty($CFG->blocksdrag)) {
|
|
|
2116 |
$this->add_body_class('drag');
|
|
|
2117 |
}
|
|
|
2118 |
|
|
|
2119 |
if ($this->_devicetypeinuse != 'default') {
|
|
|
2120 |
$this->add_body_class($this->_devicetypeinuse . 'theme');
|
|
|
2121 |
}
|
|
|
2122 |
|
|
|
2123 |
if (!empty($CFG->themedesignermode)) {
|
|
|
2124 |
$this->add_body_class('themedesignermode');
|
|
|
2125 |
}
|
|
|
2126 |
|
|
|
2127 |
// Add class for behat site to apply behat related fixes.
|
|
|
2128 |
if (defined('BEHAT_SITE_RUNNING')) {
|
|
|
2129 |
$this->add_body_class('behat-site');
|
|
|
2130 |
}
|
|
|
2131 |
}
|
|
|
2132 |
|
|
|
2133 |
/**
|
|
|
2134 |
* Loads the activity record for the current CM object associated with this
|
|
|
2135 |
* page.
|
|
|
2136 |
*
|
|
|
2137 |
* This will load {@link moodle_page::$_module} with a row from the related
|
|
|
2138 |
* module table in the database.
|
|
|
2139 |
* For instance if {@link moodle_page::$_cm} is a forum then a row from the
|
|
|
2140 |
* forum table will be loaded.
|
|
|
2141 |
*/
|
|
|
2142 |
protected function load_activity_record() {
|
|
|
2143 |
global $DB;
|
|
|
2144 |
if (is_null($this->_cm)) {
|
|
|
2145 |
return;
|
|
|
2146 |
}
|
|
|
2147 |
$this->_module = $DB->get_record($this->_cm->modname, array('id' => $this->_cm->instance));
|
|
|
2148 |
}
|
|
|
2149 |
|
|
|
2150 |
/**
|
|
|
2151 |
* This function ensures that the category of the current course has been
|
|
|
2152 |
* loaded, and if not, the function loads it now.
|
|
|
2153 |
*
|
|
|
2154 |
* @return void
|
|
|
2155 |
* @throws coding_exception
|
|
|
2156 |
*/
|
|
|
2157 |
protected function ensure_category_loaded() {
|
|
|
2158 |
if (is_array($this->_categories)) {
|
|
|
2159 |
return; // Already done.
|
|
|
2160 |
}
|
|
|
2161 |
if (is_null($this->_course)) {
|
|
|
2162 |
throw new coding_exception('Attempt to get the course category for this page before the course was set.');
|
|
|
2163 |
}
|
|
|
2164 |
if ($this->_course->category == 0) {
|
|
|
2165 |
$this->_categories = array();
|
|
|
2166 |
} else {
|
|
|
2167 |
$this->load_category($this->_course->category);
|
|
|
2168 |
}
|
|
|
2169 |
}
|
|
|
2170 |
|
|
|
2171 |
/**
|
|
|
2172 |
* Loads the requested category into the pages categories array.
|
|
|
2173 |
*
|
|
|
2174 |
* @param int $categoryid
|
|
|
2175 |
* @throws moodle_exception
|
|
|
2176 |
*/
|
|
|
2177 |
protected function load_category($categoryid) {
|
|
|
2178 |
global $DB;
|
|
|
2179 |
$category = $DB->get_record('course_categories', array('id' => $categoryid));
|
|
|
2180 |
if (!$category) {
|
|
|
2181 |
throw new moodle_exception('unknowncategory');
|
|
|
2182 |
}
|
|
|
2183 |
$this->_categories[$category->id] = $category;
|
|
|
2184 |
$parentcategoryids = explode('/', trim($category->path, '/'));
|
|
|
2185 |
array_pop($parentcategoryids);
|
|
|
2186 |
foreach (array_reverse($parentcategoryids) as $catid) {
|
|
|
2187 |
$this->_categories[$catid] = null;
|
|
|
2188 |
}
|
|
|
2189 |
}
|
|
|
2190 |
|
|
|
2191 |
/**
|
|
|
2192 |
* Ensures that the category the current course is within, as well as all of
|
|
|
2193 |
* its parent categories, have been loaded.
|
|
|
2194 |
*
|
|
|
2195 |
* @return void
|
|
|
2196 |
*/
|
|
|
2197 |
protected function ensure_categories_loaded() {
|
|
|
2198 |
global $DB;
|
|
|
2199 |
$this->ensure_category_loaded();
|
|
|
2200 |
if (!is_null(end($this->_categories))) {
|
|
|
2201 |
return; // Already done.
|
|
|
2202 |
}
|
|
|
2203 |
$idstoload = array_keys($this->_categories);
|
|
|
2204 |
array_shift($idstoload);
|
|
|
2205 |
$categories = $DB->get_records_list('course_categories', 'id', $idstoload);
|
|
|
2206 |
foreach ($idstoload as $catid) {
|
|
|
2207 |
$this->_categories[$catid] = $categories[$catid];
|
|
|
2208 |
}
|
|
|
2209 |
}
|
|
|
2210 |
|
|
|
2211 |
/**
|
|
|
2212 |
* Ensure the theme has not been loaded yet. If it has an exception is thrown.
|
|
|
2213 |
*
|
|
|
2214 |
* @throws coding_exception
|
|
|
2215 |
*/
|
|
|
2216 |
protected function ensure_theme_not_set() {
|
|
|
2217 |
// This is explicitly allowed for webservices though which may process many course contexts in a single request.
|
|
|
2218 |
if (WS_SERVER) {
|
|
|
2219 |
return;
|
|
|
2220 |
}
|
|
|
2221 |
|
|
|
2222 |
if (!is_null($this->_theme)) {
|
|
|
2223 |
throw new coding_exception('The theme has already been set up for this page ready for output. ' .
|
|
|
2224 |
'Therefore, you can no longer change the theme, or anything that might affect what ' .
|
|
|
2225 |
'the current theme is, for example, the course.',
|
|
|
2226 |
'Stack trace when the theme was set up: ' . format_backtrace($this->_wherethemewasinitialised));
|
|
|
2227 |
}
|
|
|
2228 |
}
|
|
|
2229 |
|
|
|
2230 |
/**
|
|
|
2231 |
* Converts the provided URL into a CSS class that be used within the page.
|
|
|
2232 |
* This is primarily used to add the wwwroot to the body tag as a CSS class.
|
|
|
2233 |
*
|
|
|
2234 |
* @param string $url
|
|
|
2235 |
* @return string
|
|
|
2236 |
*/
|
|
|
2237 |
protected function url_to_class_name($url) {
|
|
|
2238 |
$bits = parse_url($url);
|
|
|
2239 |
$class = str_replace('.', '-', $bits['host']);
|
|
|
2240 |
if (!empty($bits['port'])) {
|
|
|
2241 |
$class .= '--' . $bits['port'];
|
|
|
2242 |
}
|
|
|
2243 |
if (!empty($bits['path'])) {
|
|
|
2244 |
$path = trim($bits['path'], '/');
|
|
|
2245 |
if ($path) {
|
|
|
2246 |
$class .= '--' . str_replace('/', '-', $path);
|
|
|
2247 |
}
|
|
|
2248 |
}
|
|
|
2249 |
return $class;
|
|
|
2250 |
}
|
|
|
2251 |
|
|
|
2252 |
/**
|
|
|
2253 |
* Combines all of the required editing caps for the page and returns them
|
|
|
2254 |
* as an array.
|
|
|
2255 |
*
|
|
|
2256 |
* @return array
|
|
|
2257 |
*/
|
|
|
2258 |
protected function all_editing_caps() {
|
|
|
2259 |
$caps = $this->_othereditingcaps;
|
|
|
2260 |
$caps[] = $this->_blockseditingcap;
|
|
|
2261 |
return $caps;
|
|
|
2262 |
}
|
|
|
2263 |
|
|
|
2264 |
/**
|
|
|
2265 |
* Returns true if the page URL has beem set.
|
|
|
2266 |
*
|
|
|
2267 |
* @return bool
|
|
|
2268 |
*/
|
|
|
2269 |
public function has_set_url() {
|
|
|
2270 |
return ($this->_url!==null);
|
|
|
2271 |
}
|
|
|
2272 |
|
|
|
2273 |
/**
|
|
|
2274 |
* Gets set when the block actions for the page have been processed.
|
|
|
2275 |
*
|
|
|
2276 |
* @param bool $setting
|
|
|
2277 |
*/
|
|
|
2278 |
public function set_block_actions_done($setting = true) {
|
|
|
2279 |
$this->_block_actions_done = $setting;
|
|
|
2280 |
}
|
|
|
2281 |
|
|
|
2282 |
/**
|
|
|
2283 |
* Are popup notifications allowed on this page?
|
|
|
2284 |
* Popup notifications may be disallowed in situations such as while upgrading or completing a quiz
|
|
|
2285 |
*
|
|
|
2286 |
* @return bool true if popup notifications may be displayed
|
|
|
2287 |
*/
|
|
|
2288 |
public function get_popup_notification_allowed() {
|
|
|
2289 |
return $this->_popup_notification_allowed;
|
|
|
2290 |
}
|
|
|
2291 |
|
|
|
2292 |
/**
|
|
|
2293 |
* Allow or disallow popup notifications on this page. Popups are allowed by default.
|
|
|
2294 |
*
|
|
|
2295 |
* @param bool $allowed true if notifications are allowed. False if not allowed. They are allowed by default.
|
|
|
2296 |
*/
|
|
|
2297 |
public function set_popup_notification_allowed($allowed) {
|
|
|
2298 |
$this->_popup_notification_allowed = $allowed;
|
|
|
2299 |
}
|
|
|
2300 |
|
|
|
2301 |
/**
|
|
|
2302 |
* Returns the block region having made any required theme manipulations.
|
|
|
2303 |
*
|
|
|
2304 |
* @since Moodle 2.5.1 2.6
|
|
|
2305 |
* @param string $region
|
|
|
2306 |
* @return string
|
|
|
2307 |
*/
|
|
|
2308 |
public function apply_theme_region_manipulations($region) {
|
|
|
2309 |
if ($this->blockmanipulations && isset($this->blockmanipulations[$region])) {
|
|
|
2310 |
$regionwas = $region;
|
|
|
2311 |
$regionnow = $this->blockmanipulations[$region];
|
|
|
2312 |
if ($this->blocks->is_known_region($regionwas) && $this->blocks->is_known_region($regionnow)) {
|
|
|
2313 |
// Both the before and after regions are known so we can swap them over.
|
|
|
2314 |
return $regionnow;
|
|
|
2315 |
}
|
|
|
2316 |
// We didn't know about both, we won't swap them over.
|
|
|
2317 |
return $regionwas;
|
|
|
2318 |
}
|
|
|
2319 |
return $region;
|
|
|
2320 |
}
|
|
|
2321 |
|
|
|
2322 |
/**
|
|
|
2323 |
* Add a report node and a specific report to the navigation.
|
|
|
2324 |
*
|
|
|
2325 |
* @param int $userid The user ID that we are looking to add this report node to.
|
|
|
2326 |
* @param array $nodeinfo Name and url of the final node that we are creating.
|
|
|
2327 |
*/
|
|
|
2328 |
public function add_report_nodes($userid, $nodeinfo) {
|
|
|
2329 |
global $USER;
|
|
|
2330 |
// Try to find the specific user node.
|
|
|
2331 |
$newusernode = $this->navigation->find('user' . $userid, null);
|
|
|
2332 |
$reportnode = null;
|
|
|
2333 |
$navigationnodeerror =
|
|
|
2334 |
'Could not find the navigation node requested. Please check that the node you are looking for exists.';
|
|
|
2335 |
if ($userid != $USER->id || $this->context->contextlevel == CONTEXT_COURSE) {
|
|
|
2336 |
// Within a course context we need to properly indicate how we have come to the page,
|
|
|
2337 |
// regardless of whether it's currently logged in user or not.
|
|
|
2338 |
// Check that we have a valid node.
|
|
|
2339 |
if (empty($newusernode)) {
|
|
|
2340 |
// Throw an error if we ever reach here.
|
|
|
2341 |
throw new coding_exception($navigationnodeerror);
|
|
|
2342 |
}
|
|
|
2343 |
// Add 'Reports' to the user node.
|
|
|
2344 |
$reportnode = $newusernode->add(get_string('reports'));
|
|
|
2345 |
} else {
|
|
|
2346 |
// We are looking at our own profile.
|
|
|
2347 |
$myprofilenode = $this->settingsnav->find('myprofile', null);
|
|
|
2348 |
// Check that we do end up with a valid node.
|
|
|
2349 |
if (empty($myprofilenode)) {
|
|
|
2350 |
// Throw an error if we ever reach here.
|
|
|
2351 |
throw new coding_exception($navigationnodeerror);
|
|
|
2352 |
}
|
|
|
2353 |
// Add 'Reports' to our node.
|
|
|
2354 |
$reportnode = $myprofilenode->add(get_string('reports'));
|
|
|
2355 |
}
|
|
|
2356 |
// Finally add the report to the navigation tree.
|
|
|
2357 |
$reportnode->add($nodeinfo['name'], $nodeinfo['url'], navigation_node::TYPE_CUSTOM, null, null,
|
|
|
2358 |
new pix_icon('i/report', $nodeinfo['name']));
|
|
|
2359 |
}
|
|
|
2360 |
|
|
|
2361 |
/**
|
|
|
2362 |
* Add some HTML to the list of actions to render in the header actions menu.
|
|
|
2363 |
*
|
|
|
2364 |
* @param string $html The HTML to add.
|
|
|
2365 |
*/
|
|
|
2366 |
public function add_header_action(string $html): void {
|
|
|
2367 |
$this->_headeractions[] = $html;
|
|
|
2368 |
}
|
|
|
2369 |
|
|
|
2370 |
/**
|
|
|
2371 |
* Get the list of HTML for actions to render in the header actions menu.
|
|
|
2372 |
*
|
|
|
2373 |
* @return string[]
|
|
|
2374 |
*/
|
|
|
2375 |
public function get_header_actions(): array {
|
|
|
2376 |
return $this->_headeractions;
|
|
|
2377 |
}
|
|
|
2378 |
|
|
|
2379 |
/**
|
|
|
2380 |
* Set the flag to indicate if the region main settings should be rendered as an action
|
|
|
2381 |
* in the header actions menu rather than at the top of the content.
|
|
|
2382 |
*
|
|
|
2383 |
* @param bool $value If the settings should be in the header.
|
|
|
2384 |
*/
|
|
|
2385 |
public function set_include_region_main_settings_in_header_actions(bool $value): void {
|
|
|
2386 |
$this->_regionmainsettingsinheader = $value;
|
|
|
2387 |
}
|
|
|
2388 |
|
|
|
2389 |
/**
|
|
|
2390 |
* Check if the region main settings should be rendered as an action in the header actions
|
|
|
2391 |
* menu rather than at the top of the content.
|
|
|
2392 |
*
|
|
|
2393 |
* @return bool
|
|
|
2394 |
*/
|
|
|
2395 |
public function include_region_main_settings_in_header_actions(): bool {
|
|
|
2396 |
return $this->_regionmainsettingsinheader;
|
|
|
2397 |
}
|
|
|
2398 |
|
|
|
2399 |
/**
|
|
|
2400 |
* Set the flag to indicate if the secondary navigation should be rendered.
|
|
|
2401 |
*
|
|
|
2402 |
* @param bool $hassecondarynavigation If the secondary navigation should be rendered.
|
|
|
2403 |
* @param bool $istablist When true, the navigation bar should be rendered and behave with a tablist ARIA role.
|
|
|
2404 |
* If false, it's rendered with a menubar ARIA role. Defaults to false.
|
|
|
2405 |
*/
|
|
|
2406 |
public function set_secondary_navigation(bool $hassecondarynavigation, bool $istablist = false): void {
|
|
|
2407 |
$this->_hassecondarynavigation = $hassecondarynavigation;
|
|
|
2408 |
$this->_hastablistsecondarynavigation = $istablist;
|
|
|
2409 |
}
|
|
|
2410 |
|
|
|
2411 |
/**
|
|
|
2412 |
* Check if the secondary navigation should be rendered.
|
|
|
2413 |
*
|
|
|
2414 |
* @return bool
|
|
|
2415 |
*/
|
|
|
2416 |
public function has_secondary_navigation(): bool {
|
|
|
2417 |
return $this->_hassecondarynavigation;
|
|
|
2418 |
}
|
|
|
2419 |
|
|
|
2420 |
/**
|
|
|
2421 |
* Check if the secondary navigation should be rendered with a tablist as opposed to a menubar.
|
|
|
2422 |
*
|
|
|
2423 |
* @return bool
|
|
|
2424 |
*/
|
|
|
2425 |
public function has_tablist_secondary_navigation(): bool {
|
|
|
2426 |
return $this->_hastablistsecondarynavigation;
|
|
|
2427 |
}
|
|
|
2428 |
|
|
|
2429 |
/**
|
|
|
2430 |
* Set the key of the secondary nav node to be activated.
|
|
|
2431 |
*
|
|
|
2432 |
* @param string $navkey the key of the secondary nav node to be activated.
|
|
|
2433 |
*/
|
|
|
2434 |
public function set_secondary_active_tab(string $navkey): void {
|
|
|
2435 |
$this->_activekeysecondary = $navkey;
|
|
|
2436 |
}
|
|
|
2437 |
|
|
|
2438 |
/**
|
|
|
2439 |
* The key of secondary nav node to activate.
|
|
|
2440 |
*
|
|
|
2441 |
* @return string|null get the key of the secondary node to activate.
|
|
|
2442 |
*/
|
|
|
2443 |
public function get_secondary_active_tab(): ?string {
|
|
|
2444 |
return $this->_activekeysecondary;
|
|
|
2445 |
}
|
|
|
2446 |
|
|
|
2447 |
/**
|
|
|
2448 |
* Set the key of the primary nav node to be activated.
|
|
|
2449 |
*
|
|
|
2450 |
* @param string $navkey
|
|
|
2451 |
*/
|
|
|
2452 |
public function set_primary_active_tab(string $navkey): void {
|
|
|
2453 |
$this->_activenodeprimary = $navkey;
|
|
|
2454 |
}
|
|
|
2455 |
|
|
|
2456 |
/**
|
|
|
2457 |
* The key of the primary nav node to activate.
|
|
|
2458 |
*
|
|
|
2459 |
* @return string|null get the key of the primary nav node to activate.
|
|
|
2460 |
*/
|
|
|
2461 |
public function get_primary_activate_tab(): ?string {
|
|
|
2462 |
return $this->_activenodeprimary;
|
|
|
2463 |
}
|
|
|
2464 |
|
|
|
2465 |
/**
|
|
|
2466 |
* Sets the navigation overflow state. This allows developers to turn off the overflow menu if they perhaps are using
|
|
|
2467 |
* some other navigation to show settings.
|
|
|
2468 |
*
|
|
|
2469 |
* @param bool $state The state of whether to show the navigation overflow.
|
|
|
2470 |
*/
|
|
|
2471 |
public function set_navigation_overflow_state(bool $state): void {
|
|
|
2472 |
$this->_navigationoverflow = $state;
|
|
|
2473 |
}
|
|
|
2474 |
|
|
|
2475 |
/**
|
|
|
2476 |
* Gets the navigation overflow state.
|
|
|
2477 |
*
|
|
|
2478 |
* @return bool The navigation overflow state.
|
|
|
2479 |
*/
|
|
|
2480 |
public function get_navigation_overflow_state(): bool {
|
|
|
2481 |
return $this->_navigationoverflow;
|
|
|
2482 |
}
|
|
|
2483 |
|
|
|
2484 |
/**
|
|
|
2485 |
* Set the status for displaying the course index.
|
|
|
2486 |
*
|
|
|
2487 |
* @param bool $state
|
|
|
2488 |
* - `true` (default) if the course index should be shown.
|
|
|
2489 |
* - `false` if the course index should be hidden.
|
|
|
2490 |
*/
|
|
|
2491 |
public function set_show_course_index(bool $state): void {
|
|
|
2492 |
$this->_showcourseindex = $state;
|
|
|
2493 |
}
|
|
|
2494 |
|
|
|
2495 |
/**
|
|
|
2496 |
* Get the current status for displaying the course index.
|
|
|
2497 |
*
|
|
|
2498 |
* @return bool
|
|
|
2499 |
*/
|
|
|
2500 |
public function get_show_course_index(): bool {
|
|
|
2501 |
return $this->_showcourseindex;
|
|
|
2502 |
}
|
|
|
2503 |
}
|