mirror of
https://github.com/moodle/moodle.git
synced 2025-08-06 01:16:44 +02:00
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.
This commit is contained in:
parent
0456fc1ac4
commit
b70094743a
37 changed files with 1646 additions and 1531 deletions
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* <code>
|
||||
* \@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;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* The lines in the CSS files using CSS constants look like:
|
||||
* <code>
|
||||
* 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;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
?>
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'."\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
|
||||
/// <head></head> 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
|
||||
/// <head></head> 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
|
||||
/// <head></head> 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:
|
||||
* <code>
|
||||
* \@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;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* The lines in the CSS files using CSS constants look like:
|
||||
* <code>
|
||||
* 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;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @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";
|
||||
}
|
||||
}
|
211
lib/pagelib.php
211
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']);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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'])) {
|
||||
|
|
826
lib/weblib.php
826
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 <body> 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 .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\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 = '<a href="'.$CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=maintenancemode">'.get_string('maintenancemode', 'admin').'</a> '.$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 .= ' <font size="1">';
|
||||
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 .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
|
||||
'?chooselog=1&id=1&modid=site_errors">'.get_string('logs').'</a>)';
|
||||
}
|
||||
$menu .= '</font>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$meta = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' .
|
||||
"\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 <html> 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 http-equiv=\"pragma\" content=\"no-cache\" />";
|
||||
$meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
|
||||
}
|
||||
@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 '<?xml version="1.0" ?>'."\n";
|
||||
if (!empty($CFG->xml_stylesheets)) {
|
||||
$stylesheets = explode(';', $CFG->xml_stylesheets);
|
||||
foreach ($stylesheets as $stylesheet) {
|
||||
echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
|
||||
}
|
||||
}
|
||||
echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
|
||||
if (!empty($CFG->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 .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
|
||||
$meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
|
||||
$meta .= '</object>'."\n";
|
||||
$meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\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 = '<a class="skip" href="#maincontent">'.get_string('tocontent', 'access').'</a>';
|
||||
if (! preg_match('/(.*<div[^>]+id="page"[^>]*>)(.*)/s', $output, $matches)) {
|
||||
preg_match('/(.*<body.*?>)(.*)/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</div>"; // content div
|
||||
}
|
||||
$output .= "\n</div>\n" . $PAGE->requires->get_end_code() . "</body>\n</html>"; // 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 = '<div class="sitelink">'.
|
||||
'<a title="Moodle '. $CFG->release .'" href="http://moodle.org/">'.
|
||||
'<img style="width:100px;height:30px" src="'.$CFG->wwwroot.'/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
|
||||
$home = true;
|
||||
|
||||
} else if ($course === 'upgrade') {
|
||||
$home = false;
|
||||
$loggedinas = '';
|
||||
$homelink = '<div class="sitelink">'.
|
||||
'<a title="Moodle '. $CFG->target_release .'" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">'.
|
||||
'<img style="width:100px;height:30px" src="'.$CFG->wwwroot.'/pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
|
||||
|
||||
} else {
|
||||
$homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.
|
||||
'/course/view.php?id='.$course->id.'">'.format_string($course->shortname).'</a></div>';
|
||||
$home = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
$course = $SITE; // Set course as site course by default
|
||||
$homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
|
||||
$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 <script> tags just inside </body> to maintain validity.
|
||||
$output = str_replace('</body>', $PAGE->requires->get_end_code() . '</body>', $output);
|
||||
|
||||
$PAGE->set_state(moodle_page::STATE_DONE);
|
||||
|
||||
if ($return) {
|
||||
return $output;
|
||||
} else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the current theme
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @global object
|
||||
* @global object
|
||||
* @global string
|
||||
* @return string
|
||||
*/
|
||||
function current_theme() {
|
||||
global $CFG, $USER, $SESSION, $COURSE, $SCRIPT;
|
||||
|
||||
if (empty($CFG->themeorder)) {
|
||||
$themeorder = array('page', 'course', 'category', 'session', 'user', 'site');
|
||||
} else {
|
||||
$themeorder = $CFG->themeorder;
|
||||
}
|
||||
|
||||
if (isloggedin() and isset($CFG->mnet_localhost_id) and $USER->mnethostid != $CFG->mnet_localhost_id) {
|
||||
require_once($CFG->dirroot.'/mnet/peer.php');
|
||||
$mnet_peer = new mnet_peer();
|
||||
$mnet_peer->set_id($USER->mnethostid);
|
||||
}
|
||||
|
||||
$theme = '';
|
||||
foreach ($themeorder as $themetype) {
|
||||
|
||||
if (!empty($theme)) continue;
|
||||
|
||||
switch ($themetype) {
|
||||
case 'page': // Page theme is for special page-only themes set by code
|
||||
if (!empty($CFG->pagetheme)) {
|
||||
$theme = $CFG->pagetheme;
|
||||
}
|
||||
break;
|
||||
case 'course':
|
||||
if (!empty($CFG->allowcoursethemes) and !empty($COURSE->theme)) {
|
||||
$theme = $COURSE->theme;
|
||||
}
|
||||
break;
|
||||
case 'category':
|
||||
if (!empty($CFG->allowcategorythemes)) {
|
||||
/// Nasty hack to check if we're in a category page
|
||||
if ($SCRIPT == '/course/category.php') {
|
||||
global $id;
|
||||
if (!empty($id)) {
|
||||
$theme = current_category_theme($id);
|
||||
}
|
||||
/// Otherwise check if we're in a course that has a category theme set
|
||||
} else if (!empty($COURSE->category)) {
|
||||
$theme = current_category_theme($COURSE->category);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'session':
|
||||
if (!empty($SESSION->theme)) {
|
||||
$theme = $SESSION->theme;
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
if (!empty($CFG->allowuserthemes) and !empty($USER->theme)) {
|
||||
if (isloggedin() and $USER->mnethostid != $CFG->mnet_localhost_id && $mnet_peer->force_theme == 1 && $mnet_peer->theme != '') {
|
||||
$theme = $mnet_peer->theme;
|
||||
} else {
|
||||
$theme = $USER->theme;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'site':
|
||||
if (isloggedin() and isset($CFG->mnet_localhost_id) and $USER->mnethostid != $CFG->mnet_localhost_id && $mnet_peer->force_theme == 1 && $mnet_peer->theme != '') {
|
||||
$theme = $mnet_peer->theme;
|
||||
} else {
|
||||
$theme = $CFG->theme;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/// A final check in case 'site' was not included in $CFG->themeorder
|
||||
if (empty($theme)) {
|
||||
$theme = $CFG->theme;
|
||||
}
|
||||
|
||||
return $theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the category theme if one exists, otherwise checks the parent categories.
|
||||
* Recursive function.
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @param integer $categoryid id of the category to check
|
||||
* @return string theme name
|
||||
*/
|
||||
function current_category_theme($categoryid=0) {
|
||||
global $COURSE, $DB;
|
||||
|
||||
/// Use the COURSE global if the categoryid not set
|
||||
if (empty($categoryid)) {
|
||||
if (!empty($COURSE->category)) {
|
||||
$categoryid = $COURSE->category;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the current category
|
||||
if ($category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
|
||||
|
||||
/// Return the category theme if it exists
|
||||
if (!empty($category->theme)) {
|
||||
return $category->theme;
|
||||
|
||||
/// Otherwise try the parent category if one exists
|
||||
} else if (!empty($category->parent)) {
|
||||
return current_category_theme($category->parent);
|
||||
}
|
||||
|
||||
/// Return false if we can't find the category record
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called by stylesheets to set up the header
|
||||
* approriately as well as the current path
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @uses PARAM_SAFEDIR
|
||||
* @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, $THEME;
|
||||
|
||||
// Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
|
||||
$lastmodified = time();
|
||||
|
||||
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
|
||||
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
|
||||
header('Cache-Control: max-age='. $lifetime);
|
||||
header('Pragma: ');
|
||||
header('Content-type: text/css'); // Correct MIME type
|
||||
|
||||
$DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
|
||||
|
||||
if (empty($themename)) {
|
||||
$themename = current_theme(); // So we have something. Normally not needed.
|
||||
} else {
|
||||
$themename = clean_param($themename, PARAM_SAFEDIR);
|
||||
}
|
||||
|
||||
theme_setup($themename);
|
||||
|
||||
if (!empty($forceconfig)) { // Page wants to use the config from this theme instead
|
||||
unset($THEME);
|
||||
include($CFG->themedir.'/'.$forceconfig.'/'.'config.php');
|
||||
}
|
||||
|
||||
/// If this is the standard theme calling us, then find out what sheets we need
|
||||
if ($themename == 'standard') {
|
||||
if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
|
||||
$THEME->sheets = $DEFAULT_SHEET_LIST;
|
||||
} else if (empty($THEME->standardsheets)) { // We can stop right now!
|
||||
echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
|
||||
exit;
|
||||
} else { // Use the provided subset only
|
||||
$THEME->sheets = $THEME->standardsheets;
|
||||
}
|
||||
|
||||
/// If we are a parent theme, then check for parent definitions
|
||||
} else if (!empty($THEME->parent) && $themename == $THEME->parent) {
|
||||
if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) { // Use all the sheets we have
|
||||
$THEME->sheets = $DEFAULT_SHEET_LIST;
|
||||
} else if (empty($THEME->parentsheets)) { // We can stop right now!
|
||||
echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
|
||||
exit;
|
||||
} else { // Use the provided subset only
|
||||
$THEME->sheets = $THEME->parentsheets;
|
||||
}
|
||||
}
|
||||
|
||||
/// Work out the last modified date for this theme
|
||||
foreach ($THEME->sheets as $sheet) {
|
||||
if (file_exists($CFG->themedir.'/'.$themename.'/'.$sheet.'.css')) {
|
||||
$sheetmodified = filemtime($CFG->themedir.'/'.$themename.'/'.$sheet.'.css');
|
||||
if ($sheetmodified > $lastmodified) {
|
||||
$lastmodified = $sheetmodified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list of all the files we want to include
|
||||
$files = array();
|
||||
|
||||
foreach ($THEME->sheets as $sheet) {
|
||||
$files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
|
||||
}
|
||||
|
||||
if ($themename == 'standard') { // Add any standard styles included in any modules
|
||||
if (!empty($THEME->modsheets)) { // Search for styles.php within activity modules
|
||||
$mods = get_plugin_list('mod');
|
||||
foreach ($mods as $mod => $moddir) {
|
||||
if (file_exists($moddir.'/styles.php')) {
|
||||
$files[] = array($moddir, 'styles.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($THEME->blocksheets)) { // Search for styles.php within block modules
|
||||
$mods = get_plugin_list('blocks');
|
||||
foreach ($mods as $mod => $moddir) {
|
||||
if (file_exists($moddir.'/styles.php')) {
|
||||
$files[] = array($moddir, 'styles.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($THEME->courseformatsheets) || $THEME->courseformatsheets) { // Search for styles.php in course formats
|
||||
$mods = get_plugin_list('format');
|
||||
foreach ($mods as $mod => $moddir) {
|
||||
if (file_exists($moddir.'/styles.php')) {
|
||||
$files[] = array($moddir, 'styles.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($THEME->gradereportsheets) || $THEME->gradereportsheets) { // Search for styles.php in grade reports
|
||||
$reports = get_plugin_list('gradereport');
|
||||
foreach ($reports as $report => $reportdir) {
|
||||
if (file_exists($reportdir.'/styles.php')) {
|
||||
$files[] = array($reportdir, 'styles.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($THEME->langsheets)) { // Search for styles.php within the current language
|
||||
if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
|
||||
$files[] = array($CFG->dirroot, 'lang/'.$lang.'/styles.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($files) {
|
||||
/// Produce a list of all the files first
|
||||
echo '/**************************************'."\n";
|
||||
echo ' * THEME NAME: '.$themename."\n *\n";
|
||||
echo ' * Files included in this sheet:'."\n *\n";
|
||||
foreach ($files as $file) {
|
||||
echo ' * '.$file[1]."\n";
|
||||
}
|
||||
echo ' **************************************/'."\n\n";
|
||||
|
||||
|
||||
/// check if csscobstants is set
|
||||
if (!empty($THEME->cssconstants)) {
|
||||
require_once("$CFG->libdir/cssconstants.php");
|
||||
/// Actually collect all the files in order.
|
||||
$css = '';
|
||||
foreach ($files as $file) {
|
||||
$css .= '/***** '.$file[1].' start *****/'."\n\n";
|
||||
$css .= file_get_contents($file[0].'/'.$file[1]);
|
||||
$ccs .= '/***** '.$file[1].' end *****/'."\n\n";
|
||||
}
|
||||
/// replace css_constants with their values
|
||||
echo replace_cssconstants($css);
|
||||
} else {
|
||||
/// Actually output all the files in order.
|
||||
if (empty($CFG->CSSEdit) && empty($THEME->CSSEdit)) {
|
||||
foreach ($files as $file) {
|
||||
echo '/***** '.$file[1].' start *****/'."\n\n";
|
||||
@include_once($file[0].'/'.$file[1]);
|
||||
echo '/***** '.$file[1].' end *****/'."\n\n";
|
||||
}
|
||||
} else {
|
||||
foreach ($files as $file) {
|
||||
echo '/* @group '.$file[1].' */'."\n\n";
|
||||
if (strstr($file[1], '.css') !== FALSE) {
|
||||
echo '@import url("'.$CFG->themewww.'/'.$file[1].'");'."\n\n";
|
||||
} else {
|
||||
@include_once($file[0].'/'.$file[1]);
|
||||
}
|
||||
echo '/* @end */'."\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $CFG->themewww.'/'.$themename; // Only to help old themes (1.4 and earlier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the global variables related to theme
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @global object Apparently not used here
|
||||
* @global object Apparently not used here
|
||||
* @global object
|
||||
* @global object
|
||||
* @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) {
|
||||
/// Sets up global variables related to themes
|
||||
|
||||
global $CFG, $THEME, $SESSION, $USER, $HTTPSPAGEREQUIRED, $PAGE;
|
||||
|
||||
/// Do not mess with THEME if header already printed - this would break all the extra stuff in global $THEME from print_header()!!
|
||||
if ($PAGE->headerprinted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($theme)) {
|
||||
$theme = current_theme();
|
||||
}
|
||||
|
||||
/// If the theme doesn't exist for some reason then revert to standardwhite
|
||||
if (!file_exists($CFG->themedir .'/'. $theme .'/config.php')) {
|
||||
$CFG->theme = $theme = 'standardwhite';
|
||||
}
|
||||
|
||||
/// Load up the theme config
|
||||
$THEME = NULL; // Just to be sure
|
||||
include($CFG->themedir .'/'. $theme .'/config.php'); // Main config for current theme
|
||||
$THEME->name = $theme;
|
||||
$THEME->dir = $CFG->themedir .'/'. $theme;
|
||||
|
||||
/// Put together the parameters
|
||||
if (!$params) {
|
||||
$params = array();
|
||||
}
|
||||
|
||||
if ($theme != $CFG->theme) {
|
||||
$params[] = 'forceconfig='.$theme;
|
||||
}
|
||||
|
||||
/// Force language too if required
|
||||
if (!empty($THEME->langsheets)) {
|
||||
$params[] = 'lang='.current_language();
|
||||
}
|
||||
|
||||
/// Convert params to string
|
||||
if ($params) {
|
||||
$paramstring = '?'.implode('&', $params);
|
||||
} else {
|
||||
$paramstring = '';
|
||||
}
|
||||
|
||||
/// Set up image paths
|
||||
if(isset($CFG->smartpix) && $CFG->smartpix==1) {
|
||||
if($CFG->slasharguments) { // Use this method if possible for better caching
|
||||
$extra='';
|
||||
} else {
|
||||
$extra='?file=';
|
||||
}
|
||||
|
||||
$CFG->pixpath = $CFG->wwwroot. '/pix/smartpix.php'.$extra.'/'.$theme;
|
||||
$CFG->modpixpath = $CFG->wwwroot .'/pix/smartpix.php'.$extra.'/'.$theme.'/mod';
|
||||
} else if (empty($THEME->custompix)) { // Could be set in the above file
|
||||
$CFG->pixpath = $CFG->wwwroot .'/pix';
|
||||
$CFG->modpixpath = $CFG->wwwroot .'/mod';
|
||||
} else {
|
||||
$CFG->pixpath = $CFG->themewww .'/'. $theme .'/pix';
|
||||
$CFG->modpixpath = $CFG->themewww .'/'. $theme .'/pix/mod';
|
||||
}
|
||||
|
||||
/// Define stylesheet loading order
|
||||
$CFG->stylesheets = array();
|
||||
if ($theme != 'standard') { /// The standard sheet is always loaded first
|
||||
$CFG->stylesheets[] = $CFG->themewww.'/standard/styles.php'.$paramstring;
|
||||
}
|
||||
if (!empty($THEME->parent)) { /// Parent stylesheets are loaded next
|
||||
$CFG->stylesheets[] = $CFG->themewww.'/'.$THEME->parent.'/styles.php'.$paramstring;
|
||||
}
|
||||
$CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/styles.php'.$paramstring;
|
||||
|
||||
/// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
|
||||
if (!empty($HTTPSPAGEREQUIRED)) {
|
||||
$CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
|
||||
$CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
|
||||
$CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
|
||||
foreach ($CFG->stylesheets as $key => $stylesheet) {
|
||||
$CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
|
||||
}
|
||||
}
|
||||
|
||||
// RTL support - only for RTL languages, add RTL CSS
|
||||
if (get_string('thisdirection') == 'rtl') {
|
||||
$CFG->stylesheets[] = $CFG->themewww.'/standard/rtl.css'.$paramstring;
|
||||
$CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/rtl.css'.$paramstring;
|
||||
}
|
||||
|
||||
// Support legacy themes, by setting sensible defaults for some of the new
|
||||
// properties that were introduced in Moodle 2.0.
|
||||
if (empty($THEME->rendererfactory)) {
|
||||
if (!empty($THEME->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.', DEBUG_DEVELOPER);
|
||||
$THEME->rendererfactory = 'custom_corners_renderer_factory';
|
||||
} else {
|
||||
$THEME->rendererfactory = 'standard_renderer_factory';
|
||||
}
|
||||
}
|
||||
if (empty($THEME->blockregions)) {
|
||||
$THEME->blockregions = array('side-pre', 'side-post');
|
||||
}
|
||||
if (empty($THEME->defaultblockregion)) {
|
||||
$THEME->defaultblockregion = 'side-post';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns text to be displayed to the user which reflects their login status
|
||||
*
|
||||
|
@ -5729,7 +4916,7 @@ function redirect($url, $message='', $delay=-1) {
|
|||
$encodedurl = preg_replace('/^.*href="([^"]*)".*$/', "\\1", clean_text('<a href="'.$encodedurl.'" />'));
|
||||
|
||||
$message .= '<a href="'. $encodedurl .'">'. get_string('continue') .'</a>';
|
||||
|
||||
|
||||
$CFG->docroot = false; // to prevent the link to moodle docs from being displayed on redirect page.
|
||||
echo $OUTPUT->redirect($encodedurl, $message, $delay);
|
||||
die();
|
||||
|
@ -6349,18 +5536,15 @@ function print_arrow($direction='up', $strsort=null, $return=false) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns boolean true if the current language is right-to-left (Hebrew, Arabic etc)
|
||||
*
|
||||
* @staticvar bool $result
|
||||
* @return bool
|
||||
* @return boolean true if the current language is right-to-left (Hebrew, Arabic etc)
|
||||
*/
|
||||
function right_to_left() {
|
||||
static $result;
|
||||
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
if (!isset($result)) {
|
||||
$result = get_string('thisdirection') == 'rtl';
|
||||
}
|
||||
return $result = (get_string('thisdirection') == 'rtl');
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue