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