From b70094743a9c2e71a1844f23d0bd71a0b49644ab Mon Sep 17 00:00:00 2001 From: tjhunt Date: Wed, 1 Jul 2009 05:54:26 +0000 Subject: [PATCH] themes: MDL-19077 change how the theme is initialised and CSS is served. This is part of http://docs.moodle.org/en/Development:Theme_engines_for_Moodle%3F $THEME is now initialised at the same time as $OUTPUT. Old functions like theme_setup are deprecated in favour of methods on $PAGE. There is a new theme_config class in outputlib.php that deals with loading the theme config.php file. CSS used to be served by themes styles.php files calling a function in weblib.php. Now it works by each theme's styles.php file doing $themename = basename(dirname(__FILE__)); require_once(dirname(__FILE__) . '/../../theme/styles.php'); which is less code to be copied into each theme. (Old-style styles.php files still work thanks to some code in deprecatedlib.php.) Admin UI for choosing a theme cleaned up. A couple of theme-specific hard-coded hacks like $THEME->cssconstants and $THEME->CSSEdit have been replaced by a more generic $THEME->customcssoutputfunction hook. See examples at the end of outputlib.php Also: * Fix setting the theme in the URL, which seems to have been broken since 1.9. * Fix up errors on a few pages caused by the new initialisation order. * MDL-19097 moodle_page::set_course should not set $COURSE unless it is $PAGE. * httpsrequired() from moodlelib.php moved to $PAGE->https_required(). * Move has_started() method to the renderer base class. * Further fixes to display of early errors. * Remove print_header/footer_old from weblib. I did not mean to commit them before. --- admin/settings/appearance.php | 1 + config-dist.php | 4 +- course/category.php | 5 - lang/en_utf8/admin.php | 2 + lib/adminlib.php | 14 +- lib/blocklib.php | 4 + lib/cssconstants.php | 102 ---- lib/deprecatedlib.php | 49 ++ lib/moodlelib.php | 20 +- lib/outputlib.php | 505 +++++++++++++++++-- lib/pagelib.php | 211 +++++++- lib/setup.php | 44 +- lib/setuplib.php | 69 ++- lib/weblib.php | 826 +------------------------------- login/index.php | 2 +- theme/anomaly/styles.php | 42 +- theme/chameleon/styles.php | 44 +- theme/colors/styles.php | 44 +- theme/cornflower/styles.php | 43 +- theme/custom_corners/styles.php | 44 +- theme/formal_white/styles.php | 44 +- theme/index.php | 262 +++++----- theme/lines/styles.php | 44 +- theme/metal/styles.php | 44 +- theme/oceanblue/styles.php | 44 +- theme/orangewhite/styles.php | 44 +- theme/orangewhitepda/styles.php | 44 +- theme/preview.php | 66 +-- theme/standard/styles.php | 44 +- theme/standard/styles_color.css | 12 + theme/standardblue/styles.php | 44 +- theme/standardgreen/styles.php | 44 +- theme/standardlogo/styles.php | 44 +- theme/standardred/styles.php | 44 +- theme/standardwhite/styles.php | 44 +- theme/styles.php | 200 ++++++++ theme/wood/styles.php | 44 +- 37 files changed, 1651 insertions(+), 1536 deletions(-) delete mode 100644 lib/cssconstants.php create mode 100644 theme/styles.php diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index 06e4db76d6..73467fdfee 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 2958a13e1d..f37f466626 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 394a769e36..fb205b3f86 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 54d27d1f55..8677e2d259 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 1d182d6bf3..0b4582f76b 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 6df93bb125..0f505cbb9f 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 382d699db6..0000000000 --- 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 1c322c85b4..27758516e5 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 290580ff4c..4c0fe9913c 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 9187a8c301..246fe3b440 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 +/// part of the page. + + + public $standardmetainclude = true; + + +/// When this is enabled (or not set!) then Moodle will try +/// to include a file meta.php from the standard theme into the +/// part of the page. + + + public $parentmetainclude = false; + +/// When this is enabled (or not set!) then Moodle will try +/// to include a file meta.php from the parent theme into the +/// part of the page. + + + public $navmenuwidth = 50; + +/// You can use this to control the cutoff point for strings +/// in the navmenus (list of activities in popup menu etc) +/// Default is 50 characters wide. + + + public $makenavmenulist = false; + +/// By setting this to true, then you will have access to a +/// new variable in your header.html and footer.html called +/// $navmenulist ... this contains a simple XHTML menu of +/// all activities in the current course, mostly useful for +/// creating popup navigation menus and so on. + + + + public $resource_mp3player_colors = 'bgColour=000000&btnColour=ffffff&btnBorderColour=cccccc&iconColour=000000&iconOverColour=00cc00&trackColour=cccccc&handleColour=ffffff&loaderColour=ffffff&font=Arial&fontColour=3333FF&buffer=10&waitForPlay=no&autoPlay=yes'; + +/// With this you can control the colours of the "big" MP3 player +/// that is used for MP3 resources. + + + public $filter_mediaplugin_colors = 'bgColour=000000&btnColour=ffffff&btnBorderColour=cccccc&iconColour=000000&iconOverColour=00cc00&trackColour=cccccc&handleColour=ffffff&loaderColour=ffffff&waitForPlay=yes'; + +/// ...And this controls the small embedded player + + + public $custompix = false; + +/// If true, then this theme must have a "pix" +/// subdirectory that contains copies of all +/// files from the moodle/pix directory, plus a +/// "pix/mod" directory containing all the icons +/// for all the activity modules. + + +///$THEME->rarrow = '►' //OR '→'; +///$THEME->larrow = '◄' //OR '←'; +///$CFG->block_search_button = link_arrow_right(get_string('search'), $url='', $accesshide=true); +/// +/// Accessibility: Right and left arrow-like characters are +/// used in the breadcrumb trail, course navigation menu +/// (previous/next activity), calendar, and search forum block. +/// +/// If the theme does not set characters, appropriate defaults +/// are set by (lib/weblib.php:check_theme_arrows). The suggestions +/// above are 'silent' in a screen-reader like JAWS. Please DO NOT +/// use < > » - these are confusing for blind users. +//////////////////////////////////////////////////////////////////////////////// + + + public $blockregions = array('side-pre', 'side-post'); + public $defaultblockregion = 'side-post'; +/// Areas where blocks may appear on any page that uses this theme. For each +/// region you list in $THEME->blockregions you must call blocks_print_group +/// with that region id somewhere in header.html or footer.html. +/// defaultblockregion is the region where new blocks will be added, and +/// where any blocks in unrecognised regions will be shown. (Suppose someone +/// added a block when anther theme was selected). +//////////////////////////////////////////////////////////////////////////////// + + /** + * @var string the name of this theme. Set automatically. + */ + public $name; + /** + * @var string the folder where this themes fiels are stored. $CFG->themedir . '/' . $this->name + */ + public $dir; + + public $rendererfactory = 'standard_renderer_factory'; + + /** + * If you want to do custom processing on the CSS before it is output (for + * example, to replace certain variable names with particular values) you can + * give the name of a function here. + * + * There are two functions avaiable that you may wish to use (defined in lib/outputlib.php): + * output_css_replacing_constants + * output_css_for_css_edit + * If you wish to write your own function, use those two as examples, and it + * should be clear what you have to do. + * + * @var string the name of a function. + */ + public $customcssoutputfunction = null; + + /** + * Load the config.php file for a particular theme, and return an instance + * of this class. (That is, this is a factory method.) + * + * @param string $themename the name of the theme. + * @return theme_config an instance of this class. + */ + public static function load($themename) { + global $CFG, $PAGE; + + // We have to use the variable name $THEME (upper case) becuase that + // is what is used in theme config.php files. + + // Set some other standard properties of the theme. + $THEME = new theme_config; + $THEME->name = $themename; + $THEME->dir = $CFG->themedir . '/' . $themename; + + // Load up the theme config + $configfile = $THEME->dir . '/config.php'; + if (!is_readable($configfile)) { + throw new coding_exception('Cannot use theme ' . $themename . + '. The file ' . $configfile . ' does not exist or is not readable.'); + } + include($configfile); + + $THEME->update_legacy_information(); + + return $THEME; + } + + /** + * Set the variable $CFG->pixpath and $CFG->modpixpath to be the right + * ones for this theme. + */ + public function setup_cfg_paths() { + global $CFG; + if (!empty($CFG->smartpix)) { + if ($CFG->slasharguments) { + // Use this method if possible for better caching + $extra = ''; + } else { + $extra = '?file='; + } + $CFG->pixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name; + $CFG->modpixpath = $CFG->httpswwwroot . '/pix/smartpix.php' . $extra . '/' . $this->name . '/mod'; + + } else if (empty($THEME->custompix)) { + $CFG->pixpath = $CFG->httpswwwroot . '/pix'; + $CFG->modpixpath = $CFG->httpswwwroot . '/mod'; + + } else { + $CFG->pixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix'; + $CFG->modpixpath = $CFG->httpsthemewww . '/' . $this->name . '/pix/mod'; + } + } + + /** + * Get the list of stylesheet URLs that need to go in the header for this theme. + * @return array of URLs. + */ + public function get_stylesheet_urls() { + global $CFG; + + // Put together the parameters + $params = '?for=' . $this->name; + + // Stylesheets, in order (standard, parent, this - some of which may be the same). + $stylesheets = array(); + if ($this->name != 'standard' && $this->standardsheets) { + $stylesheets[] = $CFG->httpsthemewww . '/standard/styles.php' . $params; + } + if (!empty($this->parent)) { + $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/styles.php' . $params; + } + + // Pass on the current language, if it will be needed. + if (!empty($this->langsheets)) { + $params .= '&lang=' . current_language(); + } + $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/styles.php' . $params; + + // Additional styles for right-to-left languages. + if (right_to_left()) { + $stylesheets[] = $CFG->httpsthemewww . '/standard/rtl.css'; + + if (!empty($this->parent) && file_exists($CFG->themedir . '/' . $this->parent . '/rtl.css')) { + $stylesheets[] = $CFG->httpsthemewww . '/' . $this->parent . '/rtl.css'; + } + + if (file_exists($this->dir . '/rtl.css')) { + $stylesheets[] = $CFG->httpsthemewww . '/' . $this->name . '/rtl.css'; + } + } + + return $stylesheets; + } + + /** + * This methon looks a the settings that have been loaded, to see whether + * any legacy things are being used, and outputs warning and tries to update + * things to use equivalent newer settings. + */ + protected function update_legacy_information() { + if (!empty($this->customcorners)) { + // $THEME->customcorners is deprecated but we provide support for it via the + // custom_corners_renderer_factory class in lib/deprecatedlib.php + debugging('$THEME->customcorners is deprecated. Please use the new $THEME->rendererfactory ' . + 'to control HTML generation. Please use $this->rendererfactory = \'custom_corners_renderer_factory\'; ' . + 'in your config.php file instead.', DEBUG_DEVELOPER); + $this->rendererfactory = 'custom_corners_renderer_factory'; + } + + if (!empty($this->cssconstants)) { + debugging('$THEME->cssconstants is deprecated. Please use ' . + '$THEME->customcssoutputfunction = \'output_css_replacing_constants\'; ' . + 'in your config.php file instead.', DEBUG_DEVELOPER); + $this->customcssoutputfunction = 'output_css_replacing_constants'; + } + + if (!empty($this->CSSEdit)) { + debugging('$THEME->CSSEdit is deprecated. Please use ' . + '$THEME->customcssoutputfunction = \'output_css_for_css_edit\'; ' . + 'in your config.php file instead.', DEBUG_DEVELOPER); + $this->customcssoutputfunction = 'output_css_for_css_edit'; + } + } +} + + /** * Base class for classes representing HTML elements, like moodle_select_menu. * @@ -1753,3 +2051,114 @@ class cli_core_renderer extends moodle_core_renderer { } } + +/** + * Output CSS while replacing constants/variables. See MDL-6798 for details + * + * Information from Urs Hunkler: + * + * This is an adaptation of Shaun Inman's "CSS Server-side Constants" for Moodle. + * http://www.shauninman.com/post/heap/2005/08/09/css_constants + * + * To use, specify $THEME->customcssoutputfunction = 'output_css_replacing_constants'; + * in your theme's config.php file. + * + * 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; + * } + * + * + * @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 a866db125d..e1693380ff 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 4131b03f7e..98785ab461 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 240e7df1b3..8c8d354dac 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 16886e86c1..a2d9f38fdc 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 'xml_doctype_extra)) { - echo ' plus '. $CFG->xml_doctype_extra; - } - echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\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"; - $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 = ''; - if (! preg_match('/(.*]+id="page"[^>]*>)(.*)/s', $output, $matches)) { - preg_match('/(.*)(.*)/s', $output, $matches); - } - $output = $matches[1]."\n". $skiplink .$matches[2]; - } - - $output = force_strict_header($output); - - if (!empty($CFG->messaging)) { - $output .= message_popup_window(); - } - - // Add in any extra JavaScript libraries that occurred during the header - $output .= $PAGE->requires->get_top_of_body_code(); - - $PAGE->set_state(moodle_page::STATE_IN_BODY); - - if ($return) { - return $output; - } else { - echo $output; - } -} - /** * This version of print_header is simpler because the course name does not have to be * provided explicitly in the strings. It can be used on the site page as in courses @@ -2656,569 +2406,6 @@ function print_header_simple($title='', $heading='', $navigation='', $focus='', } } - -/** - * Can provide a course object to make the footer contain a link to - * to the course home page, otherwise the link will go to the site home - * - * @global object - * @global object - * @global object - * @global object Apparently not used in this function - * @global string - * @global object - * @param mixed $course course object, used for course link button or - * 'none' means no user link, only docs link - * 'empty' means nothing printed in footer - * 'home' special frontpage footer - * @param object $usercourse course used in user link - * @param boolean $return output as string - * @return mixed string or void - */ -function print_footer_old($course=NULL, $usercourse=NULL, $return=false) { - global $USER, $CFG, $THEME, $COURSE, $SITE, $PAGE; - - if (defined('ADMIN_EXT_HEADER_PRINTED') and !defined('ADMIN_EXT_FOOTER_PRINTED')) { - admin_externalpage_print_footer(); - return; - } - - $PAGE->set_state(moodle_page::STATE_PRINTING_FOOTER); - -/// Course links or special footer - if ($course) { - if ($course === 'empty') { - // special hack - sometimes we do not want even the docs link in footer - $output = ''; - if (!empty($THEME->open_header_containers)) { - for ($i=0; $i<$THEME->open_header_containers; $i++) { - $output .= print_container_end_all(); // containers opened from header - } - } else { - //1.8 theme compatibility - $output .= "\n"; // content div - } - $output .= "\n\n" . $PAGE->requires->get_end_code() . "\n"; // close page div started in header - if ($return) { - return $output; - } else { - echo $output; - return; - } - - } else if ($course === 'none') { // Don't print any links etc - $homelink = ''; - $loggedinas = ''; - $home = false; - - } else if ($course === 'home') { // special case for site home page - please do not remove - $course = $SITE; - $homelink = ''; - $home = true; - - } else if ($course === 'upgrade') { - $home = false; - $loggedinas = ''; - $homelink = ''; - - } else { - $homelink = ''; - $home = false; - } - - } else { - $course = $SITE; // Set course as site course by default - $homelink = ''; - $home = false; - } - -/// Set up some other navigation links (passed from print_header by ugly hack) - $menu = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : ''; - $title = isset($THEME->title) ? $THEME->title : ''; - $button = isset($THEME->button) ? $THEME->button : ''; - $heading = isset($THEME->heading) ? $THEME->heading : ''; - $navigation = isset($THEME->navigation) ? $THEME->navigation : ''; - $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : ''; - - -/// Set the user link if necessary - if (!$usercourse and is_object($course)) { - $usercourse = $course; - } - - if (!isset($loggedinas)) { - $loggedinas = user_login_string($usercourse, $USER); - } - - if ($loggedinas == $menu) { - $menu = ''; - } - -/// there should be exactly the same number of open containers as after the header - if ($THEME->open_header_containers != open_containers()) { - debugging('Unexpected number of open containers: '.open_containers().', expecting '.$THEME->open_header_containers, DEBUG_DEVELOPER); - } - -/// Provide some performance info if required - $performanceinfo = ''; - if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) { - $perf = get_performance_info(); - if (defined('MDL_PERFTOLOG') && !function_exists('register_shutdown_function')) { - error_log("PERF: " . $perf['txt']); - } - if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) { - $performanceinfo = $perf['html']; - } - } - -/// Include the actual footer file - - ob_start(); - include($CFG->footer); - $output = ob_get_contents(); - ob_end_clean(); - - // Put the end of page