|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00029 defined('MOODLE_INTERNAL') || die(); 00030 00090 class moodle_page { 00092 const STATE_BEFORE_HEADER = 0; 00093 const STATE_PRINTING_HEADER = 1; 00094 const STATE_IN_BODY = 2; 00095 const STATE_DONE = 3; 00098 00099 00100 protected $_state = self::STATE_BEFORE_HEADER; 00101 00102 protected $_course = null; 00103 00108 protected $_cm = null; 00109 00115 protected $_module = null; 00116 00120 protected $_context = null; 00121 00131 protected $_categories = null; 00132 00133 protected $_bodyclasses = array(); 00134 00135 protected $_title = ''; 00136 00137 protected $_heading = ''; 00138 00139 protected $_pagetype = null; 00140 00141 protected $_pagelayout = 'base'; 00142 00148 protected $_layout_options = array(); 00149 00150 protected $_subpage = ''; 00151 00152 protected $_docspath = null; 00153 00155 protected $_legacyclass = null; 00156 00157 protected $_url = null; 00158 00159 protected $_alternateversions = array(); 00160 00161 protected $_blocks = null; 00162 00163 protected $_requires = null; 00164 00165 protected $_blockseditingcap = 'moodle/site:manageblocks'; 00166 00167 protected $_block_actions_done = false; 00168 00169 protected $_othereditingcaps = array(); 00170 00171 protected $_cacheable = true; 00172 00173 protected $_focuscontrol = ''; 00174 00175 protected $_button = ''; 00176 00177 protected $_theme = null; 00179 protected $_navigation = null; 00181 protected $_settingsnav = null; 00183 protected $_navbar = null; 00185 protected $_headingmenu = null; 00186 00191 protected $_wherethemewasinitialised = null; 00192 00194 protected $_opencontainers; 00195 00202 protected $_periodicrefreshdelay = null; 00203 00210 protected $_legacypageobject = null; 00211 00217 protected $_legacybrowsers = array('MSIE' => 6.0); 00218 00225 protected $_devicetypeinuse = null; 00226 00227 protected $_https_login_required = false; 00228 00229 protected $_popup_notification_allowed = true; 00230 00234 00242 protected function magic_get_state() { 00243 return $this->_state; 00244 } 00245 00250 protected function magic_get_headerprinted() { 00251 return $this->_state >= self::STATE_IN_BODY; 00252 } 00253 00262 protected function magic_get_course() { 00263 global $SITE; 00264 if (is_null($this->_course)) { 00265 return $SITE; 00266 } 00267 return $this->_course; 00268 } 00269 00278 protected function magic_get_cm() { 00279 return $this->_cm; 00280 } 00281 00288 protected function magic_get_activityrecord() { 00289 if (is_null($this->_module) && !is_null($this->_cm)) { 00290 $this->load_activity_record(); 00291 } 00292 return $this->_module; 00293 } 00294 00300 protected function magic_get_activityname() { 00301 if (is_null($this->_cm)) { 00302 return null; 00303 } 00304 return $this->_cm->modname; 00305 } 00306 00312 protected function magic_get_category() { 00313 $this->ensure_category_loaded(); 00314 if (!empty($this->_categories)) { 00315 return reset($this->_categories); 00316 } else { 00317 return null; 00318 } 00319 } 00320 00328 protected function magic_get_categories() { 00329 $this->ensure_categories_loaded(); 00330 return $this->_categories; 00331 } 00332 00337 protected function magic_get_context() { 00338 if (is_null($this->_context)) { 00339 if (CLI_SCRIPT or NO_MOODLE_COOKIES) { 00340 // cli scripts work in system context, do not annoy devs with debug info 00341 // very few scripts do not use cookies, we can safely use system as default context there 00342 } else { 00343 debugging('Coding problem: $PAGE->context was not set. You may have forgotten ' 00344 .'to call require_login() or $PAGE->set_context(). The page may not display ' 00345 .'correctly as a result'); 00346 } 00347 $this->_context = get_context_instance(CONTEXT_SYSTEM); 00348 } 00349 return $this->_context; 00350 } 00351 00356 protected function magic_get_pagetype() { 00357 global $CFG; 00358 if (is_null($this->_pagetype) || isset($CFG->pagepath)) { 00359 $this->initialise_default_pagetype(); 00360 } 00361 return $this->_pagetype; 00362 } 00363 00368 protected function magic_get_bodyid() { 00369 return 'page-'.$this->pagetype; 00370 } 00371 00377 protected function magic_get_pagelayout() { 00378 return $this->_pagelayout; 00379 } 00380 00385 protected function magic_get_layout_options() { 00386 return $this->_layout_options; 00387 } 00388 00393 protected function magic_get_subpage() { 00394 return $this->_subpage; 00395 } 00396 00401 protected function magic_get_bodyclasses() { 00402 return implode(' ', array_keys($this->_bodyclasses)); 00403 } 00404 00409 protected function magic_get_title() { 00410 return $this->_title; 00411 } 00412 00417 protected function magic_get_heading() { 00418 return $this->_heading; 00419 } 00420 00425 protected function magic_get_headingmenu() { 00426 return $this->_headingmenu; 00427 } 00428 00433 protected function magic_get_docspath() { 00434 if (is_string($this->_docspath)) { 00435 return $this->_docspath; 00436 } else { 00437 return str_replace('-', '/', $this->pagetype); 00438 } 00439 } 00440 00446 protected function magic_get_url() { 00447 global $FULLME; 00448 if (is_null($this->_url)) { 00449 debugging('This page did not call $PAGE->set_url(...). Using '.s($FULLME), DEBUG_DEVELOPER); 00450 $this->_url = new moodle_url($FULLME); 00451 // Make sure the guessed URL cannot lead to dangerous redirects. 00452 $this->_url->remove_params('sesskey'); 00453 } 00454 return new moodle_url($this->_url); // Return a clone for safety. 00455 } 00456 00461 protected function magic_get_alternateversions() { 00462 return $this->_alternateversions; 00463 } 00464 00469 protected function magic_get_blocks() { 00470 global $CFG; 00471 if (is_null($this->_blocks)) { 00472 if (!empty($CFG->blockmanagerclass)) { 00473 $classname = $CFG->blockmanagerclass; 00474 } else { 00475 $classname = 'block_manager'; 00476 } 00477 $this->_blocks = new $classname($this); 00478 } 00479 return $this->_blocks; 00480 } 00481 00486 protected function magic_get_requires() { 00487 global $CFG; 00488 if (is_null($this->_requires)) { 00489 $this->_requires = new page_requirements_manager(); 00490 } 00491 return $this->_requires; 00492 } 00493 00498 protected function magic_get_cacheable() { 00499 return $this->_cacheable; 00500 } 00501 00506 protected function magic_get_focuscontrol() { 00507 return $this->_focuscontrol; 00508 } 00509 00514 protected function magic_get_button() { 00515 return $this->_button; 00516 } 00517 00522 protected function magic_get_theme() { 00523 if (is_null($this->_theme)) { 00524 $this->initialise_theme_and_output(); 00525 } 00526 return $this->_theme; 00527 } 00528 00534 protected function magic_get_devicetypeinuse() { 00535 if (empty($this->_devicetypeinuse)) { 00536 $this->_devicetypeinuse = get_user_device_type(); 00537 } 00538 return $this->_devicetypeinuse; 00539 } 00540 00546 protected function magic_get_legacythemeinuse() { 00547 debugging('$PAGE->legacythemeinuse is a deprecated property - please use $PAGE->devicetypeinuse and check if it is equal to legacy.', DEBUG_DEVELOPER); 00548 return ($this->devicetypeinuse == 'legacy'); 00549 } 00550 00556 protected function magic_get_periodicrefreshdelay() { 00557 return $this->_periodicrefreshdelay; 00558 } 00559 00565 protected function magic_get_opencontainers() { 00566 if (is_null($this->_opencontainers)) { 00567 $this->_opencontainers = new xhtml_container_stack(); 00568 } 00569 return $this->_opencontainers; 00570 } 00571 00576 protected function magic_get_navigation() { 00577 if ($this->_navigation === null) { 00578 $this->_navigation = new global_navigation($this); 00579 } 00580 return $this->_navigation; 00581 } 00582 00587 protected function magic_get_navbar() { 00588 if ($this->_navbar === null) { 00589 $this->_navbar = new navbar($this); 00590 } 00591 return $this->_navbar; 00592 } 00593 00598 protected function magic_get_settingsnav() { 00599 if ($this->_settingsnav === null) { 00600 $this->_settingsnav = new settings_navigation($this); 00601 $this->_settingsnav->initialise(); 00602 } 00603 return $this->_settingsnav; 00604 } 00605 00614 public function __get($name) { 00615 $getmethod = 'magic_get_' . $name; 00616 if (method_exists($this, $getmethod)) { 00617 return $this->$getmethod(); 00618 } else { 00619 throw new coding_exception('Unknown property ' . $name . ' of $PAGE.'); 00620 } 00621 } 00622 00630 public function __set($name, $value) { 00631 if (method_exists($this, 'set_' . $name)) { 00632 throw new coding_exception('Invalid attempt to modify page object', "Use \$PAGE->set_$name() instead."); 00633 } else { 00634 throw new coding_exception('Invalid attempt to modify page object', "Unknown property $name"); 00635 } 00636 } 00637 00639 00647 public function get_renderer($component, $subtype = null, $target = null) { 00648 return $this->magic_get_theme()->get_renderer($this, $component, $subtype, $target); 00649 } 00650 00655 public function has_navbar() { 00656 if ($this->_navbar === null) { 00657 $this->_navbar = new navbar($this); 00658 } 00659 return $this->_navbar->has_items(); 00660 } 00661 00667 public function user_is_editing() { 00668 global $USER; 00669 return !empty($USER->editing) && $this->user_allowed_editing(); 00670 } 00671 00675 public function user_can_edit_blocks() { 00676 return has_capability($this->_blockseditingcap, $this->_context); 00677 } 00678 00682 public function user_allowed_editing() { 00683 if ($this->_legacypageobject) { 00684 return $this->_legacypageobject->user_allowed_editing(); 00685 } 00686 return has_any_capability($this->all_editing_caps(), $this->_context); 00687 } 00688 00693 public function debug_summary() { 00694 $summary = ''; 00695 $summary .= 'General type: ' . $this->pagelayout . '. '; 00696 if (!during_initial_install()) { 00697 $summary .= 'Context ' . print_context_name($this->_context) . ' (context id ' . $this->_context->id . '). '; 00698 } 00699 $summary .= 'Page type ' . $this->pagetype . '. '; 00700 if ($this->subpage) { 00701 'Sub-page ' . $this->subpage . '. '; 00702 } 00703 return $summary; 00704 } 00705 00707 00713 public function set_state($state) { 00714 if ($state != $this->_state + 1 || $state > self::STATE_DONE) { 00715 throw new coding_exception('Invalid state passed to moodle_page::set_state. We are in state ' . 00716 $this->_state . ' and state ' . $state . ' was requested.'); 00717 } 00718 00719 if ($state == self::STATE_PRINTING_HEADER) { 00720 $this->starting_output(); 00721 } 00722 00723 $this->_state = $state; 00724 } 00725 00738 public function set_course($course) { 00739 global $COURSE, $PAGE; 00740 00741 if (empty($course->id)) { 00742 throw new coding_exception('$course passed to moodle_page::set_course does not look like a proper course object.'); 00743 } 00744 00745 $this->ensure_theme_not_set(); 00746 00747 if (!empty($this->_course->id) && $this->_course->id != $course->id) { 00748 $this->_categories = null; 00749 } 00750 00751 $this->_course = clone($course); 00752 00753 if ($this === $PAGE) { 00754 $COURSE = $this->_course; 00755 moodle_setlocale(); 00756 } 00757 00758 if (!$this->_context) { 00759 $this->set_context(get_context_instance(CONTEXT_COURSE, $this->_course->id)); 00760 } 00761 } 00762 00767 public function set_context($context) { 00768 if ($context === null) { 00769 // extremely ugly hack which sets context to some value in order to prevent warnings, 00770 // use only for core error handling!!!! 00771 if (!$this->_context) { 00772 $this->_context = get_context_instance(CONTEXT_SYSTEM); 00773 } 00774 return; 00775 } 00776 00777 // ideally we should set context only once 00778 if (isset($this->_context)) { 00779 if ($context->id == $this->_context->id) { 00780 // fine - no change needed 00781 } else if ($this->_context->contextlevel == CONTEXT_SYSTEM or $this->_context->contextlevel == CONTEXT_COURSE) { 00782 // hmm - not ideal, but it might produce too many warnings due to the design of require_login 00783 } else if ($this->_context->contextlevel == CONTEXT_MODULE and $this->_context->id == get_parent_contextid($context)) { 00784 // hmm - most probably somebody did require_login() and after that set the block context 00785 } else { 00786 // we do not want devs to do weird switching of context levels on the fly, 00787 // because we might have used the context already such as in text filter in page title 00788 debugging('Coding problem: unsupported modification of PAGE->context from '.$this->_context->contextlevel.' to '.$context->contextlevel); 00789 } 00790 } 00791 00792 $this->_context = $context; 00793 } 00794 00803 public function set_cm($cm, $course = null, $module = null) { 00804 global $DB; 00805 00806 if (!isset($cm->id) || !isset($cm->course)) { 00807 throw new coding_exception('Invalid $cm parameter for $PAGE object, it has to be instance of cm_info or record from the course_modules table.'); 00808 } 00809 00810 if (!$this->_course || $this->_course->id != $cm->course) { 00811 if (!$course) { 00812 $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); 00813 } 00814 if ($course->id != $cm->course) { 00815 throw new coding_exception('The course you passed to $PAGE->set_cm does not correspond to the $cm.'); 00816 } 00817 $this->set_course($course); 00818 } 00819 00820 // make sure we have a $cm from get_fast_modinfo as this contains activity access details 00821 if (!($cm instanceof cm_info)) { 00822 $modinfo = get_fast_modinfo($this->_course); 00823 $cm = $modinfo->get_cm($cm->id); 00824 } 00825 $this->_cm = $cm; 00826 00827 // unfortunately the context setting is a mess, let's try to work around some common block problems and show some debug messages 00828 if (empty($this->_context) or $this->_context->contextlevel != CONTEXT_BLOCK) { 00829 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 00830 $this->set_context($context); 00831 } 00832 00833 if ($module) { 00834 $this->set_activity_record($module); 00835 } 00836 } 00837 00844 public function set_activity_record($module) { 00845 if (is_null($this->_cm)) { 00846 throw new coding_exception('You cannot call $PAGE->set_activity_record until after $PAGE->cm has been set.'); 00847 } 00848 if ($module->id != $this->_cm->instance || $module->course != $this->_course->id) { 00849 throw new coding_exception('The activity record your are trying to set does not seem to correspond to the cm that has been set.'); 00850 } 00851 $this->_module = $module; 00852 } 00853 00862 public function set_pagetype($pagetype) { 00863 $this->_pagetype = $pagetype; 00864 } 00865 00872 public function set_pagelayout($pagelayout) { 00880 $this->_pagelayout = $pagelayout; 00881 } 00882 00889 public function set_subpage($subpage) { 00890 if (empty($subpage)) { 00891 $this->_subpage = ''; 00892 } else { 00893 $this->_subpage = $subpage; 00894 } 00895 } 00896 00900 public function add_body_class($class) { 00901 if ($this->_state > self::STATE_BEFORE_HEADER) { 00902 throw new coding_exception('Cannot call moodle_page::add_body_class after output has been started.'); 00903 } 00904 $this->_bodyclasses[$class] = 1; 00905 } 00906 00910 public function add_body_classes($classes) { 00911 foreach ($classes as $class) { 00912 $this->add_body_class($class); 00913 } 00914 } 00915 00919 public function set_title($title) { 00920 $title = format_string($title); 00921 $title = str_replace('"', '"', $title); 00922 $this->_title = $title; 00923 } 00924 00928 public function set_heading($heading) { 00929 $this->_heading = format_string($heading); 00930 } 00931 00935 public function set_headingmenu($menu) { 00936 $this->_headingmenu = $menu; 00937 } 00938 00947 public function set_category_by_id($categoryid) { 00948 global $SITE, $DB; 00949 if (!is_null($this->_course)) { 00950 throw new coding_exception('Attempt to manually set the course category when the course has been set. This is not allowed.'); 00951 } 00952 if (is_array($this->_categories)) { 00953 throw new coding_exception('Course category has already been set. You are not allowed to change it.'); 00954 } 00955 $this->ensure_theme_not_set(); 00956 $this->set_course($SITE); 00957 $this->load_category($categoryid); 00958 $this->set_context(get_context_instance(CONTEXT_COURSECAT, $categoryid)); 00959 } 00960 00968 public function set_docs_path($path) { 00969 $this->_docspath = $path; 00970 } 00971 00982 public function set_url($url, array $params = null) { 00983 global $CFG; 00984 00985 if (is_string($url)) { 00986 if (strpos($url, 'http') === 0) { 00987 // ok 00988 } else if (strpos($url, '/') === 0) { 00989 // we have to use httpswwwroot here, because of loginhttps pages 00990 $url = $CFG->httpswwwroot . $url; 00991 } else { 00992 throw new coding_exception('Invalid parameter $url, has to be full url or in shortened form starting with /.'); 00993 } 00994 } 00995 00996 $this->_url = new moodle_url($url, $params); 00997 00998 $fullurl = $this->_url->out_omit_querystring(); 00999 if (strpos($fullurl, "$CFG->httpswwwroot/") !== 0) { 01000 debugging('Most probably incorrect set_page() url argument, it does not match the httpswwwroot!'); 01001 } 01002 $shorturl = str_replace("$CFG->httpswwwroot/", '', $fullurl); 01003 01004 if (is_null($this->_pagetype)) { 01005 $this->initialise_default_pagetype($shorturl); 01006 } 01007 if (!is_null($this->_legacypageobject)) { 01008 $this->_legacypageobject->set_url($url, $params); 01009 } 01010 } 01011 01024 public function ensure_param_not_in_url($param) { 01025 $discard = $this->url; // Make sure $this->url is lazy-loaded; 01026 $this->_url->remove_params($param); 01027 } 01028 01038 public function add_alternate_version($title, $url, $mimetype) { 01039 if ($this->_state > self::STATE_BEFORE_HEADER) { 01040 throw new coding_exception('Cannot call moodle_page::add_alternate_version after output has been started.'); 01041 } 01042 $alt = new stdClass; 01043 $alt->title = $title; 01044 $alt->url = $url; 01045 $this->_alternateversions[$mimetype] = $alt; 01046 } 01047 01053 public function set_focuscontrol($controlid) { 01054 $this->_focuscontrol = $controlid; 01055 } 01056 01062 public function set_button($html) { 01063 $this->_button = $html; 01064 } 01065 01072 public function set_blocks_editing_capability($capability) { 01073 $this->_blockseditingcap = $capability; 01074 } 01075 01082 public function set_other_editing_capability($capability) { 01083 if (is_array($capability)) { 01084 $this->_othereditingcaps = array_unique($this->_othereditingcaps + $capability); 01085 } else { 01086 $this->_othereditingcaps[] = $capability; 01087 } 01088 } 01089 01093 public function set_cacheable($cacheable) { 01094 $this->_cacheable = $cacheable; 01095 } 01096 01106 public function set_periodic_refresh_delay($delay=null) { 01107 if ($this->_state > self::STATE_BEFORE_HEADER) { 01108 throw new coding_exception('You cannot set a periodic refresh delay after the header has been printed'); 01109 } 01110 if ($delay===null) { 01111 $this->_periodicrefreshdelay = null; 01112 } else if (is_int($delay)) { 01113 $this->_periodicrefreshdelay = $delay; 01114 } 01115 } 01116 01124 public function force_theme($themename) { 01125 $this->ensure_theme_not_set(); 01126 $this->_theme = theme_config::load($themename); 01127 } 01128 01137 public function https_required() { 01138 global $CFG; 01139 01140 if (!is_null($this->_url)) { 01141 throw new coding_exception('https_required() must be used before setting page url!'); 01142 } 01143 01144 $this->ensure_theme_not_set(); 01145 01146 $this->_https_login_required = true; 01147 01148 if (!empty($CFG->loginhttps)) { 01149 $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); 01150 } else { 01151 $CFG->httpswwwroot = $CFG->wwwroot; 01152 } 01153 } 01154 01161 public function verify_https_required() { 01162 global $CFG, $FULLME; 01163 01164 if (is_null($this->_url)) { 01165 throw new coding_exception('verify_https_required() must be called after setting page url!'); 01166 } 01167 01168 if (!$this->_https_login_required) { 01169 throw new coding_exception('verify_https_required() must be called only after https_required()!'); 01170 } 01171 01172 if (empty($CFG->loginhttps)) { 01173 // https not required, so stop checking 01174 return; 01175 } 01176 01177 if (strpos($this->_url, 'https://')) { 01178 // detect if incorrect PAGE->set_url() used, it is recommended to use root-relative paths there 01179 throw new coding_exception('Invalid page url specified, it must start with https:// for pages that set https_required()!'); 01180 } 01181 01182 if (!empty($CFG->sslproxy)) { 01183 // it does not make much sense to use sslproxy and loginhttps at the same time 01184 return; 01185 } 01186 01187 // now the real test and redirect! 01188 // NOTE: do NOT use this test for detection of https on current page because this code is not compatible with SSL proxies, 01189 // instead use strpos($CFG->httpswwwroot, 'https:') === 0 01190 if (strpos($FULLME, 'https:') !== 0) { 01191 // this may lead to infinite redirect on misconfigured sites, in that case use $CFG->loginhttps=0; in /config.php 01192 redirect($this->_url); 01193 } 01194 } 01195 01198 01203 protected function starting_output() { 01204 global $CFG; 01205 01206 if (!during_initial_install()) { 01207 $this->blocks->load_blocks(); 01208 if (empty($this->_block_actions_done)) { 01209 $this->_block_actions_done = true; 01210 if ($this->blocks->process_url_actions($this)) { 01211 redirect($this->url->out(false)); 01212 } 01213 } 01214 $this->blocks->create_all_block_instances(); 01215 } 01216 01217 // If maintenance mode is on, change the page header. 01218 if (!empty($CFG->maintenance_enabled)) { 01219 $this->set_button('<a href="' . $CFG->wwwroot . '/' . $CFG->admin . 01220 '/settings.php?section=maintenancemode">' . get_string('maintenancemode', 'admin') . 01221 '</a> ' . $this->button); 01222 01223 $title = $this->title; 01224 if ($title) { 01225 $title .= ' - '; 01226 } 01227 $this->set_title($title . get_string('maintenancemode', 'admin')); 01228 } else { 01229 // Show the messaging popup if there are messages 01230 message_popup_window(); 01231 } 01232 01233 $this->initialise_standard_body_classes(); 01234 } 01235 01244 public function initialise_theme_and_output() { 01245 global $OUTPUT, $PAGE, $SITE; 01246 01247 if (!empty($this->_wherethemewasinitialised)) { 01248 return; 01249 } 01250 01251 if (!during_initial_install()) { 01252 // detect PAGE->context mess 01253 $this->magic_get_context(); 01254 } 01255 01256 if (!$this->_course && !during_initial_install()) { 01257 $this->set_course($SITE); 01258 } 01259 01260 if (is_null($this->_theme)) { 01261 $themename = $this->resolve_theme(); 01262 $this->_theme = theme_config::load($themename); 01263 $this->_layout_options = $this->_theme->pagelayout_options($this->pagelayout); 01264 } 01265 01266 $this->_theme->setup_blocks($this->pagelayout, $this->blocks); 01267 01268 if ($this === $PAGE) { 01269 $OUTPUT = $this->get_renderer('core'); 01270 } 01271 01272 $this->_wherethemewasinitialised = debug_backtrace(); 01273 } 01274 01282 protected function resolve_theme() { 01283 global $CFG, $USER, $SESSION; 01284 01285 if (empty($CFG->themeorder)) { 01286 $themeorder = array('course', 'category', 'session', 'user', 'site'); 01287 } else { 01288 $themeorder = $CFG->themeorder; 01289 // Just in case, make sure we always use the site theme if nothing else matched. 01290 $themeorder[] = 'site'; 01291 } 01292 01293 $mnetpeertheme = ''; 01294 if (isloggedin() and isset($CFG->mnet_localhost_id) and $USER->mnethostid != $CFG->mnet_localhost_id) { 01295 require_once($CFG->dirroot.'/mnet/peer.php'); 01296 $mnetpeer = new mnet_peer(); 01297 $mnetpeer->set_id($USER->mnethostid); 01298 if ($mnetpeer->force_theme == 1 && $mnetpeer->theme != '') { 01299 $mnetpeertheme = $mnetpeer->theme; 01300 } 01301 } 01302 01303 foreach ($themeorder as $themetype) { 01304 switch ($themetype) { 01305 case 'course': 01306 if (!empty($CFG->allowcoursethemes) && !empty($this->_course->theme) && $this->devicetypeinuse == 'default') { 01307 return $this->_course->theme; 01308 } 01309 01310 case 'category': 01311 if (!empty($CFG->allowcategorythemes) && $this->devicetypeinuse == 'default') { 01312 $categories = $this->categories; 01313 foreach ($categories as $category) { 01314 if (!empty($category->theme)) { 01315 return $category->theme; 01316 } 01317 } 01318 } 01319 01320 case 'session': 01321 if (!empty($SESSION->theme)) { 01322 return $SESSION->theme; 01323 } 01324 01325 case 'user': 01326 if (!empty($CFG->allowuserthemes) && !empty($USER->theme) && $this->devicetypeinuse == 'default') { 01327 if ($mnetpeertheme) { 01328 return $mnetpeertheme; 01329 } else { 01330 return $USER->theme; 01331 } 01332 } 01333 01334 case 'site': 01335 if ($mnetpeertheme) { 01336 return $mnetpeertheme; 01337 } 01338 // First try for the device the user is using. 01339 $devicetheme = get_selected_theme_for_device_type($this->devicetypeinuse); 01340 if (!empty($devicetheme)) { 01341 return $devicetheme; 01342 } 01343 // Next try for the default device (as a fallback) 01344 $devicetheme = get_selected_theme_for_device_type('default'); 01345 if (!empty($devicetheme)) { 01346 return $devicetheme; 01347 } 01348 // The default device theme isn't set up - use the overall default theme. 01349 return theme_config::DEFAULT_THEME; 01350 } 01351 } 01352 } 01353 01354 01363 protected function initialise_default_pagetype($script = null) { 01364 global $CFG, $SCRIPT; 01365 01366 if (isset($CFG->pagepath)) { 01367 debugging('Some code appears to have set $CFG->pagepath. That was a horrible deprecated thing. ' . 01368 'Don\'t do it! Try calling $PAGE->set_pagetype() instead.'); 01369 $script = $CFG->pagepath; 01370 unset($CFG->pagepath); 01371 } 01372 01373 if (is_null($script)) { 01374 $script = ltrim($SCRIPT, '/'); 01375 $len = strlen($CFG->admin); 01376 if (substr($script, 0, $len) == $CFG->admin) { 01377 $script = 'admin' . substr($script, $len); 01378 } 01379 } 01380 01381 $path = str_replace('.php', '', $script); 01382 if (substr($path, -1) == '/') { 01383 $path .= 'index'; 01384 } 01385 01386 if (empty($path) || $path == 'index') { 01387 $this->_pagetype = 'site-index'; 01388 } else { 01389 $this->_pagetype = str_replace('/', '-', $path); 01390 } 01391 } 01392 01393 protected function initialise_standard_body_classes() { 01394 global $CFG, $USER; 01395 01396 $pagetype = $this->pagetype; 01397 if ($pagetype == 'site-index') { 01398 $this->_legacyclass = 'course'; 01399 } else if (substr($pagetype, 0, 6) == 'admin-') { 01400 $this->_legacyclass = 'admin'; 01401 } 01402 $this->add_body_class($this->_legacyclass); 01403 01404 $pathbits = explode('-', trim($pagetype)); 01405 for ($i=1;$i<count($pathbits);$i++) { 01406 $this->add_body_class('path-'.join('-',array_slice($pathbits, 0, $i))); 01407 } 01408 01409 $this->add_body_classes(get_browser_version_classes()); 01410 $this->add_body_class('dir-' . get_string('thisdirection', 'langconfig')); 01411 $this->add_body_class('lang-' . current_language()); 01412 $this->add_body_class('yui-skin-sam'); // Make YUI happy, if it is used. 01413 $this->add_body_class('yui3-skin-sam'); // Make YUI3 happy, if it is used. 01414 $this->add_body_class($this->url_to_class_name($CFG->wwwroot)); 01415 01416 $this->add_body_class('pagelayout-' . $this->_pagelayout); // extra class describing current page layout 01417 01418 if (!during_initial_install()) { 01419 $this->add_body_class('course-' . $this->_course->id); 01420 $this->add_body_class('context-' . $this->_context->id); 01421 } 01422 01423 if (!empty($this->_cm)) { 01424 $this->add_body_class('cmid-' . $this->_cm->id); 01425 } 01426 01427 if (!empty($CFG->allowcategorythemes)) { 01428 $this->ensure_category_loaded(); 01429 foreach ($this->_categories as $catid => $notused) { 01430 $this->add_body_class('category-' . $catid); 01431 } 01432 } else { 01433 $catid = 0; 01434 if (is_array($this->_categories)) { 01435 $catids = array_keys($this->_categories); 01436 $catid = reset($catids); 01437 } else if (!empty($this->_course->category)) { 01438 $catid = $this->_course->category; 01439 } 01440 if ($catid) { 01441 $this->add_body_class('category-' . $catid); 01442 } 01443 } 01444 01445 if (!isloggedin()) { 01446 $this->add_body_class('notloggedin'); 01447 } 01448 01449 if (!empty($USER->editing)) { 01450 $this->add_body_class('editing'); 01451 if (optional_param('bui_moveid', false, PARAM_INT)) { 01452 $this->add_body_class('blocks-moving'); 01453 } 01454 } 01455 01456 if (!empty($CFG->blocksdrag)) { 01457 $this->add_body_class('drag'); 01458 } 01459 01460 if ($this->_devicetypeinuse != 'default') { 01461 $this->add_body_class($this->_devicetypeinuse . 'theme'); 01462 } 01463 } 01464 01465 protected function load_activity_record() { 01466 global $DB; 01467 if (is_null($this->_cm)) { 01468 return; 01469 } 01470 $this->_module = $DB->get_record($this->_cm->modname, array('id' => $this->_cm->instance)); 01471 } 01472 01473 protected function ensure_category_loaded() { 01474 if (is_array($this->_categories)) { 01475 return; // Already done. 01476 } 01477 if (is_null($this->_course)) { 01478 throw new coding_exception('Attempt to get the course category for this page before the course was set.'); 01479 } 01480 if ($this->_course->category == 0) { 01481 $this->_categories = array(); 01482 } else { 01483 $this->load_category($this->_course->category); 01484 } 01485 } 01486 01487 protected function load_category($categoryid) { 01488 global $DB; 01489 $category = $DB->get_record('course_categories', array('id' => $categoryid)); 01490 if (!$category) { 01491 throw new moodle_exception('unknowncategory'); 01492 } 01493 $this->_categories[$category->id] = $category; 01494 $parentcategoryids = explode('/', trim($category->path, '/')); 01495 array_pop($parentcategoryids); 01496 foreach (array_reverse($parentcategoryids) as $catid) { 01497 $this->_categories[$catid] = null; 01498 } 01499 } 01500 01501 protected function ensure_categories_loaded() { 01502 global $DB; 01503 $this->ensure_category_loaded(); 01504 if (!is_null(end($this->_categories))) { 01505 return; // Already done. 01506 } 01507 $idstoload = array_keys($this->_categories); 01508 array_shift($idstoload); 01509 $categories = $DB->get_records_list('course_categories', 'id', $idstoload); 01510 foreach ($idstoload as $catid) { 01511 $this->_categories[$catid] = $categories[$catid]; 01512 } 01513 } 01514 01515 protected function ensure_theme_not_set() { 01516 if (!is_null($this->_theme)) { 01517 throw new coding_exception('The theme has already been set up for this page ready for output. ' . 01518 'Therefore, you can no longer change the theme, or anything that might affect what ' . 01519 'the current theme is, for example, the course.', 01520 'Stack trace when the theme was set up: ' . format_backtrace($this->_wherethemewasinitialised)); 01521 } 01522 } 01523 01524 protected function url_to_class_name($url) { 01525 $bits = parse_url($url); 01526 $class = str_replace('.', '-', $bits['host']); 01527 if (!empty($bits['port'])) { 01528 $class .= '--' . $bits['port']; 01529 } 01530 if (!empty($bits['path'])) { 01531 $path = trim($bits['path'], '/'); 01532 if ($path) { 01533 $class .= '--' . str_replace('/', '-', $path); 01534 } 01535 } 01536 return $class; 01537 } 01538 01539 protected function all_editing_caps() { 01540 $caps = $this->_othereditingcaps; 01541 $caps[] = $this->_blockseditingcap; 01542 return $caps; 01543 } 01544 01546 01551 public function get_type() { 01552 debugging('Call to deprecated method moodle_page::get_type. Please use $PAGE->pagetype instead.'); 01553 return $this->get_pagetype(); 01554 } 01555 01560 function get_format_name() { 01561 return $this->get_pagetype(); 01562 } 01563 01568 public function get_courserecord() { 01569 debugging('Call to deprecated method moodle_page::get_courserecord. Please use $PAGE->course instead.'); 01570 return $this->get_course(); 01571 } 01572 01577 public function get_legacyclass() { 01578 if (is_null($this->_legacyclass)) { 01579 $this->initialise_standard_body_classes(); 01580 } 01581 debugging('Call to deprecated method moodle_page::get_legacyclass.'); 01582 return $this->_legacyclass; 01583 } 01584 01589 function blocks_get_positions() { 01590 debugging('Call to deprecated method moodle_page::blocks_get_positions. Use $PAGE->blocks->get_regions() instead.'); 01591 return $this->blocks->get_regions(); 01592 } 01593 01598 function blocks_default_position() { 01599 debugging('Call to deprecated method moodle_page::blocks_default_position. Use $PAGE->blocks->get_default_region() instead.'); 01600 return $this->blocks->get_default_region(); 01601 } 01602 01606 function blocks_get_default() { 01607 debugging('Call to deprecated method moodle_page::blocks_get_default. This method has no function any more.'); 01608 } 01609 01613 function blocks_move_position(&$instance, $move) { 01614 debugging('Call to deprecated method moodle_page::blocks_move_position. This method has no function any more.'); 01615 } 01616 01621 function url_get_parameters() { 01622 debugging('Call to deprecated method moodle_page::url_get_parameters. Use $this->url->params() instead.'); 01623 return $this->url->params(); 01624 } 01625 01630 function url_get_path() { 01631 debugging('Call to deprecated method moodle_page::url_get_path. Use $this->url->out() instead.'); 01632 return $this->url->out(); 01633 } 01634 01639 function url_get_full($extraparams = array()) { 01640 debugging('Call to deprecated method moodle_page::url_get_full. Use $this->url->out() instead.'); 01641 return $this->url->out(true, $extraparams); 01642 } 01643 01647 function set_legacy_page_object($pageobject) { 01648 return $this->_legacypageobject = $pageobject; 01649 } 01650 01655 function print_header($_) { 01656 if (is_null($this->_legacypageobject)) { 01657 throw new coding_exception('You have called print_header on $PAGE when there is not a legacy page class present.'); 01658 } 01659 debugging('You should not longer be doing print_header via a page class.', DEBUG_DEVELOPER); 01660 $args = func_get_args(); 01661 call_user_func_array(array($this->_legacypageobject, 'print_header'), $args); 01662 } 01663 01668 function get_id() { 01669 debugging('Call to deprecated method moodle_page::get_id(). It should not be necessary any more.', DEBUG_DEVELOPER); 01670 if (!is_null($this->_legacypageobject)) { 01671 return $this->_legacypageobject->get_id(); 01672 } 01673 return 0; 01674 } 01675 01680 function get_pageid() { 01681 debugging('Call to deprecated method moodle_page::get_pageid(). It should not be necessary any more.', DEBUG_DEVELOPER); 01682 if (!is_null($this->_legacypageobject)) { 01683 return $this->_legacypageobject->get_id(); 01684 } 01685 return 0; 01686 } 01687 01692 function get_modulerecord() { 01693 return $this->cm; 01694 } 01695 01696 public function has_set_url() { 01697 return ($this->_url!==null); 01698 } 01699 01700 public function set_block_actions_done($setting = true) { 01701 $this->_block_actions_done = $setting; 01702 } 01703 01709 public function get_popup_notification_allowed() { 01710 return $this->_popup_notification_allowed; 01711 } 01712 01718 public function set_popup_notification_allowed($allowed) { 01719 $this->_popup_notification_allowed = $allowed; 01720 } 01721 } 01722 01729 function page_import_types($path) { 01730 global $CFG; 01731 debugging('Call to deprecated function page_import_types.', DEBUG_DEVELOPER); 01732 } 01733 01741 function page_create_instance($instance) { 01742 global $PAGE; 01743 return page_create_object($PAGE->pagetype, $instance); 01744 } 01745 01751 function page_create_object($type, $id = NULL) { 01752 global $CFG, $PAGE, $SITE, $ME; 01753 debugging('Call to deprecated function page_create_object.', DEBUG_DEVELOPER); 01754 01755 $data = new stdClass; 01756 $data->pagetype = $type; 01757 $data->pageid = $id; 01758 01759 $classname = page_map_class($type); 01760 if (!$classname) { 01761 return $PAGE; 01762 } 01763 $legacypage = new $classname; 01764 $legacypage->init_quick($data); 01765 01766 $course = $PAGE->course; 01767 if ($course->id != $SITE->id) { 01768 $legacypage->set_course($course); 01769 } else { 01770 try { 01771 $category = $PAGE->category; 01772 } catch (coding_exception $e) { 01773 // Was not set before, so no need to try to set it again. 01774 $category = false; 01775 } 01776 if ($category) { 01777 $legacypage->set_category_by_id($category->id); 01778 } else { 01779 $legacypage->set_course($SITE); 01780 } 01781 } 01782 01783 $legacypage->set_pagetype($type); 01784 01785 $legacypage->set_url($ME); 01786 $PAGE->set_url(str_replace($CFG->wwwroot . '/', '', $legacypage->url_get_full())); 01787 01788 $PAGE->set_pagetype($type); 01789 $PAGE->set_legacy_page_object($legacypage); 01790 return $PAGE; 01791 } 01792 01798 function page_map_class($type, $classname = NULL) { 01799 global $CFG; 01800 01801 static $mappings = array( 01802 PAGE_COURSE_VIEW => 'page_course', 01803 ); 01804 01805 if (!empty($type) && !empty($classname)) { 01806 $mappings[$type] = $classname; 01807 } 01808 01809 if (!isset($mappings[$type])) { 01810 debugging('Page class mapping requested for unknown type: '.$type); 01811 return null; 01812 } else if (empty($classname) && !class_exists($mappings[$type])) { 01813 debugging('Page class mapping for id "'.$type.'" exists but class "'.$mappings[$type].'" is not defined'); 01814 return null; 01815 } 01816 01817 return $mappings[$type]; 01818 } 01819 01829 class page_base extends moodle_page { 01834 var $id = NULL; 01835 01837 01838 // HTML OUTPUT SECTION 01839 01840 // SELF-REPORTING SECTION 01841 01842 // Simple stuff, do not override this. 01843 function get_id() { 01844 return $this->id; 01845 } 01846 01847 // Initialize the data members of the parent class 01848 function init_quick($data) { 01849 $this->id = $data->pageid; 01850 } 01851 01852 function init_full() { 01853 } 01854 } 01855 01867 class page_course extends page_base { 01868 } 01869 01879 class page_generic_activity extends page_base { 01880 // Although this function is deprecated, it should be left here because 01881 // people upgrading legacy code need to copy it. See 01882 // http://docs.moodle.org/dev/Migrating_your_code_to_the_2.0_rendering_API 01883 function print_header($title, $morenavlinks = NULL, $bodytags = '', $meta = '') { 01884 global $USER, $CFG, $PAGE, $OUTPUT; 01885 01886 $this->init_full(); 01887 $replacements = array( 01888 '%fullname%' => format_string($this->activityrecord->name) 01889 ); 01890 foreach ($replacements as $search => $replace) { 01891 $title = str_replace($search, $replace, $title); 01892 } 01893 01894 $buttons = '<table><tr><td>'.$OUTPUT->update_module_button($this->modulerecord->id, $this->activityname).'</td>'; 01895 if ($this->user_allowed_editing()) { 01896 $buttons .= '<td><form method="get" action="view.php"><div>'. 01897 '<input type="hidden" name="id" value="'.$this->modulerecord->id.'" />'. 01898 '<input type="hidden" name="edit" value="'.($this->user_is_editing()?'off':'on').'" />'. 01899 '<input type="submit" value="'.get_string($this->user_is_editing()?'blockseditoff':'blocksediton').'" /></div></form></td>'; 01900 } 01901 $buttons .= '</tr></table>'; 01902 01903 if (!empty($morenavlinks) && is_array($morenavlinks)) { 01904 foreach ($morenavlinks as $navitem) { 01905 if (is_array($navitem) && array_key_exists('name', $navitem)) { 01906 $link = null; 01907 if (array_key_exists('link', $navitem)) { 01908 $link = $navitem['link']; 01909 } 01910 $PAGE->navbar->add($navitem['name'], $link); 01911 } 01912 } 01913 } 01914 01915 $PAGE->set_title($title); 01916 $PAGE->set_heading($this->course->fullname); 01917 $PAGE->set_button($buttons); 01918 echo $OUTPUT->header(); 01919 } 01920 }