diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php
index 06e4db76d6b..73467fdfee3 100644
--- a/admin/settings/appearance.php
+++ b/admin/settings/appearance.php
@@ -11,6 +11,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$temp->add(new admin_setting_configcheckbox('allowuserthemes', get_string('allowuserthemes', 'admin'), get_string('configallowuserthemes', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('allowcoursethemes', get_string('allowcoursethemes', 'admin'), get_string('configallowcoursethemes', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('allowcategorythemes', get_string('allowcategorythemes', 'admin'), get_string('configallowcategorythemes', 'admin'), 0));
+ $temp->add(new admin_setting_configcheckbox('allowthemechangeonurl', get_string('allowthemechangeonurl', 'admin'), get_string('configallowthemechangeonurl', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('allowuserblockhiding', get_string('allowuserblockhiding', 'admin'), get_string('configallowuserblockhiding', 'admin'), 1));
$temp->add(new admin_setting_configcheckbox('showblocksonmodpages', get_string('showblocksonmodpages', 'admin'), get_string('configshowblocksonmodpages', 'admin'), 0));
$temp->add(new admin_setting_configselect('hideactivitytypenavlink', get_string('hideactivitytypenavlink', 'admin'), get_string('confighideactivitytypenavlink', 'admin'), 0,
diff --git a/config-dist.php b/config-dist.php
index 2958a13e1de..f37f4666266 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -291,8 +291,8 @@ $CFG->admin = 'admin';
// Set the priority of themes from highest to lowest. This is useful (for
// example) in sites where the user theme should override all other theme
// settings for accessibility reasons. You can also disable types of themes
-// by removing them from the array. The default setting is:
-// $CFG->themeorder = array('page', 'course', 'category', 'session', 'user', 'site');
+// (other than site) by removing them from the array. The default setting is:
+// $CFG->themeorder = array('course', 'category', 'session', 'user', 'site');
// NOTE: course, category, session, user themes still require the
// respective settings to be enabled
//
diff --git a/course/category.php b/course/category.php
index 394a769e36f..fb205b3f86a 100644
--- a/course/category.php
+++ b/course/category.php
@@ -63,11 +63,6 @@
}
}
- if(!empty($CFG->allowcategorythemes) && isset($category->theme)) {
- // specifying theme here saves us some dbqs
- theme_setup($category->theme);
- }
-
/// Print headings
$numcategories = $DB->count_records('course_categories');
diff --git a/lang/en_utf8/admin.php b/lang/en_utf8/admin.php
index 54d27d1f557..8677e2d2596 100644
--- a/lang/en_utf8/admin.php
+++ b/lang/en_utf8/admin.php
@@ -12,6 +12,7 @@ $string['allowediplist'] = 'Allowed IP list';
$string['allowemailaddresses'] = 'Allowed email domains';
$string['allowobjectembed'] = 'Allow EMBED and OBJECT tags';
$string['allowrenames'] = 'Allow renames';
+$string['allowthemechangeonurl'] = 'Allow theme changes in the URL';
$string['allowuserblockhiding'] = 'Allow users to hide blocks';
$string['allowusermailcharset'] = 'Allow user to select character set';
$string['allowuserswitchrolestheycantassign'] = 'Allow users without the assign roles capability to switch roles';
@@ -82,6 +83,7 @@ $string['configallowobjectembed'] = 'As a default security measure, normal users
$string['configallowoverride'] = 'You can allow people with the roles on the left side to override some of the column roles';
$string['configallowoverride2'] = 'Select which role(s) can be overridden by each role in the left column.
Note that these settings only apply to users who have either the capability moodle/role:override or the capability moodle/role:safeoverride allowed.';
$string['configallowswitch'] = 'Select which roles a user may switch to, based on which roles they already have. In addition to an entry in this table, a user must also have the moodle/role:switchroles capability to be able to switch.
Note that it is only possible to switch to roles that have the moodle/course:view capability, and that do not have the moodle/site:doanything capability, so some columns in this table are disabled.';
+$string['configallowthemechangeonurl'] = 'If you turn this setting on, then the theme can by changed by adding theme={themename}&sesskey={sesskey} to any Moodle URL.';
$string['configallowunenroll'] = 'If this is set \'Yes\', then students are allowed to unenrol themselves from courses whenever they like. Otherwise they are not allowed, and this process will be solely controlled by the teachers and administrators.';
$string['configallowuserblockhiding'] = 'Do you want to allow users to hide/show side blocks throughout this site? This feature uses Javascript and cookies to remember the state of each collapsible block, and only affects the user\'s own view.';
$string['configallowusermailcharset'] = 'Enabling this, every user in the site will be able to specify his own charset for email.';
diff --git a/lib/adminlib.php b/lib/adminlib.php
index 1d182d6bf3e..0b4582f76b8 100644
--- a/lib/adminlib.php
+++ b/lib/adminlib.php
@@ -5205,9 +5205,6 @@ function admin_get_root($reload=false, $requirefulltree=true) {
$ADMIN->purge_children($requirefulltree);
}
- // Some parts of the tree require $CFG->pixpath.
- $OUTPUT->initialise_deprecated_cfg_pixpath();
-
if (!$ADMIN->loaded) {
// we process this file first to create categories first and in correct order
require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
@@ -5275,7 +5272,7 @@ function admin_apply_default_settings($node=NULL, $unconditional=true) {
* @return int number of changed settings
*/
function admin_write_settings($formdata) {
- global $CFG, $SITE, $PAGE, $DB;
+ global $CFG, $SITE, $DB;
$olddbsessions = !empty($CFG->dbsessions);
$formdata = (array)$formdata;
@@ -5314,9 +5311,12 @@ function admin_write_settings($formdata) {
require_logout();
}
- // now update $SITE - it might have been changed
- $SITE = $DB->get_record('course', array('id'=>$SITE->id));
- $PAGE->set_course($SITE);
+ // Now update $SITE - just update the fields, in case other people have a
+ // a reference to it (e.g. $PAGE, $COURSE).
+ $newsite = $DB->get_record('course', array('id'=>$SITE->id));
+ foreach (get_object_vars($newsite) as $field => $value) {
+ $SITE->$field = $value;
+ }
// now reload all settings - some of them might depend on the changed
admin_get_root(true);
diff --git a/lib/blocklib.php b/lib/blocklib.php
index 6df93bb125d..0f505cbb9f9 100644
--- a/lib/blocklib.php
+++ b/lib/blocklib.php
@@ -329,6 +329,10 @@ class block_manager implements ArrayAccess {
return;
}
+ if (!isset($this->defaultregion)) {
+ $this->page->initialise_theme_and_output();
+ }
+
if (is_null($includeinvisible)) {
$includeinvisible = $this->page->user_is_editing();
}
diff --git a/lib/cssconstants.php b/lib/cssconstants.php
deleted file mode 100644
index 382d699db64..00000000000
--- a/lib/cssconstants.php
+++ /dev/null
@@ -1,102 +0,0 @@
-.
-
-/**
- * Plug in constants/variables - See MDL-6798 for details
- *
- * Information from Urs Hunkler:
- *
- *
- * More flexible themes with CSS constants: An option for Moodle retro themes and easy colour palette variants.
- *
- * I adopted Shaun Inman's "CSS Server-side Constants" to Moodle: http://www.shauninman.com/post/heap/2005/08/09/css_constants
- *
- * With setting "cssconstants" to true in "config.php" you activate the CSS constants. If "cssconstants" is missing or set to "false" the
- * replacement function is not used.
- *
- * $THEME->cssconstants = true;
- * By setting this to true, you will be able to use CSS constants
- *
- * The constant definitions are written into a separate CSS file named like "constants.css" and loaded first in config.php. You can use constants for any CSS properties. The constant definition looks like:
- *
- * \@server constants {
- * fontColor: #3a2830;
- * aLink: #116699;
- * aVisited: #AA2200;
- * aHover: #779911;
- * pageBackground: #FFFFFF;
- * backgroundColor: #EEEEEE;
- * backgroundSideblockHeader: #a8a4e9;
- * fontcolorSideblockHeader: #222222;
- * color1: #98818b;
- * color2: #bd807b;
- * color3: #f9d1d7;
- * color4: #e8d4d8;
- * }
- *
- *
- * The lines in the CSS files using CSS constants look like:
- *
- * body {
- * font-size: 100%;
- * background-color: pageBackground;
- * color: fontColor;
- * font-family: 'Bitstream Vera Serif', georgia, times, serif;
- * margin: 0;
- * padding: 0;
- * }
- * div#page {
- * margin: 0 10px;
- * padding-top: 5px;
- * border-top-width: 10px;
- * border-top-style: solid;
- * border-top-color: color3;
- * }
- * div.clearer {
- * clear: both;
- * }
- * a:link {
- * color: aLink;
- * }
- *
- *
- * @package moodlecore
- * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
- /**
- * Replaces CSS Constants within CSS string
- *
- * @param string $css
- * @return string
- */
-function replace_cssconstants($css) {
- if (preg_match_all("/@server\s+(?:variables|constants)\s*\{\s*([^\}]+)\s*\}\s*/i",$css,$matches)) {
- $variables = array();
- foreach ($matches[0] as $key=>$server) {
- $css = str_replace($server,'',$css);
- preg_match_all("/([^:\}\s]+)\s*:\s*([^;\}]+);/",$matches[1][$key],$vars);
- foreach ($vars[1] as $var=>$value) {
- $variables[$value] = $vars[2][$var];
- }
- }
- $css = str_replace(array_keys($variables),array_values($variables),$css);
- }
- return ($css);
-}
-
-?>
diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php
index 1c322c85b46..27758516e5c 100644
--- a/lib/deprecatedlib.php
+++ b/lib/deprecatedlib.php
@@ -1765,6 +1765,55 @@ class custom_corners_renderer_factory extends standard_renderer_factory {
}
+/**
+ * Used to be used for setting up the theme. No longer used by core code, and
+ * should not have been used elsewhere.
+ *
+ * The theme is now automatically initialised before it is first used. If you really need
+ * to force this to happen, just reference $PAGE->theme.
+ *
+ * To force a particular theme on a particular page, you can use $PAGE->force_theme(...).
+ * However, I can't think of any valid reason to do that outside the theme selector UI.
+ *
+ * @deprecated
+ * @param string $theme The theme to use defaults to current theme
+ * @param array $params An array of parameters to use
+ */
+function theme_setup($theme = '', $params=NULL) {
+ throw new coding_exception('The function theme_setup is no longer required, and should no longer be used. ' .
+ 'The current theme gets initialised automatically before it is first used.');
+}
+
+/**
+ * @deprecated use $PAGE->theme->name instead.
+ * @return string the name of the current theme.
+ */
+function current_theme() {
+ global $PAGE;
+ // TODO, uncomment this once we have eliminated all references to current_theme in core code.
+ // debugging('current_theme is deprecated, use $PAGE->theme->name instead', DEBUG_DEVELOPER);
+ return $PAGE->theme->name;
+}
+
+/**
+ * This used to be the thing that theme styles.php files used to do all the work.
+ * This is now handled differently. You should copy theme/standard/styes.php
+ * into your theme.
+ *
+ * @deprecated
+ * @param int $lastmodified Always gets set to now
+ * @param int $lifetime The max-age header setting (seconds) defaults to 300
+ * @param string $themename The name of the theme to use (optional) defaults to current theme
+ * @param string $forceconfig Force a particular theme config (optional)
+ * @param string $lang Load styles for the specified language (optional)
+ */
+function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
+ global $CFG, $PAGE, $THEME, $showdeprecatedstylesheetsetupwarning;
+ $showdeprecatedstylesheetsetupwarning = true;
+ include($CFG->dirroot . '/theme/styles.php');
+ exit;
+}
+
/**
* Prints some red text using echo
*
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index 290580ff4ce..4c0fe9913cf 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -8208,26 +8208,10 @@ function address_in_subnet($addr, $subnetstr) {
*
* By using this function properly, we can ensure 100% https-ized pages
* at our entire discretion (login, forgot_password, change_password)
- *
- * @global object
- * @global bool
*/
function httpsrequired() {
-
- global $CFG, $HTTPSPAGEREQUIRED;
-
- if (!empty($CFG->loginhttps)) {
- $HTTPSPAGEREQUIRED = true;
- $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
- $CFG->httpsthemewww = str_replace('http:', 'https:', $CFG->themewww);
-
- // change theme URLs to https
- theme_setup();
-
- } else {
- $CFG->httpswwwroot = $CFG->wwwroot;
- $CFG->httpsthemewww = $CFG->themewww;
- }
+ global $PAGE;
+ $PAGE->https_required();
}
/**
diff --git a/lib/outputlib.php b/lib/outputlib.php
index 9187a8c3017..246fe3b440d 100644
--- a/lib/outputlib.php
+++ b/lib/outputlib.php
@@ -24,30 +24,10 @@
*
* @package moodlecore
* @copyright 2009 Tim Hunt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later (5)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-function initialise_theme_and_output() {
- global $CFG, $OUTPUT, $PAGE, $THEME;
- if (!($OUTPUT instanceof bootstrap_renderer)) {
- return; // Already done.
- }
- if (!isset($CFG->theme) || empty($PAGE)) {
- // Too soon to do anything.
- return;
- }
- theme_setup();
- if (CLI_SCRIPT) {
- $rendererfactory = new cli_renderer_factory($THEME, $PAGE);
- } else {
- $classname = $THEME->rendererfactory;
- $rendererfactory = new $classname($THEME, $PAGE);
- }
- $OUTPUT = $rendererfactory->get_renderer('core');
-}
-
-
/**
* A renderer factory is just responsible for creating an appropriate renderer
* for any given part of Moodle.
@@ -377,6 +357,14 @@ class moodle_renderer_base {
$this->page = $page;
}
+ /**
+ * Have we started output yet?
+ * @return boolean true if the header has been printed.
+ */
+ public function has_started() {
+ return $this->page->state >= moodle_page::STATE_IN_BODY;
+ }
+
protected function output_tag($tagname, $attributes, $contents) {
return $this->output_start_tag($tagname, $attributes) . $contents .
$this->output_end_tag($tagname);
@@ -795,19 +783,22 @@ class moodle_core_renderer extends moodle_renderer_base {
$output .= sprintf($metarefesh, $this->page->periodicrefreshdelay, $this->page->url->out());
}
+ // TODO get rid of $CFG->javascript. We should be able to do everything
+ // with $PAGE->requires.
ob_start();
include($CFG->javascript);
$output .= ob_get_contents();
ob_end_clean();
$output .= $this->page->requires->get_head_code();
+ // List alternate versions.
foreach ($this->page->alternateversions as $type => $alt) {
$output .= $this->output_empty_tag('link', array('rel' => 'alternate',
'type' => $type, 'title' => $alt->title, 'href' => $alt->url));
}
// Add the meta page from the themes if any were requested
- // TODO kill this.
+ // TODO See if we can get rid of this.
$PAGE = $this->page;
$metapage = '';
if (!isset($THEME->standardmetainclude) || $THEME->standardmetainclude) {
@@ -886,17 +877,6 @@ class moodle_core_renderer extends moodle_renderer_base {
}
}
- /**
- * Checks if we are in the body yet or not and returns true if we are in
- * the body, false if we havn't reached it yet
- *
- * @uses moodle_page::STATE_IN_BODY
- * @return bool True for in body, false if before
- */
- public function has_started() {
- return ($this->page->state >= moodle_page::STATE_IN_BODY);
- }
-
/**
* Redirects the user by any means possible given the current state
*
@@ -906,13 +886,6 @@ class moodle_core_renderer extends moodle_renderer_base {
* The redirect function should really only be called before page output has started
* however it will allow itself to be called during the state STATE_IN_BODY
*
- * @global object
- * @uses DEBUG_DEVELOPER
- * @uses DEBUG_ALL
- * @uses moodle_page::STATE_BEFORE_HEADER
- * @uses moodle_page::STATE_PRINTING_HEADER
- * @uses moodle_page::STATE_IN_BODY
- * @uses moodle_page::STATE_DONE
* @param string $encodedurl The URL to send to encoded if required
* @param string $message The message to display to the user if any
* @param int $delay The delay before redirecting a user, if $message has been
@@ -949,8 +922,6 @@ class moodle_core_renderer extends moodle_renderer_base {
$this->metarefreshtag = ''."\n";
$this->page->requires->js_function_call('document.location.replace', array($url))->after_delay($delay+3);
}
- $this->page->set_generaltype('popup');
- $this->page->set_title('redirect');
$output = $this->header();
$output .= $this->notification($message, $messageclass);
$output .= $this->footer();
@@ -984,11 +955,6 @@ class moodle_core_renderer extends moodle_renderer_base {
output_starting_hook();
$this->page->set_state(moodle_page::STATE_PRINTING_HEADER);
- // Add any stylesheets required using the horrible legacy mechanism. TODO kill this.
- foreach ($CFG->stylesheets as $stylesheet) {
- $this->page->requires->css($stylesheet, true);
- }
-
// Find the appropriate page template, based on $this->page->generaltype.
$templatefile = $this->find_page_template();
if ($templatefile) {
@@ -1355,7 +1321,7 @@ class moodle_core_renderer extends moodle_renderer_base {
}
if (!empty($backtrace)) {
$output .= $this->notification('Stack trace: ' .
- format_backtrace($backtrace, true), 'notifytiny');
+ format_backtrace($backtrace), 'notifytiny');
}
}
@@ -1461,6 +1427,338 @@ class moodle_core_renderer extends moodle_renderer_base {
}
+/**
+ *This class represents the configuration variables of a Moodle theme.
+ *
+ * Normally, to create an instance of this class, you should use the
+ * {@link theme_config::load()} factory method to load a themes config.php file.
+ *
+ * @copyright 2009 Tim Hunt
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ */
+class theme_config {
+ /**
+ * @var array The names of all the stylesheets from this theme that you would
+ * like included, in order.
+ */
+ public $sheets = array('styles_layout', 'styles_fonts', 'styles_color');
+
+ public $standardsheets = true;
+
+/// This variable can be set to an array containing
+/// filenames from the *STANDARD* theme. If the
+/// array exists, it will be used to choose the
+/// files to include in the standard style sheet.
+/// When false, then no files are used.
+/// When true or NON-EXISTENT, then ALL standard files are used.
+/// This parameter can be used, for example, to prevent
+/// having to override too many classes.
+/// Note that the trailing .css should not be included
+/// eg $THEME->standardsheets = array('styles_layout','styles_fonts','styles_color');
+////////////////////////////////////////////////////////////////////////////////
+
+
+ public $parent = null;
+
+/// This variable can be set to the name of a parent theme
+/// which you want to have included before the current theme.
+/// This can make it easy to make modifications to another
+/// theme without having to actually change the files
+/// If this variable is empty or false then a parent theme
+/// is not used.
+////////////////////////////////////////////////////////////////////////////////
+
+
+ public $parentsheets = false;
+
+/// This variable can be set to an array containing
+/// filenames from a chosen *PARENT* theme. If the
+/// array exists, it will be used to choose the
+/// files to include in the standard style sheet.
+/// When false, then no files are used.
+/// When true or NON-EXISTENT, then ALL standard files are used.
+/// This parameter can be used, for example, to prevent
+/// having to override too many classes.
+/// Note that the trailing .css should not be included
+/// eg $THEME->parentsheets = array('styles_layout','styles_fonts','styles_color');
+////////////////////////////////////////////////////////////////////////////////
+
+
+ public $modsheets = true;
+
+/// When this is enabled, then this theme will search for
+/// files named "styles.php" inside all Activity modules and
+/// include them. This allows modules to provide some basic
+/// layouts so they work out of the box.
+/// It is HIGHLY recommended to leave this enabled.
+
+
+ public $blocksheets = true;
+
+/// When this is enabled, then this theme will search for
+/// files named "styles.php" inside all Block modules and
+/// include them. This allows Blocks to provide some basic
+/// layouts so they work out of the box.
+/// It is HIGHLY recommended to leave this enabled.
+
+
+ public $langsheets = false;
+
+/// By setting this to true, then this theme will search for
+/// a file named "styles.php" inside the current language
+/// directory. This allows different languages to provide
+/// different styles.
+
+
+ public $courseformatsheets = true;
+
+/// When this is enabled, this theme will search for files
+/// named "styles.php" inside all course formats and
+/// include them. This allows course formats to provide
+/// their own default styles.
+
+
+ public $metainclude = false;
+
+/// When this is enabled (or not set!) then Moodle will try
+/// to include a file meta.php from this theme into the
+///
+ * \@server constants {
+ * fontColor: #3a2830;
+ * aLink: #116699;
+ * aVisited: #AA2200;
+ * aHover: #779911;
+ * pageBackground: #FFFFFF;
+ * backgroundColor: #EEEEEE;
+ * backgroundSideblockHeader: #a8a4e9;
+ * fontcolorSideblockHeader: #222222;
+ * color1: #98818b;
+ * color2: #bd807b;
+ * color3: #f9d1d7;
+ * color4: #e8d4d8;
+ * }
+ *
+ *
+ * The lines in the CSS files using CSS constants look like:
+ *
+ * body {
+ * font-size: 100%;
+ * background-color: pageBackground;
+ * color: fontColor;
+ * font-family: 'Bitstream Vera Serif', georgia, times, serif;
+ * margin: 0;
+ * padding: 0;
+ * }
+ * div#page {
+ * margin: 0 10px;
+ * padding-top: 5px;
+ * border-top-width: 10px;
+ * border-top-style: solid;
+ * border-top-color: color3;
+ * }
+ * div.clearer {
+ * clear: both;
+ * }
+ * a:link {
+ * color: aLink;
+ * }
+ *
+ *
+ * @param array $files an arry of the CSS fiels that need to be output.
+ */
+function output_css_replacing_constants($files) {
+ global $CFG;
+ // Get all the CSS.
+ $toreplace = array($CFG->dirroot, $CFG->themedir);
+ ob_start();
+ foreach ($files as $file) {
+ $shortname = str_replace($toreplace, '', $file);
+ echo '/******* ' . $shortname . " start *******/\n\n";
+ @include_once($file);
+ echo '/******* ' . $shortname . " end *******/\n\n";
+ }
+ $css = ob_get_contents();
+ ob_end_clean();
+
+ if (preg_match_all("/@server\s+(?:variables|constants)\s*\{\s*([^\}]+)\s*\}\s*/i", $css, $matches)) {
+ $variables = array();
+ foreach ($matches[0] as $key => $server) {
+ $css = str_replace($server, '', $css);
+ preg_match_all("/([^:\}\s]+)\s*:\s*([^;\}]+);/", $matches[1][$key], $vars);
+ foreach ($vars[1] as $var => $value) {
+ $variables[$value] = $vars[2][$var];
+ }
+ }
+ $css = str_replace(array_keys($variables), array_values($variables), $css);
+ }
+ echo $css;
+}
+
+/**
+ * This CSS output function will link to CSS files rather than including them
+ * inline.
+ *
+ * The single CSS files can then be edited and saved with interactive
+ * CSS editors like CSSEdit. Any files that have a .php extension are still included
+ * inline.
+ *
+ * @param array $files an arry of the CSS fiels that need to be output.
+ */
+function output_css_for_css_edit($files) {
+ global $CFG;
+ $toreplace = array($CFG->dirroot, $CFG->themedir);
+ foreach ($files as $file) {
+ $shortname = str_replace($toreplace, '', $file);
+ echo '/* @group ' . $shortname . " */\n\n";
+ if (strpos($file, '.css') !== false) {
+ echo '@import url("' . $file . '");'."\n\n";
+ } else {
+ @include_once($file);
+ }
+ echo "/* @end */\n\n";
+ }
+}
\ No newline at end of file
diff --git a/lib/pagelib.php b/lib/pagelib.php
index a866db125db..e1693380ff8 100644
--- a/lib/pagelib.php
+++ b/lib/pagelib.php
@@ -125,6 +125,14 @@ class moodle_page {
protected $_button = '';
+ protected $_theme = null;
+
+ /**
+ * Then the theme is initialsed, we save the stack trace, for use in error messages.
+ * @var array stack trace.
+ */
+ protected $_wherethemewasinitialised = null;
+
/**
* Sets the page to refresh after a given delay (in seconds) using meta refresh
* in {@link standard_head_html()} in outputlib.php
@@ -346,15 +354,12 @@ class moodle_page {
public function get_blocks() {
global $CFG, $THEME;
if (is_null($this->_blocks)) {
- initialise_theme_and_output();
if (!empty($CFG->blockmanagerclass)) {
$classname = $CFG->blockmanagerclass;
} else {
$classname = 'block_manager';
}
$this->_blocks = new $classname($this);
- $this->_blocks->add_regions($THEME->blockregions);
- $this->_blocks->set_default_region($THEME->defaultblockregion);
}
return $this->_blocks;
}
@@ -395,6 +400,17 @@ class moodle_page {
return $this->_button;
}
+ /**
+ * Please do not call this method directly, use the ->theme syntax. {@link __get()}.
+ * @return string the initialised theme for this page.
+ */
+ public function get_theme() {
+ if (is_null($this->_theme)) {
+ $this->initialise_theme_and_output();
+ }
+ return $this->_theme;
+ }
+
/**
* Please do not call this method directly use the ->periodicrefreshdelay syntax
* {@link __get()}
@@ -482,28 +498,28 @@ class moodle_page {
* @param object the course to set as the global course.
*/
public function set_course($course) {
- global $COURSE;
+ global $COURSE, $PAGE;
if (empty($course->id)) {
throw new coding_exception('$course passed to moodle_page::set_course does not look like a proper course object.');
}
- if ($this->_state > self::STATE_BEFORE_HEADER) {
- throw new coding_exception('Cannot call moodle_page::set_course after output has been started.');
- }
+ $this->ensure_theme_not_set();
if (!empty($this->_course->id) && $this->_course->id != $course->id) {
$this->_categories = null;
}
$this->_course = clone($course);
- $COURSE = $this->_course;
+
+ if ($this === $PAGE) {
+ $COURSE = $this->_course;
+ moodle_setlocale();
+ }
if (!$this->_context) {
$this->set_context(get_context_instance(CONTEXT_COURSE, $this->_course->id));
}
-
- moodle_setlocale();
}
/**
@@ -646,6 +662,7 @@ class moodle_page {
if (is_array($this->_categories)) {
throw new coding_exception('Course category has already been set. You are not allowed to change it.');
}
+ $this->ensure_theme_not_set();
$this->set_course($SITE);
$this->load_category($categoryid);
$this->set_context(get_context_instance(CONTEXT_COURSECAT, $categoryid));
@@ -771,6 +788,46 @@ class moodle_page {
}
}
+ /**
+ * Force this page to use a particular theme.
+ *
+ * Please use this cautiously. It is only intended to be used by the themes selector
+ * admin page, and theme/styles.php.
+ *
+ * @param $themename the name of the theme to use.
+ */
+ public function force_theme($themename) {
+ global $PAGE, $THEME;
+ $this->ensure_theme_not_set();
+ $this->_theme = theme_config::load($themename);
+ if ($this === $PAGE) {
+ $THEME = $this->_theme;
+ }
+ }
+
+ /**
+ * This function sets the $HTTPSPAGEREQUIRED global
+ * (used in some parts of moodle to change some links)
+ * and calculate the proper wwwroot to be used
+ *
+ * By using this function properly, we can ensure 100% https-ized pages
+ * at our entire discretion (login, forgot_password, change_password)
+ */
+ public function https_required() {
+ global $CFG, $HTTPSPAGEREQUIRED;
+
+ $this->ensure_theme_not_set();
+
+ if (!empty($CFG->loginhttps)) {
+ $HTTPSPAGEREQUIRED = true;
+ $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
+ $CFG->httpsthemewww = str_replace('http:', 'https:', $CFG->themewww);
+ } else {
+ $CFG->httpswwwroot = $CFG->wwwroot;
+ $CFG->httpsthemewww = $CFG->themewww;
+ }
+ }
+
/// Initialisation methods =====================================================
/// These set various things up in a default way.
@@ -794,6 +851,129 @@ class moodle_page {
$this->initialise_standard_body_classes();
$this->blocks->load_blocks();
+
+ // Add any stylesheets required using the horrible legacy mechanism.
+ if (!empty($CFG->stylesheets)) {
+ debugging('Some code on this page is using the horrible legacy mechanism $CFG->stylesheets to include links to ' .
+ 'extra stylesheets. This is deprecated. Please use $PAGE->requires->css(...) instead.', DEBUG_DEVELOPER);
+ foreach ($CFG->stylesheets as $stylesheet) {
+ $this->page->requires->css($stylesheet, true);
+ }
+ }
+
+ // Require theme stylesheets.
+ $stylesheets = $this->theme->get_stylesheet_urls();
+ foreach ($stylesheets as $stylesheet) {
+ $this->requires->css($stylesheet, true);
+ }
+ }
+
+ /**
+ * Method for use by Moodle core to set up the theme. Do not
+ * use this in your own code.
+ *
+ * Make sure the right theme for this page is loaded. Tell our
+ * blocks_manager about the theme block regions, and then, if
+ * we are $PAGE, set up the globals $THEME and $OUTPUT.
+ */
+ public function initialise_theme_and_output() {
+ global $OUTPUT, $PAGE, $SITE, $THEME;
+
+ if (!$this->_course) {
+ $this->set_course($SITE);
+ }
+
+ if (is_null($this->_theme)) {
+ $themename = $this->resolve_theme();
+ $this->_theme = theme_config::load($themename);
+ }
+
+ $this->blocks->add_regions($this->_theme->blockregions);
+ $this->blocks->set_default_region($this->_theme->defaultblockregion);
+
+ if ($this === $PAGE) {
+ $THEME = $this->_theme;
+ $this->_theme->setup_cfg_paths();
+ if (CLI_SCRIPT) {
+ $classname = 'cli_renderer_factory';
+ } else {
+ $classname = $this->_theme->rendererfactory;
+ }
+ $rendererfactory = new $classname($this->_theme, $this);
+ $OUTPUT = $rendererfactory->get_renderer('core');
+ }
+
+ $this->_wherethemewasinitialised = debug_backtrace();
+ }
+
+ /**
+ * Work out the theme this page should use.
+ *
+ * This depends on numerous $CFG settings, and the properties of this page.
+ *
+ * @return string the name of the theme that should be used on this page.
+ */
+ protected function resolve_theme() {
+ global $CFG, $USER, $SESSION;
+
+ if (empty($CFG->themeorder)) {
+ $themeorder = array('course', 'category', 'session', 'user', 'site');
+ } else {
+ $themeorder = $CFG->themeorder;
+ // Just in case, make sure we always use the site theme if nothing else matched.
+ $themeorder[] = 'site';
+ }
+
+ $mnetpeertheme = '';
+ if (isloggedin() and isset($CFG->mnet_localhost_id) and $USER->mnethostid != $CFG->mnet_localhost_id) {
+ require_once($CFG->dirroot.'/mnet/peer.php');
+ $mnetpeer = new mnet_peer();
+ $mnetpeer->set_id($USER->mnethostid);
+ if ($mnetpeer->force_theme == 1 && $mnetpeer->theme != '') {
+ $mnetpeertheme = $mnetpeer->theme;
+ }
+ }
+
+ $theme = '';
+ foreach ($themeorder as $themetype) {
+ switch ($themetype) {
+ case 'course':
+ if (!empty($CFG->allowcoursethemes) and !empty($this->course->theme)) {
+ return $this->course->theme;
+ }
+
+ case 'category':
+ if (!empty($CFG->allowcategorythemes)) {
+ $categories = $this->categories;
+ foreach ($categories as $category) {
+ if (!empty($category->theme)) {
+ return $category->theme;
+ }
+ }
+ }
+
+ case 'session':
+ if (!empty($SESSION->theme)) {
+ return $SESSION->theme;
+ }
+
+ case 'user':
+ if (!empty($CFG->allowuserthemes) and !empty($USER->theme)) {
+ if ($mnetpeertheme) {
+ return $mnetpeertheme;
+ } else {
+ return $USER->theme;
+ }
+ }
+
+ case 'site':
+ if ($mnetpeertheme) {
+ return $mnetpeertheme;
+ } else {
+ return $CFG->theme;
+ }
+ }
+ }
}
/**
@@ -835,7 +1015,7 @@ class moodle_page {
}
protected function initialise_standard_body_classes() {
- global $CFG;
+ global $CFG, $USER;
$pagetype = $this->pagetype;
if ($pagetype == 'site-index') {
@@ -948,6 +1128,15 @@ class moodle_page {
}
}
+ protected function ensure_theme_not_set() {
+ if (!is_null($this->_theme)) {
+ throw new coding_exception('The theme has already been set up for this page ready for output. ' .
+ 'Therefore, you can no longer change the theme, or anything that might affect what ' .
+ 'the current theme is, for example, the course.',
+ 'Stack trace when the theme was set up: ' . format_backtrace($this->_wherethemewasinitialised));
+ }
+ }
+
protected function url_to_class_name($url) {
$bits = parse_url($url);
$class = str_replace('.', '-', $bits['host']);
diff --git a/lib/setup.php b/lib/setup.php
index 4131b03f7e9..98785ab461d 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -126,7 +126,7 @@ global $MCACHE;
* A global to define if the page being displayed must run under HTTPS.
*
* Its primary goal is to allow 100% HTTPS pages when $CFG->loginhttps is enabled. Default to false.
- * Its enabled only by the httpsrequired() function and used in some pages to update some URLs
+ * Its enabled only by the $PAGE->https_required() function and used in some pages to update some URLs
*
* @global bool $HTTPSPAGEREQUIRED
* @name $HTTPSPAGEREQUIRED
@@ -188,13 +188,10 @@ global $SCRIPT;
}
-/// store settings from config.php in array in $CFG - we can use it later to detect problems and overrides
+/// Store settings from config.php in array in $CFG - we can use it later to detect problems and overrides
$CFG->config_php_settings = (array)$CFG;
-/// Set httpswwwroot default value (this variable will replace $CFG->wwwroot
-/// inside some URLs used in HTTPSPAGEREQUIRED pages.
- $CFG->httpswwwroot = $CFG->wwwroot;
-
+/// Set up some paths.
$CFG->libdir = $CFG->dirroot .'/lib';
if (!isset($CFG->themedir)) {
@@ -202,6 +199,11 @@ global $SCRIPT;
$CFG->themewww = $CFG->wwwroot.'/theme';
}
+/// Set httpswwwroot default value (this variable will replace $CFG->wwwroot
+/// inside some URLs used in HTTPSPAGEREQUIRED pages.
+ $CFG->httpswwwroot = $CFG->wwwroot;
+ $CFG->httpsthemewww = $CFG->themewww;
+
require_once($CFG->libdir .'/setuplib.php'); // Functions that MUST be loaded first
/// Time to start counting
@@ -524,20 +526,18 @@ global $SCRIPT;
$SESSION = &$_SESSION['SESSION'];
$USER = &$_SESSION['USER'];
-/// Load up theme variables (colours etc)
-
- $CFG->httpsthemewww = $CFG->themewww;
-
- if (isset($_GET['theme'])) {
- if ($CFG->allowthemechangeonurl || confirm_sesskey()) {
- $themename = clean_param($_GET['theme'], PARAM_SAFEDIR);
- if (($themename != '') and file_exists($CFG->themedir.'/'.$themename)) {
- $SESSION->theme = $themename;
- }
- unset($themename);
+/// Process theme change in the URL.
+ if (!empty($CFG->allowthemechangeonurl) && ($urlthemename = optional_param('theme', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
+ try {
+ theme_config::load($urlthemename); // Makes sure the theme can be loaded without errors.
+ $SESSION->theme = $urlthemename;
+ } catch (Exception $e) {
+ debugging('Failed to set the theme from the URL.', DEBUG_DEVELOPER, $e->getTrace());
}
}
+ unset($urlthemename);
+/// Ensure a valid theme is set.
if (!isset($CFG->theme)) {
$CFG->theme = 'standardwhite';
}
@@ -547,19 +547,19 @@ global $SCRIPT;
/// in the language file. Otherwise, if the admin hasn't specified a locale
/// then use the one from the default language. Otherwise (and this is the
/// majority of cases), use the stored locale specified by admin.
- if (isset($_GET['lang']) && ($lang = clean_param($_GET['lang'], PARAM_SAFEDIR))) {
- if (file_exists($CFG->dataroot .'/lang/'. $lang) or file_exists($CFG->dirroot .'/lang/'. $lang)) {
+ if (($lang = optional_param('lang', '', PARAM_SAFEDIR))) {
+ if (file_exists($CFG->dataroot .'/lang/'. $lang) or
+ file_exists($CFG->dirroot .'/lang/'. $lang)) {
$SESSION->lang = $lang;
} else if (file_exists($CFG->dataroot.'/lang/'.$lang.'_utf8') or
- file_exists($CFG->dirroot .'/lang/'.$lang.'_utf8')) {
+ file_exists($CFG->dirroot .'/lang/'.$lang.'_utf8')) {
$SESSION->lang = $lang.'_utf8';
}
}
+ unset($lang);
setup_lang_from_browser();
- unset($lang);
-
if (empty($CFG->lang)) {
if (empty($SESSION->lang)) {
$CFG->lang = 'en_utf8';
diff --git a/lib/setuplib.php b/lib/setuplib.php
index 240e7df1b30..8c8d354dacd 100644
--- a/lib/setuplib.php
+++ b/lib/setuplib.php
@@ -179,22 +179,48 @@ function default_exception_handler($ex, $isupgrade = false, $plugin = null) {
$CFG->debug = DEBUG_DEVELOPER;
}
- // If another exception is thrown when we are already handling one, or during $OUTPUT->header,
- // and if we did not take special measures, we would just get a very cryptic message
- // "Exception thrown without a stack frame in Unknown on line 0", rather than the true error.
- // Therefore, we do take special measures.
- foreach ($backtrace as $stackframe) {
- if (isset($stackframe['function']) && isset($stackframe['type']) &&
- $stackframe['type'] == '->' && $stackframe['function'] == 'header') {
- echo bootstrap_renderer::early_error($message, $moreinfourl, $link, debug_backtrace());
- exit(1); // General error code
- }
+ if (is_stacktrace_during_output_init($backtrace)) {
+ echo bootstrap_renderer::early_error($message, $moreinfourl, $link, $backtrace);
+ } else {
+ echo $OUTPUT->fatal_error($message, $moreinfourl, $link, $backtrace, $debuginfo);
}
- echo $OUTPUT->fatal_error($message, $moreinfourl, $link, debug_backtrace());
exit(1); // General error code
}
+/**
+ * This function encapsulates the tests for whether an exception was thrown in the middle
+ * of initialising the $OUTPUT variable and starting output.
+ *
+ * If another exception is thrown then, and if we do not take special measures,
+ * we would just get a very cryptic message "Exception thrown without a stack
+ * frame in Unknown on line 0". That makes debugging very hard, so we do take
+ * special measures in default_exception_handler, with the help of this function.
+ *
+ * @param array $backtrace the stack trace to analyse.
+ * @return boolean whether the stack trace is somewhere in output initialisation.
+ */
+function is_stacktrace_during_output_init($backtrace) {
+ $dangerouscode = array(
+ array('function' => 'header', 'type' => '->'),
+ array('class' => 'bootstrap_renderer'),
+ );
+ foreach ($backtrace as $stackframe) {
+ foreach ($dangerouscode as $pattern) {
+ $matches = true;
+ foreach ($pattern as $property => $value) {
+ if (!isset($stackframe[$property]) || $stackframe[$property] != $value) {
+ $matches = false;
+ }
+ }
+ if ($matches) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
/**
* Abort execution, displaying an error message.
*
@@ -751,16 +777,21 @@ class bootstrap_renderer {
*/
protected $initialising = false;
+ /**
+ * Have we started output yet?
+ * @return boolean true if the header has been printed.
+ */
+ public function has_started() {
+ return false;
+ }
+
public function __call($method, $arguments) {
- global $OUTPUT;
+ global $OUTPUT, $PAGE;
// If lib/outputlib.php has been loaded, call it.
- if (!$this->initialising && function_exists('initialise_theme_and_output')) {
- $this->initialising = true;
- initialise_theme_and_output(debug_backtrace());
- if (!($OUTPUT instanceof bootstrap_renderer)) {
- return call_user_func_array(array($OUTPUT, $method), $arguments);
- }
+ if (!empty($PAGE)) {
+ $PAGE->initialise_theme_and_output();
+ return call_user_func_array(array($OUTPUT, $method), $arguments);
}
$this->initialising = true;
@@ -782,6 +813,8 @@ class bootstrap_renderer {
*/
public static function early_error($message, $moreinfourl, $link, $backtrace,
$debuginfo = null, $showerrordebugwarning = false) {
+ global $CFG;
+
// In the name of protocol correctness, monitoring and performance
// profiling, set the appropriate error headers for machine comsumption
if (isset($_SERVER['SERVER_PROTOCOL'])) {
diff --git a/lib/weblib.php b/lib/weblib.php
index 16886e86c1f..a2d9f38fdc2 100644
--- a/lib/weblib.php
+++ b/lib/weblib.php
@@ -2357,256 +2357,6 @@ function send_headers($contenttype, $cacheable = true) {
@header('Accept-Ranges: none');
}
-/**
- * Print a standard header
- *
- * @param string $title Appears at the top of the window
- * @param string $heading Appears at the top of the page
- * @param string $navigation Array of $navlinks arrays (keys: name, link, type) for use as breadcrumbs links
- * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
- * @param string $meta Meta tags to be added to the header
- * @param boolean $cache Should this page be cacheable?
- * @param string $button HTML code for a button (usually for module editing)
- * @param string $menu HTML code for a popup menu
- * @param boolean $usexml use XML for this page
- * @param string $bodytags This text will be included verbatim in the tag (useful for onload() etc)
- * @param bool $return If true, return the visible elements of the header instead of echoing them.
- * @return string|void If return=true then string else void
- */
-function print_header_old($title='', $heading='', $navigation='', $focus='',
- $meta='', $cache=true, $button=' ', $menu='',
- $usexml=false, $bodytags='', $return=false) {
-
- global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $COURSE, $PAGE;
-
- if (gettype($navigation) == 'string' && strlen($navigation) != 0 && $navigation != 'home') {
- debugging("print_header() was sent a string as 3rd ($navigation) parameter. "
- . "This is deprecated in favour of an array built by build_navigation(). Please upgrade your code.", DEBUG_DEVELOPER);
- }
-
- $PAGE->set_state(moodle_page::STATE_PRINTING_HEADER);
-
- $heading = format_string($heading); // Fix for MDL-8582
-
- if (CLI_SCRIPT) {
- $output = $heading."\n";
- if ($return) {
- return $output;
- } else {
- echo $output;
- return;
- }
- }
-
-/// Add the required stylesheets
- $stylesheetshtml = '';
- foreach ($CFG->stylesheets as $stylesheet) {
- $stylesheetshtml .= ''."\n";
- }
- $meta = $stylesheetshtml.$meta;
-
-
-/// Add the meta page from the themes if any were requested
-
- $metapage = '';
-
- if (!isset($THEME->standardmetainclude) || $THEME->standardmetainclude) {
- ob_start();
- include_once($CFG->dirroot.'/theme/standard/meta.php');
- $metapage .= ob_get_contents();
- ob_end_clean();
- }
-
- if ($THEME->parent && (!isset($THEME->parentmetainclude) || $THEME->parentmetainclude)) {
- if (file_exists($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php')) {
- ob_start();
- include_once($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php');
- $metapage .= ob_get_contents();
- ob_end_clean();
- }
- }
-
- if (!isset($THEME->metainclude) || $THEME->metainclude) {
- if (file_exists($CFG->dirroot.'/theme/'.current_theme().'/meta.php')) {
- ob_start();
- include_once($CFG->dirroot.'/theme/'.current_theme().'/meta.php');
- $metapage .= ob_get_contents();
- ob_end_clean();
- }
- }
-
- $meta = $meta."\n".$metapage;
- $meta .= $PAGE->requires->get_head_code();
-
-/// Set up some navigation variables
-
- if (is_newnav($navigation)){
- $home = false;
- } else {
- if ($navigation == 'home') {
- $home = true;
- $navigation = '';
- } else {
- $home = false;
- }
- }
-
-/// This is another ugly hack to make navigation elements available to print_footer later
- $THEME->title = $title;
- $THEME->heading = $heading;
- $THEME->navigation = $navigation;
- $THEME->button = $button;
- $THEME->menu = $menu;
- $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
-
- if ($button == '') {
- $button = ' ';
- }
-
- if (!empty($CFG->maintenance_enabled)) {
- $button = ''.get_string('maintenancemode', 'admin').' '.$button;
- if(!empty($title)) {
- $title .= ' - ';
- }
- $title .= get_string('maintenancemode', 'admin');
- }
-
- if (!$menu and $navigation) {
- if (empty($CFG->loginhttps)) {
- $wwwroot = $CFG->wwwroot;
- } else {
- $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
- }
- $menu = user_login_string($COURSE);
- }
-
- if (isset($SESSION->justloggedin)) {
- unset($SESSION->justloggedin);
- if (!empty($CFG->displayloginfailures)) {
- if (!empty($USER->username) and $USER->username != 'guest') {
- if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
- $menu .= ' ';
- if (empty($count->accounts)) {
- $menu .= get_string('failedloginattempts', '', $count);
- } else {
- $menu .= get_string('failedloginattemptsall', '', $count);
- }
- if (has_capability('coursereport/log:view', get_context_instance(CONTEXT_SYSTEM))) {
- $menu .= ' ('.get_string('logs').')';
- }
- $menu .= '';
- }
- }
- }
- }
-
-
- $meta = '' .
- "\n" . $meta . "\n";
- if (!$usexml) {
- @header('Content-Type: text/html; charset=utf-8');
- }
- @header('Content-Script-Type: text/javascript');
- @header('Content-Style-Type: text/css');
-
- //Accessibility: added the 'lang' attribute to $direction, used in theme tag.
- $direction = get_html_lang($dir=true);
-
- if ($cache) { // Allow caching on "back" (but not on normal clicks)
- @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
- @header('Pragma: no-cache');
- @header('Expires: ');
- } else { // Do everything we can to always prevent clients and proxies caching
- @header('Cache-Control: no-store, no-cache, must-revalidate');
- @header('Cache-Control: post-check=0, pre-check=0', false);
- @header('Pragma: no-cache');
- @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
- @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
-
- $meta .= "\n";
- $meta .= "\n";
- }
- @header('Accept-Ranges: none');
-
- $currentlanguage = current_language();
-
- if (empty($usexml)) {
- $direction = ' xmlns="http://www.w3.org/1999/xhtml"'. $direction; // See debug_header
- } else {
- $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
- if(!$mathplayer) {
- header('Content-Type: application/xhtml+xml');
- }
- echo ''."\n";
- if (!empty($CFG->xml_stylesheets)) {
- $stylesheets = explode(';', $CFG->xml_stylesheets);
- foreach ($stylesheets as $stylesheet) {
- echo 'wwwroot .'/'. $stylesheet .'" ?>' . "\n";
- }
- }
- echo ''."\n";
- $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
- xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
- xmlns:xlink=\"http://www.w3.org/1999/xlink\"
- $direction";
- if($mathplayer) {
- $meta .= ''."\n";
- $meta .= '' . "\n";
- }
- }
-
- // Clean up the title
-
- $title = format_string($title); // fix for MDL-8582
- $title = str_replace('"', '"', $title);
-
- // Create class and id for this page
- $pageid = $PAGE->pagetype;
- $pageclass = $PAGE->bodyclasses;
- $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
-
- ob_start();
- include($CFG->header);
- $output = ob_get_contents();
- ob_end_clean();
-
- // container debugging info
- $THEME->open_header_containers = open_containers();
-
- // Skip to main content, see skip_main_destination().
- if ($pageid=='course-view' or $pageid=='site-index' or $pageid=='course-index') {
- $skiplink = ''.get_string('tocontent', 'access').'';
- if (! preg_match('/(.*